Im vorangegangenen Abschnitt wurde die Funktion „v_PortConfig()“ in ein eigenes Sourcemodul ausgelagert und mit einem eigenen H-File versehen. Die Dateien wurden im Verzeichnis Library gespeichert. Somit kann diese Funktion auch von anderen Projekten verwendet werden. Dieses Verfahren hat zudem den Vorteil, dass eine Änderung bzw. Fehlerbeseitigung nur einmal durchgeführt werden muss.
Wie sie den Projekten Test_SAA1064.uv2 (siehe Seite 42), Test_DS1621.uv2 (siehe Seite 45) und Test_PCF8574.uv2 (siehe Seite 39) entnehmen können, werden die Funktionen „v_SetNextI2CVal()“, „v_StartI2CFrans()“, „v_WaitI2CFinish()“, „v_InitI2C()“, die ISR „v_I2CInterrupt()“ und die Struktur „Slave“ immer wieder für die Programmierung des I²C-Busses benötigt.
Erstellen sie ein Projekt, in dem die oben aufgezählten Funktionen im Sourcemodul „I2C_LIB.c“ abgespeichert sind, sowie ein H-File mit den Funktions- und Variablendeklarationen. Das Sourcemodul „I2C_LIB.c“ und das H-File „I2C_LIB.h“ sollen im Verzeichnis „Library“ abgespeichert werden. Das Testprogramm soll die Zahl „2004“ auf den vier Segmenten ausgeben.
F !! Vergessen sie nicht, den Eintrag „..\library“ im Eingabefeld des C51-Compilers vorzunehmen (siehe Abbildung 43, Seite 53). !!
Die Variable „btI2cFinished“ und „ucI2Coff” werden mit dem Schlüsselwort static versehen (siehe Listing 22, und ‚). Dieses Schlüsselwort bewirkt, dass die Variable nicht von einem anderen Sourcemodul aus angesprochen werden kann. Das Schlüsselwort static gehört zu den Definitionen der Gültigkeitsbereiche. Eine ausführliche Beschreibung zu allen Schlüsselwörtern für die Gültigkeitsbereiche finden sie im Kapitel 6 „Gültigkeitsbereiche von Variablen“ [1].
|
Projektname |
Verzeichnis |
Verwendete Sourcemodule |
|
I2C_LIB |
Test_I2C_Lib |
Test_I2C_Lib.c |
|
|
..\Library |
I2C_LIB.c |
Tabelle 38 Projekt I2C_Lib
|
‚
|
#include <REG932.H> #include <I2C_LIB.H>
struct stI2C Slave; static bit btI2cFinished;
v_InitI2C(void) { P1M1 |= 0x0C; // SDA und SCL als Open-Drain P1M2 |= 0x0C; I2SCLH = 19; // I2C-Geschwindigkeit ~100 KHz I2SCLL = 19; I2CON = 0x44; // I2C-Hardware aktivieren EI2C = 1; // I2C Interrupt freigeben }
v_SetNextI2CVal(unsigned char ucVal) { Slave.Val[Slave.DataLen] = ucVal; Slave.DataLen++; }
v_StartI2CTrans(void) { btI2cFinished = 0; I2CON = 0x64; // Startbedingung setzen }
void v_WaitI2CFinish(void) { while (btI2cFinished == 0); }
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) 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) btI2cFinished = 1; break; } } |
Listing 22 Inhalt von I2C_LIB.c
|
|
// Strukturdeklaration struct stI2C { unsigned char Adr; unsigned char DataLen; unsigned char Val[10]; };
// Variablendeklarationen extern struct stI2C Slave;
// Funktionsdeklarationen extern v_InitI2C(void); extern v_SetNextI2CVal(unsigned char ucVal); extern v_StartI2CTrans(void); extern void v_WaitI2CFinish(void); |
Listing 23 Inhalt von I2C_LIB.h
|
|
#include <REG932.H> #include <I2C_LIB.H>
enum enSAASEG {NULL = 0x3F, EINS = 0x06, ZWEI = 0x5B, DREI = 0x4F, VIER = 0x66, FUENF = 0x6D, SECHS = 0x7D, SIEBEN = 0x07, ACHT = 0x7F, NEUN = 0x67, PUNKT = 0x80};
void main(void) { v_InitI2C(); EA = 1; // allgemeine Interruptsperre aufheben Slave.Adr = 0x70; // Adresse SAA1064 v_SetNextI2CVal(0); // Subadresse auf 0 (Control-Byte) v_SetNextI2CVal(0xF7); // dynamic alternating mode v_StartI2CTrans(); v_WaitI2CFinish();
v_SetNextI2CVal(1); // Ausgabe der Zahl 2004 v_SetNextI2CVal(ZWEI); v_SetNextI2CVal(NULL); v_SetNextI2CVal(NULL); v_SetNextI2CVal(VIER); v_StartI2CTrans(); v_WaitI2CFinish();
while(1); } |
Listing 24 Inhalt von Test_I2C_Lib.c
Erweitern sie das Projekt so, dass die Initialisierung der Portpins in „v_InitI2C()“ mit Hilfe der Funktion „v_PortConfig()“ durchgeführt wird.
Fügen sie das Sourcemodul PortConfig.c zum Projekt hinzu (siehe Tabelle 39). Im Sourcemodul I2C_Lib.c muss das H-File PortConfig.h eingebunden (siehe Listing 25, ) und in der Funktion „v_InitI2C()“ die Oder-Verknüpfungen auf den Port 1 gegen den Funktionsaufruf „v_PortConfig()“ ausgetauscht werden (siehe ‚).
|
Projektname |
Verzeichnis |
Verwendete Sourcemodule |
|
I2C_LIB |
Test_I2C_Lib |
Test_I2C_Lib.c |
|
|
..\Library |
I2C_LIB.c, PortConfig.c |
Tabelle 39 Projekt I2C_Lib
|
‚ |
#include <REG932.H> #include <I2C_LIB.H> #include <PortConfig.H>
struct stI2C Slave; static bit btI2cFinished;
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 } |
Listing 25 Auszug aus von I2C_Lib.c
Erstellen sie die Funktion „v_StartI2CandWait()“ (siehe Listing 26). Sie soll die Funktionalität der Funktionen „v_StartI2CTrans()“ und „v_WaitI2CFinish()“ enthalten. Der Vorteil dieser Funktion ist, dass gleich auf das Ende der I²C-Übertragung gewartet wird. Der Nachteil dieser Funktion besteht darin, dass das Programm in der Zwischenzeit keine anderen Aufgaben durchführen kann.
|
|
void v_StartI2CandWait(void) { btI2cFinished = 0; I2CON = 0x64; // Startbedingung setzen while (btI2cFinished == 0); // Auf Stoppdingung warten } |
Listing 26 Auszug aus von I2C_Lib.c
Für das Beschreiben und Auslesen von I²C-Bausteinen ist es zweckmäßig, eigene Funktionen zu erstellen, die das Beschreiben der I²C-Struktur, das Setzen der Startadresse sowie der Werte übernehmen. Das Beschreiben eines I²C-Bausteins übernimmt die Funktion „uc_Data2I2C()” (siehe Listing 27, ), das Auslesen von Werten die Funktion „uc_I2C2Data()“ (siehe ‚). Eine ausführliche Beschreibung der Funktionsweise dieser Funktionen finden sie im Kapitel 11 I²C-Bus [2]. Sollen mehr als 8 Werte auf dem I²C-Baustein geschrieben werden, muss die Größe des Arrays in der Strukturdefinition „stI2C“ (siehe Listing 23, ) vergrößert werden.
Ist die Offset-Adresse im verwendeten Baustein nur ein Byte groß, haben sie folgende Möglichkeiten:
1. Wenn sie nur I²C-Bausteine verwenden, die über eine 1-Byte Offset-Adresse angesprochen werden, können sie den zweiten Übergabeparameter auf den Datentyp „unsigned char“ ändern und die Zeile mit der MSB-Zuweisung (siehe „) löschen.
2. Verwenden sie I²C-Bausteine, die eine 1-Byte Adresse benötigen, müssen sie eine zusätzliche Funktion definieren, die diese unterstützt oder einen weiteren Parameter mit übergeben, der diese Information enthält.
|
„
‚
ƒ ƒ ƒ „ |
#include <I2C_LIB.H> #include <I2C_EEPROM_LIB.h>
unsigned char uc_Data2I2C(unsigned char ucI2CDev, unsigned int uiI2CAddr, unsigned char *pucSrc) { unsigned char ucLen = *pucSrc; pucSrc++; Slave.Adr = ucI2CDev; v_SetNextI2CVal(uiI2CAddr/256); // Subadresse (MSB) v_SetNextI2CVal((unsigned char)uiI2CAddr); // Subadresse (LSB)
while (ucLen--) { v_SetNextI2CVal(*pucSrc); pucSrc++; } v_StartI2CandWait(); return (ucI2CError); }
unsigned char uc_I2C2Data(unsigned char ucI2CDev, unsigned int uiI2CAddr, unsigned char *pucSrc) { unsigned char ucLen = *pucSrc; pucSrc++; Slave.Adr = ucI2CDev; // Startadresse setzen v_SetNextI2CVal(uiI2CAddr/256); // Subadresse (MSB) v_SetNextI2CVal((unsigned char)uiI2CAddr); // Subadresse (LSB) v_StartI2CandWait();
// Werte aus dem EEPROM auslesen Slave.Adr = Slave.Adr | 1; // Adresse AT24C256 + RD Slave.DataLen = ucLen; // ein Bytes wird gelesen v_StartI2CandWait();
// Werte in ResBuf kopieren if (ucI2CError == I2C_OK) { unsigned char ucLoop; for (ucLoop = 0; ucLoop < ucLen; ucLoop++) { *pucSrc = Slave.Val[ucLoop]; } } return (ucI2CError); } |
Listing 27 Auszug aus von I2C_EEPROM_Lib.c