Interní EEPROM

Page

Stránky / Arduino / Pod kapotou Arduina /

Každé Arduino má tři druhy paměti. Flash, ve které je program, RAM, ve které se uchovávají údaje během běhu programu a EEPROM, která slouží k trvalému ukládání údajů. Různé verze Arduina mají EEPROM různé velikosti. Vzhledem k tomu, že si zde obvykle budete uchovávat pouze data o konfiguraci, bez problémů vám tato velikost bude stačit.

IDE 1.6.2

IDE 1.6.2 přineslo takové změny, které umožňují pracovat s EEPROM pohodlným způsobem. K ovládání EEPROM slouží třída EEPROMClass, která je pro programátora přístupná pomocí globální proměnné EEPROM. Třída má následující funkce.

  • operator [] - Přístup do EEPROM jako do pole bajtů.
  • read - Čtení z EEPROM.
  • write - Zapisování do EEPROM.
  • update - Zapisování do EEPROM, ale pouze v případě, že se nová hodnota liší. Šetří to opotřebení EEPROM, ale to vám hrozí pouze tehdy, budete-li s EEPROM zacházet neopatrně.
  • get - Šablonová funkce pro čtení celých objektů. Ve starších verzích IDE můžete použít funkci EEPROM_readAnything.
  • put - Šablonová funkce pro zapisování celých objektů. Ve starších verzích IDE můžete použít funkci EEPROM_writeAnything.
  • length - Funkcí lze zjistit aktuální velikost přístupné EEPROM pro váš model Arduina.

 

IDE 1.6.1 a starší

Následující text popisuje ovládání EEPROM pro starší verze IDE Arduina.

V tomto článku se budeme věnovat interní EEPROM, která se nachází v Arduinu. Budeme předpokládat, že máte nastudovány základní příklady, které jsou dodávány k Arduinu. Pokud ne, podívejte se na příklady, které se nacházejí v programech eeprom_clear, eeprom_write a eeprom_read.

Programátor je pohodlný tvor

Pokud si prohlédnete, jak se používají funkce read a write, zjistíte, že jsou dobré pro čtení a zapisování jednotlivých znaků, na složitější údaje je jejich použití trošku nepraktické. Proto není od věci trošku si práci usnadnit. Naštěstí celou práci vykonal už někdo za nás a napsal veškerý kód za nás na stránce http://playground.arduino.cc/Code/EEPROMWriteAnything.

#include <EEPROM.h>
#include <Arduino.h>  // for type definitions

template <class T> int EEPROM_writeAnything(int ee, const T& value)
{
    const byte* p = (const byte*)(const void*)&value;
    unsigned int i;
    for (i = 0; i < sizeof(value); i++)
	  EEPROM.write(ee++, *p++);
    return i;
}

template <class T> int EEPROM_readAnything(int ee, T& value)
{
    byte* p = (byte*)(void*)&value;
    unsigned int i;
    for (i = 0; i < sizeof(value); i++)
	  *p++ = EEPROM.read(ee++);
    return i;
}

Jelikož ne každý se živí programováním profesionálně, na první pohled se mohou tyto dvě šablonové funkce jevit nepochopitelně. Ale je to jen vtipné využití šablonových funkcí, které kompilátor umí použít. Nebudeme si vysvětlovat podrobnosti o šablonách, k tomu bychom mohli potřebovat mnoho stránek textu. Zájemci o podrobnosti si mohou rozšířit znalosti na Wikipedii v článcích Template (C++) a Standard Template Library.

Zaměříme se jen na to, jak funkce fungují. Základem pro pochopení je to, že kompilátor dokáže namísto class T doplnit definici libovolného typu (i struktury, nebo třídy) a vygenerovat k tomu zdrojový kód. Udělá to tehdy, když se natrefí na nějaký kód, který použije definovanou šablonu. Při bližším pohledu na kód vidíme, že dělá následující věc:

  • Vytvoří pointer na začátek dat, která funkce dostala jako argument T.
  • Pomocí funkce sizeof si spočítá délku typu T v paměti.
  • A pak už jen zapíše nebo přečte údaje.

Pokud chceme používat tyto funkce, musíme si je uložit do souboru EEPROMAnything.h a uložit jej nebo do adresáře s funkcemi EEPROM, nebo soubor pouze přiložit do adresáře v vašem projektu.

