IIC-Bus - Fernsehtechnik am AVR

 

Materialbedarf

 

Anz. Bezeichnung Datenblatt
1 Batterie/Spannungsquelle 9V  
1 Spannungsregler 7805
1 ATMega8 AVR-Prozessor
1 PCF8574
2 Transistor BC548C (BC546C-BC550C)
2 Widerstand 220 Ohm  
1 Widerstand 10 kOhm  
2 Widerstand 4,7 kOhm  
4 Widerstand 47 kOhm  
1 Elektrolytkondensator 100 µF/16V  
1 Kondensator 100nF  
2 Mikrotaster
2 Standard-Leuchtdiode 3mm oder 5mm 3mm, 5mm

 

 

Was IIC eigentlich ist

 

AVR-Mikrokontroller bieten schon Einiges an Hardware 'On Board' an. AD-Wandler, PWM oder Zähler um nur Einige zu nennen. Doch oft ist man gezwungen, den Prozessor um einige Komponenten zu erweitern weil man z.B. eine Echtzeituhr, erheblich größeren EEPROM-Bereich oder etliche weitere Ports benötigt. Hierzu gibt es mehrere Möglichkeiten. Eine davon ist der Anschluss von IIC-Komponenten am AVR. IIC (oder I2C) wird bei Atmel auch TWI genannt, stellt aber das gleiche System dar.

 

IIC wurde ursprünglich von der Firma Philips (Heutiges NXP) dazu entwickelt um die Steuerung von digitalen Elementen in Unterhaltungsgeräte zu vereinfachen. 2 Leitungen sind doch erheblich leichter zu verlegen als etliche 10 Stück. Um z.B. eine 2-stellige LED-Anzeige zu steuern, reichen nun die erwähnten 2 Leitungen anstelle von 14. Kommen noch z.B. Kanalwahltasten hinzu, hätte man ohne IIC recht schnell 100 Leitungen erreicht.

 

Heute wird dieses System für alles Mögliche verwendet. Angefangen von einfachen Porterweiterungen, über Speicher und Uhren-IC, Displays bis hin zu Multiprozessor-Kommunikation.

 

Der IIC-Bus besteht aus 2 Leitungen. Eine Leitung (SCL) gibt den Takt vor, mit dessen Geschwindigkeit die Daten auf der 2. Leitung (SDA) transportiert werden sollen. Hierbei wird der Takt immer von einem Master vorgegeben. Während die Datenleitung, je nach Erfordernis, vom Master zum Slave oder umgekehrt geschaltet ist.

Beim IIC-Bus können mehrere Bausteine im Bussystem angeschlossen werden. Damit die einzelnen Bausteine wissen, welches Modul gerade angesprochen werden soll, wird beim Start der Kommunikation immer erst die Slave-Adresse übermittelt. Das Modul, welches angesprochen wird, meldet sich mit einem 'Acknowledge' und wartet auf Befehle. Die anderen Bausteine hingegen machen 'dicht' und bleiben passiv während der Transaktion.

Da dieses Bussystem schon etwas älter ist, wurden hier so genannte Open Collector oder Open Drain-Ausgänge verwendet. Dies bedeutet, es wird nur das 0-Signal durchgeschaltet. Um ein 1-Signal zu bekommen, werden PullUp-Widerstände im Bus benötigt. Der Wert dieser Widerstände hängt vom verwendetem Baustein ab. Hier gibt das Datenblatt näher Auskunft. Sind mehrere IIC-Bausteine angeschlossen, so sollten die Widerstände sich an dem kleinsten Wert orientieren.

Die Taktfrequenz der SCL-Leitung ist auf Standardmäßig 100 kHz festgelegt. Es gibt auch einen Fast Mode. Hier wird die Taktfrequenz auf 400 kHz erhöht. Jüngere Bausteine können sogar den 'Fast Mode Plus' mit 1 MHz Taktfrequenz. Die neuesten Entwicklungen in der IIC-Technik gehen inzwischen schon in Richtung 'High Speed Mode' mit 3,4 MHz.

Leider muss man die Taktfrequenz immer am langsamsten Baustein im Bus orientieren. Werden also mehrere verschiedene Elemente im Bus betrieben, wird man meistens nicht über 100 kHz oder 400 kHz hinaus kommen.

 

 

Kommunikation mittels IIC

 

