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:
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)
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ží)
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ů)
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
#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 } }
Při pohledu na obsah paměti EEPROM si můžeme všimnout že:
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.
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ě.
Home
| V1.00 17.7.2022 /
| By Michal Dudka (m.dudka@seznam.cz) /