Um Daten bzw. Konstanten auslagern zu können und dessen Zugriffszeit auf den Speicher nicht kritisch ist, werden in den meisten Applikationen für die Abspeicherung serielle EEPROMs verwendet. Diese Bausteine sind sehr günstig und bieten Speicherbereiche von 128 Byte (z. B. AT24C01) bis zu 128 kByte (z. B. AT24C1024) an. Für die folgende Beschreibung wird der AT24C256 mit einer Speichergröße von 32 kByte verwendet.
Um diese zusätzliche Schaltung am Experimentierboard aufbauen zu können, wurde die Steckplatine TYP II verwendet. Die Bestellbezeichnung finden sie im Kapitel 0, Seite 130. Abbildung 55 enthält den Schaltungsaufbau für den AT24C256. Die Adressen A0 und A1 sind auf VCC gelegt. Somit lässt sich der Baustein auf der Adresse 0xA0 ansprechen. Das Signal WP (Write Protect) ist mit GND verbunden. Somit kann der Baustein programmiert werden. Abbildung 56 enthält den zusätzlichen Aufbau mit der Steckplatine. Die Spannungsversorgung wird mit den Buchsen XVCC1 und GND (siehe rechte Bildseite) realisiert.
|
|
Abbildung 55 Schaltungsaufbau für den AT24C256 ( I²C EEPROM)
|
|
Abbildung 56 Aufbau der zusätzlichen Schaltung für das I²C EEPROM
Um zu überprüfen, ob sich das serielle EEPROM ansprechen lässt, verwenden sie das Projekt I2C_TEST.uv2 von Seite 36. Ist der Baustein korrekt angeschlossen, muss das Array „result“ die Einträge aus Abbildung 57 enthalten.
|
|
Abbildung 57 Watch & Call Stack Window mit AT24C256 I²C EEPROM (0xA0)
Es soll ein Projekt erstellt werden, das das EEPROM beschreibt und wieder ausliest. Der Schreibzyklus für das 24C256 EEPROM ist wie folgt aufgebaut:
1. Startbedingung
2. Schreiben der Baustein-Adresse (0xA0)
3. Schreiben der MSB Adresse
4. Schreiben der LSB Adresse
5. Schreiben eines oder mehrere Werte
6. Stoppbedingung
Nachdem die Stoppbedingung gesetzt ist, muss gewartet werden, bis die interne Programmierung beendet ist. Solange die Programmierung erfolgt, wird bei der Adressierung des EEPROMs ein „NACK“ zurückgesendet. Erst wenn ein „ACK“ zurückgeliefert wird, kann mit der nächsten Programmierung bzw. mit dem Auslesen des EEPROMs begonnen werden.
Um das Ergebnis von ACK und NACK des EEPROMs auswerten zu können, muss die I²C-Library um die globale Variable ucI2CError erweitert werden (siehe Listing 59, ). Sie enthält den Status des I²C-Protokolls. Beim Start des Protokolls wird ucI2CError auf „I2C_OK“ gesetzt (siehe ‚). Wird in der ISR eine unerwartete case-Anweisung durchlaufen, wird ucI2CError auf einen Fehler gesetzt (siehe ƒ und „). Somit kann im Hauptprogramm mit Hilfe von ucI2CError überprüft werden, ob das I²C-Protokoll korrekt durchgeführt wurde. Die Definitionen für ucI2CError sind im H-File „I2C_LIB.h“ abgespeichert (siehe Listing 58, ).
|
|
// Aktueller Stand: V1.1 // Aenderungen bei: // V1.1: Variable ucI2CError eingefuehrt // Die Fehlercodes sind im enum-Feld enI2CERROR abgelegt. // Strukturdeklaration struct stI2C { unsigned char Adr; unsigned char DataLen; unsigned char Val[10]; };
enum enI2CERROR {I2C_OK, NACK_WRADR, NACK_WRDATA, UNDEFINED};
// Variablendeklarationen extern struct stI2C Slave; extern bit btI2cFinished; extern unsigned char ucI2CError;
// Funktionsdeklarationen extern void v_InitI2C(void); extern void v_SetNextI2CVal(unsigned char ucVal); extern void v_StartI2CTrans(void); extern void v_WaitI2CFinish(void); extern void v_StartI2CandWait(void); |
Listing 58 Inhalt von I2C_LIB.h
|
‚
‚
ƒ
„
|
// Aktueller Stand: V1.1 // Aenderungen bei: // V1.1: Variable ucI2CError eingeführt
#include <REG932.H> #include <I2C_LIB.H> #include <PortConfig.H>
struct stI2C Slave; unsigned char ucI2CError; static bit btI2cFinished;
void v_InitI2C(void) { // SDA und SCL als Open-Drain konfigurieren v_PortConfig(Port1, Pin3 | Pin4, OpenDrain); I2SCLH = 19; // I2C-Geschwindigkeit ~100 KHz I2SCLL = 19; I2CON = 0x44; // I2C-Hardware aktivieren EI2C = 1; // I2C Interrupt freigeben }
void v_SetNextI2CVal(unsigned char ucVal) { Slave.Val[Slave.DataLen] = ucVal; Slave.DataLen++; }
void v_WaitI2CFinish(void) { while (btI2cFinished == 0); }
void v_StartI2CTrans(void) { btI2cFinished = 0; ucI2CError = I2C_OK; I2CON = 0x64; // Startbedingung setzen }
void v_StartI2CandWait(void) { btI2cFinished = 0; ucI2CError = I2C_OK; I2CON = 0x64; // Startbedingung setzen while (btI2cFinished == 0); // Auf Stoppbedingung warten }
static unsigned char ucI2COff; void v_I2CInterrupt(void) interrupt 6 { switch(I2STAT) // Auswertung des Statusregisters { case 0x08: // Status: "START gesendet" case 0x10: // Status: "Repeat Start wurde gesendet" I2DAT = Slave.Adr; ucI2COff = 0; I2CON = 0x44; // (STO, STA, SI = 0, ACK =1) break; case 0x18: // Status: "SLA+W gesendet; ACK empfangen" case 0x28: // Status: "byte gesendet; ACK empfangen" if (ucI2COff < Slave.DataLen) { I2DAT = Slave.Val[ucI2COff]; // naechsten Wert ucI2COff++; // schreiben I2CON = 0x44; } else { Slave.DataLen = 0; btI2cFinished = 1; I2CON = 0x54; // Stoppbedingung setzen (STO =1) } break; case 0x20: // Status: "SLA+W gesendet, NACK empfangen" I2CON = 0x54; // Stoppbedingung setzen (STO =1) ucI2CError = NACK_WRADR; btI2cFinished = 1; break; case 0x40: // Status: "SLA+R gesendet, ACK empfangen" if (ucI2COff < (Slave.DataLen )) { if (ucI2COff < (Slave.DataLen)) I2CON=0x44; //ACK else I2CON=0x40; //NACK } break; case 0x50: // Status: "Datenbyte empfangen, ACK gesendet" if (ucI2COff < (Slave.DataLen )) { Slave.Val[ucI2COff] = I2DAT; ucI2COff++; if (ucI2COff < (Slave.DataLen - 1)) I2CON=0x44; // ACK gesendet else I2CON=0x40; // NACK gesendet } break; case 0x58: // Status: "Datenbyte empfangen, NACK gesendet" Slave.Val[ucI2COff] = I2DAT; ucI2COff++; I2CON = 0x54; // Stoppbedingung setzen (STO =1) btI2cFinished = 1; break; default: I2CON = 0x54; // Stoppbedingung setzen (STO =1) ucI2CError = UNDEFINED; btI2cFinished = 1; break; } } |
Listing 59 Auszug aus I2C_LIB.c
Nachdem sie die Anpassungen in der I²C-Library durchgeführt haben, erstellen sie das Projekt aus Tabelle 63. Es sollen auf die ersten vier Adressen im EEPROM die Werte 0x01 bis 0x04 abspeichert und danach die Werte zur Überprüfung ausgelesen werden. Nach dem Programmieren der Werte muss die Startadresse im EEPROM wieder gesetzt werden (siehe Listing 60, ). Bei diesem Setzen wird gleichzeitig überprüft, ob die Programmierung des EEPROMs beendet ist. Ist dies nicht der Fall, wird die while-Schleife durchlaufen (siehe ‚). Erst wenn die Variable ucI2CError den Wert I2C_OK enthält, liefert das EEPROM ein „ACK“ zurück. Jetzt kann der Inhalt des EEPROMs wieder ausgelesen werden (siehe ƒ). Das Ergebnis können sie sich im Watch- &Call Stack Window anzeigen lassen.
|
Projektname |
Verzeichnis |
Verwendete Sourcemodule |
|
I2C_EEPROM |
Test_I2C_EEPROM |
TestI2CEEPROM.c |
|
|
..\Library |
PortConfig.c, I2C_Lib.c |
Tabelle 63 Projekt I2C_EEPROM
|
‚ ‚
ƒ ƒ ƒ
|
#include <REG932.H> #include <I2C_LIB.H> #include <PortConfig.H>
void main(void) { v_InitI2C(); EA = 1;
// Werte in das EEPROM schreiben Slave.Adr = 0xA0; // Adresse AT24C256 v_SetNextI2CVal(0x00); // Subadresse (MSB) v_SetNextI2CVal(0x00); // Subadresse (LSB) v_SetNextI2CVal(0x01); // Wert schreiben v_SetNextI2CVal(0x02); // Wert schreiben v_SetNextI2CVal(0x03); // Wert schreiben v_SetNextI2CVal(0x04); // Wert schreiben v_StartI2CandWait();
// Startadresse setzen v_SetNextI2CVal(0x00); // Subadresse (MSB) v_SetNextI2CVal(0x00); // Subadresse (LSB) v_StartI2CandWait();
// Warten bis EEPROM wieder bereit while (ucI2CError != I2C_OK) v_StartI2CandWait();
// Werte aus dem EEPROM auslesen Slave.Adr = 0xA0 + 1; // Adresse AT24C256 + RD Slave.DataLen = 4; // ein Bytes wird gelesen v_StartI2CandWait();
while(1); } |
Listing 60 Inhalt von Test_I2C_EEPROM.c
|
|
Abbildung 58 Watch & Call Stack Window mit Ergebnis EEPROM lesen
Für das Programmieren von EEPROMs ist es zweckmäßig, eine eigene Funktion zu erstellen, die das Beschreiben des EEPROMs übernimmt. Diese Funktion soll auch in der Lage sein, die Programmierung von EEPROM-Pages zu unterstützen. Die Größe einer EEPROM-Page wird mit der Definition „PAGESIZE“ angegeben (siehe Listing 61, ). Die Berechnung der noch zu beschreibenden EEPROM-Page wird mit der Modulo-Operation durchgeführt (siehe ƒ).
F !! Vergessen sie nicht, das Array in der Deklaration der I²C-Struktur „stI2C“ (H-File I2C_LIB.h) auf die Größe der EEPROM-Page anzupassen. !!
In der ersten while-Schleife (siehe ‚) wird auf die Anzahl der noch zu schreibenden Werte geprüft. Solange „ucLen” ungleich “0” ist, wird diese Schleife wiederholt. In der zweiten Schleife wird eine zusätzliche Überprüfung auf die Pagegrenze durchgeführt (siehe „). Die innere while-Schleife wird erst verlassen, wenn entweder ucLen den Wert „0“ enthält oder die Pagegrenze erreicht ist. Ist eine der beiden Bedingungen eingetreten, wird die EEPROM-Page beschrieben (siehe …). Mit diesem Schleifenaufbau wird auch sichergestellt, dass die innere I²C-Struktur „Slave“ nicht überschrieben wird.
Im Testbeispiel wird der Inhalt vom Array „ucStartBuf“ (siehe Listing 62, ) in das EEPROM ab der Adresse 0x00FE mit Hilfe der Funktion „uc_Data2I2C_EEPROM()“ geschrieben (siehe ‚). Mit der Funktion „uc_I2C2Data()“ wird der Inhalt ab der Adresse 0x00FB ausgelesen (siehe ƒ). Die Anzahl der zu lesenden Bytes wird mit dem ersten Byte im Array „ucResBuf“ festgelegt (siehe „).
|
Projektname |
Verzeichnis |
Verwendete Sourcemodule |
|
Test_I2C_EEPROM_LIB |
Test_I2C_EEPROM_LIB |
TestEEPROM_Lib.c |
|
|
..\Library |
PortConfig.c, I2C_Lib.c I2C_EEPROM_Lib.c |
Tabelle 64 Projekt Test_I2C_EEPROM_LIB
|
‚
ƒ
„
…
|
#include <I2C_LIB.H> #include <I2C_EEPROM_LIB.h> #define PAGESIZE 64
unsigned char uc_Data2I2C_EEPROM(unsigned char ucI2CDev, unsigned int uiI2CAddr, unsigned char *pucSrc) { unsigned char ucLen = *pucSrc; unsigned char ucPageOffset; pucSrc++; Slave.Adr = ucI2CDev;
while (ucLen) { ucPageOffset = uiI2CAddr % PAGESIZE; v_SetNextI2CVal(uiI2CAddr/256); // Subaddr.(MSB) v_SetNextI2CVal((unsigned char)uiI2CAddr); // Subaddr.(LSB) while(ucLen && ucPageOffset < PAGESIZE) { v_SetNextI2CVal(*pucSrc); pucSrc++; ucPageOffset++; uiI2CAddr++; ucLen--; } v_StartI2CandWait(); // Warten bis Schreiben einer Page beendet v_StartI2CandWait(); while(ucI2CError != I2C_OK) v_StartI2CandWait(); } return (ucI2CError); } |
Listing 61 Auszug aus I2C_EEPROM_Lib.c
|
‚ „ ƒ
|
#include <REG932.H> #include <PortConfig.H> #include <I2C_LIB.H> #include <I2C_EEPROM_LIB.h>
unsigned char xdata ucStartBuf[100]={16, "123-Hello World"}; unsigned char xdata ucResBuf[100];
void main(void) { v_InitI2C(); EA = 1;
uc_Data2I2C_EEPROM(0xA0, 0xfe, ucStartBuf); ucResBuf[0] = 20; uc_I2C2Data(0xA0, 0xfe, ucResBuf); while(1); } |
Listing 62 Inhalt von TestEEPROM_Lib.c