/** * \file twislave.c * \brief I2C slave library. taken from http://www.roboternetz.de/wissen/index.php/TWI_Slave_mit_avr-gcc * \author Uwe Grosse-Wortmann (uwegw), reformatted by Ronald Schaten * \version $Id: twislave.c,v 1.1 2008/07/16 05:44:45 rschaten Exp $ * * License: GNU GPL v2 (see License.txt) */ #include // enthaelt z. B. die Bezeichnungen fuer die Statuscodes in TWSR #include // dient zur behandlung der Interrupts #include // definiert den Datentyp uint8_t #include "twislave.h" // Bei zu alten AVR-GCC-Versionen werden die Interrupts anders genutzt, daher // in diesem Fall mit Fehlermeldung abbrechen #if (__GNUC__ * 100 + __GNUC_MINOR__) < 304 # error "This library requires AVR-GCC 3.4.5 or later, update to newer AVR-GCC compiler!" #endif // Schutz vor unsinnigen Buffergroessen #if (buffer_size > 254) # error Buffer zu gross gewaehlt! Maximal 254 Bytes erlaubt. #endif #if (buffer_size < 2) # error Buffer muss mindestens zwei Byte gross sein! #endif volatile uint8_t buffer_adr; //< "Adressregister" fuer den Buffer /** * Initalisierung des TWI-Interface. Muss zu Beginn aufgerufen werden, sowie * bei einem Wechsel der Slave Adresse. * \param adr gewuenschte Slave-Adresse */ void init_twi_slave(uint8_t adr) { TWAR = adr; // Adresse setzen TWCR &= ~(1 << TWSTA) | (1 << TWSTO); TWCR |= (1 << TWEA) | (1 << TWEN) | (1 << TWIE); buffer_adr = 0xff; sei(); } // Je nach Statuscode in TWSR muessen verschiedene Bitmuster in TWCR geschreiben werden (siehe Tabellen im Datenblatt). // Makros fuer die verwendeten Bitmuster: // ACK nach empfangenen Daten senden/ ACK nach gesendeten Daten erwarten #define TWCR_ACK TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (0 << TWWC); // NACK nach empfangenen Daten senden/ NACK nach gesendeten Daten erwarten #define TWCR_NACK TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (0 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (0 << TWWC); // switched to the non adressed slave mode... #define TWCR_RESET TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA) | (0 << TWSTA) | (0 << TWSTO) | (0 << TWWC); // Die Bitmuster fuer TWCR_ACK und TWCR_RESET sind gleich. Dies ist kein Fehler und dient nur der Uebersicht! /** * ISR, die bei einem Ereignis auf dem Bus ausgeloest 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 pruefen und noetige Aktion bestimmen switch (TW_STATUS) { case TW_SR_SLA_ACK: // 0x60 Slave Receiver, wurde adressiert TWCR_ACK; // naechstes Datenbyte empfangen, ACK danach buffer_adr = 0xFF; // Bufferposition ist undefiniert break; case TW_SR_DATA_ACK: // 0x80 Slave Receiver,Daten empfangen data = TWDR; // Empfangene Daten auslesen if (buffer_adr == 0xFF) { // erster Zugriff, Bufferposition setzen // Kontrolle ob gewuenschte Adresse im erlaubten bereich if (data <= buffer_size) { buffer_adr = data; // Bufferposition wie adressiert setzen } else { buffer_adr = 0; // Adresse auf Null setzen. Ist das sinnvoll? } TWCR_ACK; // naechstes Datenbyte empfangen, ACK danach, um naechstes Byte anzufordern } else { // weiterer Zugriff, Daten empfangen rxbuffer[buffer_adr] = data; // Daten in Buffer schreiben buffer_adr++; // Buffer-Adresse weiterzaehlen fuer naechsten Schreibzugriff if (buffer_adr < (buffer_size - 1)) { // im Buffer ist noch Platz fuer mehr als ein Byte TWCR_ACK; // naechstes Datenbyte empfangen, ACK danach, um naechstes Byte anzufordern } else { // es kann nur noch ein Byte kommen, dann ist der Buffer voll TWCR_NACK; // letztes Byte lesen, dann NACK, um vollen Buffer zu signaliseren } } break; case TW_ST_SLA_ACK: // ?!? case TW_ST_DATA_ACK: // 0xB8 Slave Transmitter, weitere Daten wurden angefordert if (buffer_adr == 0xFF) { // zuvor keine Leseadresse angegeben! buffer_adr = 0; } TWDR = txbuffer[buffer_adr]; // Datenbyte senden buffer_adr++; // bufferadresse fuer naechstes Byte weiterzaehlen if (buffer_adr < (buffer_size - 1)) { // im Buffer ist mehr als ein Byte, das gesendet werden kann TWCR_ACK; // naechstes Byte senden, danach ACK erwarten } else { TWCR_NACK; // letztes Byte senden, danach NACK erwarten } break; case TW_ST_DATA_NACK: // 0xC0 Keine Daten mehr gefordert case TW_SR_DATA_NACK: // 0x88 case TW_ST_LAST_DATA: // 0xC8 Last data byte in TWDR has been transmitted (TWEA = "0"); ACK has been received case TW_SR_STOP: // 0xA0 STOP empfangen default: TWCR_RESET; // Uebertragung beenden, warten bis zur naechsten Adressierung break; } }