Bei den ausgegebenen Werten handelt es sich um die Adressen, unter denen Sie die Uhr und den EEPROM ansprechen. Eine erste Kontaktaufnahme erfolgt mit den I2C-Tools. Dem Datenblatt [3] zufolge findet sich der Chip 24C32N im Adressbereich 0x50 bis 0x57. Die konkrete Adresse kodieren Sie durch den Anschluss der Pins A0, A1 und A2 an die Spannung Vcc. Sind alle drei Pins angeschlossen, wie bei der RTC DS3231 der Fall, hört der Chip auf die Adresse 0x57.
Der 24C32N erlaubt es, den Speicher byteweise oder in Blöcken von 32 Byte zu beschreiben. Das Schreiben in Blöcken beschleunigt den Vorgang erheblich, da hierbei interne Puffer zum Einsatz kommen. Für einen ersten Test genügen jedoch die Programme i2cset und i2cget aus den I2C-Tools. Im Folgenden ersparen wir uns mit einigen kleinen Skripten die Handarbeit, die ansonsten für das Vor- und Nachbereiten der Daten erforderlich wäre. Vergessen Sie nicht, die Skripte nach dem Abspeichern ausführbar zu machen.
Das Skript aus Listing 2 schreibt eine Zeichenkette in das EEPROM. Der verwendete 24C32N erlaubt das Schreiben von bis zu 32 Byte an einem Stück, weshalb das Skript in Zeile 6 prüft, ob der Text dieser Länge entspricht. Es adressiert den Speicher als ein einziges langes Feld. Die I2C-Tools erwarten die Angaben zu Adresse und Bytes in hexadezimaler Form.
Listing 2
#!/bin/bash
I2C_ADRESSE="0x57"
DATA_ADRESSE="0x00 0x00"
# Nicht länger als 32 Zeichen am Stück:
TEXT="Hallo Welt auf einem EEPROM"
DATA_LEN=${#TEXT}
[ $DATA_LEN -gt 32 ] && exit
WORTE32=$(echo $TEXT | hexdump -e '32/1 "0x%02X " "\n"' | sed 's/0x //g')
sudo i2cset -y 1 $I2C_ADRESSE $DATA_ADRESSE $WORTE32 i
echo "$DATA_LEN Bytes geschrieben"
Das Skript beginnt einfach an der hexadezimalen Adresse 0x00 0x00, also am Anfang. Die höchste hexadezimale Adresse beim 24C32N lautet 0x0F 0xFF. Auf diese Weise adressieren Sie bei Bedarf jedes der 4096 Bytes einzeln. Zeile 7 konvertiert die Zeichen in hexadezimale Werte und macht daraus Blöcke zu je 32 Zeichen. Das Sed-Kommando schneidet nicht konvertierte Zeichen ab; diese erscheinen als 0x.
Um die Zeichen wieder auszulesen, nutzt das Skript in Listing 3 das Programm i2cget, das Auslesen geschieht Byte für Byte. Zunächst braucht das Skript eine Angabe dazu, wie viele Bytes Sie lesen wollen. Daher übergeben Sie die Anzahl als ersten Parameter (Zeile 3). Laut Datenblatt des 24C32N leiten Sie das Lesen des Speichers mit einem Schreibzugriff auf die Datenadresse ein. Aus diesem Grund findet sich in Zeile 5 ein solcher Aufruf.
Danach erfolgt das byteweise Auslesen durch den wiederholten Aufruf von i2cget. Der interne Zeiger für die Adressen springt bei diesem Baustein automatisch weiter. Da das Skript von einem Text ausgeht, konvertiert es für die Ausgabe zusätzlich die Bytes. Die Ausgabe des Skripts sieht im Beispiel dann so aus wie in Listing 4.
Listing 3
#!/bin/bash I2C_ADRESSE="0x57" DATA_ADRESSE="0x00 0x00" DATA_LEN=$1 sudo i2cset -y 1 $I2C_ADRESSE $DATA_ADRESSE i for i in $(seq 1 $DATA_LEN); do echo $(sudo i2cget -y 1 $I2C_ADRESSE) | xxd -r; done echo echo "$DATA_LEN Bytes gelesen"
Listing 4
$ ./readEE.sh 27 Hallo Welt auf einem EEPROM 27 Bytes gelesen
Programmierung
Für echte Anwendungen eignen sich Skripte nicht wirklich. Jeder EEPROM-Chip weist spezifische Eigenheiten auf, die die I2C-Tools kaum oder gar nicht abdecken. Daher programmieren Entwickler diese Chips meist in C oder in einer Interpretersprache mit entsprechenden Bibliotheken. Python etwa verfügt mit Smbus über eine solche Bibliothek. Sie installieren sie mit sudo apt-get install python-smbus nach. Das kleine Skript in Listing 5 schreibt die Zahl 0x01 in den 24C32N und liest sie anschließend wieder aus.
Listing 5
import smbus import time I2C_ADDRESS = 0x57 bus = smbus.SMBus(1) bus.write_byte(I2C_ADDRESS,0x01) time.sleep(0.2) #Read all the unput lines value=bus.read_byte(I2C_ADDRESS) print "%02X" % value
Das Programmieren eines EEPROM in C erfordert schon etwas Übung in dieser Sprache. Die Bibliothek Bcm2835 von Mike McCauley [4] erleichtert das Programmieren des SPI-Busses. Diese Bibliothek unterstützt außerdem auch das Entwickeln mit dem I2C-Bus. Das Lesen der Datenblätter nimmt sie Ihnen aber nicht ab: Dort erfahren Sie, welche Bits Sie in welcher Reihenfolge an den Chip zu übermitteln müssen.
Darüber hinaus muss die Kommunikation nach einem exakten Takt erfolgen, den Grafiken im Datenblatt beschreiben. Bei den Versuchen geht dann leicht etwas Zeit ins Land, bis ein Baustein wie der 25A512 [5] halbwegs verstanden ist. Das Beispiel in Listing 6 programmiert den 25A512 mithilfe der BCM2835-Bibliothek: Es schreibt die Zeichenkette “Hallo” in das EEPROM und liest sie anschließend wieder aus. Abbildung 3 zeigt den Aufbau der Schaltung. Beachten Sie, dass andere SPI-EEPROMs durchaus einen anderen Aufbau erfordern. Die vom 25A512 unterstützten Kommandos finden sich in dessen Datenblatt.

Abbildung 3: Der Anschluss des SPI-EEPROMs 25A512 am GPIO-Port des RasPi. Er lässt sich jedoch nicht zwangsläufig auf andere SPI-EEPROMs übertragen.
Listing 6
#include <bcm2835.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define READ 0b00000011
#define WRITE 0b00000010
#define WREN 0b00000110
#define WRDI 0b00000100
#define PE 0b01000010
#define SE 0b11011000
#define CE 0b11000111
int main(int argc, char **argv) {
uint8_t buffer[9];
uint16_t adresse = 0x0000;
int i;
if(!bcm2835_init()) return 1;
bcm2835_spi_begin();
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_128);
/* Schreiben */
bcm2835_spi_chipSelect(BCM2835_SPI_CS0);
buffer[0] = WREN;
bcm2835_spi_transfer(buffer[0]);
bcm2835_spi_chipSelect(BCM2835_SPI_CS2);
bcm2835_spi_chipSelect(BCM2835_SPI_CS0);
buffer[0] = WRITE;
buffer[1] = (adresse >> 8);
buffer[2] = (adresse & 0x00ff);
strcpy(buffer + 3,"Hallo");
printf("Cmd: WRITE, Adresse: 0x%04X: %s.\n", (buffer[1]<<8) + buffer[2],buffer + 3);
bcm2835_spi_writenb(buffer, 9);
bcm2835_spi_chipSelect(BCM2835_SPI_CS2);
delay(100);
/* Lesen */
bcm2835_spi_chipSelect(BCM2835_SPI_CS0);
buffer[0] = READ;
buffer[1] = (adresse >> 8);
buffer[2] = (adresse & 0x00ff);
bcm2835_spi_writenb(buffer, 9);
printf("Cmd: READ, Adresse: 0x%04X: %s.\n", (buffer[1]<<8) + buffer[2],buffer + 3);
bcm2835_spi_chipSelect(BCM2835_SPI_CS2);
bcm2835_spi_end();
bcm2835_close();
return 0;
}
Das Programm aus Listing 6 folgt streng dem Datenblatt des 25A512. Zunächst definiert es die Kommandos aus der Tabelle 2-1 des Datenblatts (Abbildung 4). Die Idee bei SPI ist, dass Programme Bytes in den Chip hineintakten und das Ergebnis gleichzeitig wieder heraustakten. Dieses Förderband übernimmt der Puffer in Zeile 15.





