Programmieren Sie das AVR EEPROM direkt aus der C-Quelle

Wenn Sie den folgenden Code in eine AVR-C-Quelle einfügen, können Sie die Sicherungen anscheinend direkt programmieren, ohne dass ein zusätzlicher Befehl oder eine .hex-Datei erforderlich ist:

#include <avr/io.h>

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

Gibt es einen ähnlichen Trick, um Werte im EEPROM zu programmieren?

Ich habe überprüft, /usr/lib/avr/include/avr/fuse.hwo ich einige Kommentare zu einem Makro finden kann, aber ich kann keinen ähnlichen Kommentar in finden, /usr/lib/avr/include/avr/eeprom.hund die Interpretation des Präprozessor-Zeugs ist ein bisschen außerhalb meiner Liga.

Es wäre wirklich praktisch, wenn ich Standard-EEPROM-Werte in den C-Quellcode aufnehmen könnte. Weiß jemand, wie man das bewerkstelligt?

edit1:

Dieser FUSES-Trick wird nur zur ISP-Zeit ausgeführt, nicht zur RUN-Zeit. Es werden also keine Sicherungen im resultierenden Assembler-Code in der Steuerung programmiert. Stattdessen durchläuft der Programmierer automatisch einen zusätzlichen SICHERUNGS-Programmierzyklus.

edit2:

Ich verwende die avr-gcc- und avrdude-Toolchain unter Linux.

Ist dies nur beim Programmieren von ISP? Die meisten Bootloader erlauben Ihnen nicht, Fuses zu programmieren, richtig?
Ich habe im Moment keine Zeit, eine vollständige Antwort zu schreiben, aber versuchen Sie als Hinweis eine Suche nach der EEMEM-Direktive. Außerdem müssen Sie möglicherweise die Linker-Einstellung ändern, um eine separate .EPP-Datei zu erstellen, die der Programmierer verwenden wird.
@angelatlarge Nur ISP-Programmierung. In diesem Setup gibt es keinen Bootloader.
Beachten Sie, dass die Antworten darauf vollständig davon abhängen, was die Toolchain in ihrer Ausgabe aufzeichnen möchte und was der Programmierer bereit ist zu analysieren. Die meisten Toolchains könnten so konfiguriert werden, dass sie einen speziellen Abschnitt erstellen (oder Daten an einer fiktiven Adresse ablegen), sodass es letztendlich darauf ankommt, dass der Programmierer sie entweder extrahieren oder von einem benutzerdefinierten Skript gesteuert werden kann, das dies tun würde.

Antworten (2)

Mit avr-gcc EEMEMkann das Makro für die Definition einer Variablen verwendet werden, siehe die libcDokumentation und ein Beispiel hier :

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

deklariert, dass sich das Array von Zeichen in einem Abschnitt namens ".eeprom" befindet, der nach der Kompilierung dem Programmierer mitteilt, dass diese Daten in das EEPROM zu programmieren sind. Abhängig von Ihrer Programmiersoftware müssen Sie dem Programmierer den Namen der ".eep"-Datei, die während des Build-Prozesses erstellt wurde, möglicherweise explizit mitteilen, oder er findet ihn implizit von selbst.

Ich habe einen etwas anderen Dateinamen verwendet, als ich eigentlich "sollte", aber dies sind die Befehle, die ich eingefügt habe, um EEPROM über die Befehlszeile (und das Makefile) zu programmieren:
Erstellen Sie eine Datei mit Intel-Hex-Daten, die vom Programmierer verwendet werden:avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 ihex $(src).elf $(src).eeprom.hex
Die eigentliche Programmierung erfolgt durch:avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U eeprom:w:$(src).eeprom.hex

