V tomto článku si ukážeme ako správne pracovať s eeprom pamäťou v Arduinu. Na začiatku si povieme ako čítať a zapisovať z/do pamäte. Na konci spomeniem nejaké tipy a triky ako si zjednodušiť prácu..

Veľkosť EEPROM pamäte v Arduino doskách

V každej verzii Arduina, môže byt rozdielna veľkosť pamäte eeprom. Tabuľka nižšie presne definuje tuto veľkosť vzhľadom na konkrétny typ/verziu Arduina.

Typ ArduinaMikrokontrolérVeľkosť EEPROM
UNO, Mini, Nano ATmega328 1024B
Leonardo ATmega32U4 1024B
Duemilanove ATmega168 512B
Mega, Mega ADK ATmega2560 4KB

Čítanie a zápis do EEPROM pamäte.

Arduino ma na čítanie a zápis knižnicu EEPROM založenú na avr knižnici avr/eeprom.h. Hlavnou a jedinou triedou knižnice EEPROM je rovnomenná trieda EEPROM. Tato trieda ma dve metódy read a write. Metoda read nám vracia uložený bajt na adrese, ktorý zadáme ako parameter. Slúži nám teda na čítanie z eepromky. Parameter je typu int, jeho hodnota môže byt v intervale od 0 do max. veľkosti eepromky mínus 1 (napr. pre verziu UNO je max hodnota 1023). Metoda write nám zapíše požadovaný bajt(hodnotu) na adresu v eeprom pamäti, slúži teda na zapisovanie dát. Metoda ma dva parametre. Prvý parameter je adresa v pamäti, druhy parameter je hodnota (bajt), ktorú chceme do eepromky uložiť. Hodnoty prvého parametra môžu byt rovnaké ako v prípade metódy read. Druhy parameter metódy write je typu byte, tzn. jeho hodnoty môžu byt v intervale od 0 do 255. V nasledujúcich príkladoch si ukážeme ako tuto knižnicu použiť..

Čítanie z EEPROM pamäte.

Na jednoduchom príklade si ukážeme ako načítať hodnotu z EEPROM pamäte.

Zdrojový kód
Info! Celý projekt (pre Atmel Studio 6) si môžete stiahnuť z github servera. Prevziať celý projekt.
#include <eeprom.h>

#define EEPROM_ADDRESS_MAX 512

byte value = 0;

void setup()
{
	Serial.begin(19200);

	readFromEeprom();
}

void loop()
{
}

void readFromEeprom()
{
	for (int i=0; i <= EEPROM_ADDRESS_MAX; i++)
	{
		value = EEPROM.read(i);
			
		Serial.print(i);
		Serial.print(" = 0x");
		Serial.println(value, HEX);
			
		delay(500);
	}
}
Výstup programu

Na obrázku je vidieť výpis dát z eeprom pamäte.

Zápis do EEPROM pamäte

Jednoduchý príklad, ktorý znázorňuje zápis do EEPROM pamäte.

Zdrojový kód
Info! Celý projekt (pre Atmel Studio 6) si môžete stiahnuť z github servera. Prevziať celý projekt.
#include <EEPROM.h>

#define EEPROM_ADDRESS_MAX 1024

void setup()
{
	Serial.begin(19200);
	
	writeToEeprom();
}

void loop()
{
}

void writeToEeprom()
{
	byte val = 0;
	for (int i = 0; i < EEPROM_ADDRESS_MAX; i++)
	{		
		Serial.print("write ");
		Serial.print(val);
		Serial.print(" to eeprom address ");
		Serial.println(i);
	
		EEPROM.write(i, val);	
		val++;
		if (val==256)
		{
			val = 0;
		}
		delay(500);
	}	
}

 

Výstup programu

Na obrázku je vidieť výpis dát, ktoré zapisujeme do eeprom pamäte.

V posledných dvoch príkladoch sme mohli vidieť ako jednoducho je možne zapisovať do eepromky v Arduinu. Problém môže však nastáť ak budeme chcieť uložiť iný dátový typ, ako napríklad int alebo double. Preto si ďalej ukážeme ako ukladať aj iné typy ako je byte.

Zápis a čítanie „všetkých“ dátových typov

Vytvoril som špeciálne pomocne funkcie na zapisovanie a čítanie zložitejších (komplexných) typov do eeprom pamäte. Funkcie využívajú základnú knižnicu avr/eeprom.h. Je tak možne použiť tieto funkcie aj pre ostatné avr mikrokontroléri.

