I2C_LED_Matrix/twislave.c
2008-07-16 05:44:45 +00:00

114 lines
5.3 KiB
C

/**
* \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 <ronald@schatenseite.de>
* \version $Id: twislave.c,v 1.1 2008/07/16 05:44:45 rschaten Exp $
*
* License: GNU GPL v2 (see License.txt)
*/
#include <util/twi.h> // enthaelt z. B. die Bezeichnungen fuer die Statuscodes in TWSR
#include <avr/interrupt.h> // dient zur behandlung der Interrupts
#include <stdint.h> // 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;
}
}