Número de serie PIC editable en archivo HEX

Actualmente tengo un número de serie codificado en mi firmware para un diseño con el que estoy trabajando. El firmware puede leer e informar el número de serie. Eso funciona bien para lo que necesito. El problema es que cada nuevo número de serie requiere que cambie mi código y vuelva a compilar. Esto es engorroso cuando hay muchas unidades para construir, tiene la posibilidad de introducir errores y es una mala práctica en general. Me dan los números de serie y el diseño del hardware es inamovible, por lo que no puedo agregar ninguna función en el hardware para serializar las unidades (EEPROM/Silicon ID Chip/Pull-Ups). Lo que me gustaría hacer es ubicar el número de serie en una dirección fija, compilar el código una vez y luego editar esa dirección en el archivo HEX compilado para cada nuevo número de serie. Se hace referencia al número en varios lugares, por lo que, idealmente, quiero definirlo y ubicarlo una vez, luego haga referencia a esa "variable" en cualquier otro lugar de mi código. ¿Alguien sabe cómo ubicar datos constantes en una ubicación de memoria direccionable específica de mi elección, usando el Compilador C18? ¿Hay una mejor manera que alguien pueda sugerir?

Aproximadamente la mitad de los PIC18 tienen entre 128 bytes y 1K de EEPROM incorporados. Asumo por su pregunta que su PIC18 es uno del 50% que no tiene EEPROM.

Respuestas (5)

Específicamente para resolver la cuestión de vincular variables a direcciones específicas en la memoria flash en el PIC18 con el compilador C18, consulte la sección "Pragmas" en hlpC18ug.chm en el directorio doc donde está instalado el compilador.

Para hacer esto, debe definir una nueva "sección" en la memoria y vincularla a una dirección de inicio para que

#pragma romdata serial_no_section=0x1700

