Programe AVR EEPROM directamente desde la fuente C

Cuando incluye el siguiente código en una fuente AVR C, aparentemente puede programar directamente los fusibles, sin necesidad de un comando adicional o un archivo .hex:

#include <avr/io.h>

FUSES = {
        .low =          LFUSE_DEFAULT ,
        .high =         HFUSE_DEFAULT ,
        .extended =     EFUSE_DEFAULT ,
};

¿Existe un truco similar para programar valores en EEPROM?

Revisé /usr/lib/avr/include/avr/fuse.hdónde puedo encontrar algunos comentarios sobre una macro, pero no puedo encontrar un comentario similar /usr/lib/avr/include/avr/eeprom.he interpretar las cosas del preprocesador está un poco fuera de mi alcance.

Sería muy útil si pudiera incluir valores predeterminados de EEPROM en el código fuente de C. ¿Alguien sabe cómo lograr eso?

edit1:

Este truco de FUSIBLES solo se ejecuta en tiempo de ISP, no en tiempo de EJECUCIÓN. Por lo tanto, no se programan fusibles en el código ensamblador resultante en el controlador. En su lugar, el programador realiza automáticamente un ciclo adicional de programación de FUSIBLES.

edit2:

Uso la cadena de herramientas avr-gcc y avrdude en Linux.

¿Esto es solo cuando se programa ISP? La mayoría de los cargadores de arranque no le permiten programar fusibles, ¿verdad?
No tengo tiempo para escribir una respuesta completa en este momento, pero como sugerencia, intente buscar en la directiva EEMEM. Además, es posible que deba cambiar la configuración del enlazador para crear un archivo .EPP separado que usará el programador.
@angelatlarge Solo programación ISP. No hay gestor de arranque en esta configuración.
Tenga en cuenta que las respuestas a esto dependen completamente de lo que la cadena de herramientas esté dispuesta a registrar en su salida y de lo que el programador esté dispuesto a analizar. La mayoría de las cadenas de herramientas se pueden configurar para crear una sección especial (o colocar datos en una dirección ficticia), por lo que, en última instancia, se trata de que el programador pueda extraerlos o pueda ser impulsado por un script personalizado que lo haga.

Respuestas (2)

Con avr-gcc, la EEMEMmacro se puede usar en la definición de una variable, consulte los libcdocumentos y un ejemplo aquí :

#include <avr/eeprom.h>
char myEepromString[] EEMEM = "Hello World!";

declara que la matriz de caracteres reside en una sección llamada ".eeprom" que, después de la compilación, le dice al programador que estos datos deben programarse en la EEPROM. Dependiendo del software de su programador, es posible que deba dar explícitamente el nombre del archivo ".eep" creado durante el proceso de compilación al programador, o puede que lo encuentre implícitamente por sí mismo.

Utilicé un nombre de archivo ligeramente diferente al que 'se supone', pero estos son los comandos que incluí para programar EEPROM desde la línea de comandos (y makefile):
Cree un archivo que contenga datos Intel Hex utilizados por el programador:avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 ihex $(src).elf $(src).eeprom.hex
La programación real se realiza mediante:avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U eeprom:w:$(src).eeprom.hex

Sí, puede escribir manualmente datos predeterminados en EEPROM en el código fuente. Primero, echa un vistazo a esta increíble guía sobre la EEPROM con AVR: Tutorial de Dean's AVR EEPROM. Además, debo agregar que es una mejor idea crear un archivo .eep que contenga los datos de la EEPROM utilizando el archivo MAKE que se programará en el dispositivo junto con el código fuente. Sin embargo, si no está familiarizado con varias operaciones de creación de archivos y vinculadores, aún puede hacerlo desde su archivo de código fuente; solo sucederá tan pronto como se encienda el circuito, deteniendo la operación inicial del programa.

Al comienzo del programa (antes de cualquier tipo de ciclo principal) podría hacer algo como esto:

#include <avr/eeprom.h>

