Programmare EEPROM AVR direttamente dalla sorgente C.


11

Quando includi il seguente codice in una sorgente AVR C, puoi apparentemente programmare direttamente i fusibili, senza la necessità di un comando aggiuntivo o di un file .hex:

#include <avr/io.h>

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

Esiste un trucco simile per programmare i valori in EEPROM?

Ho controllato /usr/lib/avr/include/avr/fuse.hdove posso trovare alcuni commenti su una macro, ma non riesco a trovare un commento simile /usr/lib/avr/include/avr/eeprom.he l'interpretazione delle cose del preprocessore è un po 'fuori dalla mia portata.

Sarebbe davvero utile se potessi includere i valori EEPROM predefiniti nel codice sorgente C. Qualcuno sa come farlo?

Edit1:

Questo trucco FUSES viene eseguito solo al momento dell'ISP, non al momento dell'esecuzione. Quindi non ci sono fusibili programmati nel codice assembly risultante nel controller. Al contrario, il programmatore esegue automaticamente un ciclo di programmazione FUSES aggiuntivo.

EDIT2:

Uso la toolchain avr-gcc e avrdude su Linux.


È solo durante la programmazione dell'ISP? La maggior parte dei bootloader non consente di programmare i fusibili, giusto?
angelatlarge

2
Non ho tempo per scrivere una risposta completa al momento, ma come suggerimento prova una ricerca sulla direttiva EEMEM. Inoltre, potrebbe essere necessario modificare le impostazioni del linker per creare un file .EPP separato che verrà utilizzato dal programmatore.
PeterJ

@angelatlarge Solo programmazione ISP. Non c'è bootloader in questa configurazione.
jippie il

2
Si noti che le risposte a questo dipendono interamente da ciò che la toolchain è disposta a registrare nel suo output e da ciò che il programmatore è disposto ad analizzare. La maggior parte delle toolchain potrebbe essere configurata per creare una sezione speciale (o mettere i dati a un indirizzo fittizio) in modo che alla fine spetti al programmatore o essere in grado di estrarlo o essere guidato da uno script personalizzato che lo farebbe.
Chris Stratton,

Risposte:


7

Con avr-gcc la EEMEMmacro può essere utilizzata nella definizione di una variabile, vedere i libcdocumenti e un esempio qui :

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

dichiara la matrice di caratteri di risiedere in una sezione denominata ".eeprom" che dopo la compilazione dice al programmatore che questi dati devono essere programmati sulla EEPROM. A seconda del software del programmatore, potrebbe essere necessario fornire esplicitamente al programmatore il nome del file ".eep" creato durante il processo di generazione, oppure potrebbe trovarlo implicitamente da solo.


Ho usato un nome file leggermente diverso da quello che dovrei "supporre", ma questi sono i comandi che ho incluso per programmare EEPROM dalla riga di comando (e makefile):
jippie

1
Crea un file contenente i dati Intel Hex utilizzati dal programmatore:avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 ihex $(src).elf $(src).eeprom.hex
jippie

1
La programmazione effettiva viene effettuata da:avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U eeprom:w:$(src).eeprom.hex
jippie

7

Sì, è possibile scrivere manualmente i dati predefiniti nella EEPROM nel codice sorgente. Per prima cosa, dai un'occhiata a questa fantastica guida sull'EEPROM con AVR: Dean's AVR EEPROM Tutorial. Inoltre, dovrei aggiungere che è una buona idea creare un file .eep contenente i dati EEPROM usando il makefile che sarà programmato sul dispositivo insieme al codice sorgente. Tuttavia, se non si ha familiarità con varie operazioni di makefile e linker, può ancora essere fatto all'interno del file del codice sorgente - accadrà non appena il circuito viene alimentato, interrompendo l'operazione iniziale del programma.

All'inizio del programma (prima di qualsiasi tipo di ciclo principale) potresti fare qualcosa del genere:

#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 funzione "aggiorna" controlla innanzitutto se quel valore è già presente, per risparmiare su scritture non necessarie, preservando la durata della EEPROM. Tuttavia, farlo per moltissime località può richiedere parecchio tempo. Potrebbe essere meglio controllare una singola posizione. Se è il valore desiderato, è possibile saltare completamente il resto degli aggiornamenti. Per esempio:

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);
}

Se stai cercando di aggiornare grandi quantità di dati, prova ad usare altre funzioni come eeprom_update_block(...). E sicuramente leggi quel tutorial; è ben scritto.

È possibile inserire tutte le istruzioni di aggiornamento EEPROM in una singola istruzione condizionale del preprocessore. Questo è molto semplice da fare:

#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_

Questo bit di codice non verrà nemmeno compilato a meno che non si esegua la seguente operazione:

#define _UPDATE_EEPROM_

È possibile lasciarlo lì come commento, quindi rimuovere il commento se è necessario modificare i valori EEPROM predefiniti. Per ulteriori informazioni sul preprocessore C, consultare questo manuale online . Penso che potresti essere più interessato alle sezioni sulle macro e alle dichiarazioni condizionali.


Sembra che la risposta corretta sia nell'ultimo paragrafo del PDF collegato. Ch. 7 Setting Initial Values.
jippie,

Sì hai ragione. Ne ho parlato nel mio primo paragrafo, ma ho continuato nel caso in cui non avessi familiarità con i file .eep e il linker nel makefile!
Kurt E. Clothier,

1
L'uso di indirizzi EEPROM statici è una cattiva pratica. È preferibile utilizzare invece l'attributo EEMEM e consentire al compilatore di gestire la distribuzione degli indirizzi. Inoltre, consiglierei di implementare / eseguire un controllo CRC su ogni sezione. Se il CRC fallisce, la sezione corrispondente contiene dati non inizializzati o danneggiati. In questo modo, è anche possibile implementare un meccanismo di fallback alla configurazione precedente in caso di danneggiamento dei dati.
Rev1.0

"L'uso di indirizzi EEPROM statici è una cattiva pratica." Perché?
angelatlarge

1
Quando si utilizzano le EEMEMvariabili, il compilatore si occupa della gestione di quale variabile risiede nella EEPROM. In questo modo si opera solo su puntatori (costanti, generati dal compilatore) alle variabili quando si accede ai dati. Se, d'altra parte, definisci esplicitamente l'indirizzo in cui risiede ciascuna variabile, dovrai occuparti tu stesso di quegli indirizzi, incluso assicurandoti che nessuna variabile occupi accidentalmente lo stesso indirizzo, sovrascrivendosi a vicenda; o ricalcolo di tutti gli indirizzi nel caso in cui la dimensione della memoria di una variabile cambi in futuro ecc.
JimmyB
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.