Erstellen von Library-Modulen

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 Pro­jek­ten verwendet werden. Dieses Verfahren hat zudem den Vorteil, dass eine Änderung bzw. Fehlerbeseitigung nur einmal durch­ge­führt werden muss.

Library für den I²C-Bus

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 Program­mie­rung des I²C-Busses benötigt.

Erstellen sie ein Projekt, in dem die oben aufgezählten Funktionen im Source­mo­dul „I2C_LIB.c“ abgespeichert sind, sowie ein H-File mit den Funktions- und Variablen­de­kla­­rationen. Das Sourcemodul „I2C_LIB.c“ und das H-File „I2C_LIB.h“ sollen im Ver­zeich­nis „Library“ abge­spei­chert werden. Das Testprogramm soll die Zahl „2004“ auf den vier Segmenten ausgeben.

F !! Vergessen sie nicht, den Eintrag „..\library“ im Ein­­gabefeld des C51-Compilers vor­zunehmen (siehe Abbildung 43, Seite 53). !!

Die Variable „btI2cFinished“ und „ucI2Coff” werden mit dem Schlüsselwort static ver­se­hen (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 Beschrei­bung zu allen Schlüsselwörtern für die Gültigkeitsbereiche finden sie im Ka­pi­tel 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 Hil­fe 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 Funk­tion „v_InitI2C()“ die Oder-Verknüpfungen auf den Port 1 gegen den Funktions­aufruf „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 Funk­tionalität der Funktionen „v_StartI2CTrans()“ und „v_WaitI2CFinish()“ ent­hal­­ten. Der Vorteil dieser Funktion ist, dass gleich auf das Ende der I²C-Übertragung ge­war­tet wird. Der Nachteil dieser Funktion besteht darin, dass das Programm in der Zwi­schen­zeit 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

Erweiterte Library-Funktionen für den I²C-Bus

Für das Beschreiben und Auslesen von I²C-Bausteinen ist es zweckmäßig, eigene Funk­tio­nen 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 Funk­tion „uc_Data2I2C()” (siehe Listing 27, ), das Auslesen von Werten die Funktion „uc_I2C2Data()“ (siehe ). Eine ausführliche Beschreibung der Funktionsweise dieser Funk­tionen 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 Struk­tur­definition „stI2C“ (siehe Listing 23, ) vergrößert  werden.

Ist die Offset-Adresse im verwendeten Baustein nur ein Byte groß, haben sie folgende Möglich­kei­ten:

1.  Wenn sie nur I²C-Bausteine verwenden, die über eine 1-Byte Offset-Adresse ange­spro­chen 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