#define ADDRESS_1 46  // This could be anything from 0 to the highest EEPROM address
#define ADDRESS_2 52  // This could be anything from 0 to the highest EEPROM address
#define ADDRESS_3 68  // This could be anything from 0 to the highest EEPROM address

uint8_t dataByte1 = 0x7F;  // Data for address 1
uint8_t dataByte2 = 0x33;  // Data for address 2
uint8_t dataByte3 = 0xCE;  // Data for address 3

eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
eeprom_update_byte((uint8_t*)ADDRESS_2, dataByte2);
eeprom_update_byte((uint8_t*)ADDRESS_3, dataByte3);

La función de "actualización" verifica primero si ese valor ya está allí, para ahorrar escrituras innecesarias, preservando la vida útil de EEPROM. Sin embargo, hacer esto para muchas ubicaciones puede llevar bastante tiempo. Podría ser mejor verificar una sola ubicación. Si es el valor deseado, entonces el resto de las actualizaciones se pueden omitir por completo. Por ejemplo:

if(eeprom_read_byte((uint8_t*)SOME_LOCATION) != DESIRED_VALUE){
  eeprom_write_byte((uint8_t*)SOME_LOCATION, DESIRED_VALUE);
  eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
  eeprom_update_byte((uint8_t*)ADDRESS_2, dataByte2);
  eeprom_update_byte((uint8_t*)ADDRESS_3, dataByte3);
}

Si está buscando actualizar grandes cantidades de datos, intente usar las otras funciones como eeprom_update_block(...). Y definitivamente lee ese tutorial; esta bien escrito

Puede colocar todas las declaraciones de actualización de EEPROM en una sola declaración condicional de preprocesador. Esto es muy sencillo de hacer:

#if defined _UPDATE_EEPROM_
  #define ADDRESS_1 46  // This could be anything from 0 to the highest EEPROM address
  uint8_t dataByte = 0x7F;  // Data for address 1
  eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
#endif // _UPDATE_EEPROM_

Este fragmento de código ni siquiera se compilará a menos que haga lo siguiente:

#define _UPDATE_EEPROM_

Puede dejar esto allí como un comentario y luego quitar el comentario si necesita cambiar los valores predeterminados de EEPROM. Para obtener más información sobre el preprocesador C, consulte este manual en línea . Creo que puede estar más interesado en las secciones sobre macros y sentencias condicionales.

Parece que la respuesta correcta está en el último párrafo del PDF vinculado. Ch. 7 Setting Initial Values.
Sí, estás en lo correcto. Lo mencioné en mi primer párrafo, ¡pero continué en caso de que no estuviera familiarizado con los archivos .eep y el enlazador en el archivo MAKE!
El uso de direcciones EEPROM estáticas es una mala práctica. Es mejor usar el atributo EEMEM en su lugar y dejar que el compilador administre la distribución de direcciones. Además, recomendaría implementar/realizar una verificación de CRC en cada sección. Si el CRC falla, la sección correspondiente contiene datos no inicializados o dañados. De esta manera, incluso puede implementar un mecanismo de respaldo a la configuración anterior en caso de corrupción de datos.
"Usar direcciones EEPROM estáticas es una mala práctica". ¿Por qué?
Cuando se usan EEMEMvariables, el compilador se encarga de administrar qué variable reside en qué lugar de la EEPROM. De esta manera, solo opera en punteros (constantes, generados por el compilador) a variables al acceder a los datos. Si, por el contrario, define explícitamente la dirección donde reside cada variable, tendrá que encargarse de esas direcciones usted mismo, lo que incluye asegurarse de que ninguna variable ocupe accidentalmente la misma dirección, sobrescribiéndose entre sí; o volver a calcular todas las direcciones en caso de que el tamaño de almacenamiento de una variable cambie en el futuro, etc.
Creo que depende de tu aplicación. Hay muchos casos en los que solo necesito almacenar una docena de bytes de datos. En ese caso, tiene más sentido simplemente ponerlo donde quiero ponerlo en lugar de jugar con el compilador. Pero estoy de acuerdo, en la mayoría de los casos, el compilador probablemente haría un mejor trabajo.