Zápisník experimentátora
Hierarchy: Externá EEPROM
V tomto článku si vysvětlíme, jak můžeme pracovat s externí EEPROM. Externí EEPROM se obvykle ovládají protokolem I2C a v tomto článku se budeme věnovat právě takovým EEPROM. Příklad bude pro 24LC16B, ale až na drobnosti bude tento návod univerzální pro libovolnou EEPROM.
Na tento experiment stačí pouze pár součástek:
Arduino je upraveno tak, že má na I2C (nachází se netradičně na extra pinech uprostřed Arduina) připájenou pin lištu s otvory, aby se pomocí dvou propojovacích vodičů dalo snadno propojit s EEPROM.
Dvě tlačítka se používají ke spuštění konkrétních testů a jsou připojeny na piny 6 a 8 na Arduinu. EEPROM je připojena k napájení a adresové piny A0, A1 a A2 jsou nezapojené. To platí ale pouze pro tento konkrétní typ EEPROM. Ostatní musí obvykle připojit tyto piny na GND nebo na VCC, čímž si nastaví I2C adresu. Je třeba si vždy zkontrolovat datasheet ke konkrétnímu typu a nastavit to podle něj.
Na ovládání EEPROM existuje dobrá knihovna extEEPROM. Nainstalujte si ji prostřednictvím správce knihoven.
Při tvorbě tohoto vzorového příkladu jsem vyšel z toho, co je přiložen ke knihovně. V době psaní tohoto článku byl přiložen příklad s chybami. Proto se orientujte podle mého vzoru.
Příklad po spuštění vypíše obsah EEPROM a čeká na stisknutí tlačítka. Dvě tlačítka mohou vyvolat následující akci:
Nebudu komentovat celý zdrojový kód, pouze jeho podstatné části. Funkce eeErase
, eeWrite
a eeRead
jsem pouze mírně pozměnil vůči originálu a nacházejí se pouze v kompletních zdrojových textech.
Externí EEPROM 24LC16B dokáže uložit 2 kB 8-bitových hodnot. V tomto případě ji reprezentuje objekt eep
. Chvíli mi trvalo, než jsem ze zdrojových textů uhodl, na co slouží parametr page size
. Pro vás je důležitá informace, že tam třeba nechat vždy hodnotu 16. Je to velikost dat, které se dají najednou přepravit a velikost je limitována i možnostmi implementace I2C v Arduine.
#include <extEEPROM.h>
//One 24LC16B EEPROMs on the bus
const uint32_t totalKBytes = 2; // for read and write test functions
const uint8_t chunkSize = 4; // this can be changed, but must be a multiple of 4 since we're writing 32-bit integers
extEEPROM eep(kbits_16, 1, 16); // device size, number of devices, page size
const uint8_t btnStart = 6; // start button
const uint8_t btnErase = 8; // erase button
Ve funkci setup nastavím dvě tlačítka jako vstupy a abych na zkušební pole nemusel dávat externí rezistory, jsou zde aktivovány interní pull-up rezistory. To znamená, že na daném pine naměříme hodnotu LOW pouze tehdy, když je mikrospínač stlačený. Sériový port je nastaven na hodnotu 115200, aby byly výpisy údajů rychlé. Po resetu Arduino se vypíše aktuální obsah EEPROM a legenda se seznamem funkcí.
void setup(void)
{
pinMode(btnStart, INPUT_PULLUP);
pinMode(btnErase, INPUT_PULLUP);
Serial.begin(115200);
uint8_t eepStatus = eep.begin(twiClock400kHz); //go fast!
if(eepStatus) {
Serial.print(F("extEEPROM.begin() failed, status = "));
Serial.println(eepStatus);
while (1);
}
// dump current EEPROM memory
dump(0,totalKBytes*1024);
Serial.println(F(""));
Serial.println(F("Press button '6' to start write test"));
Serial.println(F("Press button '8' to start erase test"));
}
Funkce loop
sleduje, zda jsme nestiskli nějaké tlačítko. Krátký delay slouží k ošetření zákmitů, ale v reálu trvá zapsání a vypsání údajů o něco déle a proto by tam volání funkce nemuselo být.
void loop(void)
{
if(digitalRead(btnStart) == LOW) {
delay(100);
eeWrite(chunkSize);
eeRead(chunkSize);
dump(0,totalKBytes*1024);
}
if(digitalRead(btnErase) == LOW) {
delay(100);
eeErase(chunkSize,0,totalKBytes*1024);
dump(0,totalKBytes*1024);
}
}
Funkce dump
slouží na kontrolní výpis údajů. Nepsal jsem ji, jen jsem na jejím konci opravil chyby, které zabraňovaly tomu, aby byl obsah paměti pěkně formátovaný. Líbí se mi, jak se její autor pokusil přehledně formátovat údaje. Pokuste se podle jejího kódu uhodnout, jak asi vypadají výsledné údaje.
void dump(uint32_t startAddr, uint32_t nBytes)
{
Serial.print(F("EEPROM DUMP 0x"));
Serial.print(startAddr, HEX);
Serial.print(F(" 0x"));
Serial.print(nBytes, HEX);
Serial.print(F(" "));
Serial.print(startAddr);
Serial.print(F(" "));
Serial.println(nBytes);
uint32_t nRows = (nBytes + 15) >> 4;
uint8_t d[16];
for(uint32_t r = 0; r < nRows; r++) {
uint32_t a = startAddr + 16 * r;
eep.read(a, d, 16);
Serial.print(F("0x"));
if ( a < 16 * 16 * 16 ) Serial.print(F("0"));
if ( a < 16 * 16 ) Serial.print(F("0"));
if ( a < 16 ) Serial.print(F("0"));
Serial.print(a, HEX); Serial.print(F(" "));
for(int c = 0; c < 16; c++) {
if(d[c] < 16)
Serial.print(F("0"));
Serial.print(d[c], HEX);
Serial.print(c == 7 ? " " : " ");
}
Serial.println(F(""));
}
}
Pokud na to nemáte čas nebo článek čtete jen ze zvědavosti a nebudete si zapojení skládat, pak výstup funkce vypadá takto.
EPROM DUMP 0x0 0x800 0 2048 0x0000 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03 0x0010 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00 07 0x0020 00 00 00 08 00 00 00 09 00 00 00 0A 00 00 00 0B 0x0030 00 00 00 0C 00 00 00 0D 00 00 00 0E 00 00 00 0F
Toto byl krátký úvod ke používání externích EEPROM. V některém z následujících článků se na ukládání dat podíváme z praktické stránky a ukážeme si, jak ukládat i složitější údaje pohodlně a bezpečně.
Zdrojový kód se nachází na GitHub.
21.06.2017