Špeciálny súbor EEPROMAnything.h:
#ifndef EEPROMANYTHING_H_
#define EEPROMANYTHING_H_

#include <avr/eeprom.h>

/* Write to EEPROM */
#define EP_WRITE_BLOCK(address, block) eeprom_write_block((const void*)&block, (void*)address, sizeof(block))
#define EP_WRITE_BYTE(address, b) eeprom_write_byte(address, b)

/* Read from EEPROM */
#define EP_READ_BLOCK(address, block) eeprom_read_block((void*)&(block), (void*)address, sizeof(block))
#define EP_READ_BYTE(address) eeprom_read_byte(address)


#endif /* EEPROMANYTHING_H_ */
Ukážka použitia funkcii:

Nahrajte program do arduina a spustite Serial Monitor v Atmel Studiu alebo v Arduino IDE a zadajte písmeno t. Zadaním tohto príkazu spustíme krátky test (funkcia runTest). Vo funkcii načítavame dáta z eeprom vo forme objektu myConfig. Object myConfig je typu config_t, čo je vlastne štruktúra. Tento náš typ config_t je pre nás vlastne ako model nastavení pre náš program/aplikáciu. Vytvorená inštancia myConfig nám uchováva všetky potrebné údaje, ktoré chceme uchovať aj po vypnutí alebo reštartovaní zariadenia.

Info! Celý projekt (pre Atmel Studio 6) si môžete stiahnuť z github servera. Prevziať celý projekt.
#include "EEPROMAnything.h"

struct config_t
{
	long test; 
};

config_t myConfig;

char inputChar;

void setup()
{
	Serial.begin(19200);				
}

void loop()
{	
	if (Serial.available()>0)
	{
		inputChar = Serial.read();
		if (inputChar=='t')
		{
			runTest();
		}
		inputChar = 0;
	}
}

void runTest()
{
	Serial.println("-> start eeprom test");		
	
	EP_READ_BLOCK(0, myConfig);
	
	Serial.print("myConfig.test=");
	Serial.println(myConfig.test);
	
	Serial.println("Set myConfig.test=123 and save.");
	myConfig.test = 123;
	
	EP_WRITE_BLOCK(0, myConfig);
	delay(100);
	EP_READ_BLOCK(0, myConfig);
	
	Serial.print("myConfig.test=");
	Serial.println(myConfig.test);
	
	Serial.println("-> end eeprom test");
}

 

Všimnite si riadok číslo 34, funkciu EP_READ_BLOCK čítame nastavenia z eeprom pamäte. Prvý parameter je adresa  a druhý parameter je objekt, ktorý chceme načítať (resp. jeho referencia).

Na riadku 42, voláme funkciu EP_WRITE_BLOCK - táto funkcia nám uloží data/nastavenia do eeprom pamäte. Jej signatúra je rovnaká ako pri predchádzajúcej funkcii na čítanie dát. 

Ešte spomeniem že typ config_t môže obsahovať viacero členov. Momentálne má jeden s názvom test (typ long). Ak budete potrebovať ukladať viacero typov dát, tak jednoducho pridáte do štruktúri config_t ďalšieho člena. viď ukážka nižšie:

struct config_t
{
	long test; 
	uint8_t pinValue;
};

 

Výstup programu po zadaní príkazu "t":

Tip 1: Vymazanie eeprom pamäte

Občas pri programovaní Arduina, by sme potrebovali vymazať dáta z eeprom pamäte a mat tak „čistý stôl“. Napríklad ak chceme Arduino použiť na iný projekt. V tomto prípade je vhodne použiť nejakú „vychytavku“. Tento kód použijeme iba raz na začiatku vývoja nového projektu. Príklad hovorí sám za seba.

Zdrojový kód na vymazanie eeprom pamäte:

Info! Celý projekt (pre Atmel Studio 6) si môžete stiahnuť z github servera. Prevziať celý projekt.
#include <EEPROM.h>

#define EEPROM_CAPACITY 512
#define PIN_LED 13

void setup()
{	
	eraseEeprom(EEPROM_CAPACITY);
}

void loop()
{
}

void eraseEeprom(unsigned int capacity)
{
	pinMode(PIN_LED, OUTPUT);
	digitalWrite(PIN_LED, HIGH);
	for (int i=0; i < capacity; i++)
	{
		EEPROM.write(i, 0xff);
		delay(20);
	}
	digitalWrite(PIN_LED, LOW);
}

