24C04 - I2C EEPROM

Abend leute,

wollte mal fix in die Runde fragen, ob es eine allgemein gültige und funktionierende Methode gibt, den zu beschreiben.

Hab viele Sketche gefunden, wenige funktionierten und noch weniger hab ich verstanden.
Benötigt man für solch einen EEPROM eine extra Library oder genügt wirklich die Wire.h ?

Mit dem I2C-Scanner hab ich die Adresse ausfindig gemacht, jedoch wird mir neben der 0x50 auch noch die 0x51 angezeigt.. Warum ist das so?

Im Datenblatt steht "512x8 (4k)". Was heißt das jetzt?
Wieviel "Platz" ist auf dem ding? 8x 512kbyte ?

als aktuellen Testsketch, den ich unter den dutzenden gefunden habe, läuft gerade dieser hier:

/* 
  *  Use the I2C bus with small EEPROMs
  *  24C01, 20C02, 24C04, 24C08, 24C16
  *  Sketch:    I2C_EEPROM_Small.pde
  *  
  *  Derived from sketch for 24C64 devices posted on
  *     http://www.arduino.cc/playground/Code/I2CEEPROM
  *  From  hkhijhe   Date: 01/10/2010
  * 
  *  This one by davekw7x
  *  March, 2011
  *
  * For a single device, connect as follows:
  * EEPROM 4 (GND) to GND
  * EEPROM 8 (Vcc) to Vcc (5 Volts)
  * EEPROM 5 (SDA) to Arduino Analog Pin 4
  * EEPROM 6 (SCL) to Arduino Analog Pin 5
  * EEPROM 7 (WP)  to GND
  * EEPROM 1 (A0)  to GND
  * EEPROM 2 (A1)  to GND
  * EEPROM 3 (A2)  to GND
  */

#include <Wire.h>


// The seven-bit device address for EEPROMs
// I'll define it here rather than hard-code it inside all of the
// functions.
//
const byte DEVADDR = 0x50;

void setup()
{
    
    Wire.begin();
    Serial.begin(9600);
    eeprom_write_byte( DEVADDR, 0, 123 );     // Testweise mal einen int-Wert von 123 anstatt 0xAB
    Serial.println("Memory written");
    delay(2000);

}

void loop()
{
   byte b = eeprom_read_byte(DEVADDR, 0);
        Serial.println(b);                // dann hier noch das ,HEX entfernt
         delay(2000);
    
}

void eeprom_write_byte(byte deviceaddress, int eeaddress, byte data)
{
    // Three lsb of Device address byte are bits 8-10 of eeaddress
    byte devaddr = deviceaddress | ((eeaddress >> 8) & 0x07);
    byte addr    = eeaddress;
    Wire.beginTransmission(devaddr);
    Wire.write(int(addr));
    Wire.write(int(data));
    Wire.endTransmission();
    delay(10);
}

  // Pages are blocks of 16 bytes, starting at 0x000.
  // That is, pages start at 0x000, 0x010, 0x020, ...
  // For a device "page write", the last byte must be
  // on the same page as the first byte.
  //
  // No checking is done in this routine.
  //
  // TODO: Do some checking, or, better yet (maybe)
  // make length an int and do repeated device
  // page writes if necessary. (Then maybe rename to
  // eeprom_write_pages or some such thing.)
  //
void eeprom_write_page(byte deviceaddress, unsigned eeaddr,
                       const byte * data, byte length)
{
    // Three lsb of Device address byte are bits 8-10 of eeaddress
    byte devaddr = deviceaddress | ((eeaddr >> 8) & 0x07);
    byte addr    = eeaddr;
    Wire.beginTransmission(devaddr);
    Wire.write(int(addr));
    for (int i = 0; i < length; i++) {
        Wire.write(data[i]);
    }
    Wire.endTransmission();
    delay(10);
}