Für die ersten Versuche, nehmen wir erst einmal einen Portexpander mit der Bezeichnung PCF8574. Es ist hierbei aber zwingend darauf zu achten, dass es wirklich der PCF8574 ist. Es existiert noch ein ähnlicher Typ, den PCF8574A. Dieser besitzt aber eine andere Slave-Adresse und würde bei den Versuchen hier nicht funktionieren. Neben der Spannungsversorgung, den 8 Ein-/Ausgängen und den bereits erwähnten SCL und SDA-Leitung findet man auch noch den Anschluss 'INT' und drei Pins mit der Bezeichnung A0-A2. Mit den Leitungen ist es möglich dem Baustein 1 von 8 Adressen zuzuweisen. Somit kann man von diesem Baustein 8 ICs gleichzeitig an einem Bus betreiben.

Die Portpins des PCF8574 liefern im '1'-Zustand gerade einmal 100µA. Hierdurch müssen wir die Ausgänge, an denen wir LEDs anschließen wollen, mit Transistoren verstärken.

 

Damit wir den PCF8574 ansprechen können, müssen wir erst einmal wissen, unter welche Adresse dieser reagiert. Hierzu wurde der Adressaufbau dieses IC hier einmal aufgestellt:

 

Bit: 7 6 5 4 3 2 1 0
Funktion: 0 0 1 0 A2 A1 A0 R/W

 

Soll dieses IC angesteuert werden so müssen wir dies mit der Basisadresse 64 (40h) tun. Jeder IC-Tyo besitzt eine andere Basisadresse. Welche dies genau ist, findet man im entsprechenden Datenblatt. Hätten wir noch weitere ICs des gleichen Typs im Bus, so müssten wir die A0-A2 Leitungen entsprechend anders setzen. Wäre z.B. A0 auf 1 gesetzt, würde das IC auf die Slave-Adresse 66 (42h) reagieren.

 

Bit 0 teilt dem Baustein mit, ob auf das IC geschrieben werden soll (Bit=0) oder ob man Daten auslesen möchte (Bit=1). Dieses Bit ist bei jedem IIC-Baustein vorhanden, während die Adressleitungen von IC zu IC unterschiedlich sind. Es gibt IIC-Schaltkreise die sogar gar keine weitere Adressierung, neben der Basisadresse, zulassen.

 

Damit wir die Kommunikation mit dem PCF8574 testen können, bauen wir uns ein kleines Testboard:

 

 

 

Wird nun das nachfolgende Programm übertragen, blinken die beiden LEDs D1 und D2.

 

$regfile "m8def.dat"
$crystal = 1000000

Config Sda = Portc.4
Config Scl = Portc.5

I2cinit

Do
  I2cstart : I2cwbyte &H40 : I2cwbyte 3 : I2cstop
  Waitms 300
  I2cstart : I2cwbyte &H40 : I2cwbyte 0 : I2cstop
  Waitms 300
Loop

 

Blinkende LEDs sind ja nun nichts besonderes mehr. Aber dieses geschieht nun nicht mehr direkt am AVR, sondern wir steuern das Ganze über IIC. Wie das Ganze vonstatten geht, schauen wir uns einmal näher an.

 

Als erstes legen wir, nach der üblichen AVR-Konfiguration, die Ports für SCL und SDA fest:

 

Config Sda = Portc.4
Config Scl = Portc.5

 

I2cinit

 

Der nachfolgende I2cinit-Befehl ist hierbei schon interessanter. Hier werden die Porteigenschaften festgelegt und die Leitungen SDA und SCL auf '1' gesetzt. Dieser Zustand ist der Ruhezustand des IIC-Buses, wenn zur Zeit keine Aktivität statt findet.

 

Nun öffnen wir eine Endlosschleife. In der ersten Zeile dieser Schleife, übermitteln wir unserem PCF8574 den Befehl, dass er D1 und D2 einschalten soll:

 

I2cstart : I2cwbyte &H40 : I2cwbyte 3 : I2cstop

 

Hierbei sind mehrere Schritte nötig. Mit I2cstart wird der IIC-Bus geöffnet und alle angeschlossenen Bausteine in einen 'Achtung'-Zustand gebracht. Hierzu wird erst SDL auf '0' gesetzt und anschließend SCL. Die Reihenfolge ist äußerst wichtig, sonst 'denken' die Bausteine, es ist noch eine Transaktion aktiv und reagieren nicht.

 

