Der I²C / TWI -bus
http://www.mikrocontroller.net/articles/AVR_TWI
● Two Wire Interface (TWI)
● Bus zum Datenaustausch zwischen mehreren Bau- steinen und Controllern in der Regel innerhalb eines Gerätes.
● Wird von einer Vielzahl von Bausteinen unterstützt.
● Basiert auf einer Entwicklung von Phillips (I²C-Bus).
● Ermöglicht auch die Kommunikation zwischen Control- lern.
● Bitserielle Übertragung
● Verbindung über die Leitungen GND, SCL, SDA.
● Two Wire Interface (TWI)
● Bus zum Datenaustausch zwischen mehreren Bau- steinen und Controllern in der Regel innerhalb eines Gerätes.
● Wird von einer Vielzahl von Bausteinen unterstützt.
● Basiert auf einer Entwicklung von Phillips (I²C-Bus).
● Ermöglicht auch die Kommunikation zwischen Control- lern.
● Bitserielle Übertragung
● Verbindung über die Leitungen GND, SCL, SDA.
● Die Kommunikation wird immer durch einen Master eingeleitet
● Bis zu 127 Slaves können über den Bus erreicht werden
● Jedes Gerät wird über Gerätenummer identifi- ziert
● Die Gerätenummer 0 ist reserviert und dient als broadcastadresse.
● Unter
http://www.peterfleury.epizy.com/avr-software.html
findet man eine sehr gute Implementation eines TWI Masters für Atmega.
● Die Kommunikation wird immer durch einen Master eingeleitet
● Bis zu 127 Slaves können über den Bus erreicht werden
● Jedes Gerät wird über Gerätenummer identifi- ziert
● Die Gerätenummer 0 ist reserviert und dient als broadcastadresse.
● Unter
http://www.peterfleury.epizy.com/avr-software.html
findet man eine sehr gute Implementation eines TWI Masters für Atmega.
● Die Gerätenummer ist eine 7-bit- Zahl.
● Bei Atmega ist Sie mit 2 multipliziert, ist also linksbündig in einem Byte gespeichert und ist somit geradzahlig.
● Die Gerätenummer besteht oft aus zwei Teilen, einem festen Teil (4-6 bit) und einem variablen Teil, der durch externe Beschaltung gebildet wird.
● Das Bit 8, das niederwertigste Bit dient der übermittlung der Übertragungsrichtung (read/
● Die Gerätenummer ist eine 7-bit- Zahl.
● Bei Atmega ist Sie mit 2 multipliziert, ist also linksbündig in einem Byte gespeichert und ist somit geradzahlig.
● Die Gerätenummer besteht oft aus zwei Teilen, einem festen Teil (4-6 bit) und einem variablen Teil, der durch externe Beschaltung gebildet wird.
● Das Bit 8, das niederwertigste Bit dient der übermittlung der Übertragungsrichtung (read/
Gerätenummer
Schaltung TWI
mit 128 bit EEPROM und Temperatursensor DS1621
Typische Anschlusspins
● SDA: Datenleitung
● SCL: Clockleitung
● A1, A2, A3 Codierungsanschlüsse für Adresseinstellung
● VCC/VDD Betriebsspannung (5V)
● GND
● Die Anschlussbelegung ist IMMER dem Datenblatt zu entnehmen. Die
Anschlussbelegung von Bausteinen in gleicher
● SDA: Datenleitung
● SCL: Clockleitung
● A1, A2, A3 Codierungsanschlüsse für Adresseinstellung
● VCC/VDD Betriebsspannung (5V)
● GND
● Die Anschlussbelegung ist IMMER dem Datenblatt zu entnehmen. Die
Anschlussbelegung von Bausteinen in gleicher
Typische Bausteine
http://www.mikrocontroller.net/articles/I2C
https://rn-wissen.de/wiki/index.php/I2C_Chip-%C3%9Cbersicht
serielle EEPROMs (24Cxx )
I/O-Portexpander (PCF8574,MCP23008 (8-bit) von Microchip, MCP23017 (16-bit) von Microchip )
I2C MUX, zum Anschluss von ICs mit gleicher, fester Adresse ( PCA9545A )
AD-Wandler (MCP3424, auch für Raspberry) DA-Wandler (TDA8444, 8x6Bit )
Uhrenbausteine (PCF8583, mit 256 Bytes RAM ) LCD-Treiber
Temperatursensoren (DS1621, LM75, TMP101 von TexasInstruments ,TMP175 von TI)
serielle EEPROMs (24Cxx )
I/O-Portexpander (PCF8574,MCP23008 (8-bit) von Microchip, MCP23017 (16-bit) von Microchip )
I2C MUX, zum Anschluss von ICs mit gleicher, fester Adresse ( PCA9545A )
AD-Wandler (MCP3424, auch für Raspberry) DA-Wandler (TDA8444, 8x6Bit )
Uhrenbausteine (PCF8583, mit 256 Bytes RAM ) LCD-Treiber
Temperatursensoren (DS1621, LM75, TMP101 von TexasInstruments ,TMP175 von TI)
Für Raspberry Addons viel benutzt
long distance treiber
https://www.nxp.com/docs/en/data-sheet/P82B96.pdf
P82B96
Adressierung von Bausteinen
● Jeder Baustein hat eine Grundadresse, diese ist aus dem Manual zu ermitteln (manchmal et- was tricky versteckt) DS1621: 1001 .
● Über die Adressbits werden die Bits 1,2 und 3 gebildet.
● Bit 0 bleibt frei, über dieses Bit wird die Über- tragungsrichtung (r/w) gesteuert.
● Jeder Baustein hat eine Grundadresse, diese ist aus dem Manual zu ermitteln (manchmal et- was tricky versteckt) DS1621: 1001 .
● Über die Adressbits werden die Bits 1,2 und 3 gebildet.
● Bit 0 bleibt frei, über dieses Bit wird die Über- tragungsrichtung (r/w) gesteuert.
1 0 0 1 A2 A1 A0 r/w
Bit 0 Bit 7
http://www.elektronik-magazin.de/page/i2c-bus-adressliste-23
Arbeitsweise
● SCL und SDL liegen auf 1.
● SDL nach 0 bei SCL=1 zeigt an, dass ein Ge- rät Daten übertragen will (Start condition).
● Wärend einer Übertragung, kann sich der Zu- stand von SDA ändern, wenn SCL=0 ist.
Wenn SCL=1, sind die Daten gültig und dürfen sich nicht ändern.
● Für 1 Byte werden 9 bit übertragen, das 9. Bit zeigt an, ob die Übertragung eines weiteren Bytes folgt (ack:Acknowledge, nak:not Ack…).
● SCL und SDL liegen auf 1.
● SDL nach 0 bei SCL=1 zeigt an, dass ein Ge- rät Daten übertragen will (Start condition).
● Wärend einer Übertragung, kann sich der Zu- stand von SDA ändern, wenn SCL=0 ist.
Wenn SCL=1, sind die Daten gültig und dürfen sich nicht ändern.
● Für 1 Byte werden 9 bit übertragen, das 9. Bit zeigt an, ob die Übertragung eines weiteren Bytes folgt (ack:Acknowledge, nak:not Ack…).
TWGCE General Call Enable (Broadcast: dev0x00, nur bei Slave belegt)
TWGCE General Call Enable (Broadcast: dev0x00, nur bei Slave belegt)
1: General Call Enable, 0: not
TWDR TWI DatenRegister
7 6 5 4 3 2 1 0
TWD7 TWD6 TWD5 TWD4 TWD3 TWD2 TWD1 TWD0
R/W R/W R/W R/W R/W R/W R/W R/W
TWDR TWI DatenRegister
7 6 5 4 3 2 1 0
TWD7 TWD6 TWD5 TWD4 TWD3 TWD2 TWD1 TWD0
R/W R/W R/W R/W R/W R/W R/W R/W
TWAR TWI SlaveAdressRegister
7 6 5 4 3 2 1 0
TWA6 TWA5 TWA4 TWA3 TWA2 TWA1 TWA0 TWGCE
R/W R/W R/W R/W R/W R/W R/W R/W
TWAR TWI SlaveAdressRegister
7 6 5 4 3 2 1 0
TWA6 TWA5 TWA4 TWA3 TWA2 TWA1 TWA0 TWGCE
R/W R/W R/W R/W R/W R/W R/W R/W
TWCR - TWI Control Register
TWCR TWI ControlRegister
7 6 5 4 3 2 1 0
TWINT TWEA TWSTA TWSTO TWWC TWEN TWIE
R/W R/W R/W R/W R R/W R R/W
TWCR TWI ControlRegister
7 6 5 4 3 2 1 0
TWINT TWEA TWSTA TWSTO TWWC TWEN TWIE
R/W R/W R/W R/W R R/W R R/W
Bit Description
TWINT TWI-Interruptflag
TWEA Ack wird gesendet
TWSTA TWI start senden TWSTO TWI Stop senden
TWWC Fehleranzeige
TWEN TWI enable
TWIE TWI Interrupt enable
Bit Description
TWINT TWI-Interruptflag
TWEA Ack wird gesendet
TWSTA TWI start senden TWSTO TWI Stop senden
TWWC Fehleranzeige
TWEN TWI enable
TWIE TWI Interrupt enable
TWSR TWI StatusRegister
7 6 5 4 3 2 1 0
TWS7 TWS6 TWS5 TWS4 TWS3 - TWPS1 TWPS0
R R R R R R R/W R/W
TWSR TWI StatusRegister
7 6 5 4 3 2 1 0
TWS7 TWS6 TWS5 TWS4 TWS3 - TWPS1 TWPS0
R R R R R R R/W R/W
Bit Description
TWS7
Statusbits TWS6
TWS5 TWS4 TWS3 TWPS1
Vorteiler TWPS0
Bit Description
TWS7
Statusbits TWS6
TWS5 TWS4 TWS3 TWPS1
Vorteiler TWPS0
Implementation von P. Fleury
void i2c_init(void);
void i2c_stop(void);
unsigned char i2c_start(unsigned char addr);
unsigned char i2c_rep_start(unsigned char addr);
void i2c_start_wait(unsigned char addr);
unsigned char i2c_write(unsigned char data);
unsigned char i2c_readAck(void);
unsigned char i2c_readNak(void);
void i2c_init(void);
void i2c_stop(void);
unsigned char i2c_start(unsigned char addr);
unsigned char i2c_rep_start(unsigned char addr);
void i2c_start_wait(unsigned char addr);
unsigned char i2c_write(unsigned char data);
unsigned char i2c_readAck(void);
unsigned char i2c_readNak(void);
Hinweise zur Nutzung des Archivs
● Im Archiv befinden sich folgende Dateien:
– i2cmaster.h (einbinden in Anwendung)
– i2cmaster.S (I²C Library für gewöhnliche Pins)
– twimaster.c (I²C Library AVR-TWI-basiert)
– test_i2cmaster.c (Testbeispiel mit 24C02)
● i2Cmaster.h mit include einbinden
● twimaster.c ins Makefile einbauen
● Die Verwenung von i2cmaster.S sollte auch gehen, ist aber nicht sinnvoll, wenn der Prozessor TWI unterstützt und die IO-Leitungen nicht anderweitig belegt sind.
● Im Archiv befinden sich folgende Dateien:
– i2cmaster.h (einbinden in Anwendung)
– i2cmaster.S (I²C Library für gewöhnliche Pins)
– twimaster.c (I²C Library AVR-TWI-basiert)
– test_i2cmaster.c (Testbeispiel mit 24C02)
● i2Cmaster.h mit include einbinden
● twimaster.c ins Makefile einbauen
● Die Verwenung von i2cmaster.S sollte auch gehen, ist aber nicht sinnvoll, wenn der Prozessor TWI unterstützt und die IO-Leitungen nicht anderweitig belegt sind.
Schreiben eines Bytes ->24C02
i2c_init();
ret=i2c_start(addr); // R/W=1:Read,0:Write i2c_write(eepromAddr);
i2c_write(val) i2c_stop();
i2c_init();
ret=i2c_start(addr); // R/W=1:Read,0:Write i2c_write(eepromAddr);
i2c_write(val) i2c_stop();
17
Schreiben von N Bytes ->24C02
i2c_init();
ret = i2c_start(Dev24C02+I2C_WRITE);
if ( ret==0 ) {
// issuing start condition ok, device accessible
i2c_write(00); // write start address = 0 (muss /8 teilbar sein) int i;
for (i=0;i<N;i++) {
i2c_write(send[i]);
if ((i+1)%8==0)
{ //set next address i2c_stop();
i2c_start_wait(Dev24C02+I2C_WRITE);
i2c_write(i+1); //Next Address page }
}
i2c_stop();
i2c_init();
ret = i2c_start(Dev24C02+I2C_WRITE);
if ( ret==0 ) {
// issuing start condition ok, device accessible
i2c_write(00); // write start address = 0 (muss /8 teilbar sein) int i;
for (i=0;i<N;i++) {
i2c_write(send[i]);
if ((i+1)%8==0)
{ //set next address i2c_stop();
i2c_start_wait(Dev24C02+I2C_WRITE);
i2c_write(i+1); //Next Address page }
}
i2c_stop();
}// end if
I2C_WRITE ist 0 und kann deshalb entfallen
Versand eines Datenbytes
Geht so nur, wenn Startadresse 0 ist. Ansonsten muss die
Startadresse noch addiert werden!
Lesen mehrerer Bytes aus 24C02
i2c_start_wait(Dev24C02+I2C_WRITE); // device and write mode i2c_write(0x00); // write address = 0
i2c_rep_start(Dev24C02+I2C_READ); // device and read mode for (i=0;i<N-1;i++)
{
read[i] = i2c_readAck(); // read one Byte if (read[i]>=' ') lcd_putc(read[i]);
}
read[i] = i2c_readNak(); // read last Byte lcd_putc(read[i]);
i2c_stop(); // set stop condition = release bus i2c_start_wait(Dev24C02+I2C_WRITE); // device and write mode i2c_write(0x00); // write address = 0
i2c_rep_start(Dev24C02+I2C_READ); // device and read mode for (i=0;i<N-1;i++)
{
read[i] = i2c_readAck(); // read one Byte if (read[i]>=' ') lcd_putc(read[i]);
}
read[i] = i2c_readNak(); // read last Byte lcd_putc(read[i]);
i2c_stop(); // set stop condition = release bus
TWI Atmega als slave
http://www.rn-wissen.de/index.php/TWI_Slave_mit_avr-gcc
● Ermöglicht Kommunikation zwischen mehreren Atmel Controllern
● Slaveadresse festlegen
● Sende- / Empfangspuffer definieren
● void init_twi_slave (uint8_t dev_addr);
● ISR (TWI_vect)
● Includes:
– #include <util/twi.h> // Statuscodes in TWSR ...
– #include <avr/interrupt.h>
#include <stdint.h> //definiert den Datentyp uint8_t
● Ermöglicht Kommunikation zwischen mehreren Atmel Controllern
● Slaveadresse festlegen
● Sende- / Empfangspuffer definieren
● void init_twi_slave (uint8_t dev_addr);
● ISR (TWI_vect)
● Includes:
– #include <util/twi.h> // Statuscodes in TWSR ...
– #include <avr/interrupt.h>
– #include <stdint.h> //definiert den Datentyp uint8_t
/*ISR, die bei einem Ereignis auf dem Bus ausgelöst wird. Im Register TWSR befindet sich dann ein Statuscode, anhand dessen die Situation festgestellt werden kann. */
ISR (TWI_vect) {
uint8_t data=0;
//TWI-Statusregister prüfen und nötige Aktion bestimmen switch (TW_STATUS)
{
/* ein master hat Kommunikationswunsch mit unserer Adresse signalisiert*/
// 0x60 Slave Receiver, wurde adressiert
case TW_SR_SLA_ACK: // 0x60 Slave Receiver, wurde adressiert TWCR_ACK; // Datenbyte empfangen, ACK danach buffer_adr=0xFF;//Bufferposition ist undefiniert
break;
/*ISR, die bei einem Ereignis auf dem Bus ausgelöst wird. Im Register TWSR befindet sich dann ein Statuscode, anhand dessen die Situation festgestellt werden kann. */
ISR (TWI_vect) {
uint8_t data=0;
//TWI-Statusregister prüfen und nötige Aktion bestimmen switch (TW_STATUS)
{
/* ein master hat Kommunikationswunsch mit unserer Adresse signalisiert*/
// 0x60 Slave Receiver, wurde adressiert
case TW_SR_SLA_ACK: // 0x60 Slave Receiver, wurde adressiert TWCR_ACK; // Datenbyte empfangen, ACK danach buffer_adr=0xFF;//Bufferposition ist undefiniert
break;
// 0x80 Slave Receiver,Daten empfangen case TW_SR_DATA_ACK:
if (TWCR&&(1<<TWINT)) data=(uint8_t)TWDR; //Daten lesen else data=0;
if (buffer_adr == 0xFF) //erster Zugriff,0x60 Bufferposition (Addresss) setzen {
//Kontrolle ob gewünschte Adresse im erlaubten Bereich
if(data<=buffer_size) buffer_adr= data; //Bufferposition wie adressiert setzen else buffer_adr=0; //Adresse auf Null setzen.(Sinnvoll?) TWCR_ACK; // ACK danach, um nächstes Byte anzufordern
}
else //weiterer Zugriff, Daten empfangen {
rxbuffer[buffer_adr]=data; //Daten in Buffer schreiben buffer_adr++; //increment Buffer-Adresse für nächsten Schreibzugriff if(buffer_adr<(buffer_size-1)) //im Buffer ist noch Platz für mehr als ein Byte TWCR_ACK; // ACK danach, um nächstes Byte anzufordern else //es kann nur noch ein Byte kommen, dann ist der Buffer voll TWCR_NACK; //letztes Byte lesen, dann NACK, um Ende zu signaliseren // 0x80 Slave Receiver,Daten empfangen
case TW_SR_DATA_ACK:
if (TWCR&&(1<<TWINT)) data=(uint8_t)TWDR; //Daten lesen else data=0;
if (buffer_adr == 0xFF) //erster Zugriff,0x60 Bufferposition (Addresss) setzen {
//Kontrolle ob gewünschte Adresse im erlaubten Bereich
if(data<=buffer_size) buffer_adr= data; //Bufferposition wie adressiert setzen else buffer_adr=0; //Adresse auf Null setzen.(Sinnvoll?) TWCR_ACK; // ACK danach, um nächstes Byte anzufordern
}
else //weiterer Zugriff, Daten empfangen {
rxbuffer[buffer_adr]=data; //Daten in Buffer schreiben buffer_adr++; //increment Buffer-Adresse für nächsten Schreibzugriff if(buffer_adr<(buffer_size-1)) //im Buffer ist noch Platz für mehr als ein Byte TWCR_ACK; // ACK danach, um nächstes Byte anzufordern else //es kann nur noch ein Byte kommen, dann ist der Buffer voll TWCR_NACK; //letztes Byte lesen, dann NACK, um Ende zu signaliseren
case TW_ST_SLA_ACK: //?!?
//0xB8 Slave Transmitter, weitere Daten wurden angefordert case TW_ST_DATA_ACK:
if (buffer_adr == 0xFF) //zuvor keine Leseadresse angegeben!
buffer_adr=0;
TWDR = txbuffer[buffer_adr]; //Datenbyte senden buffer_adr++; //bufferadresse für nächstes Byte weiterzählen if(buffer_adr<(buffer_size-1)) //noch mehr als ein Byte im Sendepuffer TWCR_ACK; //nächstes Byte senden, danach ACK erwarten else
TWCR_NACK; //letztes Byte senden, danach NACK erwarten break;
case TW_ST_SLA_ACK: //?!?
//0xB8 Slave Transmitter, weitere Daten wurden angefordert case TW_ST_DATA_ACK:
if (buffer_adr == 0xFF) //zuvor keine Leseadresse angegeben!
buffer_adr=0;
TWDR = txbuffer[buffer_adr]; //Datenbyte senden buffer_adr++; //bufferadresse für nächstes Byte weiterzählen if(buffer_adr<(buffer_size-1)) //noch mehr als ein Byte im Sendepuffer TWCR_ACK; //nächstes Byte senden, danach ACK erwarten else
TWCR_NACK; //letztes Byte senden, danach NACK erwarten break;
// 0xC0 Keine Daten mehr gefordert case TW_ST_DATA_NACK:
// 0x88
case TW_SR_DATA_NACK:
// 0xC8 Last data byte in TWDR has been transmitted // (TWEA = “0”); ACK has been received case TW_ST_LAST_DATA:
// 0xA0 STOP empfangen case TW_SR_STOP:
//Übertragung beenden, warten bis zur nächsten Adressierung default: TWCR_RESET;
break;
} //end.switch (TW_STATUS)
// 0xC0 Keine Daten mehr gefordert case TW_ST_DATA_NACK:
// 0x88
case TW_SR_DATA_NACK:
// 0xC8 Last data byte in TWDR has been transmitted // (TWEA = “0”); ACK has been received case TW_ST_LAST_DATA:
// 0xA0 STOP empfangen case TW_SR_STOP:
//Übertragung beenden, warten bis zur nächsten Adressierung default: TWCR_RESET;
break;
} //end.switch (TW_STATUS)