Algoritmus ukládání dat

Jelikož v EEPROM jsou implicitně uloženy náhodné údaje (i když čisté Arduino zde bude mít uloženy nuly), které mohou být ještě náhodnější po vašich předchozích experimentech, je dobré se nespoléhat na tuto náhodu a použít následující algoritmus.

  • Své údaje si uložte do struktury.
  • Na začátku struktury umístěte 2-4 rozlišovacích znaků, pomocí kterých zjistíte, zda EEPROM obsahuje náhodné znaky, nebo je zformátována s vašimi údaji.
  • V prvním kroku načtete EEPROM do dočasné proměnné. Pokud se rozlišovací znaky shodují, překopírujete dočasnou proměnnou do globální, kde si ukládáte své konfigurace.
  • Pokud se neshoduje, zapíšete do EEPROM obsah globální proměnné, kde jsou implicitní údaje.
  • Následně zapisujete do EEPROM obsah globální proměnné jen tehdy, když dojde ke změně konfiguračních údajů.

Pokud budete dodržovat tento postup, nebudete mít nikdy chaos v tom, co za data jsou v EEPROM.

EEPROM prakticky pro Arduino Uno

Pojďme nyní předchozí algoritmus odzkoušet na konkrétním příkladu. Údaje se ukládají ve struktuře cfg. Máme jednu globální proměnnou typu cfg se jménem c, která má nastavené implicitní hodnoty. Ve funkci setup vidíme, jak si vytvoříme lokální proměnnou tmp, do které přečteme obsah EEPROM. Pokud se identifikační znaky v tmp neshodují s řetězcem T1, pak předpokládáme první spuštění programu na nezkonfigurovaném systému a uložíme obsah našeho implicitního nastavení do EEPROM. Je-li nalezena shoda, zapíšeme pouze obsah tmp do c(c++ je natolik inteligentní, aby za nás doplnilo kód operátora přiřazení). Abychom ale i něco viděli, při každém načtení z EEPROM mírně upravíme obsah obou proměnných. Takto při každém stisku tlačítka reset uvidíme změnu v uložených údajích.

#include <EEPROM.h>
#include "EEPROMAnything.h"

struct cfg {
  char ident[2];
  int minimum;
  int maximum;
};

cfg c={{'T','1'},0,1234};

const int address = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println("Arduino Uno EEPROM demo");
  
  cfg tmp;
  EEPROM_readAnything(address,tmp);
  if(!(tmp.ident[0]=='T' && tmp.ident[1]=='1'))
    {
    Serial.println("Default value");  
    EEPROM_writeAnything(address,c);
    }
  else
    {
    Serial.println("EEPROM stored value");  
    c=tmp;
    c.minimum++;
    c.maximum--;
    EEPROM_writeAnything(address,c);
    }
    
Serial.print("cfg.minimum=");
Serial.println(c.minimum);  
Serial.print("cfg.maximum=");
Serial.println(c.maximum);  
}

Zdrojový kód příkladu je v souboru eeprom_practical_01.zip.

EEPROM prakticky pro ATtiny85

Abychom odzkoušeli i malého bratříčka Arduina, můžeme si vyzkoušet příklad i na něm. Zdrojový kód je skoro identický, je tam pouze použitá knihovna SoftwareSerial k obsluze sériového portu. Aby tento příklad fungoval, je třeba propojit ATtiny85 a například Arduino MEGA 2560 podle tohoto návodu. Zdrojový kód příkladu je v souboru eeprom_practical_02.zip.

Velikosti EEPROM

V následující tabulce je uvedena velikost EEPROM pro běžné druhy Arduina.

 Typ Velikost
 Uno 1024
 MEGA 2560 4096
 ATtiny85 512
 Due  *
 Zero 16kb emulácia **

* Do dnešního dne jsem žil v přesvědčení, že by měla být EEPROM na Arduino Due emulována přes flash. Ale momentálně na stránce nic takového nemají uvedeno. Takže když si najdu čas, prozkoumám tento problém podrobněji.

** A pravděpodobně ani Zero emulaci nakonec nemá.

Zdrojové texty

Zdrojové texty příkladů naleznete na GitHub.


17.02.2022


Menu