martes, 4 de diciembre de 2012

Algunos detalles de implementación con Omron

Relacionado con las clases en Sistemas de Control Automático de el Máster en Automática y Robótica, la práctica consiste en realizar la programación de un grupo de presión de cuatro bombas. A raíz de esto se han comentado algunas estructuras de programación en clase para gestionar ciertas cosas. A continuación expongo unos ejemplos de como gestionar estas estructuras.

Registro de desplazamiento, con este registro se apunta a la bomba a encender, con otro igual a la siguiente bomba a apagar y por último habrá otro para gestionar la bomba que deberá arrancar por variador en el siguiente ciclo de trabajo.


Respecto a la gestión de tareas, muchos tienen aún dudas de como funcionan estas estructuras. Los bloques TKOF y TKON lo que hacen es activar y desactivar unos registros especiales (marcas) llamadas TK00, TK01 y así sucesivamente. Cada una de ellas está asociada al estado de un "programa" y cuando digo programa me refiero a lo que en CX-Programmer se considera programa (ver árbol de proyecto a la izquierda de CX-Programmer). De manera que estas mismas marcas pueden ser empleadas para activar una determinada sección de código como en el ejemplo que se muestra a continuación:


En este ejemplo, la marca TK01 (es decir cuando el programa con tarea de prioridad 1 está en ejecución), se activa una sección de código donde se detiene el programa con tarea de prioridad 0 y se ejecuta el programa con tarea de prioridad 3.

Sobre la gestión de arranques y paradas de bombas, mi consejo es emplear un biestable RS utilizando la función KEEP. Como se muestra en en la figura, la linea de la primera conexión del biestable contiene la condidicón de arranque de un contactor de la bomba 1, y la segunda linea contiene la condición de parada del mismo contactor. (No es necesario tener las mismas condiciones, sólo son a nivel ilustrativo, no deben tomarse al pie de la letra).




lunes, 3 de diciembre de 2012

Comunicaciones Modbus RTU con Automatas OMRON CP1L

En este articulo, se pretende dar una guía de como montar un sistema de gestión de comunicaciones con autómatas CP1L, que permita gestionar el envío y recepción de información a través de protocolo modbus RTU, de forma sencilla y estandarizada. Permite la gestión de las comunicaciones de una forma centralizada y extensible, por lo que sin mucha complicación es posible extender el uso para nuevos tipos de tramas. En este enlace se puede encontrar un documento sobre como realizar las comunicaciones modbus RTU de forma sencilla con un autómata CP1L. Este documento es un extracto de la guía completa de dicho autómata. Aquí veremos los conceptos más importantes y como aplicarlos a un caso particular.

En primer lugar comenzaremos viendo como configurar el puerto de comunicaciones del autómata. Si se realiza un doble click en el menú de la izquierda sobre el ítem del árbol de proyecto "Configuración" aparecerá el cuadro de dialogo de configuraciones generales del autómata. Una vez ahí, se elige la pestaña "Configuración de entrada Puerto Serie 1" ó "Configuración de entrada Puerto Serie 2" según corresponda y se selecciona la opción Configuración Personalizada. Una vez ahí, hay que configura la velocidad del puerto, y el formato de trama (el más común es 8,1,N - 8 bits, 1 de parada y sin control de flujo). En la casilla Modo, hay que seleccionar el sistema que interese, en el caso que nos atañe seleccionaremos Puerta de enlace serie.


Una vez configurado el puerto de configuraciones hay que cargarla al autómata, seleccionando que envíe la configuración (hay que activar la casilla correspondiente en el dialogo de carga de programa). Y a continuación es preciso reiniciar el autómata para que cargue la nueva configuración. 

Una vez configurado, se puede comenzar a comunicar, pero primero veamos los detalles básicos a tener en cuenta. La configuración de la trama modbus, se almacena en direcciones de memoria DM (que dependen del puerto y de la CPU utilizada). Los autómatas CP1L-M incorporan dos puertos de comunicaciones, y la trama se almacena entre la dirección D32200 y 32249 para el puerto 1 y entre la D32300 y 32349 para el puerto 2. Sin embargo para la CPU CP1L-L sólo hay un puerto de comunicaciones y se emplean los últimos (D32300 y 32349), cuidado con esto último.

De igual modo, la respuesta recibida se almacena en las direcciones entre D32250 y 32299 para el puerto 1 y entre la D32350 y 32399 para el puerto 2 en las CPUs CP1L-M. Y entre las D32350 y D32399 en las versiones CP1L-L de la CPU.

En las siguientes figuras se muestra el formato que debe tener la trama para poder ser enviada y para poder procesar adecuadamente la respuesta recibida. En primer lugar se puede ver que utilizando como dirección base 32300 ó 32200 según corresponda, se tiene que en la primera palabra base+0 se debe escribir la dirección de esclavo modbus al que se desea mandar el comando. Para ello se escribirá en la parte baja de la palabra y no en la parte alta  (que queda reservada para trabajos del sistema). De igual modo ocurre en la segunda palabra con dirección base+1, en la que se escribe la función de modbus a emplear. Esto no coincide con la definición estandar de modbus. Según el protocolo modbus, estos dos datos estarían incluidos en los primeros 16 bits enviados, a modo de cabecera, por lo que no hay que confundir una cosa con la otra. A continuación se escribe en base+2 el número de bytes a enviar a partir de esta palabra, es decir si el comando completo tiene una longitud de 6 bytes en total (1 dirección de esclavo, 1 byte del código de función y 4 del resto del comando), habrá que escribir un 4 en esta dirección. A continuación de aquí, se debe escribir normalmente el resto del comando, utilizando adecuadamente la parte alta y baja de los registros.

Veamos un ejemplo practico:

Si hay que enviar un comando Run a un variador de frecuencia MX2 que actúe como esclavo con dirección 1, el comando a enviar sería 01-05-00-00-FF-00  (para conocer mas detalles sobre este mensaje puedes consultar aquí la referencia de aplicación del protocolo modbus en modbus.org). Los dos primeros bytes correspondería como se ha dicho con la dirección de esclavo modbus y código de función, mientras que los cuatro siguientes son la dirección de memoria donde se desea escribir y el valor (FF00 en bobinas ó Coils representa que se desea escribir un 1). También como curiosidad, si se observa el Apendice B (página 279) manual del variador MX2 referenciado antes, se puede observar que la dirección de memoria para el comando Run indicada en el manual es 0001 y en el comando se escribe la 0000, esto se debe a que en Modbus para direccionar la posición N de memoria hay que escribir la N-1. Por tanto habría que escribir un 0000 en la dirección base+3 y un FF00 en la dirección base+4.
.

El formato de las respuestas es el mismo desplazado a las direcciones comentadas antes. En la parte baja del registro con dirección base+50 se tiene la dirección del esclavo que ha respondido y en la parte baja de la dirección base+51 se tiene el código de función del comando al que se está respondiendo. En la dirección base+52 el código de error (si es el caso) y en la posición base+53 se tiene el número de bytes recibidos, los cuales se encontrarán en las direcciones siguientes.


