Dies ist eine alte Version des Dokuments!
10. I2C Schnittstelle
Ziele
Nach dieser Lektion sollten Sie:
- wissen wie die Kommunikation zwischen I2C Master und Slave funktioniert
Video
Statemachine für Datenpaket
Statemachine der I2C Kommunikation
I2C in Kürze und Zeitverlaufsdiagramm
Übertragung
Für die I2C Übertragung „trommelt“ der Master-IC auf der Taktleitung (SCL). Bei jedem „Trommelschlag“ (SCL=High), darf der Slave die Datenleitung (SDA) lesen.
D.h. während der Datenübertragung bleibt die Datenleitung bei SCL=High konstant.
Eine Flanke (=Signalwechsel) auf der Datenleitung während SCL=High definiert Beginn und Ende der Kommunikation.
Eine fallende Flanke auf SDA bei SCL=High stellt das Startbit dar, eine steigende Flanke das Stoppbit.
Läuft keine Kommunikation sind Daten- und Tankleitung auf High.
Startbedingung
Um die Übertragung zu beginnen muss die Startbedingung eingeleitet werden. Während SCL HIGH ist (a), geht SDA von HIGH auf LOW. Anschließend startet SCL mit LOW (b).
Für eine Startbedingung werden die Bits innerhalb des TWCR wie folgt gesetzt:
TWCR = (1<<TWINT)|(1<<TWEN); // Setting TWINT clears interupt flag // to set the following state: | (1<<TWIE ) // Enable TWI Interrupt. --> nur wichtig, falls der Interrupt geprüft wird! | (1<<TWSTA)|(0<<TWSTO); // Initiate a START condition.
Übertragung
Die entscheidende Voraussetzung für eine erfolgreiche Bitübertragung ist, dass sich der Zustand von SDA nur ändern darf solange SCL auf LOW ist. Allerdings ist der Zustand von SDA erst gültig, wenn SCL auf HIGH ist.
Für die Übertragung eines Bytes muss TWDR und TWCR wie folgt gesetzt werden.
Zunächst wird die Übertragung der Adresse (SLA_W
) betrachtet:
TWDR = SLA_W; // Load SLA_W into TWDR TWCR = (1<<TWINT)|(1<<TWEN); // Setting TWINT clears interupt flag // to start transmission of address
Die Daten (DATA
) werden in gleicher Weise übertragen:
TWDR = DATA; // Load DATA into TWDR TWCR = (1<<TWINT)|(1<<TWEN); // Setting TWINT clears interupt flag // to start transmission of address
Stoppbedingung
Die Stoppbedingung beendet die Übertragung. SCL geht auf HIGH (c), anschließend wechselt die SDA-Leitung von LOW nach HIGH (d).
Für eine Stoppbedingung werden die Bits innerhalb des TWCR wie folgt gesetzt:
TWCR = (1<<TWINT)|(1<<TWEN); // Setting TWINT clears interupt flag // to set the following state: | (1<<TWIE ) // Enable TWI Interrupt. --> nur wichtig, falls der Interrupt geprüft wird! | (0<<TWSTA)|(1<<TWSTO); // Initiate a STOP condition.
Software
einfache Anwendung
komplexere Anwendung
Als Beispiel wurde die Temperaturmessung gewählt
Bibliotheken
- TWI Module as I2C Master (AVR315):
- Application Note: doc2564.pdf
- Library und Beispielcode: avr315.zip
- TWI Module as I2C Slave (AVR311):
- Application Note: doc2565.pdf
- Library und Beispielcode: avr311.zip
- alternative und schlanke Implementierung des Slaves von The Gouger (GitHub)
(Kopie vom 16.01.22: avr-i2c-slave-master.zip)
Code
TWI Master
/* ---------------------------------------------------------------------------- Experiment 10: I2C Kommunikation ============= =============================== Dateiname : I2C_SimpleMaster.c Autoren : Tim Fischer (Hochschule Heilbronn, Fakultaet T1) Datum : 16.01.2022 Version : 1.0 Hardware: Simulide 1.0.0 >R810 Software: Entwicklungsumgebung: AtmelStudio 7.0 C-Compiler: AVR/GNU C Compiler 5.4.0 Funktion : TBD Displayanzeige: TBD Tastenfunktion: keine Jumperstellung: keine Fuses im uC: CKDIV8: Aus (keine generelle Vorteilung des Takts) // ----------------------------------------------------------------------------*/ // Deklarationen ============================================================== // Festlegung der Quarzfrequenz #define F_CPU 8000000UL // CPU Frequenz von 8MHz #define F_SCL 100000L // Baudrate von 100 kHz // Include von Header-Dateien #include <avr/interrupt.h> #include <util/delay.h> // Konstanten #define SET_BIT(BYTE, BIT) ((BYTE) |= (1 << (BIT))) // Bit Zustand in Byte setzen #define CLR_BIT(BYTE, BIT) ((BYTE) &= ~(1 << (BIT))) // Bit Zustand in Byte loeschen #define TGL_BIT(BYTE, BIT) ((BYTE) ^= (1 << (BIT))) // Bit Zustand in Byte wechseln (toggle) //Funktionsprototypen void I2C_Init(); void I2C_transmitStart(); void I2C_transmitDataOrAddress(char Data); void I2C_transmitStop(); uint8_t TWI_Address = 0b0001010; uint8_t TWI_Data = 0b00110111; int main(void) { while (1) { I2C_Init(); // Initialisierung von TWI anstoßen I2C_transmitStart(); // Startbit schreiben I2C_transmitDataOrAddress((TWI_Address<<1) + 0);// Adresse senden I2C_transmitDataOrAddress(TWI_Data);// Daten senden I2C_transmitStop(); // Stoppbit schreiben _delay_us(1); } } ///////////////////////////////////////// // I2C Initialisierung ///////////////////////////////////////// void I2C_Init() { TWSR = CLR_BIT(TWSR, TWPS0);// Es wird kein Prescaler verwendet TWSR = CLR_BIT(TWSR, TWPS1);// TWCR = 0; // TWBR = ((F_CPU/F_SCL)-16)/2;// die Bitrate wird mittels CPU Frequenz und Serial Clock Frequenz ermittelt } ///////////////////////////////////////// // I2C Startbit senden ///////////////////////////////////////// void I2C_transmitStart() { TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA); // TWSTA = Startbit aktivieren, TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch setzen) while (!(TWCR & (1<<TWINT))); // warten bis Übertragung erfolgreich, Trigger ist hier das Setzen von TWINT } ///////////////////////////////////////// // I2C Adressbyte/Daten senden ///////////////////////////////////////// void I2C_transmitDataOrAddress(char Data) { TWDR = Data; TWCR = (1<<TWINT)|(1<<TWEN); // TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch setzen) while (!(TWCR & (1<<TWINT))); // warten bis Übertragung erfolgreich, Trigger ist hier das Setzen von TWINT } ///////////////////////////////////////// // I2C Stoppbit senden ///////////////////////////////////////// void I2C_transmitStop() { TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); // TWSTO = Stopptbit aktivieren, TWEN = TWI starten (ENable), TWINT = Interrupt bit löschen (durch setzen) }
TWI Slave
/* ---------------------------------------------------------------------------- Experiment 10: I2C Kommunikation ============= =============================== Dateiname : I2C_SimpleMaster.c Autoren : Tim Fischer (Hochschule Heilbronn, Fakultaet T1) Datum : 23.06.2021 Version : 1.0 Hardware: Simulide 0.5.16-RC5 Software: Entwicklungsumgebung: AtmelStudio 7.0 C-Compiler: AVR/GNU C Compiler 5.4.0 Funktion: tbd // ----------------------------------------------------------------------------*/ // Deklarationen ============================================================== // Festlegung der Quarzfrequenz #define F_CPU 16000000UL #define F_SCL 10000L //100 kHz // Include von Header-Dateien #include <avr/interrupt.h> // Konstanten #define SET_BIT(BYTE, BIT) ((BYTE) |= (1 << (BIT))) // Bit Zustand in Byte setzen #define CLR_BIT(BYTE, BIT) ((BYTE) &= ~(1 << (BIT))) // Bit Zustand in Byte loeschen #define TGL_BIT(BYTE, BIT) ((BYTE) ^= (1 << (BIT))) // Bit Zustand in Byte wechseln (toggle) //Funktionsprototypen void I2C_Init(); void I2C_setAddress(char Address); char I2C_readData(); uint8_t TWI_Address = 0b0001010; uint8_t TWI_AddressMask = 0b11111110; int main(void) { DDRD= 0xFF; // Auf DDRC die Daten ausgeben I2C_setAddress(TWI_Address); // eigene Adresse setzen while (1) { PORTD = I2C_readData(); // Daten an PortC ausgeben } } ////////////////////////////////////////////////// // Setzen der I2C Adresse auf die der Slave hört ////////////////////////////////////////////////// void I2C_setAddress(char Address) { TWAR = (Address<<1); // Adresse in das Pseudoregister schreiben TWAMR= TWI_AddressMask; // Adressmaske in das Pseudoregister schreiben TWCR = (1<<TWEA)|(1<<TWEN); // Enable Ack, Enable Interupt und Enable TIW } ////////////////////////////////////////////////// // Auslesen der übermittelten Daten ////////////////////////////////////////////////// char I2C_readData() { while (!(TWCR & (1<<TWINT))); // warte solange bis TWINT gesetzt ist return TWDR; // übermittle Daten }
Beispiele
- Simulide:
…\share\simulide\examples\Arduino\sofware_i2c_lcd\i2c_lcd-arduino
(hierbei wird Software I2C eingesetzt) - Software I2C:
- Library von Peter Fleury: library, Dokumentation
weiterführende Unterlagen
- Die „Microchip University“ hat auch eine schöne Einführung in I2C. Hier wird aber nicht die Implementierung in Microchip Studio auf einem AVR-Chip gezeigt, sondern in MPLAB X auf einem PIC. D.h. der Code ist nicht direkt übertragbar.