Esto crea una nueva sección llamada "serial_no_section" que comienza en la dirección 0x1700 en la memoria flash (programa) (porque definimos "romdata" en el #pragma).

Directamente después de la línea #pragma, define tu(s) variable(s) así:

#pragma romdata serial_no_section=0x1700
const rom int mySerialNumber = 0x1234;
#pragma romdata

Ahora tiene 0x12 en la dirección 0x1700 y 0x34 en la dirección 0x1701 en la memoria (porque PIC18 usa el modelo little-endian). La "rom const" garantiza que el compilador sepa que se trata de un tipo de variable const y que la variable se encuentra en la memoria "rom" y, por lo tanto, debe accederse a ella a través de instrucciones de lectura de tabla.

La #pragma romdatadeclaración final asegura que las siguientes declaraciones de variables estén vinculadas a las secciones de memoria predeterminadas, ya que el vinculador considera que encaja en lugar de continuar en la sección "serial_no_section".

Ahora todo el código puede simplemente hacer referencia a la variable "mySerialNumber", y sabrá exactamente en qué dirección se puede encontrar el número de serie en la memoria.

Editar el código HEX puede ser un poco desafiante ya que necesita calcular la suma de verificación para cada línea que edite. Estoy trabajando en una clase de C++ para decodificar y codificar archivos HEX de Intel, lo que debería facilitarlo, pero aún no está terminado. La decodificación de archivos funciona, la codificación nuevamente aún no está implementada. El proyecto (si está interesado) está aquí https://github.com/codinghead/Intel-HEX-Class

Espero que esto ayude

He hecho el número de serie (s/n para abreviar) de una manera similar a lo que describe Joel. Estaba usando el compilador PIC18F4620 y CCS. La ubicación de s/n en la memoria Flash fue forzada a los últimos 4 bytes. Como estaba usando solo el 80% de Flash, mi compilador y enlazador no escribían código ejecutable en los últimos 4 bytes.

Luego tuve 2 formas alternativas para escribir s/n en unidades individuales:

  • El depurador en circuito CCS (ICD) tenía una función que permitía manipular cualquier ubicación dentro de Flash. Fue un truco útil. En revisiones posteriores lo han eliminado, lamentablemente.
  • PIC tenía un enlace serial a una PC. s/n fue subido a través de él. El firmware tenía una rutina que recibiría el s/n y lo almacenaría en Flash.

Responder al comentario de Joel

No sé sobre C18, pero el compilador CCS viene con funciones de biblioteca write_program_eeprom(...)y read_program_eeprom(...). Así es como se ven en el ensamblaje.

................... escribir_programa_eeprom(i_ADDR, iWord);
EF84: BSFFD0.6
EF86: CLRF FF8
EF88: MOVLW 7F
EF8A: MOVWF FF7
EF8C: MOVIL F0
EF8E: MOVWF FF6
EF90: MOVLB 0
EF92: SUJETADOR EF24
EF94: MOVIL F0
EF96: MOVWF FF6
EF98: MOVFF 490, FF5
EF9C: TBLWT*+
EF9E: MOVFF 491, FF5
EFA2: TBLWT*
EFA4: FBC FA6.6
EFA6: BSF FA6.4
EFA8: LLAMADA EF3C
EFAA: RCALL EF3C
EFAC: CLRF FF8
EFAE: CLRF FF8
..................... iPalabra = read_program_eeprom(i_ADDR);
EF60: CLRF FF8
EF62: MOVLW 7F
EF64: MOVWF FF7
EF66: MOV LW F0
EF68: MOVWF FF6
EF6A: TBLRD*+
EF6C: MOVF FF5,W
EF6E: TBLRD*
EF70: MOVFF FF5,03
EF74: CLRF FF8
EF76: MOVLB 4
EF78: MOVWF x90
EF7A: MOVFF 03.491
¡Sí, esto es exactamente lo que estoy pidiendo! ¿Puede describir qué atributos de código usó para empaquetar su S/N en los últimos bytes de flash?
"El firmware tenía una rutina que recibiría el s/n y lo almacenaría en Flash" write_program_eeprom(...)y read_program_eeprom(...). ¡La EEPROM y Flash son dos cosas diferentes!
@m.Alin Así es como se llamaban las funciones "enlatadas" que venían con el compilador CCS. De hecho, escribirían y leerían Flash.

He hecho esto un par de veces. Por lo general, defino un área de información de firmware en una ubicación fija en la memoria del programa, luego escribo un programa que crea un archivo HEX serializado a partir del archivo HEX de plantilla. Todas estas son cosas fáciles de hacer.

En producción, ejecuta el programa de serialización una vez que han pasado todas las pruebas. Crea el archivo HEX temporal con el número de serie único, que se programa en el PIC, luego se elimina el archivo HEX temporal.

No dejaría que la ubicación fuera reubicable, luego tendría que encontrarla. Eso puede cambiar cada compilación a medida que el enlazador mueve las cosas. Lo he hecho para PIC muy pequeños como la serie 10F donde estas constantes son parte de las instrucciones MOVLW. En esos casos, hago que lea el archivo MAP sobre la marcha para determinar dónde están esas ubicaciones. Tengo un código de análisis de archivos MPLINK MAP en una biblioteca solo para ese propósito.

Para poner algo en una ubicación fija, defina un segmento en una dirección fija. El enlazador colocará dichos segmentos absolutos primero, luego los reubicables a su alrededor. No olvide usar CODE_PACK en lugar de solo CODE en un PIC 18, de lo contrario, tendrá que lidiar con palabras de instrucciones completas en lugar de bytes individuales. Por ejemplo (simplemente tecleado, no pase el ensamblador):

.fwinfo code_pack h'1000' ;área de información de firmware en una dirección conocida fija
         db h'FFFFFFFF' ;número de serie, rellenado por el programa de producción
         db fwtype; tipo ID de este firmware
         db fwver ;número de versión
         db fwseq ;número de secuencia de compilación

Sugeriría almacenar el número de serie en una dirección fija. Dependiendo de su compilador/enlazador y la parte en cuestión, hay algunos enfoques que podría tomar:

  1. Defina una sección reubicable que contendrá el número de serie, use una directiva #pragma en su código para forzar el número de serie en esa sección y forzar la dirección de esa sección dentro de la especificación del enlace.
  2. Para las partes que pueden leer la memoria del código directamente, excluya un área de la memoria del área que el enlazador puede usar (es decir, dígale que la parte es, por ejemplo, cuatro bytes más pequeña de lo que realmente es), y luego lea el número de serie en el código usando algo como `((long const sin firmar *)0x3FFC)`.
  3. Para las partes que no pueden leer la memoria de código directamente, es posible que pueda poner instrucciones "RETLW" en alguna dirección fija y luego convencer al enlazador de que hay funciones invocables que devuelven 'byte' en esas direcciones. Entonces uno diría, por ejemplo, "out_hex(ser_byte0()); out_hex(ser_byte1()); out_hex(ser_byte2()); para generar los bytes del número de serie.
Todos los PIC 18F pueden leer su memoria de programa a través del mecanismo de lectura de tablas.
Eso es verdad. Algunas de las partes de 14 bits también pueden leer la memoria del programa. Sin embargo, incluso en los PIC que pueden leer la memoria del programa, el retlwenfoque suele ser mucho más rápido (en los PIC de 18 bits, de aa calltomará retlwcuatro ciclos en total; usar clrf TBLPTRU/movlw xx/movwf TBLPTRH/movlw xx/movwf TBLPTRL/tblrd *+/movf TABLAT,wtomaría ocho).

Yo haría lo contrario: compilar y vincular el código, luego averiguar dónde se almacena el valor del archivo del vinculador. Es posible que deba ubicar la variable explícitamente en un segmento que está asignado a flash.

No pedí esto, pero el software de PC que proporciono para mi programador Wisp648 tiene la capacidad de leer un archivo .hex, modificar una ubicación específica y volver a escribir el archivo .hex (en el mismo archivo o en otro). No es necesario que mi programador esté presente. La fuente está disponible (en Python), la licencia permite todos los usos: www.voti.nl/xwisp Podría ser útil una vez que haya resuelto su problema principal.

¡Gracias @Wouter van Ooijen! Estoy tratando de evitar el enfoque de "descubrir dónde está almacenado el valor", ya que eso probablemente significará que el valor se reubica en compilaciones sucesivas y me requerirá (o algún tipo triste que viene detrás de mí) para averiguar dónde está el valor se encuentra nuevamente, seguramente introduciendo problemas.