logo_elektromys.eu

/ EEPROM |

V tomto krátkém tutoriálu si kladu za cíl seznámit čtenáře s knihovnou avr/eeprom.h ze skupiny "standardních" knihoven avr-libc. Nemám v úmyslu zabíhat příliš do hloubky a rozebírat všechny možnosti práce s vestavěnou EEPROM, protože čtenáři začátečníkovi bohatě postačí to co nabízí knihovna. Na úvod si dovolím napsat co nejstručněji základní charakteristiky pamětí v mikrokontrolérech Atmel, abychom se zorientovali. Máme:

Termín EEPROM může být trochu matoucí, neboť i paměť Flash je obecně typu "EEPROM", ale protože tuto terminologii používá datasheet, budeme se jí držet. EEPROM je prostě paměť kam si může program nekomplikovaně ukládat data, která mají zůstat zachována i po vypnutí. Může to být různé uživatelské nastavení, kalibrační údaje atp. K jednoduché manipulaci s EEPROM slouží knihovna avr/eeprom.h, která obsahuje funkce write, read, update a eeprom_is_ready a eeprom_busy_wait

Funkce existují ve variantách pro zápis/čtení uint8_t (byte), uint16_t (word), uint32_t (dword), float a pole (block). Jak je asi patrné z názvu, funkce read z paměti čte, funkce write zapisují a funkce update zapisují (ale jen ty bajty jejichž hodnota se změnila). Použivání těchto funkcí je přímočaré a jednoduché. Předveďme si to třeba na funkci eeprom_write_word(). Funkce má dva argumenty, prvním z nich je adresa kam v paměti EEPROM chceme proměnnou uložit a druhým argumentem je proměnná jejíž obsah se tam má uložit. Knihovna pracuje s adresním prostorem počítaným od nuly. Má-li náš mikrokontrolér například 256B (Atmega4808) EEPROM paměti, můžeme ukládat na adresy 0 až 255. Tedy například:

  uint16_t pocet=15;
  eeprom_write_word(0x40,pocet);  // ulož na adresu 0x40 do EEPROM 16bitovou proměnnou "pocet" (přepíše byty s adresami 0x40 a 0x41)
  pocet=eeprom_read_word(0x40);  // zkopíruj z adresy 0x40 v EEPROM 16bitovou hodnotu do proměnné "pocet" (v RAM)
  
  uint8_t x[16];
  eeprom_write_block(x,0x20,16); // ulož obsah pole x na adresy 0x20 až 0x2F v EEPROM 
  eeprom_read_block(x,0x20,16); // zkopíruj 16 bajtů od adresy 0x20 z EEPROM do pole x (v RAM)

Ruční zadávání adres je sice na první pohled přehledné, ale veškerá zodpovědnost za správné rozvržení adres je jen na vás. Typické chyby pak mohou vypadat třeba takto:

  uint16_t x,y;
  eeprom_write_word(0x10,x); // zapíše na adresy 0x10 a 0x11 obsah proměnné x
  eeprom_write_word(0x11,x); // zapíše na adresy 0x11 a 0x12 obsah proměnné y, čímž poškodí uložený obsah proměnné x (ztráta dat)
  eeprom_write_word(0xFF,x); // zapíše na adresy 0xFF a 0x100 obsah proměnné x (na MCU s 256B paměti EEPROM se na adresu 0x100 nic neuloží) 

Komplikace vyvstanou zvláště v případě rozsáhlejšího použití EEPROM. Vhodnější je nechat rozvržení adres na linkeru. K tomu účelu slouží atribut EEMEM, kterým sdělujeme že daná proměnná bude v paměti EEPROM.

   uint16_t EEMEM eeword; // dekalruji proměnnou typu uint16_t která má být v EEPROM
   uint16_t word; // deklaruji proměnnou typu uint16_t (v RAM)
   word = eeprom_read_word(&eeword); // načti z EEPROM obsah proměnné eeword do proměnné word v RAM. Operátor & (reference) vyčíslí adresu proměnné eeword (a tu předá funkci eeprom_read...)
   eeprom_write_word(&eeword,word); // zapiš obsah proměnné word do EEPROM na adresu proměnné eeword (tedy "do proměnné" eeword)
   eeprom_update_word(&eeword,word); // proveď totéž co na předchozím řádku s tím rozdílem že v EEPROM přepisuj jen ty byty které mají jinou hodnotu
   
   uint8_t EEMEM eeblock[16];
   uint8_t block[16]; 
   eeprom_write_block(block,eeblock,sizeof(block)); // zapiš do EEPROM na adresu pole eeblock obsah celého pole block (celkem 16 bytů), název pole je ukazatel, takže operátor & nepíšeme (!)
   eeprom_read_block(block, eeblock,sizeof(block)); // vyčti do pole block z EEPROM obsah pole eeblock (celkem 16 bytů)