Una vez que se tiene configurada la función a enviar, si se desea enviar el comando a través del puerto serie, se utiliza la palabra de función A640  para las CPUs CP1L-L en el puerto 1 ó bien para las CPUs CP1L-M en el puerto 2. Lo mismo que se explica a continuación, se realiza con la palabra A641 para el puerto 1 en las CPUs CP1L-M. En nusetro caso utilizamos una CPU CP1L tipo L, por lo que para enviar se activa la bobina A640.0 y cuando se reciba una respuesta correcta la bobina A640.1 se activará automáticamente. Si por el contrariop se recibe una respuesta errónea o si se produce un problema de comunicación se activará la bobina A640.2. Para gestionar esto se propone crear un programa (tarea con una prioridad 1,2 ó 3) en el que se creará una sección llamada comunicaciones_common y que contendrá la gestión común de las comunicaciones. Esta sección contendrá el siguiente código:


Comenzaremos explicando este código desde el centro, ya que todo el código se sustenta en la variable com_EnviandoTrama. Se trata de una variable con enclavamiento, que se activa con la señal set_EnviandoTrama y se desactiva con reset_EnviandoTrama. Esta señal es la que activa el bit A640.0 que indica el inicio de la comunicación. Además se emplean dos temporizadores para gestionar el envío automático de una trama en caso de error del envío.  La parte interesante de este sistema es que se centraliza la comunicación en variables independientes que habrá que ir añadiendo a las dos últimos diagramas, en la activación de set_EnviandoTrama, se añade el flanco de subida de las variables de comunicación según la trama que se desea enviar y en la de reset se añade el flanco de bajada de la misma variable. Es decir, cada vez que se desea añadir un nuevo comando a utilizar, se crea una variable asociada que gestione dicho comando (en el ejemplo com_LeerConsigna) que en su flanco de subida activa set_EnviandoTrama y en su flanco de bajada activa reset_EnviandoTrama.

Después se crea una sección común llamada, por ejemplo, comun_escribirDireccion. En ella se debe realizar la escritura en las direcciones base+(lo que toque) de aquellas cosas comunes en todos los comandos configurados. Esto no es obligatorio pero permite ahorrar algo de memoria. En este caso todos los comandos configurados se dirigen al esclavo 4 y tienen una longitud de 6 bytes (4 excluyendo la dirección de esclavo y la función de modbus) por lo que se añade el código correspondiente en esa sección:


A continuación se aconseja utilizar una sección nueva por cada comando donde se configurará la trama de envio. En nuestro caso _COM_LecturaConsigna. Esta escritura requerirá que se encuentren activas la señal com_EnviandoTrama y la variable de control de comunicaciones del comando correspondiente, tal como se puede ver a continuación:

Como se ve en el caso anterior, aquí se configura el resto del comando a enviar. En la parte final de esta sección, se tiene un control de set y reset de dicha variable, de forma que no se actúa nunca directamente sobre la variable sino a través de señales de ser y reset. De forma que si se requiere activar dicho comando desde diferentes sitios del código se puede añadir señales de set y reset según sea necesario, evitando así escribir varias veces sobre la misma variable.

Para el tratamiento de la respuesta hay que extraer los datos de las direcciones comentadas anteriormente (esto será común en muchos casos) y por último se moverá el resultado de la extracción a una dirección (configurada adecuadamente). Esto último como puede verse si que depende directamente de que sea un comando u otro el que se esté configurando, por este motivo se añade la nueva condición a dicha acción, en concreto en este caso se realiza una división del valor (adecuación del mismo a la escala deseada) y un movimiento final a la variable donde lo podremos consultar cuando lo deseemos a partir de este momento (a modo de variable temporal en un registro DM). Por último se puede observar que se utiliza una señal com_Realizada para indicar que la comunicación ha terminado (y se ha extraído la información de la respuesta recibida). Esta señal se activa con la señal set_EnviandoTrama de manera que se activa en el instante en que se va a iniciar una comunicación.

Por último, el uso de este sistema para la realización de una comunicación se puede resumir en tres estados. Un primer estado que activa la señal de envío a través de set_LeerConsigna. Un segundo estado que espera a que la comunicación se realice satisfactoriamente (tiene como condición un flanco de subida de la señal com_Realizada) y por último un estado que realiza el reset de la variable activando la señal reset_LeerConsigna. Este último habrá de esperar a que se desactive la variable com_EnviandoTrama para activar un flag que permite salir de dicho estado. Dichas condiciones y activaciones se deben realizar en las secciones correspondientes según la metodología que se esté empleando. La programación si se emplean las guías recomendadas es muy sencilla aplicando este sistema a cualquiera de ellas. Aquí un extracto de como quedaría el uso comentado:


Como ventajas de este mecanismo, es que permite ampliar fácilmente el uso de nuevos comandos modbus, permite el uso de estos desde diferentes puntos del código de forma sencilla y eficiente a nivel de espacio. Además su diseño modular permite su uso a través de infinidad de modos de programación aunque se recomienda su uso junto a las guías generales de programación de autómatas OMRON publicadas en este blog.

miércoles, 28 de noviembre de 2012

GUÍA ESTÁNDAR DE PROGRAMACIÓN DE AUTOMATISMOS NO SECUENCIALES APLICADA A AUTOMATAS OMRON EN LENGUAJE LADDER (GEPNAS)


Cómo último ejemplo de programación de diagramas de estados con autómatas Omron, en este articulo se presenta una guía sencilla para implementar sistemas no secuenciales. Es importante tener en cuenta que siempre que sea posible es recomendable aproximar el problema a una solución secuencial, y sólo en el caso de que esto no sea posible hay que recurrir a diagramas de estados no secuenciales. Por simplicidad se emplea el mismo ejemplo que en el caso secuencial, aunque como ya se ha explicado un problema secuencial habría que resolverlo con la guía GEPAS.

En este caso, el esquema de contactos se divide en tres zonas, la zona de Trigger y condiciones de sensorización, establecimiento de estados y salidas o acciones de estado. 

El primero de estos, al igual que en la guía GEPAS se tiene que dado un estado se espera una determinada combinación de señales (condición de transición) para que el sistema pase a un nuevo estado. Para realizar esto, se utiliza una variable de memoria DX para almacenar el valor del estado que se modifica con la función MOV en los casos que sea necesario.


Para el caso del establecimiento de estado, lo único que se debe realizar es establecer el estado a través de un bloque de comparación que activa el flag correspondiente a dicho estado como se muestra a continuación:


Lo expuesto en el caso de establecimiento de estado permite poder utilizar la condición de estar en un determinado estado en cualquier momento del programa, mientras que si cada vez que fuese necesario utilizar esto como condición se utilizase el comparador se consumiría más memoria que con esta opción.

Por último, el bloque de salidas es el mismo que se daba en la guía GEPAG.


Cualquier duda, será contestada a través de los comentarios así queda para futuras consultas y otras personas podrán beneficiarse de la aportación.

martes, 27 de noviembre de 2012

GUÍA ESTÁNDAR DE PROGRAMACIÓN DE AUTOMATISMOS SECUENCIALES APLICADA A AUTOMATAS OMRON EN LENGUAJE LADDER (GEPAS)

En la anterior entrada de esta serie, se propone como plantear la solución general a un problema dado utilizando los recursos que CX-Programer proporciona para los autómatas incluso de la gama más baja. En esta ocasión se va a abordar como solucionar el caso de la programación de los subestados derivados del caso anterior. Al abordar el problema que se plantea en uno de estos subestados, pueden darse dos casos, el primero es que dicho subestado sea una maquina secuencial o que no lo sea. En este articulo se pretende mostrar una de las formas más eficaces de programar un sistema secuencial, que además es aplicable a la mayoría de los autómatas del mercado ya que no emplea ninguna característica propia de Omron ni de ningún otro sistema.