Ja, Sie können Standarddaten im Quellcode manuell in das EEPROM schreiben. Schauen Sie sich zuerst diese großartige Anleitung zum EEPROM mit AVR an: Dean's AVR EEPROM Tutorial. Außerdem sollte ich hinzufügen, dass es eine bessere Idee ist, eine .eep-Datei zu erstellen, die die EEPROM-Daten enthält, indem das Makefile verwendet wird, das zusammen mit dem Quellcode auf das Gerät programmiert wird. Wenn Sie jedoch mit verschiedenen Makefile- und Linker-Operationen nicht vertraut sind, können Sie dies immer noch aus Ihrer Quellcodedatei heraus tun - es geschieht einfach, sobald die Schaltung mit Strom versorgt wird, wodurch die anfängliche Programmoperation blockiert wird.

Am Anfang des Programms (vor jeder Art von Hauptschleife) könnten Sie so etwas tun:

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

Die „Update“-Funktion prüft zuerst, ob dieser Wert bereits vorhanden ist, um unnötige Schreibvorgänge zu vermeiden und die EEPROM-Lebensdauer zu erhalten. Dies kann jedoch für sehr viele Standorte einige Zeit in Anspruch nehmen. Es könnte besser sein, einen einzelnen Standort zu überprüfen. Ist es der gewünschte Wert, dann können die restlichen Updates komplett übersprungen werden. Zum Beispiel:

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

Wenn Sie große Datenmengen aktualisieren möchten, versuchen Sie es mit anderen Funktionen wie eeprom_update_block(...). Und lesen Sie auf jeden Fall dieses Tutorial. es ist gut geschrieben.

Sie könnten alle EEPROM-Aktualisierungsanweisungen in eine einzige Bedingungsanweisung des Präprozessors packen. Das geht ganz einfach:

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

Dieses Stück Code wird nicht einmal kompiliert, es sei denn, Sie tun Folgendes:

#define _UPDATE_EEPROM_

Sie können dies dort als Kommentar hinterlassen und dann auskommentieren, wenn Sie die Standard-EEPROM-Werte ändern müssen. Weitere Informationen zum C-Präprozessor finden Sie in diesem Online-Handbuch . Ich denke, Sie interessieren sich am meisten für die Abschnitte über Makros und bedingte Anweisungen.

Es sieht so aus, als ob die richtige Antwort im letzten Absatz des verlinkten PDFs steht. Ch. 7 Setting Initial Values.
Ja du hast Recht. Ich habe das in meinem ersten Absatz erwähnt, bin aber weitergefahren, falls Sie mit .eep-Dateien und dem Linker im Makefile nicht vertraut sind!
Die Verwendung statischer EEPROM-Adressen ist eine schlechte Praxis. Es ist besser, stattdessen das EEMEM-Attribut zu verwenden und den Compiler die Adressverteilung verwalten zu lassen. Außerdem würde ich empfehlen, eine CRC-Prüfung für jeden Abschnitt zu implementieren/durchzuführen. Wenn der CRC fehlschlägt, enthält der entsprechende Abschnitt nicht initialisierte oder beschädigte Daten. Auf diese Weise können Sie im Falle einer Datenbeschädigung sogar einen Rückfallmechanismus auf die vorherige Konfiguration implementieren.
"Die Verwendung statischer EEPROM-Adressen ist eine schlechte Praxis." Warum?
Bei der Verwendung von EEMEMVariablen kümmert sich der Compiler darum, welche Variable sich wo im EEPROM befindet. Auf diese Weise operieren Sie beim Zugriff auf die Daten nur mit (konstanten, vom Compiler generierten) Zeigern auf Variablen. Wenn Sie andererseits die Adresse, an der sich jede Variable befindet, explizit definieren, müssen Sie sich selbst um diese Adressen kümmern, einschließlich des Sicherstellens, dass keine Variablen versehentlich dieselbe Adresse belegen und sich gegenseitig überschreiben; oder alle Adressen neu berechnen, falls sich die Speichergröße einer Variablen in Zukunft ändert usw.
Ich denke es kommt auf deine Anwendung an. Es gibt viele Fälle, in denen ich nur etwa ein Dutzend Bytes an Daten speichern muss. In diesem Fall ist es am sinnvollsten, es einfach dort abzulegen, wo ich es ablegen möchte, anstatt mit dem Compiler herumzuspielen. Aber ich stimme zu, in den meisten Fällen würde der Compiler wahrscheinlich einen besseren Job machen.