Programuj AVR EEPROM bezpośrednio ze źródła C.

11

Po dołączeniu następującego kodu do źródła AVR C najwyraźniej możesz bezpośrednio zaprogramować bezpieczniki, bez potrzeby stosowania dodatkowej komendy lub pliku .hex:

#include <avr/io.h>

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

Czy istnieje podobna sztuczka do programowania wartości w EEPROM?

Sprawdziłem, /usr/lib/avr/include/avr/fuse.hgdzie mogę znaleźć komentarze na temat makra, ale nie mogę znaleźć podobnego komentarza, /usr/lib/avr/include/avr/eeprom.ha interpretacja rzeczy związanych z preprocesorem jest trochę poza moją ligą.

Byłoby naprawdę przydatne, gdybym mógł dołączyć domyślne wartości EEPROM do kodu źródłowego C. Czy ktoś wie, jak to osiągnąć?

edycja1:

Ta sztuczka FUSES jest wykonywana tylko w czasie ISP, a nie w czasie RUN. Dlatego w wynikowym kodzie zestawu w sterowniku nie programuje się żadnych bezpieczników. Zamiast tego programator automatycznie przechodzi przez dodatkowy cykl programowania BEZPIECZNIKÓW.

edycja2:

Używam avr-gcc i avrdude toolchain w systemie Linux.

jippie
źródło
Czy to tylko podczas programowania ISP? Większość programów ładujących nie pozwala programować bezpieczników, prawda?
angelatlarge
2
W tej chwili nie mam czasu na napisanie pełnej odpowiedzi, ale jako podpowiedź spróbuj wyszukać dyrektywę EEMEM. Ponadto może być konieczna zmiana ustawień linkera, aby utworzyć osobny plik .EPP, z którego będzie korzystał programista.
PeterJ
@angelatlarge Tylko programowanie ISP. W tej konfiguracji nie ma bootloadera.
jippie
2
Zauważ, że odpowiedzi na to zależą całkowicie od tego, co łańcuch narzędzi chce zapisać w swoich wynikach oraz od tego, co programista jest skłonny przeanalizować. Większość łańcuchów narzędzi można skonfigurować w taki sposób, aby tworzyły specjalną sekcję (lub umieszczały dane pod fikcyjnym adresem), więc ostatecznie sprowadza się to do tego, że programiści mogą albo je wyodrębnić, albo być w stanie być sterowanym przez niestandardowy skrypt, który by to zrobił.
Chris Stratton

Odpowiedzi:

7

Z avr-gcc EEMEMmakro można wykorzystać do definicji zmiennej, zobacz libcdokumentację i przykład tutaj :

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

deklaruje tablicę znaków do umieszczenia w sekcji o nazwie „.eeprom”, która po kompilacji informuje programistę, że dane te należy zaprogramować w pamięci EEPROM. W zależności od oprogramowania programisty może być konieczne jawne nadanie programistowi nazwy pliku „.eep” utworzonego podczas procesu kompilacji lub może on sam w sposób niejawny go znaleźć.

JimmyB
źródło
Użyłem nieco innej nazwy pliku niż „powinienem”, ale są to polecenia, które
załączyłem
1
Utwórz plik zawierający dane Intel Hex używane przez programistę:avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 ihex $(src).elf $(src).eeprom.hex
jippie
1
Rzeczywiste programowanie jest wykonywane przez:avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U eeprom:w:$(src).eeprom.hex
jippie
7

Tak, możesz ręcznie zapisać domyślne dane do EEPROM w kodzie źródłowym. Najpierw zapoznaj się z tym niesamowitym przewodnikiem po pamięci EEPROM z AVR: samouczek AVR EEPROM Deana. Powinienem również dodać, że lepszym pomysłem jest utworzenie pliku .eep zawierającego dane EEPROM przy użyciu makefile, który zostanie zaprogramowany na urządzeniu wraz z kodem źródłowym. Jeśli jednak nie znasz różnych operacji na plikach makefile i linkerach, nadal możesz to zrobić z poziomu pliku kodu źródłowego - stanie się to tak szybko, jak tylko obwód zostanie włączony, blokując początkowe działanie programu.

Na początku programu (przed jakąkolwiek pętlą główną) możesz zrobić coś takiego:

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

Funkcja „update” najpierw sprawdza, czy ta wartość już tam jest, aby zaoszczędzić na niepotrzebnych zapisach, zachowując żywotność pamięci EEPROM. Jednak zrobienie tego dla bardzo wielu lokalizacji może zająć sporo czasu. Lepiej sprawdzić jedną lokalizację. Jeśli jest to pożądana wartość, resztę aktualizacji można całkowicie pominąć. Na przykład:

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

Jeśli chcesz zaktualizować duże ilości danych, spróbuj użyć innych funkcji, takich jak eeprom_update_block(...). I zdecydowanie przeczytaj ten samouczek; jest dobrze napisane.

Można umieścić wszystkie instrukcje aktualizacji pamięci EEPROM w pojedynczej instrukcji warunkowej preprocesora. Jest to bardzo proste do zrobienia:

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

Ten fragment kodu nie zostanie nawet skompilowany, chyba że wykonasz następujące czynności:

#define _UPDATE_EEPROM_

Możesz zostawić to jako komentarz, a następnie odkomentować, jeśli chcesz zmienić domyślne wartości EEPROM. Aby uzyskać więcej informacji na temat preprocesora C, zapoznaj się z tym podręcznikiem online . Myślę, że najbardziej interesują cię sekcje dotyczące makr i instrukcji warunkowych.

Kurt E. Clothier
źródło
Wygląda na to, że poprawna odpowiedź znajduje się w ostatnim akapicie połączonego pliku PDF. Ch. 7 Setting Initial Values.
jippie
Tak, masz rację. Wspomniałem o tym w moim pierwszym akapicie, ale kontynuowałem na wypadek, gdybyś nie był zaznajomiony z plikami .eep i linkerem w pliku makefile!
Kurt E. Clothier
1
Używanie statycznych adresów EEPROM jest złą praktyką. Lepiej jest zamiast tego użyć atrybutu EEMEM i pozwolić kompilatorowi zarządzać dystrybucją adresów. Ponadto zaleciłbym wdrożenie / wykonanie kontroli CRC w każdej sekcji. Jeśli CRC zawiedzie, odpowiednia sekcja zawiera niezainicjowane lub uszkodzone dane. W ten sposób można nawet wdrożyć mechanizm awaryjny do poprzedniej konfiguracji w przypadku uszkodzenia danych.
Rev 1.0
„Używanie statycznych adresów EEPROM jest złą praktyką”. Dlaczego?
angelatlarge
1
Podczas korzystania ze EEMEMzmiennych kompilator dba o to, która zmienna znajduje się w EEPROM. W ten sposób operujesz tylko na (stałych, generowanych przez kompilator) wskaźnikach zmiennych podczas uzyskiwania dostępu do danych. Z drugiej strony, jeśli wyraźnie określisz adres, pod którym znajduje się każda zmienna, będziesz musiał sam zająć się tymi adresami, w tym upewnić się, że żadne zmienne przypadkowo nie zajmują tego samego adresu, zastępując się nawzajem; lub ponowne obliczenie wszystkich adresów na wypadek zmiany wielkości pamięci zmiennej w przyszłości itp.
JimmyB