Proměnným s atributem EEMEM můžeme přiřadit i počáteční hodnoty. Po "buildnutí projektu" vznikne soubor s koncovkou .eep ve kterém je obsah EEPROM včetně počátečních hodnot. Pomocí programovacího nástroje (v Atmel studiu Tools->Device_Programming->Memories) je možné .eep soubor do paměti zapsat a přiřadit tak proměnným počáteční hodnoty.

    uint16_t EEMEM eeword=1234; // proměnná typu uint16_t v EEPROM, počáteční hodnota 1234 je v souboru .eep
    uint8_t EEMEM eeblock[6]={1,2,3,4,5,6}; // pole o šesti prvcích v paměti EEPROM, počáteční hodnoty jsou v souboru .eep

To je asi tak vše co je nezbytně nutné vědět. Na závěr si sestavíme malý program, který počítá kolikrát byl MCU spuštěn nebo resetován.

#include <avr/io.h>
#include <avr/eeprom.h>

uint32_t EEMEM ee_startcounter=0; // deklarujeme v paměti (4 bytovou) proměnnou ee_startcounter
uint32_t startcounter; // proměnná v RAM

int main(void){
 startcounter = eeprom_read_dword(&ee_startcounter); // načti z EEPROM obsah ee_startcounter (počet startů/restartů MCU)
 startcounter++; // inkremenuj počítadlo startů
 eeprom_update_dword(&ee_startcounter,startcounter); // zapiš do EEPROM hodnotu o jedno větší
 while (1){
  asm("nop"); // není co dělat
 }
}
Kdykoli mohu do EEPROM nahrát obsah eep. souboru čímž vynuluji proměnnou ee_startcounter.
Obsah EEPROM při debugování

Při pohledu na obsah paměti EEPROM si můžeme všimnout že:

/ Podrobnější pohled |

Adresace a vůbec zacházení s pamětí EEPROM se u různých Atmelů liší. Starší čipy (Atmega328, Atriny85 atp.) používají jiné mechanismy než nové čipy (AVR 0,1,2 series). Například na Atmega4808 práci s EEPROM (a FLASH) zprostředkovává NVMCTRL. EEPROM má fyzické adresy 0x1400 až 0x1500 (256B) a je rozdělena na "page" o velikosti 64B. Zapisovat lze jen do smazaných buněk, takže pro přepsání obsahu EEPROM je potřeba provést dvě operace - mazání a zápis. Obě akce jdou naštěstí spustit jedním příkazem, takže NVMCTRL sám buňku nejprve vymaže a poté do ní zapíše požadovanou hodnotu. Operace s EEPROM probíhá na pozadí, takže jádro může spustit zápis do paměti a poté se věnovat jiné činnosti. Dokud ale probíhá nějaká operace je EEPROM nepřístupná a to i pro čtení. Smazání nebo zápis jednoho bytu trvá přibližně 2ms. Přepsání jednoho bytu (mazání + zápis) pak 4ms. To všechno knihovna eeprom.h nějak řeší a vy se o to nemusíte starat. Ne vždy ale všechny potřebné operace provede zrovna tou nejrychlejší cestou. Například nevhodně provedený zápis 16ti 32bitových hodnot spolkne čtvrt sekundy (a nemusel by). Dovolím si tedy v několika bodech sepsat některá fakta, která vám mohou usnadnit práci s EEPROM.

| Závěr /

Pokud bychom se kompletně oprostili od knihovny eeprom.h, mohli bychom provádět některé operace ještě efektivněji. Například bychom mohli zapisovat kompletně neblokujícím způsobem. NVMCTRL totiž umí volat přerušení když je paměť připravena k dalšímu zápisu. Nebo bychom si mohli vybranou část paměti předem smazat a zkrátit tím pak dobu zápisu. Případně můžeme zapisovat vybranou skupinu bytů v rámci jedné page zároveň atp. Ale to už bychom odběhli příliš daleko od původního záměru - seznámit se s elementární funkcionalitou knihovny eeprom.h. Možná někdy příště.

| Odkazy /

Home
| V1.00 17.7.2022 /
| By Michal Dudka (m.dudka@seznam.cz) /