Nachdem die Start-Bedingung hergestellt worden ist, wird die Slave-Adresse übermittelt. Als erstes wird das höchste Bit übermittelt. Nach den 8 Bit der Slave-Adresse muss der Master den SDA-Port auf Eingang stellen und einen 9. Takt senden. Nun sendet der IIC-Baustein, der angesprochen wurde ein ACK-Signal (Acknowledge). Hierbei wird die SDA-Leitung auf '0' gezogen. Gibt es kein Baustein mit der Adresse oder ist der Baustein momentan nicht in der Lage zu kommunizieren, so bleibt die SDA-Leitung auf '1'. Es wird also ein 'NACK' (No Acknowledge) empfangen. Hierdurch kann der Master prüfen, ob der Baustein vorhanden und aktiv ist.

 

Jetzt ist unser PCF8574 bereit, Daten zu empfangen, was mit dem zweiten I2cwbyte-Befehl geschieht. Da wir hier die beiden Portpins P0 und P1 aktivieren möchten, senden wir eine 3 zum Baustein. Auch hier wird in der Regel ein ACK-Signal zurück gesendet.

 

Zum Schluss muss die Transaktion noch beendet werden. Hierzu wird die Stop-Bedingung mit dem I2cstop-Befehl gesendet. Dabei wird erst SCL auf '1' gesetzt und anschließend SDA. Hierbei muss ebenso auf die Reihenfolge geachtet werden, damit die Bausteine wissen, dass die Übertragung beendet ist.

 

In unserem Programm folgt nun eine Wartezeit und anschließend eine weitere Übertragung, aber dieses Mal mit 0 als Datenwert. Die LEDs gehen also nun wieder aus. Danach erfolgt eine weitere Wartezeit und durch den Loop-Befehl wiederholt sich alles. Die LEDs blinken.

 

 

Der PCF8574 als Eingabeport

 

Wie im obigen Schalt-/Bauplan schon zu sehen ist, kann man unseren Interface-Baustein auch als Eingabeport verwenden. Hierzu müssen wir dem PCF8574 nur sagen, dass wir, anstatt Ports zu setzen, Ports auslesen wollen. Dies tun wir indem wir bei der Adressierung das Bit 0 auf 1 setzen. Wir als bei unserem Beispiel anstelle 64 (40h) den Wert 65 (41h) als Adressierung senden. Bei den nächsten Takten sendet der PCF8574 nun den Zustand der Ports.

 

Schauen wir uns das Ganze mal wieder mit einem kleinen Programm an:

 

$regfile "m8def.dat"
$crystal = 1000000

Dim Dat As Byte

Config Sda = Portc.4
Config Scl = Portc.5

I2cinit

Do
  I2cstart : I2cwbyte &H41 : I2crbyte Dat , Nack : I2cstop
  Shift Dat , Right , 4
  I2cstart : I2cwbyte &H40 : I2cwbyte Dat : I2cstop
Loop

 

Wird dieses Programm gestartet und eine oder beide Tasten betätigt, leuchten die LEDs entsprechend auf. Bei diesem Programm wird permanent die beiden Tasten abgefragt und wieder am Port ausgegeben.

 

Um dies zu ermöglichen, benötigen wir eine Puffer-Variable 'Dat' um den Tasten-Zustand zwischen zu speichern. Daher wird diese im Programmkopf definiert.

 

Interessant ist die Tastenabfrage selbst:

 

I2cstart : I2cwbyte &H41 : I2crbyte Dat , Nack : I2cstop

 

Mit I2cstart wird, wie bekannt, der IIC-Bus geöffnet. Als nächstes adressieren wir unseren PCF8574. Da wir hier aber nun vom Port lesen wollen, müssen wir bei der Adressierungs-Adresse das Bit 0 setzen. Daher wird jetzt 41h auf den Bus geschrieben.

 

Nun können wir den Port auslesen. Dies geschieht mit dem I2crbyte-Befehl. Neben der Variablen, in der das empfangene Byte gespeichert werden soll, gibt es noch eine weitere Angabe. 'Nack' sagt dem AVR das er nach dem Empfangen des Datenbytes ein 'No Acknowledge' senden soll. Hiermit teilen wir dem Port-Baustein mit, dass wir keine weitere Daten wünschen. Würden wir 'Ack' (Acknowledge) senden, liefert der PCF8574 munter weiter Daten. Der IIC-Bus könnte hier dann auch nicht mehr richtig geschlossen werden, was die Kommunikation erheblich stören würde.

 