En principio como siempre lo ideal es plantear el problema como un diagrama de estados secuencial con los diferentes pasos desarrollados:

E1-->E2-->E3-->E1

A partir de aquí hay que definir 5 zonas en nuestro programa, las condiciones de sensorización, el reset de control, el contador, el establecimiento del estado y las salidas.

Las condiciones de sensorización tienen la finalidad de lanzar un trigger que es utilizado como señal para cambiar de estado, esto se consigue a través del contador que utiliza esta señal para realizar una cuenta. Para evitar que esté contando permanentemente, el trigger se desactiva a sí mismo de manera que sólo se produce un pulso de un ciclo, (también podría emplearse una operación DIFU).

El reset de control es el encargado de activar la señal de RESET en caso que no estar en ninguno de los estados definidos en el sistema.



La zona del contador contiene el contador y las señales de cuenta y de RESET que realizan las acciones comentadas anteriormente.


El establecimiento del estado realiza una comparación con el número de contador y establece la marca del estado correspondiente a la cuenta actual (esta marca es la empleada en los casos anteriores como indicador que de se está en un determinado estado y también se utiliza en el control).



Por último la sección de control, básicamente contiene el código correspondiente a cada estado, en base a si está activa o no la marca correspondiente a dicho estado. Esto debería contener las señales a activar en cada estado o en un sistema muy sencillo podrían ser las salidas aunque no se recomienda activar las salidas directamente, es mejor tener una sección donde se gestionen las salidas y emplear alguna señal intermedia para activar la salida a través de un contacto.


Evidentemente este método habrá que adaptarlo a la solución concreta. Siguiendo esta guía en la implementación de diagramas de secuencia para resolver los problemas de automatización secuenciales dejará clara sus ventajas frente a otros métodos, a pesar de que no es el más eficiente a nivel de memoria utilizada, es fácilmente extensible, trazable y muy claro.

martes, 20 de noviembre de 2012

Creando una Recreativa con Raspberry Pi -- Parte 1

