Los que llevamos un tiempo en esto de la robótica para concursos nos vamos dando cuenta que se pierde mucho tiempo en muchas cosas para hacer las pruebas a nuestros robots y dejarlos configurados correctamente. Una de las cosas que lleva más tiempo es reprogramar el robot cada vez que queremos cambiar algo en el firmware que no funciona: tienes que ir a por el robot, cogerlo, pararlo, llevarlo hasta el ordenador, quitarle la batería, ponerle el cable ICSP, seleccionar el fichero .hex, programarlo, ponerle la batería de nuevo, llevarlo a la pista, dejarlo en ella y activarlo.
Con este artículo nos vamos a ahorrar todos los pasos mencionados simplemente programando el firmware del robot mediante bluetooth gracias a un bootloader.
Aunque este artículo esté enfocado a la baby orangutan (atmel328P) y el módulo bluetooth CSR, podría funcionar con otros microcontroladores AVR (incluido Arduino) y otros módulos bluetooth.
Como comentaba antes, la posibilidad de programar el firmware de nuestro microcontrolador AVR mediante bluetooth se basa en un bootloader. Los bootloader permiten reprogramar el microcontrolador sin necesidad de tener que usar siempre un programador ICSP. En realidad no es más que un programa que se carga en la zona alta de la memoria y que cambiando los fuse bits del microcontrolador hacemos que sea el primero que se ejecute. Esta gran ventaja nos permite que el bootloader esté comprobando durante unos segundos si se envía información desde el puerto serie del microcontrolador, si es así, reprograma el microcontolador (excepto la zona de memoria del bootloader) con el código enviado y lo ejecuta, si no es así, entonces pasado un tiempo ejecuta el código que hubiese anteriormente. Como el puerto serie ya ha sido configurado por el bootloader, en nuestro programa ya podremos usar las rutinas de envío y recepción por el puerto serie sin la necesidad de tenerlo que configurar de nuevo.
El objetivo de este artículo no es explicar cómo hacer un bootloader, sino utilizarlo directamente. Por eso voy a usar uno ya hecho: KAVR. Este bootloader espera por el puerto serie que se le envíe el fichero .hex del código a programar, por lo que podríamos usar cualquier programa como Minicom (linux), Hyperterminal (windows xp) o, como en mi caso, Tera Term (windows 7).
Antes de usarlo hay que retocar los fuentes, por lo que debemos bajarnos el código fuente, descomprimirlo, entrar en el directorio bootloaderskavr-0.2_328P-14s y editar el fichero Makefile. Dentro de este hay 3 valores que cambiar dependiendo de nuestro sistema:
- F_CPU: La velocidad de nuestro microncontrolador en hertzios, en el caso de la Baby Orangutan es 20000000.
- BAUD: La velocidad del puerto serie en bps, en mi caso el bluetooth está configurado a 115200.
- TIMEOUT: El tiempo que esperará el bootloader a posibles datos en el puerto serie antes de ejecutar el firmware anterior, en mi caso le he puesto 5000 (5 segundos).
Dentro de la misma carpeta tenemos que editar el fichero kavr.c y añadir a la lista de cabeceras la siguiente:
1 |
#include <avr/wdt.h> |
y justo antes de la línea
1 |
UBRR0H = UBRRH_VALUE; |
añadir las siguientes dos líneas que desactivan el watchdog (que explicaré más adelante) en el bootloader:
1 2 |
MCUSR = 0; wdt_disable(); |
Después hay que recompilarlo para que nos genere el fichero kavr.hex Para ello usaremos el toolchain de AVR, que en mi caso está en directorio C:Program Files (x86)AtmelAVR ToolsAVR Toolchainbin:
1 2 |
C:Program Files (x86)\Atmel\AVR Tools\AVR Toolchain\bin\make clean C:Program Files (x86)\Atmel\AVR Tools\AVR Toolchain\bin\make |
A continuación debéis activar en el microcontrolador los fuse bits BOOTSZ y BOOTRST para indicarle que va a tener un bootloader, el BOOTSZ debe ser de 512 palabras para que quepa el KAVR:
Finalmente enviamos el fichero kavr.hex mediante ICSP al microcontrolador, esta será la última vez que tengáis que hacerlo 😉 .
Ya tenemos nuestro bootloader activo. Ahora hay que hacer nuestro programa, compilarlo y enviárselo mediante bluetooth a nuestro microcontrolador. Para ello previamente deberemos haber vinculado nuestro dispositivo bluetooth a nuestro PC. Después abriremos el puerto serie que se ha creado en la vinculación con los siguientes parámetros 115200,8N1,XON/XOFF:
Debido a la alta velocidad de transmisión he necesitado poner un retardo en el envío de cada línea de 1 ms, pero este ha sido mi caso y no se si os pasará igual si usáis otro módulo bluetooth (si ponéis todo el conjunto a una velocidad menor, por ejemplo 19200, no será necesario ese retardo en el envío con el módulo CSR). Lo del control de errores XON/XOFF lo implementa el bootloader, permite que el microcontrolador le diga al software de control de puerto serie que pare el flujo de datos mientras escribe en la memoria flash, así no se pierdan bytes por el camino al no poder procesarlos a tiempo.
Una vez abierto el canal de comunicación el bootloader nos enviará el texto KAVR indicando que está esperando a que le enviemos un fichero .hex:
Si no le enviamos el fichero en 5 segundos lanzará el programa que esté en la posición cero de la memoria flash, pero si no existiese tal programa, volvería el bootloader a ejecutarse. El fichero debe enviarse como fichero de texto, en Tera Term se hace en el menú FileSend File…
Si la programación ha ido mal, el bootloader enviará el símbolo ?. Si ha ido bien enviará el carácter S e inmediatamente se ejecutará el código.
Ahora sólo queda conseguir que cuando queramos programar otro firmware no tengamos la necesidad de acercarnos al robot y pulsar el botón de reset para que se resetee el microcontrolador y arranque el bootloader de nuevo. Por desgracia en los AVR no hay posibilidad de resetear mediante software, sin embargo hay un truco para lograrlo: El watchdog es un temporizador que si llega al final de su cuenta resetea el micro, por lo tanto para que no ocurra esto se debe poner a 0 el contador cada poco tiempo. El truco consiste en que cuando el microcontrolador reciba una orden a través del puerto serie, este lance un bucle infinito que no actualice el contador del watchdog, provocando su reseteo y en consecuencia ejecutando el bootloader.
A continuación pongo un ejemplo de código muy sencillo: Lo que hace el programa es activar la interrupción de recepción de carácteres. Después apaga y enciende un led en el pin PD7 cada 500 ms. indefinidatmente, pero cuando recibe una R por el puerto serie, cambia el valor de una variable a 1. Dentro del bucle principal se comprueba si esa variable está a 1, y si es así, envía OK por el puerto serie, activa el watchdog y se mete en un bucle infinito para provocar el reseteo.
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 |
#include <avr/io.h> #include <util/delay.h> #include <avr/wdt.h> #include <avr/interrupt.h> volatile char reseteo = 0; ISR(USART_RX_vect) { if(UDR0 == 'R') reseteo = 1; } void escribe_uart(char caracter) { loop_until_bit_is_set(UCSR0A, UDRE0); UDR0 = caracter; } int main(void) { DDRD |= _BV(PD7); UCSR0B |= _BV(RXCIE0); sei(); for(;;) { PORTD &= ~_BV(PD7); _delay_ms(500); PORTD |= _BV(PD7); _delay_ms(500); if(reseteo) { escribe_uart('O'); escribe_uart('K'); wdt_enable(WDTO_15MS); for(;;); } } return 0; } |
En la siguiente imagen podemos ver cómo arranca el bootloader con el texto KAVR, como no hay nada que programar no sale nada más y arranca el programa principal, pero al cabo de poco se le ha enviado al programa una R y este ha respondido con un OK, a continuación el microcontrolador se ha reseteado arrancando de nuevo el bootloader y mostrando otra vez KAVR, se le ha enviado un fichero .hex y el bootloader ha respondido con una S indicando que todo ha ido bien y procediendo a ejecutar el programa principal:
Y aquí una demostración en vídeo: