Autor Tema: Hacer un bootloader uno mismo  (Leído 3916 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Icat

  • PIC12
  • **
  • Mensajes: 68
Hacer un bootloader uno mismo
« en: 01 de Febrero de 2021, 15:36:38 »
Hola muy buenas, después de varios días buscando y buscando por San Google no he encontrado ningún sitio al menos que yo me haya dado cuenta de como hacer tu propio Bootloader.

Me refiero a programarte tu propio bootloader en Mplab sin utilizar uno de terceras personas que en algunas ocasiones puede incurrir en problemas legales y eso mejor evitarlo a toda costa.

La pregunta es si alguien conoce o sabe como hacer un bootloader desde cero en MPLAB para el PIC18F4550 ya que la idea es desarrollar un producto con este microcontrolador y que en el futuro si se ha de actualizar se pueda realizar in situ sin necesidad de volvérmelo a traer y quemar de nuevo el PIC etc, etc...

Muchas gracias por adelantado a todos.

Desconectado elreypic2

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1297
Re:Hacer un bootloader uno mismo
« Respuesta #1 en: 01 de Febrero de 2021, 18:17:55 »
Que tal Icat,

Como bien sabes MPLAB no es un lenguaje de programación, sino un entorno gráfico de usuario en el que puedes integrar diferentes lenguajes de programación, como pueden ser: C, BASIC, Assembler. Por lo que no entiendo a qué te refieres con realizar un bootloader en MPLAB.
Tampoco mencionas cuál interface de comunicación pretendes utilizar, si USB o serial (TTL o RS232).
Otra cosa muy importante es el software para enviar el archivo hex hacia el microcontrolador, deseas realizar tu propio software o deseas utilizar alguna terminal ya existente como: Hyperterminal, Teraterm u otra (si es que pretendes utilizar comunicación serial para el bootloader).

En fin, son varios detalles que necesitas definir para comenzar a realizar tu propio bootloader. Que si bien es un concepto que puede encontrar en la propia hoja de datos del microcontrolador en cuestión. Esto está en la sección de Self-programing o Flash-programing.

Ahora bien, lo más fácil es utilizar comunicación serial asíncrona (serial TTL o RS232), ya que es la forma más sencilla de comunicación.

Yo te recomiendo que busques la información sobre el PIC Tiny-blootloader, es gratuito y muy fácil de usar. Además de que está bien documentado para darte una idea de cómo pudieras realizar el tuyo propio.

Saludos y espero esto te sirve de punto de inicio.

elreypic.

Desconectado Picuino

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5892
    • Picuino
Re:Hacer un bootloader uno mismo
« Respuesta #2 en: 01 de Febrero de 2021, 18:37:23 »
Para esa aplicación, además te interesa que los archivos estén encriptados. Para esto se suele utilizar el algoritmo XTEA.

Desconectado vixctor

  • PIC16
  • ***
  • Mensajes: 110
Re:Hacer un bootloader uno mismo
« Respuesta #3 en: 01 de Febrero de 2021, 19:15:19 »
Hacer un bootloader NO es dificil, son 4 conceptos a saber:

1.- Como están configurados los HOLDING REGISTERS del PIC a programar, en tu caso el PIC18F4550: Algunos PICs usan holdings de 64 y otros de 128 bytes

2.- El tamaño propio del código del bootloader: El cual depende de que tan eficiente lo programes y cuanto código que ayude a la corrección de errores requieras: El Tiny bootoloader ocupa menos de 256 bytes por ejemplo mientras que los de microchip ocupan entre 1 y 2 KB

3.- Que tu programa sepa interpretar el código HEX que vas a actualizar, lo cual NO es dificil: El formato HEX lo creo INTEL y hay mucha información de como está codificado en internet

4.- El mecanismo para la carga del programa: Puede ser por firmware, por interruptor o una combinación: Si es por firmware vas a requerir identificar 1 o mas caracteres que le indiquen a microcontrolador que entre a modo de programación y se vaya al bootloader, si es por switch, el valor del switch se puede checar cuando se aplica alimentación al circuito.

En lo personal he implementado el cpódigo del bootloader en varios PICS, basandome en el Tiny Bootloader para construirlo, de hecho, he añadido PICS que NO se encuentran en el programa original tales como el PIC18F46K22, el PIC18F87K22 y los PICS series 16F1xxx

También he creado el programa que actualiza el archivo hex en productos comerciales: La APP notifica que hay una nueva versión de firmware, descarga el nuevo archivo HEX y lo transfiere al producto a través de Bluetooth.

El secreto está en saber que hacer si la transferencia falla a mitad del proceso para que el cliente no termine con un pisapapeles...

Saludos

Desconectado Icat

  • PIC12
  • **
  • Mensajes: 68
Re:Hacer un bootloader uno mismo
« Respuesta #4 en: 02 de Febrero de 2021, 14:26:10 »
Varias cosas JEJE

La placa estara conectada mediante USB por lo tanto se actualizara a través del puerto USB, la idea es realizar todo el Bootloader propio para evitar problemas legales y nuestro propio software para la actualización y diagnostico (esto ya llegara más adelante) primeramente estoy leyendo mucho en internet sobre bootloaders pero lo típico es descargar x archivo volcarlo y a correr (entiéndase que existen matices que me he saltado como ajustarlo a tus necesidades etc etc) y claro yo no quiero hacer eso si no desarrollar uno propio.

El tema de la encriptación no lo había mirado activamente, si que en algún lado lo he leído pero por el momento no me preocupa ya que voy pasito a pasito pero evidentemente me será necesario 100%

Por ultimo daros las gracias a todos por vuestras respuestas espero haber aclarado un poco más mi idea jejeje

Desconectado Icat

  • PIC12
  • **
  • Mensajes: 68
Re:Hacer un bootloader uno mismo
« Respuesta #5 en: 02 de Febrero de 2021, 14:47:54 »
Lo que si hire haciendo es buscar el tema de Tiny Bootloader y ver como esta echo y estudiarlo

Desconectado remi04

  • PIC24F
  • *****
  • Mensajes: 657
Re:Hacer un bootloader uno mismo
« Respuesta #6 en: 02 de Febrero de 2021, 15:31:58 »
Aquí te dejo el mío por si te vale para estudiarlo.

 Usando los macros de opciones puede elegir si lo compilas para USB o para USART.  En USB ocupa casi 20 veces más  :shock:
 Está en CCS.  Tengo otro que además incluye opciones para compilar un bootloader con encriptado por XTEA, pero no lo que querido poner aqui por que es mas lioso aún, sobre todo si lo que quieres es estudiarlo.

 Un saludo.



Código: C
  1. /*USB-CDC BOOTLOADER PIC_18F4550 by Remi04*/
  2.  
  3. // Datos de la memoria del PIC 18F4550
  4. /* Flash = 32768 bytes
  5.  * Write_size holder = 32 bytes
  6.  * erase_size holder = 64 bytes
  7.  */
  8.  
  9. // Requerimientos de espacio para el bootloader:
  10.  
  11. // USANDO USB:
  12. // Requerimiento de espacio para el bootloader: 5.872 bytes (Sin uso de led indicador)
  13. //                                              5.992 bytes (con uso de led indicador)
  14. // En ambos casos el espacio reservado es 6016 bytes (Multiplo de 64 bytes mas cercano del valor del espacio requerido) para que la funcion "write_program_memory()" trabaje adecuadamente.
  15.  
  16.  
  17.  
  18. // USANDO USART:
  19. // Requerimiento de espacio para el bootloader: 1.216 bytes (Sin uso de led indicador)
  20. //                                              1.336 bytes (con uso de led indicador)
  21. // En ambos casos el espacio reservado es 1344 bytes (Multiplo de 64 bytes mas cercano del valor del espacio requerido) para que la funcion "write_program_memory()" trabaje adecuadamente.
  22.  
  23. // Las opciones se configuran mediante macros mas abajo.
  24.  
  25.  
  26. /* FUNCIONAMIENTO
  27.  *
  28.  * Este programa utiliza un led bicolor con cátodo a común cuyos ánodos son conectado a los puertos D6 (Led rojo, y D7 led verde) con su correspondiente resistencia de limitación.
  29.  * Si no se quiere utilizar el led, comentar la macro al efecto mas abajo para ahorrar espacio de flash ocupada por el bootloader.
  30.  * El pin de VCC del conector USB es conectado al pin D5 y en este caso no provee alimentación al pic. Solo se usa para detectar si hay o no conexión fisica usb con el equipo host.
  31.  * El led es usado para indicar el funcionamiento y progreso del bootloader.
  32.  *
  33.  */
  34.  
  35.  
  36.  
  37. /* COMO CARGAR UN FIRMWARE
  38.  *
  39.  * Para cargar un fichero *hex utilizar un terminal exterior rs232, como por ejemplo, Coolterm, Realterm, etc.
  40.  * -Conectar la placa pic al ordenador por usb o por usart segun se elija la compilación. (El usb no debería alimentar al pic).
  41.  * -Dar alimentación a la placa pic. El led encenderá en rojo.
  42.  * -Si la compilación es para USB El programa quedará en espera de enumeración por parte del ordenador. Una vez que el pic haya sido enumerado el led parpadeará 3 veces y quedará encendido en verde permanente.
  43.  * -A partir de ahora el envío del firmware es posible.
  44.  * - Si la compilación es para USART se usa un pulsador para llamar al bootloader. Pulsarlo antes de dar alimentacion y mantenerlo pulsado 2 segundos.
  45.  *
  46.  * - Iniciar el software terminal elegido y configurar el puerto en la velocidad de baudios elegida en la compilación, 8N1 con la opción de control de flujo XON habilitada.
  47.  * - Establecer la comunicaión con el botón "conectar" correspondiente.
  48.  * - Elegir el fichero a enviar.
  49.  * - El fichero se enviará al pic. El led irá emitiendo parpadeos por cada linea hex que haya sido correctamente recibida, decodificada y grabada.
  50.  *
  51.  * - Concluida la carga del firmware el bootloader saltará automáticamente a iniciarla.  
  52.  */
  53.  
  54. /* INDICACIONES DE ERROR.
  55.  * Led parpadeando en color ambar (rojo + verde simultaneos) = Error de Checksum.   Desconectar todo y repetir el proceso.
  56.  * Led parpadeando alternativamente entre rojo y verde) = Error por dirección de memoria fuera del rango permitido.
  57.  */
  58.  
  59.  
  60.  
  61.  
  62.  
  63. #include <18F4550.h>  //18F4550
  64. #fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,CCP2B3
  65. #use delay(clock=48000000)
  66.  
  67.  
  68. // Macros de configuración de compilación.
  69. //#define USE_LED                        // Comentar si no se quiere utilizar el led.
  70. //#define USE_USB                        // Si no se quiere utilizar el modulo USB, el bootloader funcionará en modo RS232 con el modulo USART utilizando los pines TX y RX del pic.
  71.                                         //  Se habilita un pulsador para llamar al bootloader en esta modalidad.
  72. #define BAUDRATE 115200                // Seleccion de velocidad del puerto usb o usart.
  73. // Fin macros de opciones y configuración.
  74.  
  75.  
  76.  
  77.  
  78. #include <string.h>
  79. #include <stdlib.h>
  80.  
  81.  
  82.  
  83. #ifdef USE_USB
  84. #include <usb_cdc.h>
  85. #define USB_CON_SENSE_PIN PIN_D5       //  El Pin D5 recibe los 5v procedentes del puerto usb del ordenador.  Este voltaje se utiliza para determinar si hay o no conexión fisica con el ordenador.
  86. #define usb_stat input(PIN_D5)         // Vdd 5v procedentes del cable usb exterior. Ese vdd se utiliza como señal para detectar que se ha conectado el cable
  87. #bit usben=0xf6d.3                   // Bit USBEN, habilita o desactiva el modulo USB del pic.
  88. #endif
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96. #ifndef USE_USB
  97. #bit push=0xf83.5              // PIN_D5 pulsador para llamar al bootloader.
  98. #endif
  99.  
  100.  
  101.  
  102.  
  103. #ifdef USE_LED
  104. #bit usbled_rojo= 0xf83.6              // PIN_D6 Led rojo de USB (bicolor).  
  105. #bit usbled_verde=0xf83.7             // PIN_D7 Led verde de USB (bicolor))
  106. #endif
  107.  
  108.  
  109. #ifndef USE_USB
  110. #use rs232(baud=BAUDRATE,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
  111. #endif
  112.  
  113. #ifdef USE_USB
  114. #define START_PROGRAM 0x1780         // Primera posicion autorizada para la escritura del programa.
  115. #else
  116. #define START_PROGRAM 0x540        
  117. #endif
  118.  
  119.  
  120.  
  121.  
  122. #org START_PROGRAM + 4,0x7ffe {}               // a partir de la posicion START_PROGRAM hasta el final de la flash es reservado para el programa a cargar
  123.                                    // pero permitimos que el bootloader ocupe hasta 4 words más para colocar el vector de arranque el cual será luego sustituido por
  124.                                   // el firmware a cargar.
  125.  
  126. char linea[43] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};    // Array para contener los caracteres ascii que representan una linea hex a su tamaño máximo.
  127.  
  128. int16 h_addr = 0;                     // Address 16 bytes MSB para la flash.
  129. int16 l_addr = 0;                    // Address 16 bits LSB para flash.
  130. int32 address = 0;                  // Address 32 bits final para la flash.    
  131.  
  132.  
  133. void get_line();                  // Detecta y recibe una linea hex completa.
  134. void decode();                    // decodifica cada linea y si es procedente, graba su contenido en el buffer.  
  135. unsigned int atoi_b16(char *s);   // Recibe dos caracteres Ascii que representan el literal de un numero hexadecimal de 8 bits y devuelve su valor entero equivalente.
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142. #org START_PROGRAM + 1,START_PROGRAM + 2                  // vector de arranque de la aplicación a cargar. Esto será sobreescrito por el "goto" inicial de la app cargada
  143. void main_program (void) {
  144.      while(1);    
  145. }                                    // fin vector de arranque
  146.  
  147.  
  148.  
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155. void main() {              
  156.     set_tris_a(0b11111111);            // Configuración de E/S de todos los puertos. Configurar según necesidades.
  157.     set_tris_b(0b11010011);
  158.     set_tris_c(0b10001111);
  159.     set_tris_d(0b00110111);
  160.     set_tris_e(0b101);
  161.     delay_ms(200);                     // Estabilizar fuente de energia y sistemas.
  162.  
  163. #ifdef USE_LED
  164.     usbled_rojo = 1;               // Estado inicial de los led´s. Bootloader inicia con el led rojo encendido.
  165.     usbled_verde = 0;
  166. #endif
  167.    
  168. #ifdef USE_USB
  169.     usb_init();                               // Inicializacion del modulo USB
  170.     usb_cdc_init();                            //configuración de parámetros de comunicación serial generada por el USB(baudios, bit paridad etc)
  171.     usb_cdc_line_coding.dwDTERrate = BAUDRATE;  // configuro la velocidad rs232 emulado por el USB de la cpu a 115.200 bps.  
  172.  
  173.    if (!usb_stat) main_program();      // Si no hay cable usb conectado en el inicio saltamos a la aplicación.
  174.     delay_ms(500);                           // Estabiliza inicializado del USB.
  175.     while(!usb_enumerated());           // Esperando enumeración por parte de Windows.
  176. #else
  177.     if (push) main_program();          // Si no hemos pulsado, saltamos a la aplicación.
  178. #endif
  179.    
  180.    
  181.    
  182.    
  183. #ifdef USE_LED
  184.     for (int i=0;i<3;i++) {
  185.         usbled_verde = 0;                    // indicamos enumerado correcto. (Tres cambios de rojo a verde del led, y lo dejamos con el verde encendido).
  186.         usbled_rojo = 1;
  187.         delay_ms(300);
  188.         usbled_rojo = 0;
  189.         usbled_verde = 1;
  190.         delay_ms(300);
  191.     }
  192. #endif                                        
  193.     while(1) get_line();                     // Vamos a la función para cargar el programa.                                                                                                                                                              
  194. }
  195.  
  196.  
  197.  
  198.  
  199.  
  200. void get_line() {  
  201.     int index = 0;            // indice para el array linea.        
  202.  
  203. #ifdef USE_USB
  204.     while (usb_cdc_getc() != ':')     // Mientras no recibamos un inicio de linea (:) esperaremos en este bucle.                      
  205.         while(!usb_cdc_kbhit())  if (!usb_stat) main_program();      // Esperamos primer caracter desde el software externo. Si desconectamos el cable usb salimos del bootloader y saltamos a la aplicación.                                                                    
  206.              
  207.     // Una vez recibido el caracter ":" (inicio de linea) salimos del bucle y comenzamos a parsearla.
  208.    
  209.    
  210.     // Parseando linea hex del fichero hexadecimal.
  211.     while(linea[index] != 0x0D)               // Recibiremos los caracter en el array hasta que se recibe un ENDLINE (0x0d).                                  
  212.         linea[index++] = usb_cdc_getc();      // Cargamos el caracter recibido en orden en el array "linea".                      
  213.                                
  214.    // Una vez recibido el endline:
  215.     usb_cdc_putc(19); // XOFF        // Enviamos XOFF al software exterior para que detenga la transferencia de datos mientras procesamos la linea.
  216. #else
  217.     while(getc() != ':')
  218.         while(!kbhit());
  219.     while(linea[index] != 0x0D)               // Recibiremos los caracter en el array hasta que se recibe un ENDLINE (0x0d).                                  
  220.         linea[index++] = getc();
  221.        
  222.    
  223. #endif
  224.    
  225.    
  226.     decode();  // Enviamos a decodificar la linea y grabarla en la flash si procede.  
  227. }
  228.  
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  
  235.  
  236. void decode() {              // Decodificamos la linea y la grabamos.
  237.      
  238.     int8 counter = 0;                  // contador numero de bytes de datos en la linea.
  239.     int8 linetype = 0;                 // Tipo de linea.
  240.     int8 data[32];                     // Buffer de preparación de los datos para enviar a la flash.
  241.     int8 i = 0;                        // control indices iteracciones varias.
  242.     int8 dataidx = 0;                  // indice paso de datos entre linea --> buffer data.
  243.     int16 timeout = 0;                 // para detectar conclusión del envío de todos los caracteres restantes en coolterm u otras al terminar la descarga de lineas.
  244.     counter = atoi_b16(&linea[0]);      // atoi_b16 tomará dos caracteres ascii que representan un numero hexadecimal y los convertirá en un entero de 8 bits.
  245.     l_addr =  make16(atoi_b16(&linea[2]),atoi_b16(&linea[4]));       // cargamos en address la dirección (16 bits bajos).
  246.     linetype = atoi_b16(&linea[6]);                                   // cargamos el tipo de linea.
  247.     address += l _addr;                                  // pasamos a address, su valor (por si tiene cargado algo en 32 bits) + el byte bajo)
  248.      
  249.     int8 left = (counter * 2) + 9;       // Counter contiene el numero de enteros de 8 bits que hay que grabar, son dos caracteres por entero, por eso multiplicamos su valor por dos.
  250.                                            //, le sumamos 9 por que los caracteres del principio de la linea ":",numero de bytes "00", address, "0000", tipo de linea "00", son 9 caracteres
  251.                                           // Aqui lo usamos para saber el numero de bytes que hay que meter en checksum, mas abajo también es utilizado por el buffer Data
  252.                                           // ahora Left contiene la posición del primer caracter de checksum.
  253.          
  254.     // Comprobación de errores en la linea recibida
  255.    
  256.     int checksum = 0;                  // valor inicial a checksum.
  257.     for (i=0; i<left; i+=2)            // el checksum se verifica con los caracteres desde posicion 0 de la linea, hasta el final excepto los dos ultimos valores (que son el propio checksum). tampoco se tiene en cuenta el endline, es decir, el LF ni el CR que terminan cada línea.
  258.         checksum += atoi_b16 (&linea[i]);  // concatenamos y sumamos todos los valores arriba indicados.
  259.     checksum = 0xff - checksum + 1;    // restamos a 255 el valor que nos da la concatenación y le sumamos 1. El resultado ha de coincidir con el valor del entero
  260.                                        // que contienen los dos ultimos caracteres de la linea.
  261.    
  262.     if (checksum != atoi_b16 (&linea[left])){   // comprobamos checksum, si no es correcto, se detiene la carga del firmware y permanecemos en bucle cerrado
  263.         while(1) {                              // indicando el error mediante el led.
  264. #ifdef USE_LED            
  265.             usbled_rojo = 1;
  266.             usbled_verde = 1;
  267.             delay_ms(800);          
  268.             usbled_rojo = 0;
  269.             usbled_verde = 0;
  270.             delay_ms(200);
  271. #endif
  272.         }
  273.     }  
  274.      
  275.     // Si checksum es correcto, procedemos a decodificar la linea:
  276.    
  277.    
  278.     if (linetype == 1) {     // Linetype = 1 significa "Envio del programa concluido".
  279.        
  280.        
  281.        
  282.       #ifdef USE_USB
  283.         usb_cdc_putc(6);    // ACK      // Acuse de recibo al software externo.
  284.         usb_cdc_putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.
  285. #else
  286.         putc(6);    // ACK      // Acuse de recibo al software externo.
  287.         putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.        
  288. #endif
  289.                          // Una vez concluida la carga del programa seguiremos aqui recibiendo caracteres procedentes del software que envía el fichero *.hex al bootloader
  290.                          // (coolterm u otros) ya que el pie del fichero *.hex suele contener metadatos como fecha de compilación y algunos mas que
  291.                          // no sirven aqui, pero que si no los recibimos, el software no puede concluir el envío y se nos puede quedar "pillado" con la barra de progreso  
  292.                          // en el 99% o incluso en el 100% esperando que atendamos esos caracteres.
  293.   #ifdef USE_USB                      
  294.         while(1) {         // Seguimos entonces recibiendo todo lo que nos envíe el software externo hasta que ya no envíe nada. Eso lo detectaremos mediante una espera            
  295.             while(!usb_cdc_kbhit()) {
  296.                 timeout++;              
  297.                 delay_ms(1);          
  298.                 if (timeout > 1000) {   // Si tras 1000 ms no se ha recibido nada, entendemos que el sotware externo a concluido.  
  299.                     delay_ms(1000);
  300.                     usben = 0;             // Detiene el modulo USB.
  301.                     main_program();        // Enviamos a ejecutar el programa principal.
  302.                 }
  303.             }
  304.         char c = usb_cdc_getc();
  305.         if (c == 0x0d) usb_cdc_putc(6);          // si recibimos endline enviamos ack para que la app siga enviando lineas.
  306.         timeout = 0;
  307.         }
  308. #else
  309.      while(1) {         // Seguimos entonces recibiendo todo lo que nos envíe el software externo hasta que ya no envíe nada. Eso lo detectaremos mediante una espera            
  310.             while(!kbhit()) {
  311.                 timeout++;              
  312.                 delay_ms(1);          
  313.                 if (timeout > 1000) {   // Si tras 1000 ms no se ha recibido nada, entendemos que el sotware externo a concluido.  
  314.                     delay_ms(1000);                  
  315.                     main_program();        // Enviamos a ejecutar el programa principal.
  316.                 }
  317.             }
  318.         char c = getc();
  319.         if (c == 0x0d) putc(6);          // si recibimos endline enviamos ack para que la app siga enviando lineas.
  320.         timeout = 0;
  321.         }      
  322. #endif
  323.      }
  324.     else if (linetype == 4) { // si el tipo de linea es "4" es por que tenemos que usar una dirección de más de 16 bits de datos, obtenemos de esta linea los otros 16 bits, y saltamos al final del bucle para pedir la siguiente linea.
  325.         h_addr = make16(atoi_b16(&linea[9]), atoi_b16(&linea[11]));  // ya tenemos en h_addr los otros 16 bits.
  326.         address = make32(h_addr,l_addr);
  327.        
  328. #ifdef USE_USB
  329.         usb_cdc_putc(6);    // ACK      // Acuse de recibo al software externo.
  330.         usb_cdc_putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.
  331. #else
  332.         putc(6);    // ACK      // Acuse de recibo al software externo.
  333.         putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.        
  334. #endif
  335.     }
  336.      
  337.     else if (linetype == 0) {  // si el tipo de linea es "0", se trata de una linea relevante de datos para la flash.            
  338.         if (((address >= START_PROGRAM) && (address < 0x7fff)) || (address == 0x300000) || (address == 0xf00000))  {  // Solo si la posicion de memoria está comprendida entre la zona reservada grabamos la flash
  339.                                // Si está fuera de rango pero es 0x300000 o 0xf00000 si que grabamos, pues son los fuses y los datos de la eeprom interna
  340. #ifdef USE_LED            
  341.             usbled_verde = 1;     // Daremos un parpadeo del led cada vez que una linea haya sido grabada en la flash.
  342. #endif
  343.             for (i = 9,dataidx = 0; i < left; i += 2)  data[dataidx++]=atoi_b16(&linea[i]); // a partir del caracter 9 de una linea, hasta la posicion "left" calculada segun "count" (numero de bytes)
  344.                                                                                             // cargamos en el buffer DATA los enteros ya convertidos de los caracteres. Aunque no está lleno, data es un array de 32 bytes  
  345.                                                                                            // debido a que la función write_program_memory en el caso de este pic, trabaja por bloques de 32 bytes
  346.             write_program_memory(address, data, counter);       // Grabamos en la flash el bloque completo de 32 bytes.
  347. #ifdef USE_LED          
  348.             usbled_verde = 0;   // Función Parpadeo led.    
  349. #endif            
  350.             address = 0;      // dejamos limpio Address para evitar acumulacion no deseada al principio de esta función.
  351. #ifdef USE_USB
  352.         usb_cdc_putc(6);    // ACK      // Acuse de recibo al software externo.
  353.         usb_cdc_putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.
  354. #else
  355.         putc(6);    // ACK      // Acuse de recibo al software externo.
  356.         putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.        
  357. #endif
  358.         }   // notese que hemos elegido que el programa ocupa a partir de la dirección START_PROGRAM. Esa dirección es el multiplo de 64 "flash_erase_size" mas cercano.
  359.             // que tengo por encima de lo minimo que ocupa este bootloader + una reserva de espacio por si el dia de mañana tuviese que actualizar el propio bootloader.
  360.             // desde el firmware.
  361.             // de modo que esta función trabajará de forma mas optima por que borrará todo el bloque de 64 bytes en cada operación multiplo,  
  362.             // y eso garantiza que no queden datos residuales de anteriores versiones del programa cargado.
  363.            // Si esto no se cumple, sería prudente efectuar un borrado completo de la zona reservada en flash antes de cargar un programa.
  364.        
  365.         else {  // Si ADDRESS está fuera de rango, generamos indicación de error y detenemos la carga de firmware.                                                    
  366.             while(1) {                              // Led rojos parpadeando alternativamente entre ellos indicando error por address fuera de rango autorizado.
  367. #ifdef USE_LED
  368.                 usbled_verde = 0;          // Led parpadea alternativamente entre verde y rojo.
  369.                 usbled_rojo = 1;
  370.                 delay_ms(500);
  371.                 usbled_verde = 1;    
  372.                 usbled_rojo = 0;
  373.                 delay_ms(500);
  374. #endif
  375.             }
  376.                  
  377.         }
  378.      
  379.     }                              
  380. }
  381.  
  382.  
  383.  
  384. unsigned int atoi_b16(char *s) {    // obtiene un numero entero en hexadecimal de 8 bits a partir de dos caracter headecimales.-
  385.    unsigned int result = 0;         // A esta funcion se le pasa como argumento la direcion de memoria donde está el primer caracter mediante &linea[8] por ejemplo
  386.    int i;                            
  387.  
  388.    for (i=0; i<2; i++,s++)  {      
  389.       if (*s >= 'A')                                // si el valor que hay en la posicion de memoria apuntada por "s" es igual o superior a 65 (A = 65)        
  390.          result = 16*result + (*s) - 'A' + 10;      // calculamos.
  391.       else
  392.          result = 16*result + (*s) - '0';            // calculamos.
  393.    }                                                  // siguiente iteracción; incrementa s++, es decir, leerá el valor de la siguiente posicion de memoria y repetirá el proceso.
  394.  
  395.    return(result);
  396. }