Es gibt IIC-Bausteine, bei dem es möglich ist, ganze Datenpakete zu übertragen. Hier muss dann so lange 'Ack' gesendet werden, bis das letzte gewünschte Byte übertragen wurde.

 

Nachdem das Byte empfangen und der IIC-Bus geschlossen wurde, wird mit 'Shift Dat,Right,4' dafür gesorgt, das der Tastanstatus an die Bit-Position der Ausgabe-LEDs verschoben wird. Die nachfolgende IIC-Befehlsfolge sendet den erhaltenen Tastanstatus wieder an den Port und schaltet entsprechend die Leuchtdioden.

 

Interrupt-Steuerung durch den PCF8574

 

Es ist oftmals nicht sinnvoll, dauernd den IIC-Port abzufragen, um festzustellen, ob sich etwas am Port getan hat. Dies blockiert den AVR für andere Aufgaben massiv. Hier wäre es doch besser, der PCF8574 'meldet' sich, wenn eine Taste betätigt wurde.

 

Dies tut er auch. Wenn man sich die Pinbelegung weiter oben genauer ansieht, so sieht man ein Pin Namens 'INT'. Dieser Pin geht kurzzeitig auf Low, wenn sich an einem Portpin das Signal ändert. Wenn wir diesen Pin nun mit einem Interrupt-Eingang verbinden, so können wir unser Programm so umschreiben, dass der PCF8574 nur dann abgefragt wird, wenn eine Taste betätigt oder los gelassen wird.

 

Um dies einmal zu testen, bauen wir den Aufbau etwas um. Leider können wir, aufgrund des Aufbaus, nur noch einen Taster anschließen:

 

 

 

Damit der AVR auch wirklich nur die IIC-Portabfrage durchführt, wenn sich der Port-Baustein per Interrupt-Signal meldet, brauchen wir ein anderes Programm:

 

$regfile "m8def.dat"
$crystal = 1000000

Dim Dat As Byte

Config Sda = Portc.4
Config Scl = Portc.5

Config Int0 = Falling

On Int0 Isr_int0

Enable Int0
Enable Interrupts

I2cinit

Do : Loop

Isr_int0:
  I2cstart : I2cwbyte &H41 : I2crbyte Dat , Nack : I2cstop
  Shift Dat , Right , 4
  I2cstart : I2cwbyte &H40 : I2cwbyte Dat : I2cstop
Return

 

Nun leuchtet die LED D1 immer auf, wenn S1 gedrückt wird. Im Unterschied zu der vorherigen Programmversion wird hier der AVR nur aktiv, wenn der PCF8574 meldet, dass sich der Zustand des Tasters geändert hat.

 

Bevor der AVR auf den Interrupt-Eingang reagiert müssen wir natürlich den Interrupt konfigurieren. Dies geschieht hier:

 

Config Int0 = Falling

On Int0 Isr_int0

Enable Int0
Enable Interrupts

 

Der PCF8574 schaltet den INT-Ausgang auf 0, womit wir also beim AVR auf eine fallende Flanke mit 'Config Int0=Falling' reagieren müssen. Meldet der Portbaustein eine Änderung, so soll die Routine 'Isr_int0' angesprungen werden. Anschließend wird noch der INT0-Eingang aktiviert und Interrupts insgesamt erlaubt.

 

Im weiteren Programmlauf wartet jetzt der AVR in einer Leerschleife, bis der PCF8674 eine Änderung meldet. Es wird dann die Unterroutine 'Isr_int0' angesprungen und die uns schon bekannte Routine abgearbeitet. Danach kehrt der AVR wieder in die Endlosschleife zurück.

 

In dieser Endlosschleife können nun natürlich andere Aufgaben ausgeführt werden.

 

Eine Anmerkung noch zum INT-Ausgang des PCF8574. Dieser bleibt so lange auf 0-Pegel, bis der Portbaustein adressiert wurde. Es spielt also keine große Rolle, wie schnell der angeschlossene Prozessor auf das Signal reagiert.

 

Wird fortgesetzt ...

 

Zurück zur Auswahlseite            Zur Hauptseite