Popis zdrojového kódu

Program je jednoduchý. Ihneď po uploadnutí sa spustí funkcia eraseEeprom.

Mazanie eeprom spočíva v zapisovaní hodnoty 0xff na každú adresu v pamäti.

Ešte sa vrátim k funkcii eraseEeprom, ktorá ma jeden parameter a tým je kapacita eeprom pamäte. Preto je nutné ešte pred uploadnutím nastaviť túto hodnotu podľa použitej verzie arduina.

Ukončenie mazania je indikované vstavanou LEDkou na vývode číslo 13.

Tip 2: „Magic number“

Čo je Magic number? Je to riešenie na problém, ktorý vzniká pri prvotnom načítaní dát z eeprom, kedy sa nám dáta v pamäti ešte nenachádzajú resp. neboli do nej ešte uložene. Viac vysvetli ukážka kódu:

Zdrojový kód
Info! Celý projekt (pre Atmel Studio 6) si môžete stiahnuť z github servera. Prevziať celý projekt.
#include "EEPROMAnything.h"
//version of user settings 'Magic Number'
#define MAGIC_NUMBER 100

struct settings_t 
{
	unsigned int magicNumber;
	char color;
	unsigned int pwm;
};

settings_t userSettings;

void setup()
{	
	Serial.begin(19200);
	
	readUserSettings();
	if (userSettings.magicNumber!=MAGIC_NUMBER)
	{
		//set default values
		userSettings.magicNumber = MAGIC_NUMBER;
		userSettings.color = 'B';
		userSettings.pwm = 255;
		saveUserSettings();				
	}		
	printUserSettings();
}

void loop()
{
}

void readUserSettings()
{
	EP_READ_BLOCK(0, userSettings);
	delay(20);
}

void saveUserSettings()
{
	EP_WRITE_BLOCK(0, userSettings);
	delay(20);
}

void printUserSettings()
{
	Serial.print("magicNumber=");
	Serial.println(userSettings.magicNumber);
	Serial.print("color=");
	Serial.println(userSettings.color);
	Serial.print("pwm=");
	Serial.println(userSettings.pwm);
}
Popis zdrojového kódu

Na začiatku máme inkludnutý súbor EEPROMAnything.h o ktorom sme si popísali niečo vyššie. Aj teraz nám dobre poslúži na ukladanie komplexného typu do eeprom pamäte.

Zase tu máme definíciu špeciálneho typu, tentokrát s názvom settings_t. Určite ste si všimli členskú premennú magicNumber. Premenná magicNumber resp. jej hodnota slúži na porovnanie správnej verzie nastavení.

Pod definíciou je inštancia typu settings_t s názvom userSettings. Táto inštancia nám uchováva užívateľské nastavenia. 

Dobre si pozrite metódu setup, v ktorej pomocou funkcie readUserSettings načítavame nastavenia z eeprom pamäte. Na ďalšom riadku porovnávam premennú magicNumber z userSettings s konštantou MAGIC_NUMBER. Toto je veľmi dôležitá podmienka, podľa ktorej zistíme, či sú data/nastavenia prvýkrát načítané z eeprom a tzn. že nemajú ešte žiadne počiatočné hodnoty. Samozrejme v podmienke tieto hodnoty ponastavujeme a nastavenia uložíme do eepromky zavolaním funkcie saveUserSettings.

Po reštartovaní arduina sa z eeprom pamäte načítajú už počiatočné hodnoty (alebo hodnoty, ktoré sme uložili počas behu programu). Tentokrát podmienka v setup metóde už neplatí.

Čo ak pridám ďalšiu položku do nastavenia? V tomto prípade stačí zmeniť hodnotu MAGIC_NUMBER a dáta sa v eeprom prepíšu. Magic number je geniálne a jednoduché riešenie na takýto problém :).

Záver

Popísali sme si v článku ako používať eeprom pamäť ci už na ukladanie jednotlivých bajtov alebo aj zložitejších štruktúr. Práve ukladanie zložitejších dátových typov je v praxi častejšie. Je prehľadnejšie si určiť vlastnú štruktúru dát, s ktorú použijeme v programe a zároveň ju celu môžeme aj uložiť do eeprom pamäte. Nezabudnite použiť pomocne funkcie pri načítavaní a zapisovaní z/do eeprom. Taktiež magic number ako riešenie na prvotný zápis/načítanie údajov. Ak budete mat nejaké otázky ohľadne tejto témy napíšte mi do komentára.