Die Aufgabe der Software ist das im Abschnitt I²C-Protokoll beschriebene I²C-Protokoll umzusetzen. Die hier beschriebenen Unterprogramme sind nur notwendig, wenn der verwendete Mikrocontroller kein I²C-Hardwaremodul besitzt. Weiters unterstützt die hier beschrieben Software nur die langsamere Taktgeschwindigkeit (100 kBit/s), und ist für die Standardtaktfrequenz des PIC (4 MHz) ausgelegt.
Für die Realisierung des Softwareprotokolls werden neben einigen PIC internen Register (SFR, Spezielle Funktions-Register) noch folgende Register benötigt:
Anmerkung:
Diese drei Register (TEMP1 bis TEMP3) sind so genannte temporäre Register. D.h. Sie
werden nur kurzzeitig verwendet und können daher auch in beliebigen anderen
Unterprogrammen verwendet werden.
Wie schon erwähnt benötigt der I²C-Bus eine Takt- und eine Datenleitung. Diese werden im Code wie folgt definiert (siehe auch Abschnitt 5 Demonstrationsbeispiel):
#define SDA PORTA,3 #define SCL PORTA,4Anmerkung:
Dieses Unterprogramm dient zur Initialisierung des Mikrocontrollers. Bei diesem Beispiel ist hier, für die impementierung des I²C-Protokolls, nur die Definition der der verwendeten Portpins als Ausgang notwendig.
Zur Nachbildung des I²C-Protokolls sind 7 Unterprogramme notwendig:
Nun aber zu den einzelnen Unterprogrammen im Detail:
Aufgabe:
Dieses Unterprogramm erzeugt die Startbedingung.
Zur Erinnerung, die Startbedingung ist wie folgt definiert:
Auf der Datenleitung (SDA) erfolgt eine High-Low-Flanke, während die Taktleitung
(SCL) High ist.
Anmerkung:
Die Anweisung call DELAY5 ruft ein Unterprogramm auf, welches eine
Verzögerung von 5µs erzeugt. Diese Verzögerungszeit ist notwendig, damit die maximale
Taktgeschwindigkeit von 100 kbit/s nicht überschritten wird. (siehe auch
DELAY5 und 4.4. Änderung bei Verwendung eines
höheren PIC-Takzes als 4MHz)
Aufgabe:
Dieses Unterprogramm erzeugt die Stoppbedingung.
Zur Erinnerung, die Stoppbedingung ist wie folgt definiert:
Auf der Datenleitung (SDA) erfolgt eine Low-High-Flanke, während die Taktleitung (SCL)
High ist.
Anmerkung:
Die Anweisung call DELAY5 ruft ein Unterprogramm auf, welches eine
Verzögerung von 5µs erzeugt. Diese Verzögerungszeit ist notwendig, damit die maximale
Taktgeschwindigkeit von 100 kbit/s nicht überschritten wird. (siehe auch
DELAY5 und 4.4. Änderung bei Verwendung eines
höheren PIC-Takzes als 4MHz)
Aufgabe:
Dieses Unterprogramm liest ein Bit vom Slave ein und sichert es im Übergabeflag
TEMP3,0. Der für die Datenübertragung erforderliche Takt (Leitung SCL) wird dabei
softwaremäßig so erzeugt, dass der Pin SCL von Low (0) auf High (1) gesetzt wird.
Nach einer bestimmten Zeit wird SCL wieder auf Low (0) zurückgesetzt. Diese Zeit wird
hauptsächlich vom Unterprogramm DELAY5 bestimmt.
Vorgehensweise:
Anmerkung:
Das Bit 0 im temporären Register TEMP3 beinhaltet das gelesene Bit. Dieses Register
dient hier nur als Übergaberegister zum übergeordneten Unterprogramm
I2C_LESEN. Das Register TEMP3 kann daher auch in anderen
Unterprogrammen verwendet werden.
Aufgabe:
Dieses Unterprogramm schreibt das im Übergabeflag TEMP3,1 stehende Bit zum Slave.
Der für die Datenübertragung erforderliche Takt (Leitung SCL) wird dabei
softwaremäßig so erzeugt, dass der Pin SCL von Low (0) auf High (1) gesetzt wird
Nach einer bestimmten Zeit wird SCL wieder auf Low (0) zurückgesetzt. Diese Zeit wird
hauptsächlich vom Unterprogramm DELAY5 bestimmt.
Vorgehensweise:
Anmerkung:
Das Bit 1 im temporären Register TEMP3 beinhaltet das zu schreibende Bit. Dieses
Register dient hier nur als Übergaberegister vom übergeordneten Unterprogramm
I2C_SCHREIBEN. Das Register TEMP3 kann daher auch in
anderen Unterprogrammen verwendet werden.
Aufgabe:
Ein Byte vom I²C-Bus bitweise mit Hilfe des Unterprogramms
I2C_BITLESEN lesen und im Übergaberegister TEMP1 sichern.
Danach ein Bestätigungsbit ausgeben.
Vorgehensweise:
Anmerkung:
Die temporären Register TEMP1 bis TEMP3 dienen hier als Übergabe- oder als
Hilfsregister. Diese Register können daher auch in anderen Unterprogrammen verwendet
werden.
Aufgabe:
Das zu schreibende Byte (befindet sich im Übergaberegister TEMP1) bitweise mit Hilfe
des Unterprogramms I2C_BITSCHREIBEN auf den I²C-Bus
schreiben. Danach ein Bestätigungsbit empfangen.
Vorgehensweise:
Anmerkung:
Die temporären Register TEMP1 bis TEMP3 dienen hier als Übergabe- oder als
Hilfsregister. Diese Register können daher auch in anderen Unterprogrammen verwendet
werden.
Aufgabe:
Dieses Unterprogramm erzeugt eine Zeitverzögerung von 5 us. Diese Verzögerungszeit
ist notwendig, damit die maximale Taktgeschwindigkeit von 100 kbit/s nicht überschritten
wird.
Anmerkung:
Bei einer PIC-Taktfrequenz von 4 MHz betragen die hier benötigten Befehle die in den
Klammern angegebenen Abarbeitungszeiten (Zykluszeiten)
call DELAY5 (2us)
nop (1us)
return (2us)
Bei Verwendung einer höheren Taktfrequenz müssen zusätzlich nop-Befehle eingefügt werden!
Die hier beschriebene Software wurde für die PIC-Standard-Taktfrequenz von 4 MHz beschrieben. eine geringere Taktfrequenz kann problemlos verwendet werde. Nicht aber eine Höhere. Wird eine höhere Taktfrequenz verwendet so muss aber nur das Unterprogramm DELAY5 geändert werden. Dieses Unterprogramm hat ja die Aufgabe die im I²C-Protokoll spezifizierte Taktrate (von 100 kHz) zu erzeugen. Also eine Verzögerung von 5 µs.
Zu beachten ist, dass die Befehle "call" und "return" jeweils zwei Taktzyklen benötigen, während der Befehl "nop" nur einen Taktzyklus benötigt. Als Taktzyklus ist bei der PIC-Familie folgende Formel definiert:
4
Taktzyklus [µs] = ------------------
Taktfrequenz [MHz]
Bei der Standard-Taktfrequenz von 4 MHz gilt also: Die Befehle "call" und "return" benötigen je 2 µs. Dies ergibt zusammen also 4 µs. Für die benötigte Verzögerungszeit von 5 µs sind also noch Befehle notwendig, die eine µs benötigen. Dies entspricht einem "nop"-Befehl, da dieser nur 1 Taktzyklus benötigt, also genau 1µs.
Bei Verwendung eines 10 MHz Taktes gilt:
4
Taktzyklus [µs] = ------- = 0.4 µs
10 MHz
Die Befehle "call" und "return" benötigen je 2 Taktzyklen, also
0.8 µs, der Befehl "nop" benötigt 0.4 µs. Es stellt sich also die Frage
wie viele "nop"-Befehle benötigt man für eine Verzögerung von 5 µs. Zieht man von
den 5 µs die Zeit für die beiden Befehle "call" und "return" ab,
bleiben 3,4 µs übrig. (5 µs – 0,8 µs – 0,8 µs = 3,4 µs). Ein "nop"–Befehl
benötigt 0,4 µs. Daraus ergibt sich, dass theoretisch 8,5 "nop"-Befehle
notwendig sind (3,4 µs / 0,4 µs = 8,5). Ein halber "nop"-Befehl existiert
natürlich nicht! Deshalb müssen 9 "nop"-Befehle verwendet werden.
Das Unterprogramm DELAY5 sieht also bei Verwendung eines 10 MHz-Quarzes wie folgt
aus:
DELAY5 nop
nop
nop
nop
nop
nop
nop
nop
nop
return
Eine bessere, "schönere" und vor allem codesparendere Möglichkeit ist die Verwendung einer Schleife anstelle dieser vielen "nop"-Befehle. Würde man für den PIC einen 20-MHz-Takt verwenden, so wären 21 (!) "nop"-Befehle notwendig.
Mit einer Schleife würde das Unterprogramm DELAY5 so aussehen:
Für 10 MHz:
DELAY5 movlw .3
movwf TEMP3
DELAY5SCHL1 decfsz TEMP3,f
goto DELAY5SCHL1
return
|
Für 20 MHz:
DELAY5 movlw .5
movwf TEMP3
DELAY5SCHL1 nop
decfsz TEMP3,f
goto DELAY5SCHL1
nop
return
|
| ==>Verzögerungszeit: 5.6 µs | ==>Verzögerungszeit: 5 µs |