Todavía no he comenzado con esta tarea en sí, pero ya estoy buscando información al respecto. Una vez probado Quake III con mi Raspberry, el próximo paso es instalar mame y crear mi propia recreativa (o algo parecido). Como esta semana me han llegado los pulsadores que pedí, he comenzado a buscar información sobre como empezar con el asunto, y me he encontrado con este link muy muy interesante sobre como instalar el mame en Raspberry, y configurarlo para que arranque sólo.... ya tengo trabajo para este fin de semana :)
Actualizado:  Parece que el link original ha caído :( a cambio os dejo la referencia original, el blog de SheaSilverman

Una foto de la maquinita que han hecho los autores del trabajo:




domingo, 18 de noviembre de 2012

GUÍA ESTÁNDAR DE PROGRAMAÓN DE AUTOMATISMOS GENERAL APLICADA A AUTOMATAS OMRON EN LENGUAJE LADDER (GEPAG)


Cuando realizamos un proyecto de automatización, lo ideal siempre es realizar un análisis previo en el que se debería incluir un diagrama de estados del funcionamiento del sistema. Si el sistema es muy sencillo quizá con solo un diagrama sea suficiente para plasmar todo los estados. En cambio, si el sistema no es suficientemente sencillo lo aconsejable es dividir el problema en subproblemas más pequeños de manera que se aíslen unas partes del problema de otras. Esto además de facilitar la trazabilidad del código y reducir enormemente los errores que se cometerán en la programación, también evitará generar diagramas de estados demasiado grandes y que muchas veces en lugar de permitir manejar mejor el problema (como es su finalidad) acaban consiguiendo todo lo contrario. De esta forma lo que se obtendría es un autómata general más grande que corresponde al diagrama de estados principal y en cada uno de los estados de este diagrama se tendrá un nuevo pequeño automatismo para gestionar cada uno de estos estados.

A continuación se va a mostrar un ejemplo de esto en el caso de autómatas Omron y utilizando lenguaje Ladder, ya que por ejemplo en el caso de autómatas de bajo coste como es la familia CP1 es el único lenguaje que en el que se puede desarrollar. El ejemplo empleado corresponde al de una práctica de la asignatura Sistemas de Control Automático del Máster en Automática y Robótica de la Universidad de Alicante. En este caso tenemos que controlar la gestión de 4 bombas que dan suministro para conseguir una presión estable en la impulsión. Dejando a un lado las particularidades del sistema, el funcionamiento se puede dividir en los siguientes tres estados según la guía Gemma que a su vez pueden ser tratar como cuatro estados en los que no entraremos en detalles:

Diagrama de estados que representa el funcionamiento del sistema General de la práctica


 Cuando se desarrolla software, lo mismo se puede programar de infinitas formas y se tiende a pensar que cualquiera de ellas es igual de buena si realiza su función. Para empezar la eficiencia de los diferentes programas tanto espacial como temporal no es la misma, pero dejando al margen este detalle que puede no ser decisivo existe un motivo más importante para hacer las cosas correctamente. Un código debe ser mantenible, fácilmente trazable, orientado a la extensión y no al cambio y lo más estándar que sea posible, es decir hay que establecer una metodología para la creación de código. El objetivo es que la parte de ingeniería resida en el análisis del problema y en el diseño de la solución en papel (a través de un diagrama de estados grafcet o similar) pero no en el código que se escribe. Para conseguir esto, se debe utilizar una guía de implementación, que dado un análisis del problema siempre termine generando el mismo código sea quien sea el programador, por lo menos en lo esencial. Este tutorial pretende ser una pequeña guía introductoria de cómo hacer esto en autómatas Omron, para una pequeña parte de los programas. En futuras guías, se mostrará como explotar estos estados y realizar pequeñas máquinas de estados secuenciales para cada caso aplicando nuevos métodos.

 Para implementar esto con autómatas Omron, hay muchas formas pero sin duda alguna la forma más eficiente es desactivar completamente la ejecución de los estados que no se estén utilizando. Esto permite ahorrar tiempo y evita que posibles casos no contemplados en estos estados (cuando no deberían estar siendo ejecutados) afecten al correcto comportamiento del sistema. Para conseguir esto hay que dividir la solución en programas diferentes, en la parte izquierda del entorno de CX-Programer se puede ver que en la última opción (programas) permite crear diferentes bloques de código a los que CX-Programer llama programas. Al añadir un nuevo programa se obtienen una pantalla donde se muestran las opciones de Nombre, Tipo de tarea y una opción de chequeo llamada Inicio de operación. El nombre es tan solo un identificador, y el tipo de tarea debería estar ordenado según el orden de ejecución que queramos, por ejemplo, en el caso expuesto tendríamos 5 programas. Un primer programa que gestionará el cambio de estados entre programas. Y cuatro programas más, uno por cada estado de la máquina de estados. Por tanto el orden de tareas que yo recomendarías sería el programa que gestiona el cambio de estados como el de mayor prioridad (por ejemplo tarea 3) y el resto consecutivamente según el orden en que entran en funcionamiento: PARO tarea 4, MARCHA tarea 5, etc.  Nótese que el estado de mayor prioridad se le asigna la tarea 3, esto es porque previo a este programa se han añadido otra serie de programas para realizar la gestión de otras características como comunicaciones, inicialización de variables u otros detalles comunes a todos los estados.


Para realizar el cambio entre estados lo que se debe hacer es detener la ejecución de los programas que no correspondan al estado que está en el instante actual en ejecución. Para ello hay que deshabilitar la casilla de chequeo de las ventanas de propiedades de los programas correspondientes a las tareas 4, 5, 6 y 7 (PARO, REPOSO, SERVICIO y PARANDO). Esto se hace así, porque así los programas permanecerán detenidos a no ser que sean activados de forma explícita utilizando la función TKON, de lo contrario en cada estado habría que apagar todos los que no estuviesen activos ya que por defecto estarían en ejecución. 
                 
                    

De esta manera, el código de cambio de estados se puede expresar de forma muy simple como una variable entera que almacena el estado entre 0 y 3 y que según el estado si se cumple la señal de transición cambia el valor de la variable por el valor del estado siguiente como se muestra a continuación:


Existe un mecanismo  óptimo para los sistemas secuenciales puros que se verá en nuevas guías. Sin embargo para sistemas que contienen transiciones cruzadas entre estados, este es el mecanismo que yo recomiendo.
Como se comentaba anteriormente, utilizando la función TKON es posible habilitar el código (programa) correspondiente al estado en que se encuentra el sistema como se muestra en la imagen. En caso de no haber desactivado la casilla Inicio de operación en los programas que contienen el código de los estados, todos estarían inactivos y habría que desactivar todos aquellos no correspondientes al estado activo utilizando la función TKOF. Esta solución es menos intuitiva y más complicada de gestionar, porque en la mayoría de casos habría que detenerlos en más de un lugar del código lo que complicaría la gestión del sistema y aumentaría la probabilidad de cometer un error. Lo que se propone en esta práctica es que cuando se produce una transición se active el nuevo estado y se desactive el estado o estados de los que se pueda proceder, este no es el único modo de hacer esto, ya que según el caso podría haber formas más optimas de hacer lo mismo.

Es importante tener en cuenta que TKOF congela el programa en el estado exacto en que se quedó, esto quiere decir que sus salidas quedan activas y su estado al salir es el último estado que se ejecutó. Por lo tanto es conveniente, reiniciar el estado en la sección de control de la máquina anterior, como se muestra en la figura. Es decir, si se va a un nuevo estado cuyo subestado depende de una variable que almacena el estado es conveniente reiniciar el valor de esta variable (iniciarlo a cero) para que el subestado se ejecute correctamente.

Un detalle a tener en cuenta es utilizar como memoria para almacenar el estado (variable Estado) una palabra del área DM ya que esta memoria está reservada para datos y es posible escribirla desde diferentes partes del código sin que el programa se queje. En caso de emplear una dirección de memoria normal, se obtendría una advertencia diciendo que la escritura de las bobinas está duplicada.

Este sistema es fácilmente extensible a diagramas generales de muchos más estados y permite la aplicación GEMMA de una forma cómoda y sencilla. En este ejemplo como sólo se pretende implementar tres estados GEMMA (diagrama MARCHA/PARO) se ha incluido en el ejemplo el desglose de dos subestados del estado F1. Esto no deja de ser una particularidad del problema concreto, pero suele ser una buena costumbre dividir F1 en entre 2 y 5 macroestados según la complejidad del problema y gestionar este nivel de F1 con este mismo método, de esta manera se simplificará la definición de los subestados del problema principal (F1).


Referencias:




sábado, 10 de noviembre de 2012

Instalando Quake III en Raspberry Pi

Hace más de tres meses que recibí mi Raspberry Pi y aún no había tenido la ocasión pero... por fin... esta semana, he pedido una tarjeta SD de 8 GB Clase 10 Extreme Pro de 95Mb/seg, un teclado y un ratón para utilizar exclusivamente en este menester. Y... ¿que mejor para probar nuestra Raspberry Pi que instalando Quake III?.

Lo único que necesitas para hacer funcionar tu Raspberry Pi, es:
  • Un transformador con salida micro USB que saque por DC al menos 700mA aunque yo recomiendo al menos 1,2A  para no quedaros cortos.
  • Una tarjeta SD, yo he pillado una de alta velocidad porque tengo entendido que la velocidad de la tarjeta es una de las cosas que más limita la potencia de Rapberry Pi.
  • Una televisión y un un conector de vídeo compuesto (RCA cable amarillo) o bien una pantalla de PC con conector hdmi.
  • Un teclado y un ratón USB.
  • Un cable de red y un router con conexión a Internet.
  • Tener puesto en la radio Rock FM o en su defecto poner una canción de los Guns and Roses.


Pero bueno, no empecemos la casa por el tejado. En primer lugar hay que instalar un SO a nuestra tarjeta SD, yo como soy un poco vago estoy en Windows y he utilizado este sistema para realizar la instalación de la imagen. En esta página podéis encontrar las diferentes imágenes preconfiguradas para instalar en la tarjeta de vuestra Raspberry Pi. Yo os recomiendo utilizar Raspbian “wheezy”que es una optimización de la versión debian para Raspberry, pero si pretendéis instalar la maquina virtual de Java, esta versión no os valdrá y tendréis que ir a la versión soft-debian pura. Bueno, en este caso nos centramos en la primera de estas, la descargáis y montáis la imagen en una tarjeta SD, aquí os dejo el enlace de la guía para principiantes. A continuación os resumo los pasos que yo seguí, en el enlace podéis encontrar varios métodos más, según las ganas que tengáis de frikear y el SO que utilicéis. Yo como ya he dicho no dispongo de mucho tiempo libre y he utilizado Windows:

  1. Descargar la imagen desde el mirror o por torrent. Cómo ya he dicho, asumo que usaremos Raspbian “wheezy” download 2012-10-28-wheezy-raspbian.zip
  2. Extraer el archivo imagen 2012-10-28-wheezy-raspbian.img del archivo .zip descargado.
  3. Insertar la tarjeta SD en tu lector de tarjetas SD y fijate bien en la letra de la unidad que le corresponde.Si la tarjeta no es nueva, deberías formatearla; de lo contrario Win32DiskImager puede fallar al grabar la imagen.
  4. Ahora descarga la herramienta Win32DiskImager. Los enlaces de descarga se encuentran al lado derecho de la página, descarga el zip con los binarios para poder ejecutarlo directamente.
  5. Extrae el ejecutable del archivo zip y ejecuta el archivo Win32DiskImager. Recuerda ejecutarlo como Administrador!
  6. Elige la imagen 2012-10-28-wheezy-raspbian.img que deberías haber extraído previamente.
  7. Selecciona la unidad correspondiente a tu tarjeta SD. Ten cuidado en seleccionar la unidad correcta ya que si te equivocas borrarás todo el contenido de la unidad que elijas ¡si te equivocas puede que pierdas toda tu información!
  8. Pincha en "Write" y espera a que termine la escritura para completar la operación.
  9. Sal del programa y extrae la tarjeta SD.
  10. Ahora puedes insertar la tarjeta en tu Raspberry Pi, ahora puedes encenderla, y si todo ha ido bien debería arrancar el sistema operativo. 




Al arrancar la Raspberry Pi hay dos cosas importantes, la primera es tener en cuenta que el usuario y contraseña por defecto para el Sistema Operativo Wheezy es usuario: pi y contraseña: raspberry, la segunda es que se debería ejecutar por defecto un script llamado raspi-config y que puede ser ejecutado en cualquier momento utilizando el comando: sudo raspi-config
En dicho script, se pueden ver las opciones mostradas a continuación entre ellas cabe destacar las siguientes:
  • Expandir la partición de memoria de tu tarjeta SD: Si estás utilizando una tarjeta de memoria de más de 4GB puedes elegir esta opción para extender la partición de memoria utilizando así toda la memoria de tu tarjeta, para ello utiliza la opción expand_rootfs
  • Para poner el teclado en español hay que elegir la opción del menú configure_keyboard y seguir las indicaciones en pantalla. Es importante tener en cuenta que hay que ir pulsando el tabulador para ir realizando las selecciones y posteriormente aceptar con la tecla enter.
  • Podéis overclockear vuestra Raspberry Pi, yo la he subido a 800Mhz para realizar estas pruebas.
  • También es importante subir la memoria dedicada a la gráfica con el comando meomry_split, os recomiendo 128Mb no pongáis los 256Mb ya que en ese caso el sistema tiende a tener parones.
Pulsando la tecla ESC se accede a la consola del SO. Lo siguiente que debéis hacer es conectar a Internet vuestra Raspberry Pi utilizando un cable Ethernet entre tu dispositivo Raspberry y un router con conexión ADSL o similar. Si todo funciona correctamente deberías poder ejecutar el comando ifconfig para comprobar que la tarjeta ha obtenido una IP válida. Para comprobar que la conexión funciona puedes ejecutar ping -c 3 www.google.es y deberías obtener respuesta de sus servidores.
Podéis encontrar más información básica sobre como hacer ciertas cosas con Linux en Raspberry Pi iniciación con raspberry.

Y ahora... vamos a comenzar con la instalación de Quake 3, os propongo la solución que a mi me función que es la que consiste en compilar Quake 3 para Raspbian, para lo que hay que seguir los pasos propuestos en este tutorial y que paso a describir a continuación:


  1. Actualiza el sistema con los siguientes comandos (el último en mi caso no se ejecutó correctamente pero sin embargo el sistema ha funcionado perfectamente):
        sudo apt-get update
        sudo apt-get dist-upgrade
        sudo rpi-update 192
    • Reinicia con el comando reboot.
  2. Instala los paquetes necesarios para realizar la compilación:
        sudo apt-get install git gcc build-essential libsdl1.2-dev
  3. Y ahora descarga el código fuente de Quake 3:
        mkdir ~/src
        cd ~/src
        git clone https://github.com/raspberrypi/quake3.git
        cd quake3
  4. Ahora hay que editar el archivo build.sh del directorio quake3 para cambiar el uso de las librerías a utilizar.
        cambia la linea 8 por:  ARM_LIBS=/opt/vc/lib
        cambia la linea 16 por: INCLUDES="-I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads"
        Comenta la linea usando el simbolo # 19:    #CROSS_COMPILE=bcm2708-
  5. Ahora ejecuta el script que realiza la compilación: ./build.sh
  6. Y... ahora puedes ir a hacerte un café, te o tomarte unas cervezas con tus colegas, porque esto tarda un buen rato (aproximadamente 1 hora).
  7. Ahora tienes que encontrar los archivos: pak0.pk3, pak1.pk3, pak2.pk3, pak3.pk3, pak4.pk3, pak5.pk3, pak6.pk3, pak7.pk3, pak8.pk3 y colocarlos en el directorio build/release-linux-arm/baseq3. Estos archivos los puedes encontrar en el cd original de tu Quake III
  8. Una vez compilado puedes crear un directorio limpio y mover ahí los archivos listados a continuación, puedes eliminar el resto del directorio:
quake3arena/ioq3ded.arm
quake3arena/ioquake3.arm
quake3arena/baseq3/cgamearm.so
quake3arena/baseq3/qagamearm.so
quake3arena/baseq3/uiarm.so
quake3arena/lib/libSDL.so
quake3arena/lib/libSDL-1.2.so.0
quake3arena/lib/libSDL-1.2.so.0.11.3
Ahora ya puedes entrar en la carpeta anterior y ejecutar el archivo ioquake3.arm. Usando el comando ./ioquake3.arm comenzará el juego.
    Quiero agradecer a Shea Silverman su trabajo por servirme de fuente de inspiración.


    Bueno, por último aquí os dejo un vídeo demostrativo del resultado, se que la calidad del vídeo no es muy buena, pero considero que tampoco hace falta mas. Como podéis ver el Quake III funciona más que fluido con la configuración apropiada, habrá que seguir probando cosas para comprobar los límites del Hardware. De momento la prueba de concepto ha sido un éxito!



    Cubieboard

    El mismo día que por fin instalo un sistema operativo a mi RaspBerry ya he pedido mi nuevo juguete. CubieBoard es la nueva placa de alto rendimiento y bajo coste que va a competir con RaspBerry Pi, esta vez desarrollada en Shenzhen China por lo que como no era raro, ofrece mayores prestaciones a un precio muy reducido. En este momento están organizando una campaña de crown funding para recopilar fondos y crear una alta producción consiguiendo reducir así los costes de fabricación. Las características de este dispositivo prometen, incorpora un microprocesador ARM cortex-A8 de 1Ghz de velocidad de reloj, 1Gb de RAM DDR3 y 4Gb de Nand Flash integrada. A continuación os dejo las características del sistema:
    • 1G ARM cortex-A8 processor, NEON, VFPv3, 256KB L2 cache
    • Mali400, OpenGL ES GPU
    • 1GB DDR3 @480MHz
    • HDMI 1080p Output
    • 10/100M Ethernet
    • 4GB Nand Flash
    • 2 USB Host, 1 slot para micro SD, 1 SATA, 1 puerto de Infrarrojos.
    • 96 pines de propósito general incluyendo I2C, SPI, RGB/LVDS, CSI/TS, FM-IN, ADC, CVBS, VGA, SPDIF-OUT, R-TP..
    • Sistemas Operativos probados Android, Ubuntu y otras distribuciones de Linux.
    Frente a los 700Mhz y sus 256 Mb de RAM en los modelos A y los primeros modelos B, 512 en los últimos modelos B. CubieBoard incluye memoria Nand Flash para el sistema operativo por lo que no es necesario comprar una tarjeta SD para poder instalar el sistema operativo, lo que hace que la diferencia de precio con Raspberry Pi, realmente se reduzca un poco. En tan solo unos días han recaudado 71.000 dolares de los 50.000 que pretendían conseguir, y aún quedan otros 28 días más, todavía la puedes conseguir por 59$ por correo ordinario (unas 3 semanas a partir de la fabricación) o por 69$ con envío rápido (unos pocos días a partir de la fabricación). La parte mala es que me imagino que no comenzarán con la fabricación hasta el día que termine la campaña, por o que no creo que pueda disfrutar de este juguetito antes de navidades.

    Aquí os dejo el vídeo promocional de la campaña que están realizando en Indigogo:


    miércoles, 5 de septiembre de 2012

    PFC: DISEÑO, CONSTRUCCIÓN Y CONTROL DE UNA ESTRUCTURA ARTUCLAR RRR

    En esta entrada, pretendo comentar los resultados de mi trabajo realizado en mi primer año como parte del grupo de Automática, Robótica y Visión Artificial (AUROVA) de la Universidad de Alicante. Las investigaciones principales del gurpo se centran en el area de Diseño y Producción Industrial (DPI), principalmente en tareas de Manipulación Inteligente y Control Visual (En la literatura científica es común encontrarlo como "Visual Servoing"). Cuando se realizan tareas de manipulación inteligente de objetos, mediante Visual Servoing con una cámara en el extremo del robot manipulador (Configuración Eye in Hand), es común que una vez alcanzado el objeto a manipular, este desaparezca de la imagen, por tanto la cámara empleada para llegar al objeto ya no es útil una vez alcanzado dicho punto. Incluso, en casos en que por la morfología del objeto, este no desaparece del espacio imagen, las características empleadas para localizarlo y manipularlo, si pueden dejar de ser detectadas por la cámara.


    Es en esta situación en la que se plantea la construcción de un pequeño robot, que pueda ser acoplado en el extremo del robot manipulador principal de forma que incorporando una cámara en su extremo, este robot permita el seguimiento de las características del objeto manipulado mientras se realiza la manipulación. Es aquí donde comienza mi trabajo dentro del grupo y desarrollo la primera de mis funciones a cargo del grupo, que consiste en diseñar y construir el mini-robot COOPER.



    Como se comentaba anteriormente, este robot, debía cumplir una serie de restricciones, de velocidad y peso importante para poder llevar a cabo su tarea, sin entorpecer el funcionamiento del robot principal. Para más información se puede consultar mi proyecto final de carrera donde hay información un poco más detallada acerca de los elementos empleados para construcción del robot y el diseño del sistema Software que lo gobierna.



    jueves, 2 de agosto de 2012

    IOExpander un nuevo Shield de Arduino que permite manejar 32 I/O mediante I2C

    El nuevo Shield IOExpander permite manejar 32 pines de I/O configurables a través I2C. Esta placa se basa en el en el chip de Microchip MCP23017. Este chip incorpora 2 puertos, un puerto A y un puerto B, cada uno de ellos de 8 I/O configurables, cada pin de estos puertos se puede configurar como entrada o como salida. Además este chip incorpora la funcionalidad para generar una señal de interrupción en un pin INTA/INTB adicional, uno por cada puerto, de forma que si se produce un cambio en una entrada del puerto A, se genera una señal de interrupción en el pin INTA y de igual forma ocurre con el puerto B.


    Siguiendo esta funcionalidad, las 32 I/O que incorpora este Shield, están organizadas en 4 puertos ya que incorpora 2 chips. Es decir, un chip A incorpora un puerto A y un puerto B de 8 I/O y a su vez el chip B incorpora otro puerto A y otro puerto B. Para el control de este Shield se ha desarrollado una librería compatible con el IDE de desarrollo de Arduino 1.0.1. Esta librería incorpora funciones para realizar las funciones principales de forma sencilla, entre ellas se encuentran configurar el modo de los pines, escribir en un puerto o pin determinado, leer de un puerto o pin determinado, habilitar la interrupción desde un determinado pin de un puerto dado.


    El Shield, está preparado para poder ser apilado, de forma que se pueden emplear varios de estos al mismo tiempo, gracias a un dipswitch incluido que permite configurar la dirección I2C de cada chip. Posteriormente, a través de el método begin de la librería es posible configurar la dirección de cada uno de los chips en el software.


    domingo, 15 de julio de 2012

    Problema instalando Visual Studio 2008: InfoPath MUI 2007

    Recientemente he necesitado instalar mi antigua versión de Visual Studio 2008 ya que me encuentro desarrollando en estos momentos, sistemas de control visual con la plataforma ViSP para Windows, y para ahorrar problemas en la compilación de dicha plataforma es recomendable utilizar esta versión del IDE. Bonita sorpresa me llevé al encontrar el siguiente mensaje:

    The following component failed to install:
    Microsoft Visual Studio Web Authoring Component

    Menos mal que una rápida búsqueda por Internet me ha llevado a mi queridísima Web de devjoker que tantas veces me ha sacado de apuros. Según explica Pablo Gumpert en este tip,  el problema reside en un componente de la instalación de Office 2007. Para poder instalar nuestro Visual Studio 2008 sólo es necesario desinstalar primero dicho componente que en la versión 2007 se trataba de un componente en fase Beta. El susodicho componente es  InfoPath MUI 2007, desconozco si el problema se debe a estar en fase Beta y tampoco sé si en la versión 2010 de Office ya se ha obtenido una versión estable que no presente este problema. Sin más, la clave para desinstalar este componente es ejecutar el comando:

    msiexec /x {30120000-0044-0C0A-0000-0000000FF1CE} 

    Yo lo he probado en Windows 7 y ha funcionado perfectamente.
    De nuevo aprovecho para dar las gracias a los amigos de devjoker.

    lunes, 7 de mayo de 2012

    Instalando PostgreSQL

    Entrar en la página de http://www.postgresql.org/ y descargar la última release, en este momento es la 9.1.3. Para ello, en la sección Download encontrarás un enlace donde puedes descargar los binarios para diferentes sistemas operativos. Para trabajar con Visual Studio los binarios que nos interesan son los de Windows. Una vez ahí hay que descargar el instalador, para ello tendrás que elegir el de 32 o 64 bits según tu sistema operativo. Una vez hecho esto se iniciará la descarga.
      
    A continuación, el instalador pedirá la contraseña de super usuario. Es muy importante recordar esta contraseña ya que será la que nos permita administrar nuestro sistema de base de datos.


    Por último se pedirá el puerto en que el servidor va a proveer los servicios de gestión de Base de datos. Esto se debe a que el sistema provee sus servicios de forma remota, es decir el servidor permitirá a los clientes conectarse a través de una conexión TCP/IP. Y una vez establecida esta conexión, a través del canal establecido se podrán realizar las consultas SQL, esta característica es común en los sistemas de gestión de Base de Datos administrados. Es importante recordar en que puerto se sirve para poder realizar la conexión al sistema posteriormente, por lo general es recomendable dejar el puerto por defecto.


    Por último hay que marcar la casilla de uso de Stack Builder que nos permitirá instalar componentes adicionales a nuestro sistema PostgreeSQL, entre ellos los drivers de conexión a BBDD desde diferentes plataformas de programación.


    Una vez iniciado Stack Builder, podremos elegir que componentes instalar. Como mínimo habremos de instalar el conector Npgsql que intalará la dll a emplear desde la plataforma .Net para conectar al sistema de gestión de BBDD.


    Al comienzo de la instalación del driver Npgsql aparecerá la ruta de instalación del mismo, en mi caso: C:\Program Files (x86)\PostgreSQL\Npgsql
    Recuerdala por que será donde estará instalada la dll a emplear desde .Net.




    jueves, 5 de abril de 2012

    Blue Tooth con Arduino

    Alberto Bordonado, ha realizado un estupendo trabajo en el que ha desarrollado un sistema donde se realiza la comunicación bidireccional de una placa Arduino con un PC a través de un interfaz BlueTooth. La imaginación me llena la cabeza de ideas... un dataloger inalámbrico, control de dispositivos remotos en casa a través de tu PC o móvil... en fín son miles las cosas que me vienen a la mente, es cuestión de dejarse llevar un poco, cosa que se me da muy bien, jejeje.

    Aquí os dejo la referencia al foro donde se ha desarrollado el proyecto, por cortesía de Alberto Bordonado.

    martes, 20 de marzo de 2012

    Programando PIC con C++: Switch VS if {...} else if {..}

    Cuando se programa microcontroladores en C++. no hay que perder de vista la arquitectura con la que se está trabajando. El código que se escriba en C++ condicionará el código ensamblador que generará el compilador empleado. Un claro ejemplo de esto es el caso de la estructura switch frente a una estructura if else if. Mucha gente pensará que los siguientes códigos en C++ son equivalentes:
    
    void main(void)
    {
     int a=0;
     while(1){
      a++;
      if(a==0){
       a=1;
      }else if(a==1){
       a=2;
      }else if(a==2){
       a=3;
      }else if (a==3){
       a=4;
      }else{
       a=0;
      }
     }
    }
    
    
    
    
    
    
    
    
    void main(void)
    {
     int a=0;
     while(1){
      a++;
      switch(a){
       case 0:
        a=1;
       break;
       case 1:
        a=2;
       break;
       case 2:
        a=3;
       break;
       case 3:
        a=4;
       break;
       default:   
        a=0;
       break;
      }
     }
    }

    No obstante el código que realmente genera el compilador no es el mismo. Por lo general el código que se genera para un switch es, en termino medio, más eficiente que en el caso de los if else. Cuando se trabaja con sistemas de tiempo real que deben interaccionar con el entorno, hay que tener en cuenta que se deben realizar ciclos con las siguientes fases :
    1. Lectura de sensores
    2. Ejecución del código y toma de decisiones
    3. Actuación (transmisión de las decisiones a los actuadores)
    Este código debería ejecutarse con unos tiempos de ciclo lo mas constantes que sea posible, es decir, que la en el tiempo de ejecución de diferentes ciclos no haya grandes diferencias. Si se utilizan las estructuras if else, intuitivamente se puede sospechar que no será la misma cantidad de comprobaciones las que se harán para ejecutar el primer if que si se ejecuta el último. Esto se debe a que para que se ejecute el último caso primero de deben haber descartado todos los anteriores. El problema es que se está haciendo ineficiente la ejecución de acciones para los últimos casos de la estructura, y además sin que esto aporte ninguna ventaja. Este tipo de códigos se pueden construir mediante estructuras switch como se muestra en el ejemplo y esta creará un código bastante más eficiente y con unos costes temporales menos extremos, los mejores y los peores casos tendrán costes temporales similares.

    Para ilustrar los ejemplos veamos el código ensamblador que se genera en cada uno de ellos para el PIC16F876A con el compilador SDCC. Para entender el código, se recomienda leer la entrada anterior, pero en todo caso al menos se habrá de tener en cuenta las siguientes indicaciones:

    • el registro r0x1000 se refiere a la parte baja del valor de a y r0x1001 a la parte alta de la misma variable.
    • La variable a es una variable entera de 16 bits, 8 por registro.
    • Cuando se habla de L(a) y H(a) en los comentarios se refiere a la parte baja y alta de la variable a respectivamente.
    • La instrucción BTFSC <reg>,<num> se salta la instrucción contigua cuando el bit <num> del registro <reg> está a 0 (clear)
    • La instrucción BTFSS <reg>,<num> se salta la instrucción contigua cuando el bit <num> del registro <reg> está a 1 (set) 
    • W es el registro acumulador de la ALU
    Código 1: estructura if else if
    
    _main ;Function start
    ; 2 exit points
    ; .line 3; ------ int a=0;
     BANKSEL r0x1000 
     CLRF r0x1000 ;se borra la parte baja de a
     CLRF r0x1001 ;se borra la parte alta de a
    _00118_DS_
    ; .line 5; ------ a++;
     BANKSEL r0x1000
     INCF r0x1000,F ;incremento de a
     BTFSC STATUS,2 ;si el resultado es cero
       ;se ejecutará la siguiente linea que 
     INCF r0x1001,F  ;incrementa la parte alta
    ; .line 6; ------ if(a==0){
     MOVF r0x1000,W ;se pone H(a) de a en W
     IORWF r0x1001,W ; OR entre W y la L(a)
     BTFSS STATUS,2 ;si el resultado no es cero
     GOTO _00115_DS_ ;se salta a esta etiqueta
    ; .line 9; ------ a=1;
     MOVLW 0x01
     MOVWF r0x1000
     CLRF r0x1001
     GOTO _00118_DS_
    _00115_DS_
    ; .line 10; ---- }else if(a==1){
     BANKSEL r0x1000
     MOVF r0x1000,W
     XORLW 0x01      ;XOR entre L(a) y 0x01
     BTFSS STATUS,2  ;si el reslutado no es 0
     GOTO _00112_DS_ ;se salta a la etiqueta
     MOVF r0x1001,W  ;de lo contrario se
     XORLW 0x00 ;se comprueba la parte alta 
     BTFSS STATUS,2  ;si H(a) no es cero
     GOTO _00112_DS_ ;se salta a la etiqueta
    ; .line 11; ---- a=2;
     MOVLW 0x02
     MOVWF r0x1000
     CLRF r0x1001
     GOTO _00118_DS_
    _00112_DS_
    
    
    ; .line 12; ---- }else if(a==2){
     BANKSEL r0x1000
     MOVF r0x1000,W
     XORLW 0x02
     BTFSS STATUS,2
     GOTO _00109_DS_
     MOVF r0x1001,W
     XORLW 0x00
     BTFSS STATUS,2
     GOTO _00109_DS_
    ; .line 13; ---- a=3;
     MOVLW 0x03
     MOVWF r0x1000
     CLRF r0x1001
     GOTO _00118_DS_
    _00109_DS_
    ; .line 14; ---- }else if(a==3){
     BANKSEL r0x1000
     MOVF r0x1000,W
     XORLW 0x03
     BTFSS STATUS,2
     GOTO _00106_DS_
     MOVF r0x1001,W
     XORLW 0x00
     BTFSS STATUS,2
     GOTO _00106_DS_
    ; .line 15; ---- a=4;
     MOVLW 0x04
     MOVWF r0x1000
     CLRF r0x1001
     GOTO _00118_DS_
    _00106_DS_
    ; .line 17; ---- a=0;
     BANKSEL r0x1000
     CLRF r0x1000
     CLRF r0x1001
     GOTO _00118_DS_
     RETURN 
    
    
    
    Cómo se puede observar en el código anterior, hasta que se ejecuta el caso por defecto, se han tenido que realizar unos 4 saltos condicionales. Teniendo en cuenta el coste de ciclos de cada una de las operaciones se tiene un mejor caso (a=0) de 11 ciclos hasta que se llega a ejecutar el código de dicha opción. El conteo de los ciclos se incluye a continuación de forma ilustrativa. Nótese que en el caso de las instrucciones de salto condicional BTFSC y BTFSS valen 2 ciclos ya que el mejor caso se considera que no se cumplen, también derivado de este hecho la operación contigua a estas tiene un coste 0 ya que en su lugar se ha ejecutado una operación nop es decir un ciclo vacío.

     (1) BANKSEL r0x1000 
     (1) CLRF r0x1000 ;se borra la parte baja de a
     (1) CLRF r0x1001 ;se borra la parte alta de a
    _00118_DS_
    ; .line 5; ------ a++;
     (1) BANKSEL r0x1000
     (1) INCF r0x1000,F ;incremento de a
     (2) BTFSC STATUS,2 ;si el resultado es cero
       ;se ejecutará la siguiente linea que 
     (0) INCF r0x1001,F  ;incrementa la parte alta
    ; .line 6; ------ if(a==0){
     (1) MOVF r0x1000,W ;se pone H(a) de a en W
     (1) IORWF r0x1001,W ; OR entre W y la L(a)
     (2) BTFSS STATUS,2 ;si el resultado no es cero
     (0) GOTO _00115_DS_ ;se salta a esta etiqueta

    Si se considera el peor caso, es decir, en el que se debe ejecutar el caso por defecto pasando por el camino más largo, se tiene un total de 42 ciclos ya que cada comparación (excepto la de a==0) tiene un coste de 10 ciclos de ejecución en su peor caso y la de a==0 tarda un ciclo más en el caso en que se ejecute el GOTO que tiene un coste de 2 ciclos, aunque el BTFSS pase a costar 1 ciclo.

    Codigo 2 : Estructura switch
    
    _main ;Function start
    ; 2 exit points
    ; .line ---- int a=0;
     BANKSEL r0x1000
     CLRF r0x1000
     CLRF r0x1001
    _00112_DS_
    ; .line 7; "ejemplo1-2.c" a++;
     BANKSEL r0x1000
     INCF r0x1000,F
     BTFSC STATUS,2
     INCF r0x1001,F
    ;---------------------------
    ;Hasta aquí es igual que antes
    ;---------------------------
    

    A partir de aquí se muestra a la derecha el caso default y a la izquierda la continuación del código que ejecutaría el caso general.
    
    ;signed compare: 
    ;left < lit(0x0=0), size=2, mask=ffff
    ; .line 8; "ejemplo1-2.c" switch(a){
     BSF STATUS,0    ;se borra el flag C
     BTFSS r0x1001,7 ;se mira signo de a
            ;si el signo de a es negativo
     BCF STATUS,0    ;se activa el flag C
     BTFSC STATUS,0  ;si el flag C está activo
     GOTO _00109_DS_ ;se manda al caso default
    ;genSkipc:3083: created from rifx:0x286044
    ;swapping arguments (AOP_TYPEs 1/2)
    ;signed compare:
    ; left >= lit(0x4=4), size=2, mask=ffff
     MOVF r0x1001,W  ;Se carga H(a) en W
     ADDLW 0x80  ;se le suma dos veces 0x80
     ADDLW 0x80  ;con esto se elimina el signo
     BTFSS STATUS,2 ;si el resultado no es cero
     GOTO _00119_DS_ ;se pasa al caso default
     MOVLW 0x04      ; se carga un 4 en W
     SUBWF r0x1000,W ;se resta L(a)
    _00119_DS_
     BTFSC STATUS,0  ;si el flag C está activo
     GOTO _00109_DS_ ;se va al default
    ;Hasta aquí sólo se ha analizado el default
    ; y se puede comprobar un mejor caso de 
    ;13 ciclos (salir por el primero GOTO)
    ; y un peor caso de 22 ciclos
    ; saliendo por el último
    
    
    ;genSkipc:3083: created from rifx:0x286044
     MOVLW HIGH(_00120_DS_);se carga la parte H
     MOVWF PCLATH ;de la direc. 120 al PCLATCH
        ;esto establece la parte alta del valor
        ;del contador de programa (PC)
     MOVLW _00120_DS_ ;mueve la parte baja de 
     BANKSEL r0x1000  ;la misma dirección a W 
     ADDWF r0x1000,W  ;y se le suma a.
     BTFSC STATUS,0  ;Si hay accarreo
     INCF PCLATH,F  ;se incrementa el PCLATCH
     BANKSEL PCL   ;Con esto se ha establecido
     MOVWF PCL ;el nuevo valor para el contador 
        ; de programa. Este ahora estará
        ; apuntando a uno de los GOTO 
        ; siguientes. Cada GOTO representa
    un case del switch.
    _00120_DS_
     GOTO _00105_DS_
     GOTO _00106_DS_
     GOTO _00107_DS_
     GOTO _00108_DS_
        ; este código consume 11 ciclos incluyendo
        ;el último GOTO
    
    
    
    

    Como se puede observar en el caso del switch, el peor caso ya no es el default sino cualquiera de los demás casos. Además todos tardarán la misma cantidad de ciclos en llegar al código que deben ejecutar que en este caso es de 7 (inicialización de a e incremento) 5+9 (comprobación del default) + 11 (caso general) = 32 ciclos. Nótese que esa cantidad de ciclos representa aproximadamente tres cuartas partes del peor caso del ejemplo inicial con la estructura if else, además este coste es constante para cualquiera de los diferentes casos del switch. Existan la cantidad de casos que existan este coste permanecerá constante siempre y cuando estos casos tengan valores consecutivos, en caso contrario se iría añadiendo complejidad a la parte que evalúa el default. En un caso general en el que se implementa una maquina de estados, esta solución es la solución idónea para programar dicho sistema ya que evitará problemas y proporcionará tiempos equivalentes para alcanzar los diferentes casos y proporcionar una respuesta adecuada, por lo que el funcionamiento global mejorará considerablemente. Además de esto, con maquinas de estados complejas, la ganancia al emplear una estructura switch frente al uso de una estructura if else crecerá linealmente.

    Por último el resto del código para este caso, donde están las etiquetas de cada uno de los case del switch:
    
    _00105_DS_
    ; .line 10; "ejemplo1-2.c" a=1;
     MOVLW 0x01
     BANKSEL r0x1000
     MOVWF r0x1000
     CLRF r0x1001
    ; .line 11; "ejemplo1-2.c" break;
     GOTO _00112_DS_
    _00106_DS_
    ; .line 13; "ejemplo1-2.c" a=2;
     MOVLW 0x02
     BANKSEL r0x1000
     MOVWF r0x1000
     CLRF r0x1001
    ; .line 14; "ejemplo1-2.c" break;
     GOTO _00112_DS_
    _00107_DS_
    ; .line 16; "ejemplo1-2.c" a=3;
     MOVLW 0x03
     BANKSEL r0x1000
     MOVWF r0x1000
     CLRF r0x1001
    ; .line 17; "ejemplo1-2.c" break;
     GOTO _00112_DS_
    _00108_DS_
    ; .line 19; "ejemplo1-2.c" a=4;
     MOVLW 0x04
     BANKSEL r0x1000
     MOVWF r0x1000
     CLRF r0x1001
    ; .line 20; "ejemplo1-2.c" break;
     GOTO _00112_DS_
    _00109_DS_
    ; .line 22; "ejemplo1-2.c" a=0;
     BANKSEL r0x1000
     CLRF r0x1000
     CLRF r0x1001
    ; .line 24; "ejemplo1-2.c" }
     GOTO _00112_DS_
     RETURN