EEPROM

Nayon sok esetben problémát okoz az Arduinok esetén, hogy az adatok alapból nem perzisztálhatók. A perzisztáció azt jelenti, hogy az adatok nem törlődnek újraindítás, komplexebb esetben redundáns átkapcsolás után sem. Az Arduino operatív memóriája teljes mértékben törlődik ilyenkor. A legegyszerűbb megoldás az, hogy az Arduino EEPROM memória-területére tároljuk az ilyen adatokat, vagy egy külső memóriát alkalmazunk adattárolásra, ami lehet akár egy SD kártya is, de egyszerűbb egy külső EEPROM-ot beépíteni. Nyilván a megoldások között segít eligazítani, hogy mennyire akarjuk terhelni az Arduino saját EEPROM-ját, illetve milyen mennyiségű adatot akarunk perzisztálni.

A különböző Arduino board-ok eltérő méretű EEPROM-mal rendelkeznek:

  • ATmega328P: 1024 bájt
  • ATmega168: 512 bájt
  • ATmega8: 512 bájt
  • ATmega1280: 4096 bájt
  • ATmega2560: 4096 bájt

Az EEPROM eléréséhez érdemes az EEPROM könyvtár funkcióit felhasználni: https://www.arduino.cc/en/Reference/EEPROM

A belső EEPROM írásánál ügyelni kell arra, hogy az maximum nagyjából 100.000 írásciklusra alkalmaz. Ez talán soknak tűnik, de ha minden ciklusban adatokat próbálunk oda menteni, gyorsan elérhetjük élettartamának a végét. Nyilván érdemes az írásokat nem túl gyakran bekövetkező eseményekhez, vagy idő-triggerhez kötni.

Példaprogram az Arduino EEPROM-hoz

/*
 * EEPROM Write
 *
 * Stores values read from analog input 0 into the EEPROM.
 * These values will stay in the EEPROM when the board is
 * turned off and may be retrieved later by another sketch.
 */
 
#include <EEPROM.h>
 
/** the current address in the EEPROM (i.e. which byte we're going to write to next) **/
int addr = 0;
 
void setup() {
  /** Empty setup. **/
}
 
void loop() {
  /***
    Need to divide by 4 because analog inputs range from
    0 to 1023 and each byte of the EEPROM can only hold a
    value from 0 to 255.
  ***/
 
  int val = analogRead(0) / 4;
 
  /***
    Write the value to the appropriate byte of the EEPROM.
    these values will remain there when the board is
    turned off.
  ***/
 
  EEPROM.write(addr, val);
 
  /***
    Advance to the next address, when at the end restart at the beginning.
 
    Larger AVR processors have larger EEPROM sizes, E.g:
    - Arduno Duemilanove: 512b EEPROM storage.
    - Arduino Uno:        1kb EEPROM storage.
    - Arduino Mega:       4kb EEPROM storage.
 
    Rather than hard-coding the length, you should use the pre-provided length function.
    This will make your code portable to all AVR processors.
  ***/
  addr = addr + 1;
  if (addr == EEPROM.length()) {
    addr = 0;
  }
 
  /***
    As the EEPROM sizes are powers of two, wrapping (preventing overflow) of an
    EEPROM address is also doable by a bitwise and of the length - 1.
 
    ++addr &= EEPROM.length() - 1;
  ***/
 
 
  delay(100);
}

AT24C256 I²C EEPROM modul

Ebben az esetben az EEPROM funkció I²C-n keresztül érhető el. Mivel jó eséllyel úgyis alkalmazunk pár másik Arduino I²C kommunikációs egységet is, így már a memóriakezelés nem fog további értékes pin-eket foglalni. Ennek az IC-nek két változata van, az AT24C128 128 kBit - 16 kByte, míg a AT24C256 256 kBit - 32 kByte címezhető, törölhető, de perzisztens adatterületet tartalmaz. Az írás / olvasás bájtonként vagy laponként (64 bájt) történhet.

AT24C256 technikai adatok

  • Tápfeszültség:
    • (alacsony feszültésgű mód): 1,8..3,6 V
    • (normál feszültésgű mód): 2,7..5,5 V
  • Kommunikáció: I²C
  • Átlapolás mód: 64 byte
  • I²C frekvencia kompatibilitás: 1 MHz (5V), 400 kHz (2,7..2,5V), 100 kHz (1,8V)
  • Írásciklusok maximális száma: 1 millió
  • Működési hőmérséklet: -55..125 °C
  • A lábak feszültségtűrése a GND-hez képest: -1..7 V
  • Maximális tápfeszültség: 6,25 V
  • Kimeneti áram: 5,0 mA

AT24C256 IC lábkiosztás

AT24C256 IC lábkiosztás

A0, A1: I²C cím
SDA: I²C adatport
SCL: I²C órajel port
WP: írásvédelem az IC-re
NC: nincs bekötve

Az IC külön is beszerezhető, de egyszerűbb a modult beszerzi, akkor már nem kell bütykölni az I²C felhúzóellenállásaival, címzés jumperekkel, és a "drótozással".

AT24C256 címkiosztás

Az EEPROM címe az A0, A1 pinek (vagy modul esetén jumperek) segítségével így alakul:

AT24C256 címkiosztás

AT24C256 telegram-felépítés

AT24C256 telegram-felépítés

Vezetékezés (modul)

AT24C256 I²C EEPROM modul vezetékezés

Szoftver

A modulhoz alkalmazásprogram a Githubról is letölthető: https://github.com/cyberp/AT24Cx

/*
  *  Use the I2C bus with EEPROM 24LC64
  *  Sketch:    eeprom.ino
  *
  *  Author: hkhijhe
  *  Date: 01/10/2010
  *
  *
  */
 
#include <Wire.h>
 
 
 
void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
    int rdata = data;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.write(rdata);
    Wire.endTransmission();
}
 
// WARNING: address is a page address, 6-bit end will wrap around
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddresspage >> 8)); // MSB
    Wire.write((int)(eeaddresspage & 0xFF)); // LSB
    byte c;
    for ( c = 0; c < length; c++)
        Wire.write(data[c]);
    Wire.endTransmission();
}
 
byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
    byte rdata = 0xFF;
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,1);
    if (Wire.available()) rdata = Wire.read();
    return rdata;
}
 
// maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
    Wire.beginTransmission(deviceaddress);
    Wire.write((int)(eeaddress >> 8)); // MSB
    Wire.write((int)(eeaddress & 0xFF)); // LSB
    Wire.endTransmission();
    Wire.requestFrom(deviceaddress,length);
    int c = 0;
    for ( c = 0; c < length; c++ )
        if (Wire.available()) buffer[c] = Wire.read();
}
 
 
 
 
void setup()
{
    char somedata[] = "this is data from the eeprom"; // data to write
    Wire.begin(); // initialise the connection
    Serial.begin(9600);
    i2c_eeprom_write_page(0x50, 0, (byte *)somedata, sizeof(somedata)); // write to EEPROM
 
    delay(100); //add a small delay
 
    Serial.println("Memory written");
}
 
void loop()
{
    int addr=0; //first address
    byte b = i2c_eeprom_read_byte(0x50, 0); // access the first address from the memory
 
    while (b!=0)
    {
        Serial.print((char)b); //print content to serial port
        addr++; //increase address
        b = i2c_eeprom_read_byte(0x50, addr); //access an address from the memory
    }
    Serial.println(" ");
    delay(2000);
}