« Última modificación: 02 de Febrero de 2021, 19:31:01 por remi04 »

Desconectado Icat

  • PIC12
  • **
  • Mensajes: 68
Re:Hacer un bootloader uno mismo
« Respuesta #7 en: 02 de Febrero de 2021, 15:52:31 »
Joe muchas gracias si usare tu ejemplo de CCS y con lo del Tiny para estudiarlos y usar San Google XDDDD

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18310
    • MicroPIC
Re:Hacer un bootloader uno mismo
« Respuesta #8 en: 03 de Febrero de 2021, 02:45:04 »
Aquí te dejo el mío por si te vale para estudiarlo.

 Usando los macros de opciones puede elegir si lo compilas para USB o para USART.  En USB ocupa casi 20 veces más  :shock:
 Está en CCS.  Tengo otro que además incluye opciones para compilar un bootloader con encriptado por XTEA, pero no lo que querido poner aqui por que es mas lioso aún, sobre todo si lo que quieres es estudiarlo.

 Un saludo.



Código: C
  1. /*USB-CDC BOOTLOADER PIC_18F4550 by Remi04*/
  2.  
  3. // Datos de la memoria del PIC 18F4550
  4. /* Flash = 32768 bytes
  5.  * Write_size holder = 32 bytes
  6.  * erase_size holder = 64 bytes
  7.  */
  8.  
  9. // Requerimientos de espacio para el bootloader:
  10.  
  11. // USANDO USB:
  12. // Requerimiento de espacio para el bootloader: 5.872 bytes (Sin uso de led indicador)
  13. //                                              5.992 bytes (con uso de led indicador)
  14. // En ambos casos el espacio reservado es 6016 bytes (Multiplo de 64 bytes mas cercano del valor del espacio requerido) para que la funcion "write_program_memory()" trabaje adecuadamente.
  15.  
  16.  
  17.  
  18. // USANDO USART:
  19. // Requerimiento de espacio para el bootloader: 1.216 bytes (Sin uso de led indicador)
  20. //                                              1.336 bytes (con uso de led indicador)
  21. // En ambos casos el espacio reservado es 1344 bytes (Multiplo de 64 bytes mas cercano del valor del espacio requerido) para que la funcion "write_program_memory()" trabaje adecuadamente.
  22.  
  23. // Las opciones se configuran mediante macros mas abajo.
  24.  
  25.  
  26. /* FUNCIONAMIENTO
  27.  *
  28.  * Este programa utiliza un led bicolor con cátodo a común cuyos ánodos son conectado a los puertos D6 (Led rojo, y D7 led verde) con su correspondiente resistencia de limitación.
  29.  * Si no se quiere utilizar el led, comentar la macro al efecto mas abajo para ahorrar espacio de flash ocupada por el bootloader.
  30.  * El pin de VCC del conector USB es conectado al pin D5 y en este caso no provee alimentación al pic. Solo se usa para detectar si hay o no conexión fisica usb con el equipo host.
  31.  * El led es usado para indicar el funcionamiento y progreso del bootloader.
  32.  *
  33.  */
  34.  
  35.  
  36.  
  37. /* COMO CARGAR UN FIRMWARE
  38.  *
  39.  * Para cargar un fichero *hex utilizar un terminal exterior rs232, como por ejemplo, Coolterm, Realterm, etc.
  40.  * -Conectar la placa pic al ordenador por usb o por usart segun se elija la compilación. (El usb no debería alimentar al pic).
  41.  * -Dar alimentación a la placa pic. El led encenderá en rojo.
  42.  * -Si la compilación es para USB El programa quedará en espera de enumeración por parte del ordenador. Una vez que el pic haya sido enumerado el led parpadeará 3 veces y quedará encendido en verde permanente.
  43.  * -A partir de ahora el envío del firmware es posible.
  44.  * - Si la compilación es para USART se usa un pulsador para llamar al bootloader. Pulsarlo antes de dar alimentacion y mantenerlo pulsado 2 segundos.
  45.  *
  46.  * - Iniciar el software terminal elegido y configurar el puerto en la velocidad de baudios elegida en la compilación, 8N1 con la opción de control de flujo XON habilitada.
  47.  * - Establecer la comunicaión con el botón "conectar" correspondiente.
  48.  * - Elegir el fichero a enviar.
  49.  * - El fichero se enviará al pic. El led irá emitiendo parpadeos por cada linea hex que haya sido correctamente recibida, decodificada y grabada.
  50.  *
  51.  * - Concluida la carga del firmware el bootloader saltará automáticamente a iniciarla.  
  52.  */
  53.  
  54. /* INDICACIONES DE ERROR.
  55.  * Led parpadeando en color ambar (rojo + verde simultaneos) = Error de Checksum.   Desconectar todo y repetir el proceso.
  56.  * Led parpadeando alternativamente entre rojo y verde) = Error por dirección de memoria fuera del rango permitido.
  57.  */
  58.  
  59.  
  60.  
  61.  
  62.  
  63. #include <18F4550.h>  //18F4550
  64. #fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,CCP2B3
  65. #use delay(clock=48000000)
  66.  
  67.  
  68. // Macros de configuración de compilación.
  69. //#define USE_LED                        // Comentar si no se quiere utilizar el led.
  70. //#define USE_USB                        // Si no se quiere utilizar el modulo USB, el bootloader funcionará en modo RS232 con el modulo USART utilizando los pines TX y RX del pic.
  71.                                         //  Se habilita un pulsador para llamar al bootloader en esta modalidad.
  72. #define BAUDRATE 115200                // Seleccion de velocidad del puerto usb o usart.
  73. // Fin macros de opciones y configuración.
  74.  
  75.  
  76.  
  77.  
  78. #include <string.h>
  79. #include <stdlib.h>
  80.  
  81.  
  82.  
  83. #ifdef USE_USB
  84. #include <usb_cdc.h>
  85. #define USB_CON_SENSE_PIN PIN_D5       //  El Pin D5 recibe los 5v procedentes del puerto usb del ordenador.  Este voltaje se utiliza para determinar si hay o no conexión fisica con el ordenador.
  86. #define usb_stat input(PIN_D5)         // Vdd 5v procedentes del cable usb exterior. Ese vdd se utiliza como señal para detectar que se ha conectado el cable
  87. #bit usben=0xf6d.3                   // Bit USBEN, habilita o desactiva el modulo USB del pic.
  88. #endif
  89.  
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96. #ifndef USE_USB
  97. #bit push=0xf83.5              // PIN_D5 pulsador para llamar al bootloader.
  98. #endif
  99.  
  100.  
  101.  
  102.  
  103. #ifdef USE_LED
  104. #bit usbled_rojo= 0xf83.6              // PIN_D6 Led rojo de USB (bicolor).  
  105. #bit usbled_verde=0xf83.7             // PIN_D7 Led verde de USB (bicolor))
  106. #endif
  107.  
  108.  
  109. #ifndef USE_USB
  110. #use rs232(baud=BAUDRATE,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
  111. #endif
  112.  
  113. #ifdef USE_USB
  114. #define START_PROGRAM 0x1780         // Primera posicion autorizada para la escritura del programa.
  115. #else
  116. #define START_PROGRAM 0x540        
  117. #endif
  118.  
  119.  
  120.  
  121.  
  122. #org START_PROGRAM + 4,0x7ffe {}               // a partir de la posicion START_PROGRAM hasta el final de la flash es reservado para el programa a cargar
  123.                                    // pero permitimos que el bootloader ocupe hasta 4 words más para colocar el vector de arranque el cual será luego sustituido por
  124.                                   // el firmware a cargar.
  125.  
  126. char linea[43] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};    // Array para contener los caracteres ascii que representan una linea hex a su tamaño máximo.
  127.  
  128. int16 h_addr = 0;                     // Address 16 bytes MSB para la flash.
  129. int16 l_addr = 0;                    // Address 16 bits LSB para flash.
  130. int32 address = 0;                  // Address 32 bits final para la flash.    
  131.  
  132.  
  133. void get_line();                  // Detecta y recibe una linea hex completa.
  134. void decode();                    // decodifica cada linea y si es procedente, graba su contenido en el buffer.  
  135. unsigned int atoi_b16(char *s);   // Recibe dos caracteres Ascii que representan el literal de un numero hexadecimal de 8 bits y devuelve su valor entero equivalente.
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142. #org START_PROGRAM + 1,START_PROGRAM + 2                  // vector de arranque de la aplicación a cargar. Esto será sobreescrito por el "goto" inicial de la app cargada
  143. void main_program (void) {
  144.      while(1);    
  145. }                                    // fin vector de arranque
  146.  
  147.  
  148.  
  149.  
  150.  
  151.  
  152.  
  153.  
  154.  
  155. void main() {              
  156.     set_tris_a(0b11111111);            // Configuración de E/S de todos los puertos. Configurar según necesidades.
  157.     set_tris_b(0b11010011);
  158.     set_tris_c(0b10001111);
  159.     set_tris_d(0b00110111);
  160.     set_tris_e(0b101);
  161.     delay_ms(200);                     // Estabilizar fuente de energia y sistemas.
  162.  
  163. #ifdef USE_LED
  164.     usbled_rojo = 1;               // Estado inicial de los led´s. Bootloader inicia con el led rojo encendido.
  165.     usbled_verde = 0;
  166. #endif
  167.    
  168. #ifdef USE_USB
  169.     usb_init();                               // Inicializacion del modulo USB
  170.     usb_cdc_init();                            //configuración de parámetros de comunicación serial generada por el USB(baudios, bit paridad etc)
  171.     usb_cdc_line_coding.dwDTERrate = BAUDRATE;  // configuro la velocidad rs232 emulado por el USB de la cpu a 115.200 bps.  
  172.  
  173.    if (!usb_stat) main_program();      // Si no hay cable usb conectado en el inicio saltamos a la aplicación.
  174.     delay_ms(500);                           // Estabiliza inicializado del USB.
  175.     while(!usb_enumerated());           // Esperando enumeración por parte de Windows.
  176. #else
  177.     if (push) main_program();          // Si no hemos pulsado, saltamos a la aplicación.
  178. #endif
  179.    
  180.    
  181.    
  182.    
  183. #ifdef USE_LED
  184.     for (int i=0;i<3;i++) {
  185.         usbled_verde = 0;                    // indicamos enumerado correcto. (Tres cambios de rojo a verde del led, y lo dejamos con el verde encendido).
  186.         usbled_rojo = 1;
  187.         delay_ms(300);
  188.         usbled_rojo = 0;
  189.         usbled_verde = 1;
  190.         delay_ms(300);
  191.     }
  192. #endif                                        
  193.     while(1) get_line();                     // Vamos a la función para cargar el programa.                                                                                                                                                              
  194. }
  195.  
  196.  
  197.  
  198.  
  199.  
  200. void get_line() {  
  201.     int index = 0;            // indice para el array linea.        
  202.  
  203. #ifdef USE_USB
  204.     while (usb_cdc_getc() != ':')     // Mientras no recibamos un inicio de linea (:) esperaremos en este bucle.                      
  205.         while(!usb_cdc_kbhit())  if (!usb_stat) main_program();      // Esperamos primer caracter desde el software externo. Si desconectamos el cable usb salimos del bootloader y saltamos a la aplicación.                                                                    
  206.              
  207.     // Una vez recibido el caracter ":" (inicio de linea) salimos del bucle y comenzamos a parsearla.
  208.    
  209.    
  210.     // Parseando linea hex del fichero hexadecimal.
  211.     while(linea[index] != 0x0D)               // Recibiremos los caracter en el array hasta que se recibe un ENDLINE (0x0d).                                  
  212.         linea[index++] = usb_cdc_getc();      // Cargamos el caracter recibido en orden en el array "linea".                      
  213.                                
  214.    // Una vez recibido el endline:
  215.     usb_cdc_putc(19); // XOFF        // Enviamos XOFF al software exterior para que detenga la transferencia de datos mientras procesamos la linea.
  216. #else
  217.     while(getc() != ':')
  218.         while(!kbhit());
  219.     while(linea[index] != 0x0D)               // Recibiremos los caracter en el array hasta que se recibe un ENDLINE (0x0d).                                  
  220.         linea[index++] = getc();
  221.        
  222.    
  223. #endif
  224.    
  225.    
  226.     decode();  // Enviamos a decodificar la linea y grabarla en la flash si procede.  
  227. }
  228.  
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  
  235.  
  236. void decode() {              // Decodificamos la linea y la grabamos.
  237.      
  238.     int8 counter = 0;                  // contador numero de bytes de datos en la linea.
  239.     int8 linetype = 0;                 // Tipo de linea.
  240.     int8 data[32];                     // Buffer de preparación de los datos para enviar a la flash.
  241.     int8 i = 0;                        // control indices iteracciones varias.
  242.     int8 dataidx = 0;                  // indice paso de datos entre linea --> buffer data.
  243.     int16 timeout = 0;                 // para detectar conclusión del envío de todos los caracteres restantes en coolterm u otras al terminar la descarga de lineas.
  244.     counter = atoi_b16(&linea[0]);      // atoi_b16 tomará dos caracteres ascii que representan un numero hexadecimal y los convertirá en un entero de 8 bits.
  245.     l_addr =  make16(atoi_b16(&linea[2]),atoi_b16(&linea[4]));       // cargamos en address la dirección (16 bits bajos).
  246.     linetype = atoi_b16(&linea[6]);                                   // cargamos el tipo de linea.
  247.     address += l _addr;                                  // pasamos a address, su valor (por si tiene cargado algo en 32 bits) + el byte bajo)
  248.      
  249.     int8 left = (counter * 2) + 9;       // Counter contiene el numero de enteros de 8 bits que hay que grabar, son dos caracteres por entero, por eso multiplicamos su valor por dos.
  250.                                            //, le sumamos 9 por que los caracteres del principio de la linea ":",numero de bytes "00", address, "0000", tipo de linea "00", son 9 caracteres
  251.                                           // Aqui lo usamos para saber el numero de bytes que hay que meter en checksum, mas abajo también es utilizado por el buffer Data
  252.                                           // ahora Left contiene la posición del primer caracter de checksum.
  253.          
  254.     // Comprobación de errores en la linea recibida
  255.    
  256.     int checksum = 0;                  // valor inicial a checksum.
  257.     for (i=0; i<left; i+=2)            // el checksum se verifica con los caracteres desde posicion 0 de la linea, hasta el final excepto los dos ultimos valores (que son el propio checksum). tampoco se tiene en cuenta el endline, es decir, el LF ni el CR que terminan cada línea.
  258.         checksum += atoi_b16 (&linea[i]);  // concatenamos y sumamos todos los valores arriba indicados.
  259.     checksum = 0xff - checksum + 1;    // restamos a 255 el valor que nos da la concatenación y le sumamos 1. El resultado ha de coincidir con el valor del entero
  260.                                        // que contienen los dos ultimos caracteres de la linea.
  261.    
  262.     if (checksum != atoi_b16 (&linea[left])){   // comprobamos checksum, si no es correcto, se detiene la carga del firmware y permanecemos en bucle cerrado
  263.         while(1) {                              // indicando el error mediante el led.
  264. #ifdef USE_LED            
  265.             usbled_rojo = 1;
  266.             usbled_verde = 1;
  267.             delay_ms(800);          
  268.             usbled_rojo = 0;
  269.             usbled_verde = 0;
  270.             delay_ms(200);
  271. #endif
  272.         }
  273.     }  
  274.      
  275.     // Si checksum es correcto, procedemos a decodificar la linea:
  276.    
  277.    
  278.     if (linetype == 1) {     // Linetype = 1 significa "Envio del programa concluido".
  279.        
  280.        
  281.        
  282.       #ifdef USE_USB
  283.         usb_cdc_putc(6);    // ACK      // Acuse de recibo al software externo.
  284.         usb_cdc_putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.
  285. #else
  286.         putc(6);    // ACK      // Acuse de recibo al software externo.
  287.         putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.        
  288. #endif
  289.                          // Una vez concluida la carga del programa seguiremos aqui recibiendo caracteres procedentes del software que envía el fichero *.hex al bootloader
  290.                          // (coolterm u otros) ya que el pie del fichero *.hex suele contener metadatos como fecha de compilación y algunos mas que
  291.                          // no sirven aqui, pero que si no los recibimos, el software no puede concluir el envío y se nos puede quedar "pillado" con la barra de progreso  
  292.                          // en el 99% o incluso en el 100% esperando que atendamos esos caracteres.
  293.   #ifdef USE_USB                      
  294.         while(1) {         // Seguimos entonces recibiendo todo lo que nos envíe el software externo hasta que ya no envíe nada. Eso lo detectaremos mediante una espera            
  295.             while(!usb_cdc_kbhit()) {
  296.                 timeout++;              
  297.                 delay_ms(1);          
  298.                 if (timeout > 1000) {   // Si tras 1000 ms no se ha recibido nada, entendemos que el sotware externo a concluido.  
  299.                     delay_ms(1000);
  300.                     usben = 0;             // Detiene el modulo USB.
  301.                     main_program();        // Enviamos a ejecutar el programa principal.
  302.                 }
  303.             }
  304.         char c = usb_cdc_getc();
  305.         if (c == 0x0d) usb_cdc_putc(6);          // si recibimos endline enviamos ack para que la app siga enviando lineas.
  306.         timeout = 0;
  307.         }
  308. #else
  309.      while(1) {         // Seguimos entonces recibiendo todo lo que nos envíe el software externo hasta que ya no envíe nada. Eso lo detectaremos mediante una espera            
  310.             while(!kbhit()) {
  311.                 timeout++;              
  312.                 delay_ms(1);          
  313.                 if (timeout > 1000) {   // Si tras 1000 ms no se ha recibido nada, entendemos que el sotware externo a concluido.  
  314.                     delay_ms(1000);                  
  315.                     main_program();        // Enviamos a ejecutar el programa principal.
  316.                 }
  317.             }
  318.         char c = getc();
  319.         if (c == 0x0d) putc(6);          // si recibimos endline enviamos ack para que la app siga enviando lineas.
  320.         timeout = 0;
  321.         }      
  322. #endif
  323.      }
  324.     else if (linetype == 4) { // si el tipo de linea es "4" es por que tenemos que usar una dirección de más de 16 bits de datos, obtenemos de esta linea los otros 16 bits, y saltamos al final del bucle para pedir la siguiente linea.
  325.         h_addr = make16(atoi_b16(&linea[9]), atoi_b16(&linea[11]));  // ya tenemos en h_addr los otros 16 bits.
  326.         address = make32(h_addr,l_addr);
  327.        
  328. #ifdef USE_USB
  329.         usb_cdc_putc(6);    // ACK      // Acuse de recibo al software externo.
  330.         usb_cdc_putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.
  331. #else
  332.         putc(6);    // ACK      // Acuse de recibo al software externo.
  333.         putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.        
  334. #endif
  335.     }
  336.      
  337.     else if (linetype == 0) {  // si el tipo de linea es "0", se trata de una linea relevante de datos para la flash.            
  338.         if (((address >= START_PROGRAM) && (address < 0x7fff)) || (address == 0x300000) || (address == 0xf00000))  {  // Solo si la posicion de memoria está comprendida entre la zona reservada grabamos la flash
  339.                                // Si está fuera de rango pero es 0x300000 o 0xf00000 si que grabamos, pues son los fuses y los datos de la eeprom interna
  340. #ifdef USE_LED            
  341.             usbled_verde = 1;     // Daremos un parpadeo del led cada vez que una linea haya sido grabada en la flash.
  342. #endif
  343.             for (i = 9,dataidx = 0; i < left; i += 2)  data[dataidx++]=atoi_b16(&linea[i]); // a partir del caracter 9 de una linea, hasta la posicion "left" calculada segun "count" (numero de bytes)
  344.                                                                                             // cargamos en el buffer DATA los enteros ya convertidos de los caracteres. Aunque no está lleno, data es un array de 32 bytes  
  345.                                                                                            // debido a que la función write_program_memory en el caso de este pic, trabaja por bloques de 32 bytes
  346.             write_program_memory(address, data, counter);       // Grabamos en la flash el bloque completo de 32 bytes.
  347. #ifdef USE_LED          
  348.             usbled_verde = 0;   // Función Parpadeo led.    
  349. #endif            
  350.             address = 0;      // dejamos limpio Address para evitar acumulacion no deseada al principio de esta función.
  351. #ifdef USE_USB
  352.         usb_cdc_putc(6);    // ACK      // Acuse de recibo al software externo.
  353.         usb_cdc_putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.
  354. #else
  355.         putc(6);    // ACK      // Acuse de recibo al software externo.
  356.         putc(17);   // XON      // Ordena al software externo que reanude la transferencia de datos.        
  357. #endif
  358.         }   // notese que hemos elegido que el programa ocupa a partir de la dirección START_PROGRAM. Esa dirección es el multiplo de 64 "flash_erase_size" mas cercano.
  359.             // que tengo por encima de lo minimo que ocupa este bootloader + una reserva de espacio por si el dia de mañana tuviese que actualizar el propio bootloader.
  360.             // desde el firmware.
  361.             // de modo que esta función trabajará de forma mas optima por que borrará todo el bloque de 64 bytes en cada operación multiplo,  
  362.             // y eso garantiza que no queden datos residuales de anteriores versiones del programa cargado.
  363.            // Si esto no se cumple, sería prudente efectuar un borrado completo de la zona reservada en flash antes de cargar un programa.
  364.        
  365.         else {  // Si ADDRESS está fuera de rango, generamos indicación de error y detenemos la carga de firmware.                                                    
  366.             while(1) {                              // Led rojos parpadeando alternativamente entre ellos indicando error por address fuera de rango autorizado.
  367. #ifdef USE_LED
  368.                 usbled_verde = 0;          // Led parpadea alternativamente entre verde y rojo.
  369.                 usbled_rojo = 1;
  370.                 delay_ms(500);
  371.                 usbled_verde = 1;    
  372.                 usbled_rojo = 0;
  373.                 delay_ms(500);
  374. #endif
  375.             }
  376.                  
  377.         }
  378.      
  379.     }                              
  380. }
  381.  
  382.  
  383.  
  384. unsigned int atoi_b16(char *s) {    // obtiene un numero entero en hexadecimal de 8 bits a partir de dos caracter headecimales.-
  385.    unsigned int result = 0;         // A esta funcion se le pasa como argumento la direcion de memoria donde está el primer caracter mediante &linea[8] por ejemplo
  386.    int i;                            
  387.  
  388.    for (i=0; i<2; i++,s++)  {      
  389.       if (*s >= 'A')                                // si el valor que hay en la posicion de memoria apuntada por "s" es igual o superior a 65 (A = 65)        
  390.          result = 16*result + (*s) - 'A' + 10;      // calculamos.
  391.       else
  392.          result = 16*result + (*s) - '0';            // calculamos.
  393.    }                                                  // siguiente iteracción; incrementa s++, es decir, leerá el valor de la siguiente posicion de memoria y repetirá el proceso.
  394.  
  395.    return(result);
  396. }

Te felicito remi, está muy bien comentado. Así da gusto


 

anything