// TODO: Change to integer data type and return -1 if can't
// read.
//
int eeprom_read_byte(byte deviceaddress, unsigned eeaddr)
{
    byte rdata = -1;

    // Three lsb of Device address byte are bits 8-10 of eeaddress
    byte devaddr = deviceaddress | ((eeaddr >> 8) & 0x07);
    byte addr    = eeaddr;

    Wire.beginTransmission(devaddr);
    Wire.write(int(addr));
    Wire.endTransmission();
    Wire.requestFrom(int(devaddr), 1);
    if (Wire.available()) {
        rdata = Wire.read();
    }
    return rdata;
}

//
// Returns number of bytes read from device
//
// Due to buffer size in the Wire library, don't read more than 30 bytes
// at a time!  No checking is done in this function.
//
// TODO: Change length to int and make it so that it does repeated
// EEPROM reads for length greater than 30.

int eeprom_read_buffer(byte deviceaddr, unsigned eeaddr,
                        byte * buffer, byte length)
{
    // Three lsb of Device address byte are bits 8-10 of eeaddress
    byte devaddr = deviceaddr | ((eeaddr >> 8) & 0x07);
    byte addr    = eeaddr;
    
    Wire.beginTransmission(devaddr);
    Wire.write(int(addr));
    Wire.endTransmission();

    Wire.requestFrom(devaddr, length);
    int i;
    for (i = 0; i < length && Wire.available(); i++) {
        buffer[i] = Wire.read();
    }
    return i;
}

//
// The display is like hexdump -C.  It will always
// begin and end on a 16-byte boundary.
//

void eeprom_dump(byte devaddr, unsigned addr, unsigned length)
{
    // Start with the beginning of 16-bit page that contains the first byte
    unsigned startaddr = addr & (~0x0f);

    // stopaddr is address of next page after the last byte
    unsigned stopaddr  = (addr + length + 0x0f) & (~0x0f);

    for (unsigned i = startaddr; i < stopaddr; i += 16) {
        byte buffer[16]; // Hold a page of EEPROM
        char outbuf[6];  //Room for three hex digits and ':' and ' ' and '\0'
        sprintf(outbuf, "%03x: ", i);
        Serial.print(outbuf);
        eeprom_read_buffer(devaddr, i, buffer, 16);
        for (int j = 0; j < 16; j++) {
            if (j == 8) {
                Serial.print(" ");
            }
            sprintf(outbuf, "%02x ", buffer[j]);
            Serial.print(outbuf);            
        }
        Serial.print(" |");
        for (int j = 0; j < 16; j++) {
            if (isprint(buffer[j])) {
                Serial.print(buffer[j]);
            }
            else {
                Serial.print('.');
            }
        }
        Serial.println("|");
    }
}

ganz oben hab ich selbst mal versucht einen int-Wert einzugeben. Schließlich will ich später mal eine Variable einfach so schreiben und auch wieder lesen können, da bringt mir HEX so direkt erstmal nichts.
Dazu musste ich dann auch noch bei der Ausgabe in der loop das ",HEX" entfernen.

So konnte ich einen Wert schreiben und lesen;

Nur was bedeuten die anderen Funktionen? Braucht man die? Was machen die?
Kann mir das jemand erklären? Der Ersteller des Playgroundeintrages hat leider nichts dazu geschrieben :frowning:

Gruß

Auf dem EEprom kannst du 4*1024 = 4096 Bits speichern
Also 512 Bytes.

EEproms sind als Pages Organisiert.

AT24C04, 4K SERIAL EEPROM:
Internally organized with 32 pages of 16 bytes each,
the 4K requires a 9-bit data word address for random word addressing.

Mit Pagewrite/read kannst du bei deinem Baustein bis zu 16 Byte auf einmal in das EEprom schreiben/lesen.
Was performanter ist als jedes Bytes einzeln.
Es gibt hier bezüglich der Startadresse was beachten.
Dies zeigt das ganz gut am Beispiel eines 24LC256 . http://www.hobbytronics.co.uk/eeprom-page-write

Nicht alle EEproms haben die gleiche Programmierlogik, darum gibt es keinen universellen Weg sie zu programmieren.
Du mußt immer das Datenblatt konsultieren.

