En esta publicación: Robot Programable: Diagrama Básico en Bloques prometo, al final, que voy a continuar con los detalles de circuito y de programación. En la entrada: El microcontrolador «cerebro» del robot programable (básico) hay mucho de circuito (hardware), ya que el artículo contiene una descripción bastante detallada de las secciones del chip microcontrolador que utilizamos por ahora, y también detalles de configuración y uso del chip que tienen una enorme relación con la programación.
Concretamente, que algo he cumplido de la promesa.
Los artículos hasta ahora venían más o menos sincronizados con el avance de las clases, pero para esta fecha obviamente estamos en receso de verano y no hay actividad, ni la habría aunque me lo propusiera, ya que por suerte los chicos asisten a una colonia de verano. A mí tampoco el calor me favorece mucho para mover las neuronas, por eso estuve dedicando algunos artículos a la mecánica, a la búsqueda de soluciones para abaratar la construcción de la base mecánica del robot.
De eso aún falta, me quedan algunas cosas por solucionar, y me dedico casi diariamente al tema. Ya les contaré.
Pero hay un programa básico ya escrito con el que hemos trabajado en clase, haciendo mover al robot. Este programa (escrito en assembler, o ASM) permite ordenar una secuencia de movimientos al robot. El programa tiene tres niveles: 1) las instrucciones básicas para mover los motores en el lenguaje del microcontrolador (son las mismas para toda la familia PIC16F, y se podrían utilizar en todos los modelos disponibles, si bien nuestro chip elegido es el PIC16F876A), 2) la inclusión posterior de las instrucciones básicas dentro de subrutinas a las que se puede llamar programando con palabras en castellano en lugar de con los mnemónicos de la programación ASM, lo cual facilita la comprensión, y finalmente 3) la conversión de estas subrutinas en macros.
Utilizando macros se programa directamente con la palabra que hemos definido para el comando; ni siquiera hace falta utilizar la instrucción CALL de ASM que se necesita para hacer correr una subrutina.
Voy a explicar poco a poco la programación, en todos los pasos.
Primero que nada: la configuración inicial del chip
Recordemos primero el diagrama en bloques, ya que hay que definir las funciones de las patas del chip teniendo en cuenta este circuito.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<span style="color: #00008b;"><b> list P = 16F876a ; indica el modelo de microprocesador a usar include "P16f876a.inc" ; provee declaraciones necesarias para el chip ERRORLEVEL 1;-302 ; para evitar los mensajes de cambio de ; banco en el resultado del compilador ; Declaraciones que definen el funcionamiento básico del chip __config _CP_OFF & _XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF & _BODEN_OFF CBLOCK 0x20 ; Declarar las variables d1 ; usada en Retardos d2 ; usada en Retardos d3 ; usada en Retardos ENDC ; ; ----------- ; INICIALIZAR ; ----------- org 0x0000 ; Indica la dirección de origen del programa clrf STATUS ; limpia el registro de estado movlw 0x00 ; valor cero 00 movwf PCLATH ; al contador de programa = comienzo goto Comienzo ; Salto a comienzo ; Comienzo ; BANCO bcf STATUS,RP0 ; Prepara bcf STATUS,RP1 ; RAM Página 0, en pág 0 están los registros ; de salida de los puertos ; ------------------------------------ ; PUERTOS movlw 0x00 movwf PORTA ; Inicializar valores del PORT A movlw 0x00 movwf PORTB ; Inicializar valores del PORT B movlw 0x80 movwf PORTC ; Inicializar valores del PORT C ; ------------------------------------ ; BANCO bsf STATUS,RP0 ; RAM Page 1, en pág 1 están los registros ; de configuración de los puertos y ; otros módulos del microcontrolador ; --------------------------------------------------------------- ; FUNCION DE LAS PATAS DE LOS PORTS ; --------------------------------------------------------------- movlw b'00000000' movwf TRISA ; Todas las patas port A como salidas movlw b'00000000' movwf TRISB ; Todas las patas port B como salidas movlw b'10111111' movwf TRISC ; Todas las patas port C como entradas excepto RC6/TX movlw 0x06 ; Todas las patas del puerto entrada/salida digital movwf ADCON1 ; esto es necesario para el buen funcionamiento ; de los puertos que tienen ; opciones analógicas (convertidor A/D) ; ; ----------------------------------------------------------- ; BANCO bcf STATUS,RP0 ; Página RAM 0, la operación normal se realiza ; en la página 0 de RAM ; ----------------------------------------------------------- </b></span> |
Es conveniente que vayamos explicando las líneas de instrucciones por partes:
1 2 3 4 5 |
<span style="color: #00008b;"><b> ; ----------------------------------------------------------- list P = 16F876a ; indica el modelo de microprocesador a usar include "P16f876a.inc" ; provee declaraciones necesarias para el chip ; -----------------------------------------------------------</b></span> |
La indicación list P es muy directa, con ella se le dice al programa de compilación que vamos a trabajar en un programa para el PIC16F876A.
La indicación include incluye en el programa, en el momento de compilarlo, todo el texto que se encuentra dentro del archivo que se indica a continuación P16f876a.inc. En estos archivos (provistos por el fabricante) se definen nombres en letras para todas las partes del microcontrolador, que de otro modo deberían estar indicadas en el programa con números hexadecimales. Esta práctica evita el trabajo de estar memorizando o buscando en una tabla estos números, y facilita la comprensión al leer el programa, y permite un fácil intercambio de programas (migración) entre diferentes microprocesadores.
Por ejemplo, dentro de P16f876a.inc define así al puerto A: PORTA EQU H’0005′. Podríamos cambiar la declaración dentro de este archivo y llamar al puerto con un nombre en español, por ejemplo: PuertoA EQU H’0005′. Luego podríamos programar utilizando este nombre y funcionaría correctamente.
1 2 3 4 5 6 |
<span style="color: #00008b;"><b>; ----------------------------------------------------------- ERRORLEVEL 1;-302 ; para evitar los mensajes de cambio de ; banco en el resultado del compilador ; ----------------------------------------------------------- </b></span> |
Esta línea no es imprescindible y se utiliza para evitar molestas indicaciones de aviso cuando se cambia de banco de RAM en el programa. Las indicaciones no son necesarias si en el programa hemos realizado correctamente los cambios de banco.
1 2 3 4 5 6 |
<span style="color: #00008b;"><b> ; ----------------------------------------------------------- ; Declaraciones que definen el funcionamiento básico del chip __config _CP_OFF & _XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF & _BODEN_OFF ; ----------------------------------------------------------- </b></span> |
Los microcontroladores poseen una serie de configuraciones que se fijan por única vez al principio de la operación y definen ciertas partes esenciales de su funcionamiento. En el ejemplo:
_CP_OFF (Code Protection) define que no protegeremos el código de ser leído desde la memoria de programa del chip. Es posible definir _CP_ON cuando ya tenemos un programa totalmente probado en un equipo que vamos a entregar y no deseamos que alguien se lo copie leyéndolo desde la memoria de programa del microcontrolador.
_XT_OSC define el modo de oscilador de reloj del chip. En este caso, la opción _XT_OSC indica que se utilizará un cristal o resonador conectado al chip para definir la frecuencia de trabajo.
_WDT_OFF está relacionado con la función de «Despertador» («Watchdog timer» en inglés) que se utiliza en aquellos casos en que se desea que el microcontrolador sea «despertado» de posibles estados en que haya quedado detenido, sea porque quedó esperando una señal de activación externa o porque falló su programa o porque se lo puso intencionalmente en ese estado por programa. El watchdog timer (WDT) puede producir un reinicio del microcontrolador PIC cada cierto período de tiempo, y recomenzar la ejecución del programa. Esto es para evitar que el dispositivo entre en un lazo infinito (se «cuelgue»), o se quede en una espera muy prolongada por un determinado evento que no ocurre. Durante la operación normal, el watchdog timer (en español «perro guardián» o «despertador») genera un reinicio del microcontrolador PIC después del final de su período WDT. También cumple la función de sacar al dispositivo del modo Sleep («Dormir»). En este caso el watchdog timer ocasiona que se despierte el microcontrolador PIC y continúe con la operación normal (sin producir reinicio), lo que se conoce como despertar WDT. No lo utilizamos en este programa en particular, de modo que fijamos _WDT_OFF, o sea, Watchdog Timer apagado.
_PWRTE_ON (Power-up Timer) Habilita un temporizador que se dispara en el encendido y permite que, durante su espera, se estabilicen todos los circuitos antes de comenzar a correr el programa.
_LVP_OFF define el modo en que se puede programar el chip. Esta definición en OFF determina que no se puede programar el chip utilizando un sistema de programación de bajo voltaje. Los programadores de PICs utilizan un voltaje de 12 volts en la pata MCLR del chip para poder escribir en su memoria de programa. Al definir el estado OFF de esta configuración se previene una programación accidental (que modificaría el programa y dejaría no operativo al microcontrolador) con los voltajes estándar de funcionamiento en sus patas.
_BODEN_OFF El bit BODEN (Brown Out Reset) en la configuración define la activación o no de una detención del microcontrolador por un descenso de voltaje de alimentación. Puede ser útil, pero no lo utilizamos en este diseño, por eso lo definimos en OFF.
1 2 3 4 5 6 7 8 |
<span style="color: #00008b;"><b>; ----------------------------------------------------------- CBLOCK 0x20 ; Declarar las variables d1 ; usada en Retardos d2 ; usada en Retardos d3 ; usada en Retardos ENDC ; ----------------------------------------------------------- </b></span> |
En este bloque se declaran las variables que necesitaremos utilizar (y se reservan sus espacios en la memoria RAM). Por defecto, las variables son bytes (8 bits).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<span style="color: #00008b;"><b> ; ----------------------------------------------------------- ; INICIALIZAR ; ----------- org 0x0000 ; Indica la dirección de origen del programa clrf STATUS ; limpia el registro de estado movlw 0x00 ; valor cero 00 movwf PCLATH ; al contador de programa = comienzo goto Comienzo ; Salto a comienzo ; Comienzo ; BANCO bcf STATUS,RP0 ; Prepara bcf STATUS,RP1 ; RAM Página 0, en pág 0 están los registros ; de salida de los puertos ; ------------------------------------ ; PUERTOS movlw 0x00 movwf PORTA ; Inicializar valores del PORT A movlw 0x00 movwf PORTB ; Inicializar valores del PORT B movlw 0x80 movwf PORTC ; Inicializar valores del PORT C ; ------------------------------------ ; BANCO bsf STATUS,RP0 ; RAM Page 1, en pág 1 están los registros ; de configuración de los puertos y ; otros módulos del microcontrolador ; --------------------------------------------------------------- ; FUNCION DE LAS PATAS DE LOS PORTS ; --------------------------------------------------------------- movlw b'00000000' movwf TRISA ; Todas las patas port A como salidas movlw b'00000000' movwf TRISB ; Todas las patas port B como salidas movlw b'10111111' movwf TRISC ; Todas las patas port C como entradas excepto RC6/TX movlw 0x06 ; Todas las patas del puerto entrada/salida digital movwf ADCON1 ; esto es necesario para el buen funcionamiento ; de los puertos que tienen ; opciones analógicas (convertidor A/D) ; ; ----------------------------------------------------------- ; BANCO bcf STATUS,RP0 ; Página RAM 0, la operación normal se realiza ; en la página 0 de RAM ; ----------------------------------------------------------- </b></span> |
Las líneas de inicialización en este bloque están ampliamente explicadas con comentarios. En la última parte, debo aclarar que los registros TRIS son los que definen si una pata de entrada/salida es una entrada o una salida. Hay un registro TRIS para cada puerto: TRISA, TRISB y TRISC. Un 0 las define como salidas, un 1 las define como entradas. Por último, los microcontroladores con puertos cuyas patas se pueden definir como entradas al módulo Convertidor Analógico Digital (Analog/Digital Converter, o ADC, en inglés) tienen un registro de configuración en el que se debe definir si se utilizarán o no esas patas como entradas analógicas. Esto lo define el registro ADCON1 y el valor a definir para utilizar todas las patas como entrada/salida digital (tal como las utilizaremos por el momento) es 0x06 (hexadecimal 06).
Esencial: La parte del programa en sí
Observando el diagrama en bloques del robot programable, y con una lectura al artículo de apoyo sobre el chip de manejo de motores, podemos determinar que uno de los motores, el derecho, se maneja a través de dos patas del puerto A, RA0 y RA1, y el otro (izquierdo) a través de las patas RA2 y RA3 del puerto A.
La tabla de señales de control para los motores es como sigue:
| | ||||||
RA0 | RA1 | ACCIÓN | | | RA2 | RA3 | ACCIÓN |
1 | 0 | AVANCE | | | 0 | 1 | AVANCE |
0 | 1 | RETROCESO | | | 1 | 0 | RETROCESO |
0 | 0 | DETENIDO | | | 0 | 0 | DETENIDO |
1 | 1 | DETENIDO | | | 1 | 1 | DETENIDO |
Por lo tanto, las instrucciones de manejo de los motores se pueden definir del siguiente modo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<span style="color: #00008b;"><b>;------------ ; SUBRUTINAS ;------------ DerechoAdelante bsf PORTA,0 ; PORTA 0 = 1 - Motor derecho ad. bcf PORTA,1 ; PORTA 1 = 0 - Motor derecho ad. return ;------------------------------------- DerechoAtras bcf PORTA,0 ; PORTA 0 = 0 = Motor derecho atr. bsf PORTA,1 ; PORTA 1 = 1 = Motor derecho atr. return ;------------------------------------- DerechoDetenido bcf PORTA,0 ; PORTA 0 = 0 = Motor derecho det. bcf PORTA,1 ; PORTA 1 = 0 = Motor derecho det. return ;------------------------------------- IzquierdoAdelante bcf PORTA,2 ; PORTA 2 = 0 = Motor izquierdo ad. bsf PORTA,3 ; PORTA 3 = 1 = Motor izquierdo ad. return ;------------------------------------- IzquierdoAtras bsf PORTA,2 ; PORTA 2 = 1 = Motor izquierdo atr. bcf PORTA,3 ; PORTA 3 = 0 = Motor izquierdo atr. return ;------------------------------------- IzquierdoDetenido bcf PORTA,2 ; PORTA 2 = 0 = Motor izquierdo det. bcf PORTA,3 ; PORTA 3 = 0 = Motor izquierdo det. return ; ----------------------------------------------------------- </b></span> |
Para realizar un movimiento necesitamos definir tiempos, y para eso, para empezar, podemos utilizar un simple rutina de «pérdida de tiempo» (el microcontrolador se queda «perdiendo tiempo» dentro de esta subrutina, y no hace ninguna otra cosa). La rutina la calculé utilizando los servicios del sitio Delay Code Generator donde se puede crear un código de retardo para PICs definiendo algunos parámetros (en el futuro usaremos un módulo TIMER del microcontrolador, pero cada cosa a su tiempo).
La rutina de pérdida de tiempo es:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<span style="color: #00008b;"><b>;------------ Retardo1seg movlw d'0' ; Frecuencia de reloj = 4 MHz ; Retardo real = 1 segundo = 1000000 ciclos ; Error = 0 % ;999997 ciclos movlw d'8' ; 0x08 movwf d1 movlw d'47' ; 0x2F movwf d2 movlw d'3' ; 0x03 movwf d3 Retardo_01 decfsz d1, f goto $+2 decfsz d2, f goto $+2 decfsz d3, f goto Retardo_01 ; 3 ciclos más goto $+1 nop return ; ----------------------------------------------------------- </b></span> |
Veamos entonces cómo es un programa que utilice estas subrutinas para ordenar al robot un movimiento en L.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<span style="color: #00008b;"><b>; ----------------------------------------------------------- Principal call DerechoAdelante call IzquierdoAdelante ; call Retardo1seg ; call DerechoAdelante call IzquierdoDetenido ; call Retardo1seg ; call DerechoAdelante call IzquierdoAdelante ; call Retardo1seg ; call DerechoDetenido call IzquierdoDetenido ; ; las siguientes llamadas al retardo son para que el robot ; se detenga unos segundos antes de moverse nuevamente en 'L' ; y repetir esta secuencia constantemente... call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg ; goto Principal ; ----------------------------------------------------------- </b></span> |
El programa completo en ASM con el método de llamado a subrutinas es el que sigue. Más abajo encontrarán un enlace para bajarse el archivo en formato TXT con el programa completo en ASM listo para compilar. En el próximo artículo presentaré la conversión del programa al método de programación con MACROS y una serie de ejemplos de programas con distintas rutinas de movimiento.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
<span style="color: #00008b;"><b>;************************************************************************ ; Procesador: PIC16F876 con Xtal 4 MHz ; Función: Manejo del robot programable ; Hardware: Protoboard ; Archivo: 01 ; Autor: Eduardo J. Carletti ; Fecha: 21 septiembre 2014 ; ; Historia: El robot avanza formando una L ; ;************************************************************************* list P = 16F876a include "P16f876a.inc" ERRORLEVEL 1;-302 ; para evitar los mensajes de cambio de ; banco en el resultado del compilador __config _CP_OFF & _XT_OSC & _WDT_OFF & _PWRTE_ON & _LVP_OFF & _BODEN_OFF CBLOCK 0x20 ; Declarar las variables d1 ; usada en Retardo1seg d2 ; usada en Retardo1seg d3 ; usada en Retardo1seg ENDC ; ; ----------- ; INICIALIZAR ; ----------- ; org 0x0000 ; Indica la dirección de origen del programa clrf STATUS ; limpia el registro de estado movlw 0x00 ; valor cero 00 movwf PCLATH ; al contador de programa = comienzo goto Comienzo ; Salto a comienzo org 0x0004 ; Comienzo ; BANCO bcf STATUS,RP0 bcf STATUS,RP1 ; RAM Página 0 ; ------------------------------------ movlw 0x00 movwf PORTA ; Inicializar PORT A movlw 0x00 movwf PORTB ; Inicializar PORT B movlw 0x80 movwf PORTC ; Inicializar PORT C ; ; ------------------------------------ ; BANCO bsf STATUS,RP0 ; RAM Page 1 ; --------------------------------------------------------------- ; FUNCION DE LAS PATAS DE LOS PORTS ; --------------------------------------------------------------- ; movlw 0x00 movwf TRISA ; Todas las patas port A salida movlw 0x00 movwf TRISB ; Todas las patas port B salida movlw b'10000000' movwf TRISC ; Todas las patas port C salida excepto RC7/RX movlw 0x06 ; Todas las patas entrada/salida digital movwf ADCON1 ; ; ----------------------------------------------------------- ; BANCO bcf STATUS,RP0 ; Página RAM 0 ; ----------------------------------------------------------- ; ----------------------------------------------------------- ; PRINCIPAL ; ----------------------------------------------------------- Principal call DerechoAdelante call IzquierdoAdelante ; call Retardo1seg ; call DerechoAdelante call IzquierdoDetenido ; call Retardo1seg ; call DerechoAdelante call IzquierdoAdelante ; call Retardo1seg ; call DerechoDetenido call IzquierdoDetenido ; call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg call Retardo1seg ; goto Principal ; ;------------------------------------- ; SUBRUTINAS ;------------------------------------- DerechoAdelante bsf PORTA,0 ; PORTA 0 = 1 - Motor derecho ad. bcf PORTA,1 ; PORTA 1 = 0 - Motor derecho ad. return ;------------------------------------- DerechoAtras bcf PORTA,0 ; PORTA 0 = 0 = Motor derecho atr. bsf PORTA,1 ; PORTA 1 = 1 = Motor derecho atr. return ;------------------------------------- DerechoDetenido bcf PORTA,0 ; PORTA 0 = 0 = Motor derecho det. bcf PORTA,1 ; PORTA 1 = 0 = Motor derecho det. return ;------------------------------------- IzquierdoAdelante bcf PORTA,2 ; PORTA 2 = 0 = Motor izquierdo ad. bsf PORTA,3 ; PORTA 3 = 1 = Motor izquierdo ad. return ;------------------------------------- IzquierdoAtras bsf PORTA,2 ; PORTA 2 = 1 = Motor izquierdo atr. bcf PORTA,3 ; PORTA 3 = 0 = Motor izquierdo atr. return ;------------------------------------- IzquierdoDetenido bcf PORTA,2 ; PORTA 2 = 0 = Motor izquierdo det. bcf PORTA,3 ; PORTA 3 = 0 = Motor izquierdo det. return ;------------------------------------- Retardo1seg movlw d'0' ; Frecuencia de reloj = 4 MHz ; Retardo real = 1 seconds = 1000000 cycles ; Error = 0 % ;999997 cycles movlw d'8' ; 0x08 movwf d1 movlw d'47' ; 0x2F movwf d2 movlw d'3' ; 0x03 movwf d3 Retardo_01 decfsz d1, f goto $+2 decfsz d2, f goto $+2 decfsz d3, f goto Retardo_01 ; 3 ciclos más goto $+1 nop return ; ---------------------------- END </b></span> |
El archivo ASM se puede bajar de AQUÍ.