Das genannte EEPROM braucht für die Adressierung der zu schreibenden oder lesenden Speicherstellen 9 Bit (512 Speicherzellen).

Der I2C Bus überträgt immer 8Bit. Mit 2 Byte (erstes Byte I2C Adresse und R/W Bit und dem 2.Bit Speicherzellenadresse)
Mit einer 8 Bit Speicherzellenadresse könte man nur 256 Speicherzellen ansprechen. Darum nimmt man bei diesem EEPROM das Bit 0 der I2C-Adresse (das niederwertigste) um die vollen 512 Speicherzellen ansprechen zu können. Daraus ergeben sich die beiden I2C Adressen 0x50 und 0x51. Siehe Kapitel 6 auf Seite 9 des genannten Datenblatts.
Ander Typen von EEPROMS machen das anders zB mit 2 SpeicherzellenadressBytes.

Zu den Page Write:
Dieses EEPROM ist in 32 Pages (Blöcke) zu 16 Byte organisiert. Das heißt ein Schreib oder Lesezugriff erfolgt immer auf die gesamte Page auch wenn nur 1 Byte geschrieben oder gelöscht wird. Darum können auch alle 16 Byte gleichzeitig geschrieben werden. Die Schreibezeit bleibt gleich. Eine Page Write programmiert man indem man die Startadresse (I2C-Adresse und Speicherzellenadresse) überträgt und dann nicht nur 1 Byte wie bei einem 1Byte Schreibzugriff sondern alle 16 Byte überträgt. Die unteren 4 Byte werden automatisch für jedes Byte erhöht. Zu beachten ist daß die Startadresse die erste Adressse einer Page sein muß (in diesem Fall ein vielfaches von 16) da ansonsten ein overflow auftritt und die restlichen Daten nicht in die nächste Page geschrieben werden sondern an der Anfang der Page.
Also wenn ich mit Adresse 8 anfange werden dei ersten 8 Byte in die Speicherzellen 8 bis 15 geschrieben, die restlichen 8 in die Speicherzellen 0 bis 7 und nicht 16 bis 24 !!!!

Grüße Uwe

Danke Uwe :slight_smile:

Hab mir zusätzlich noch ein Video auf Youtube angesehen, bei dem die I²C Kommunikation auch nochmal tiefergehend mit EEPROMs, und weiteren I²C Bausteinen erklärt wird..

Wers sehen will:

Sehr interessant und Informativ. Leider Englisch, aber trotzdem gut zu verstehen.

Bei diesen EEproms ist das "Problem" das du auf einer Adresse nur 2kB ansprechen kannst das heisst dan der 0x50 findest du die ersten 2k und auf den 0x51 die zweiten 2k.
Aus dem Handbuch:

The 4K EEPROM only uses the A2
and A1 device add
ress bits with the third bit being a
memory page address bit. The two device address bits must compare to their corre-
sponding hard-wired input pins
. The A0 pin is no connect

Hier die Belegung also bei A1 + A2 untere 2k ergeben sich aus A1+A2 und obere 2k A1+A2+1

Schau mal im Handbuch unter das Bildchen mit der erklärung

Figure 7. Device Address

Page 1 (untere 2k)
0x50=101 0000
Page 2 (obere 2k)
0x51=101 0001

das R/W bit wird von der wire.h geschrieben (ob lesen oder schreiben auf die Adresse)

Der erkennt man das ganz gut

Gruß
Der Dani

Ja, so hab ich das auch verstanden.. (im Video auch gut erklärt) das 8. Bit wird über den Befehl den man der Wire. mitgibt entsprechend gesendet.

Wire.read oder Wire.write wählt das aus, somit braucht man nur eine 7 bit lange Adresse anzugeben. Das 8. Bit braucht einen selbst nicht zu interessieren. Natürlich nur, bei der Adressangabe des Chips. Die Speicherbereichsadresse ist natürlich dann als 8bit Adresse anzugeben.

Soweit hab ichs zumindest verstanden/aufgefasst.