Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


(Slave sourcecode)
 
(26 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
 
Programm für einen AVR mit [[TWI]] (Hardware-[[I2C]])-Schnittstelle als Slave.
 
Programm für einen AVR mit [[TWI]] (Hardware-[[I2C]])-Schnittstelle als Slave.
 +
Manchmal stellt sich die Aufgabe, mehrere AVRs per [[I2C]] zu vernetzen. Ein Beispiel ist die Erweiterung eines bestehenden Systems um einen leistungsstärkeren Controller. Dies ist etwa beim [[Asuro]] oder [[Yeti]] denkbar, um z.B. mehr Ein/Ausgänge oder Speicherplatz zu bekommen.
  
{{FarbigerRahmen|
+
Man kann aber beim Bau eines Roboters auch von Anfang an auf ein System von mehreren vernetzten Controllern setzen. So kann man verschiedene Aufgabe, die weitgehend unabhängig voneinader gleichzeitg erledigt werden müssen, besser verteilen. Denkbar ist z.B. ein Controller für die Motorsteuerung, einer für die Sensorik, einer für Ein-und Ausgabe (wie LCD und Bedientaster),... , und ein zentraler Controller, der die Richtung vorgibt und alle anderen Controller mit Befehlen versorgt. Ein konkretes Beispiel ist der Roboterbausatz Nibo (siehe: [http://www.nicai-systems.de/nibo.html]) von nicai-systems, der einen Atmel ATmega128 als Hauptcontroller und zwei Atmel ATtiny44 als Controller für die Motorsteuerung und die IR-Sensorik einsetzt. Die Kommunikation läuft mit 400 kHz über den I2C-Bus mit dem ATmega128 als Master.  
Noch nicht 100% getestet! Es können noch Fehler enthalten sein. Bei mir (uwegw) funktioniert das System weitestgehend problemlos.  
+
Schließlich kann ein entsprechend programmierter AVR auch als Ersatz für handelsübliche I2C-Bauteile dienen. Der kleinste AVR mit Hardware-I2C, der ATmega48, ist mit 1,30€ (Reichelt) billiger als viele normale I2C-ICs. Er kann bei entsprechender Programmierung z.B. die Aufgaben von zwei PCF8574 (8bit-Portexpander, 1,25€) und einem PCF8591 (4fach AD-Wandler, 2,35€) übernehmen und außerdem noch als I2C-EEPROM mit 256 Bytes dienen.
Es müssten aber noch diverse Sonderfälle wie falsch adressierte Bufferzugriffe abgefangen werden... Also insgesamt eher Alpha-Status!
+
  
Bekannte Fehler:
+
Das System ist als eine Art Dualport-RAM konzipiert, Master und Slave teilen sich also einen Speicherbereich und können darüber Daten austauschen. Die Ansteuerung läuft wie bei den bekannten I2C-EEPROMs. Der Master schreibt erst die Adresse, die er lesen bzw- schreiben will. Bei einem Schreibzugriff überträgt er nun die Daten. Bei einem Lesezugriff wird ein repeated start im Lesemodus durchgeführt, dann können die Daten byteweise eingelesen werden. Die Adressen werden dabei automatisch weitergezählt, sodass mehrere Bytes in Folge verarbeitet werden können.
Zugriff auf das letzte Bufferelement scheint nicht korrekt zu funktionieren. Workaround: Buffer ein Byte größe definieren als benötigt.
+
  
<pre>
+
Getestet wurde der Code für zwei Mega32 mit dem AVR Studio. Beachte: im AVR-Studio ist default ein ATmega128 eingestellt, unter Projekt -> Configuration Options kann das Device geändert werden, außerdem ist die Taktfrequenz des Controllers einzustellen. Im Projekt wird für den Master die Dateien twimaster.c und i2cmaster.h hinzugefügt, und für den Slave die twislave.c und twislave.h.
 +
Wenn stattdessen ein makefile verwendet wird, müssen dort unter SRC die c-Dateien eingetragen werden.
  
Update 16.1.2010: Fehler gefunden:
+
Die Verbindung zwischen Master und Slave erfolgt über die Pins SCL und SDA, außerdem muss ein gemeinsamer GND vorhanden sein. Abschlusswiderstände (4,7kOhm) nicht vergessen, am besten beim Master, siehe auch I2C-Definitionen.  
  
case TW_ST_DATA_ACK:
 
...
 
...
 
                buffer_adr++; //Buffer-Adresse weiterzählen für nächsten Schreibzugriff
 
Fehler ---> if(buffer_adr<(buffer_size-1))
 
 
da buffer_adr vor dem Vergleich schon erhöht wurde, ist es so richtig:
 
 
Richtig --->    if(buffer_adr<(buffer_size))
 
 
</pre>
 
}}
 
 
Manchmal stellt sich die Aufgabe, mehrere AVRs per [[I2C]] zu vernetzen. Ein Beispiel ist die Erweiterung eines bestehenden Systems um einen leistungsstärkeren Controller. Dies ist etwa beim [[Asuro]] oder [[Yeti]] denkbar, um z.B. mehr Ein/Ausgänge oder Speicherplatz zu bekommen.
 
 
Man kann aber beim Bau eines Roboters auch von Anfang an auf ein System von mehreren vernetzten Controllern setzen. So kann man verschiedene Aufgabe, die weitgehend unabhängig voneinader gleichzeitg erledigt werden müssen, besser verteilen. Denkbar ist z.B. ein Controller für die Motorsteuerung, einer für die Sensorik, einer für Ein-und Ausgabe(wie LCD und Bedientaster),... , und ein zentraler Controller, der die Richtung vorgibt und alle anderen Controller mit Befehlen versorgt. Ein konkretes Beispiel ist der Roboterbausatz Nibo (siehe: [http://www.nicai-systems.de/nibo.html]) von nicai-systems, der einen Atmel ATmega128 als Hauptcontroller und zwei Atmel ATtiny44 als Controller für die Motorsteuerung und die IR-Sensorik einsetzt. Die Kommunikation läuft mit 400 kHz über den I2C-Bus mit dem ATmega128 als Master.
 
 
Schließlich kann ein entsprechend programmierter AVR auch als Ersatz für handelsübliche I2C-Bauteile dienen. Der kleinste AVR mit Hardware-I2C, der ATmega8, ist mit 1,70€ (Reichelt) billiger als viele normale I2C-ICs. Ein ATmega8 kann bei entsprechender Programmierung z.B. die Aufgaben von zwei PCF8574 (8bit-Portexpander, 1,70€) und einem PCF8591 (4fach AD-Wandler, 2,90€) übernehmen und außerdem noch als I2C-EEPROM mit 512 Bytes dienen.
 
  
 
Das folgende Programm (twislave.c) steuert das TWI (Hardware-I2C)-Interface eines AVRs als Slave an. Es müsste auf allen AVRs der Mega-Reihe funktionieren, die über eine TWI-Schnittstelle verfügen.  
 
Das folgende Programm (twislave.c) steuert das TWI (Hardware-I2C)-Interface eines AVRs als Slave an. Es müsste auf allen AVRs der Mega-Reihe funktionieren, die über eine TWI-Schnittstelle verfügen.  
  
Das System ist als eine Art Dualport-RAM konzipiert, Master und Slave teilen sich also einen Speicherbereich und können darüber Daten austauschen.
+
== Master ==
 +
Ein Testprogramm für den Master. Es wird die [http://homepage.hispeed.ch/peterfleury/avr-software.html#libs I2C-Master-Bibliothek von Peter Fleury] verwendet.
 +
Es wird geprüft, ob der Slave bereit ist, dann werden drei Bytes erst in den Slave geschrieben, und anschließend wieder ausgelesen.
 +
<pre>
  
 +
// I2C-Master-Routinen von Peter Fleury verwenden
 +
// siehe http://homepage.hispeed.ch/peterfleury/avr-software.html#libs
 +
// Hier sind auch die Dateien: i2cmaster.h und twimaster.c zu finden, die benötigt werden
 +
// Letztes Update des Codes 5. April 2010 durch HannoHupmann
  
 
+
#include <avr/io.h>
Ein Codeschnipsel für den Master. Es wird die [http://homepage.hispeed.ch/peterfleury/avr-software.html#libs I2C-Master-Bibliothek von Peter Fleury] verwendet.
+
#include "i2cmaster.h"   
Es wird geprüft, ob der Slave bereit ist, dann werden die ersten drei Bytes aus dem txbuffer des Slaves gelesen und in byte0..2 abgespeichert.
+
<pre>
+
#include "i2cmaster.c//I2C-Master-Routinen von Peter Fleury verwenden (siehe http://homepage.hispeed.ch/peterfleury/avr-software.html#libs)
+
 
#define SLAVE_ADRESSE 0x50
 
#define SLAVE_ADRESSE 0x50
uint8_t byte0;
 
uint8_t byte1;
 
uint8_t byte2;
 
  
      if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum lesen?
+
uint8_t byte1 = 42;
      {
+
uint8_t byte2 = 43;
        i2c_write(0x00); //Buffer Startadresse zum Auslesen
+
uint8_t byte3 = 44;
        i2c_rep_start(SLAVE_ADRESSE+I2C_READ); //Lesen beginnen
+
  
            byte0= i2c_readAck();
+
int main(void)
            byte1= i2c_readAck();
+
{
            byte2= i2c_readNak(); //letztes Byte lesen, darum kein ACK
+
  i2c_init();         // init I2C interface
        i2c_stop();
+
      }
+
</pre>
+
  
Die twislave.c für den Slave. Stand: 25.03.07
 
<pre>
 
#ifndef _TWISLAVE_H
 
#define _TWISLAVE_H
 
  
#include <util/twi.h> //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
+
if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum schreiben?
#include <avr/interrupt.h> //dient zur behandlung der Interrupts
+
{
#include <stdint.h> //definiert den Datentyp uint8_t
+
  i2c_write(0x00);  // Buffer Startadresse setzen
 +
  i2c_write(byte1); // Drei Bytes schreiben...
 +
  i2c_write(byte2);
 +
  i2c_write(byte3);
 +
  i2c_stop();      // Zugriff beenden
 +
}
 +
else
 +
{
 +
  /* Hier könnte eine Fehlermeldung ausgegeben werden... */
 +
}
  
  
/*
 
Dieses Programm in einer separaten Datei (z.B. twislave.c) abspeichern und in das eigene Programm
 
einbinden.
 
  
Betrieb eines AVRs mit Hardware-TWI-Schnittstelle als Slave. Zu Beginn muss init_twi_slave mit der gewünschten
+
if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum lesen?
Slave-Adresse als Parameter aufgerufen werden. Der Datenaustausch mit dem Master erfolgt über die Buffer  
+
{
rxbuffer und txbuffer, auf die von Master und Slave zugegriffen werden kann.
+
  i2c_write(0x00); //Buffer Startadresse zum Auslesen
rxbuffer und txbuffer sind globale Variablen (Array aus uint8_t).
+
  i2c_rep_start(SLAVE_ADRESSE+I2C_READ); //Lesen beginnen
Die Ansteuerung des rxbuffers, in den der Master schreiben kann, erfolgt ähnlich wie bei einem normalen I2C-EEPROM.
+
Man sendet zunächst die Bufferposition, an die man schreiben will, und dann die Daten. Die Bufferposition wird
+
automatisch hochgezählt, sodass man mehrere Datenbytes hintereinander schreiben kann, ohne jedesmal
+
die Bufferadresse zu schreiben.
+
Um den txbuffer vom Master aus zu lesen, überträgt man zunächst in einem Schreibzugriff die gewünschte Bufferposition und
+
liest dann nach einem repeated start die Daten aus. Die Bufferposition wird automatisch hochgezählt, sodass man mehrere
+
Datenbytes hintereinander lesen kann, ohne jedesmal die Bufferposition zu schreiben.
+
  
Autor: Uwe Große-Wortmann (uwegw)
+
  byte1= i2c_readAck(); // Bytes lesen...
Status: Testphase, keine Garantie für ordnungsgemäße Funktion!
+
  byte2= i2c_readAck();
letze Änderungen:
+
  byte3= i2c_readNak(); // letztes Byte lesen, darum kein ACK
23.03.07 Makros für TWCR eingefügt. Abbruch des Sendens, wenn der TXbuffer komplett gesendet wurde.
+
  i2c_stop();          // Zugriff beenden
24.03.07 verbotene Buffergrößen abgefangen
+
}
25.03.07 nötige externe Bibliotheken eingebunden
+
else
 +
{
 +
  /* Hier könnte eine Fehlermeldung ausgegeben werden... */
 +
}
  
 +
if ((byte1 != 42)||(byte2 != 43)||(byte3 != 44))
 +
{
 +
  /* Die Bytes wurden nicht korrekt übertragen und wieder gelesen! */
 +
}
 +
    for(;;);
 +
}
  
Abgefangene Fehlbedienung durch den Master:
+
// Code wurde von HannoHupmann auf einem Mega32 getestet
- Lesen über die Grenze des txbuffers hinaus
+
</pre>
- Schreiben über die Grenzen des rxbuffers hinaus
+
- Angabe einer ungültigen Schreib/Lese-Adresse
+
- Lesezuggriff, ohne vorher Leseadresse geschrieben zu haben
+
  
  
*/
+
== Slave ==
+
Hier soll nun der Code behandelt werden der für den Slave-Controller notwendig ist. Der vorgestellte Code schreibt die Register des Slaves an die Serielle Schnittstelle, mit einem Terminal Programm kann man sich die Ausgabe ansehen und überprüfen ob alles korrekt funktioniert.
 +
Der Code ist wieder an den von Peter Fleury angelehnt.
  
 +
Der Slave sollte ohne Master folgende Ausgabe liefern:
 +
<pre>
 +
i2cdata:
 +
10
 +
11
 +
12
 +
13
 +
usw.
 +
</pre>
 +
Die Register werden der Reihe nach mit ihrem Wert angezeigt. Wird der Master hinzugefügt sollte nun folgende Ausgabe im Terminal erscheinen:
 +
<pre>
 +
i2cdata:
 +
42
 +
43
 +
44
 +
13
 +
usw.
 +
</pre>
  
 +
== Slave Testprogramm ==
 +
Ein Testprogramm für den Slave. Es verwendet zur Ausgabe die UART-lib von P. Fleury, die auf derselben Homepage wie die TWI-Master-Lib zu finden ist.
 +
<pre>
 +
/*
 +
Testprogramm für den Slave
 +
Der Buffer wird mit Werten gefüllt. Dann wird er fortlaufend über die serielle Schnittstelle ausgegeben.
 +
Nun kann man dort sehen, wenn der Master einen Wert ändert
 +
*/
 +
#include <util/twi.h>     //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
 +
#include <avr/interrupt.h>  //dient zur Behandlung der Interrupts
 +
#include <stdint.h>     //definiert den Datentyp uint8_t
 +
#include "twislave.h"
 +
#include "uart.h"          //UART-Lib von P- Fleury
 +
#include <stdlib.h>        //nötig für Zahlumwandlung mit itoa
 +
#include <util/delay.h>
  
//%%%%%%%% von Benutzer konfigurierbare Einstellungen %%%%%%%%
 
  
#define buffer_size 8 //Größe der Buffer in Byte (2..254)
+
#define BAUD 9600 //Baudrate
 +
#define SLAVE_ADRESSE 0x50 //Die Slave-Adresse
  
 +
//Hilfsfunktion, um Zahlen über das UART auszugeben
 +
void uart_puti( const int val )
 +
{
 +
    char buffer[8];
 +
    uart_puts( itoa(val, buffer, 10) );
  
//%%%%%%%% Globale Variablen, die vom Hauptprogramm genutzt werden %%%%%%%%
+
}
  
/*Der Buffer, in dem die empfangenen Daten gespeichert werden. Der Slave funktioniert ähnlich  wie ein normales
+
int main (void)
Speicher-IC [I2C-EEPROM], man sendet die Adresse, an die man schreiben will, dann die Daten, die interne Speicher-Adresse
+
{
wird dabei automatisch hochgezählt*/
+
//TWI als Slave mit Adresse slaveadr starten
volatile uint8_t rxbuffer[buffer_size];
+
init_twi_slave(SLAVE_ADRESSE);
  
/*Der Sendebuffer, der vom Master ausgelesen werden kann.*/
+
//i2cdatamit Werten füllen, die der Master auslesen und ändern kann
volatile uint8_t txbuffer[buffer_size];
+
for(uint8_t i=0;i<i2c_buffer_size;i++)
 +
{
 +
i2cdata[i]=10+i;
 +
}
  
 +
//Serielle Schnittstelle aktivieren
 +
uart_init((UART_BAUD_SELECT((BAUD),F_CPU)));
 +
uart_puts("I2C-Test\r\n");
 +
uart_puts("Teste I2C-Slave mit Adresse "); uart_puti(SLAVE_ADRESSE);
 +
uart_puts("\r\n");
 +
uart_puts("\r\n"); //Leerzeile
  
//%%%%%%%% Funktionen, die vom Hauptprogramm aufgerufen werden können %%%%%%%%
+
//in einer Endlosschleife den Inhalt der Buffer ausgeben
+
while(1)
/*Initaliserung des TWI-Inteface. Muss zu Beginn aufgerufen werden, sowie bei einem Wechsel der Slave Adresse
+
{
Parameter: adr: gewünschte Slave-Adresse*/
+
uart_puts("i2cdata:\r\n");
void init_twi_slave (uint8_t adr);
+
for(uint8_t i=0;i<i2c_buffer_size;i++)
 +
{
 +
uart_puti(i2cdata[i]);
 +
uart_puts("\r\n");
 +
}
 +
uart_puts("\r\n");//leerzeile
 +
_delay_ms(500);
 +
} //end.while
 +
} //end.main
  
  
 +
</pre>
  
//%%%%%%%% ab hier sind normalerweise keine weiteren Änderungen erforderlich! %%%%%%%%//
+
== Slave sourcecode ==
//____________________________________________________________________________________//
+
Die twislave.c für den Slave. Stand: 05.09.2012
 
+
<pre>
#include <util/twi.h> //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
+
#include <util/twi.h>     //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
 
+
#include <avr/interrupt.h>  //dient zur Behandlung der Interrupts
 
+
#include <stdint.h>        //definiert den Datentyp uint8_t     
//Bei zu alten AVR-GCC-Versionen werden die Interrupts anders genutzt, daher in diesem Fall mit Fehlermeldung abbrechen
+
#include "twislave.h"
#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 Buffergrößen
+
#if (buffer_size > 254)
+
#error Buffer zu groß gewählt! Maximal 254 Bytes erlaubt.
+
#endif
+
 
+
#if (buffer_size < 2)
+
#error Buffer muss mindestens zwei Byte groß sein!
+
#endif
+
 
+
  
 +
//%%%%%%%% Globale Variablen, die vom Hauptprogramm genutzt werden %%%%%%%%
 +
/*Der Buffer, in dem die Daten gespeichert werden.
 +
Aus Sicht des Masters läuft der Zugrif auf den Buffer genau wie bei einem I2C-EEPROm ab.
 +
Für den Slave ist es eine globale Variable
 +
*/
 
volatile uint8_t buffer_adr; //"Adressregister" für den Buffer
 
volatile uint8_t buffer_adr; //"Adressregister" für den Buffer
  
 
/*Initaliserung des TWI-Inteface. Muss zu Beginn aufgerufen werden, sowie bei einem Wechsel der Slave Adresse
 
/*Initaliserung des TWI-Inteface. Muss zu Beginn aufgerufen werden, sowie bei einem Wechsel der Slave Adresse
Parameter: adr: gewünschte Slave-Adresse
+
Parameter adr: gewünschte Slave-Adresse
 
*/
 
*/
void init_twi_slave (uint8_t adr)
+
void init_twi_slave(uint8_t adr)
 
{
 
{
TWAR= adr; //Adresse setzen
+
        TWAR= adr; //Adresse setzen
 
TWCR &= ~(1<<TWSTA)|(1<<TWSTO);
 
TWCR &= ~(1<<TWSTA)|(1<<TWSTO);
 
TWCR|= (1<<TWEA) | (1<<TWEN)|(1<<TWIE);
 
TWCR|= (1<<TWEA) | (1<<TWEN)|(1<<TWIE);
Zeile 169: Zeile 198:
 
//ACK nach empfangenen Daten senden/ ACK nach gesendeten Daten erwarten
 
//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);   
 
#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     
 
//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);
 
#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 für TWCR_ACK und TWCR_RESET sind gleich. Dies ist kein Fehler und dient nur der Übersicht!
+
//switch to the non adressed slave mode...
 +
#define TWCR_RESET TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(1<<TWSTO)|(0<<TWWC); 
  
  
Zeile 183: Zeile 212:
 
{
 
{
 
uint8_t data=0;
 
uint8_t data=0;
 
  
 
switch (TW_STATUS) //TWI-Statusregister prüfen und nötige Aktion bestimmen  
 
switch (TW_STATUS) //TWI-Statusregister prüfen und nötige Aktion bestimmen  
 
{
 
{
  
case TW_SR_SLA_ACK: // 0x60 Slave Receiver, wurde adressiert
+
// Slave Receiver
TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach
+
 
 +
case TW_SR_SLA_ACK: // 0x60 Slave Receiver, Slave wurde adressiert
 +
TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach senden
 
buffer_adr=0xFF; //Bufferposition ist undefiniert
 
buffer_adr=0xFF; //Bufferposition ist undefiniert
 
break;
 
break;
 
 
case TW_SR_DATA_ACK: // 0x80 Slave Receiver,Daten empfangen
+
case TW_SR_DATA_ACK: // 0x80 Slave Receiver, ein Datenbyte wurde empfangen
 
data=TWDR; //Empfangene Daten auslesen
 
data=TWDR; //Empfangene Daten auslesen
 
if (buffer_adr == 0xFF) //erster Zugriff, Bufferposition setzen
 
if (buffer_adr == 0xFF) //erster Zugriff, Bufferposition setzen
 
{
 
{
 
 
//Kontrolle ob gewünschte Adresse im erlaubten bereich
 
//Kontrolle ob gewünschte Adresse im erlaubten bereich
if(data<=buffer_size)
+
if(data<i2c_buffer_size+1)
 
{
 
{
 
buffer_adr= data; //Bufferposition wie adressiert setzen
 
buffer_adr= data; //Bufferposition wie adressiert setzen
Zeile 205: Zeile 234:
 
else
 
else
 
{
 
{
buffer_adr=0; //Adresse auf Null setzen. Ist das sinnvoll?
+
buffer_adr=0; //Adresse auf Null setzen. Ist das sinnvoll? TO DO!
 
}
 
}
 
TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
 
TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
 
}
 
}
else //weiterer Zugriff, Daten empfangen
+
else //weiterer Zugriff, nachdem die Position im Buffer gesetzt wurde. NUn die Daten empfangen und speichern
 
{
 
{
rxbuffer[buffer_adr]=data; //Daten in Buffer schreiben
+
buffer_adr++; //Buffer-Adresse weiterzählen für nächsten Schreibzugriff
+
if(buffer_adr<i2c_buffer_size+1)
if(buffer_adr<(buffer_size-1)) //im Buffer ist noch Platz für mehr als ein Byte
+
 
{
 
{
TWCR_ACK;// nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
+
i2cdata[buffer_adr]=data; //Daten in Buffer schreibe
}
+
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
+
 
}
 
}
 +
buffer_adr++; //Buffer-Adresse weiterzählen für nächsten Schreibzugriff
 +
TWCR_ACK;
 
}
 
}
 
break;
 
break;
  
case TW_ST_SLA_ACK: //?!?
+
 
case TW_ST_DATA_ACK: //0xB8 Slave Transmitter, weitere Daten wurden angefordert
+
//Slave transmitter
 +
 
 +
case TW_ST_SLA_ACK: //0xA8 Slave wurde im Lesemodus adressiert und hat ein ACK zurückgegeben.
 +
//Hier steht kein break! Es wird also der folgende Code ebenfalls ausgeführt!
 +
 +
case TW_ST_DATA_ACK: //0xB8 Slave Transmitter, Daten wurden angefordert
  
 
if (buffer_adr == 0xFF) //zuvor keine Leseadresse angegeben!  
 
if (buffer_adr == 0xFF) //zuvor keine Leseadresse angegeben!  
Zeile 231: Zeile 262:
 
buffer_adr=0;
 
buffer_adr=0;
 
}
 
}
TWDR = txbuffer[buffer_adr]; //Datenbyte senden
+
buffer_adr++; //bufferadresse für nächstes Byte weiterzählen
+
if(buffer_adr<i2c_buffer_size+1)
if(buffer_adr<(buffer_size-1)) //im Buffer ist mehr als ein Byte, das gesendet werden kann
+
 
{
 
{
TWCR_ACK; //nächstes Byte senden, danach ACK erwarten
+
TWDR = i2cdata[buffer_adr]; //Datenbyte senden
 +
buffer_adr++; //bufferadresse für nächstes Byte weiterzählen
 
}
 
}
 
else
 
else
 
{
 
{
TWCR_NACK; //letztes Byte senden, danach NACK erwarten
+
TWDR=0; //Kein Daten mehr im Buffer
 
}
 
}
 +
TWCR_ACK;
 
break;
 
break;
 
+
case TW_SR_STOP:
case TW_ST_DATA_NACK: //0xC0 Keine Daten mehr gefordert  
+
            TWCR_ACK;
case TW_SR_DATA_NACK: //0x88  
+
        break;
case TW_ST_LAST_DATA: //0xC8  Last data byte in TWDR has been transmitted (TWEA = “0”); ACK has been received
+
case TW_ST_DATA_NACK: // 0xC0 Keine Daten mehr gefordert  
case TW_SR_STOP: // 0xA0 STOP empfangen
+
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
 
default:
 
default:
     TWCR_RESET; //Übertragung beenden, warten bis zur nächsten Adressierung
+
     TWCR_RESET;
 
break;
 
break;
 
 
 
 
} //end.switch (TW_STATUS)
 
} //end.switch (TW_STATUS)
 
} //end.ISR(TWI_vect)
 
} //end.ISR(TWI_vect)
  
 
#endif //#ifdef _TWISLAVE_H
 
 
////Ende von twislave.c////
 
////Ende von twislave.c////
 
  
 
</pre>
 
</pre>
  
 
+
== Slave header ==
EIn Testprogramm für den Slave.  
+
Die zugehörige Headerdatei twislave.h für den Slave. Stand: 05.09.2012
 
<pre>
 
<pre>
/*
 
Testprogramm für die twislave.c
 
Der txbuffer wird mit Werten gefüllt. Dann werden txbuffer und rxbuffer
 
fortlaufend über die serielle Schnittstelle ausgegeben.
 
  
Beispiel für die Ausgabe in einem beliebigen Terminalprogramm:
+
#ifndef _TWISLAVE_H
 +
#define _TWISLAVE_H
  
rxbuffer
+
#include <util/twi.h>   //enthaelt z.B. die Bezeichnungen fuer die Statuscodes in TWSR
1
+
#include <avr/interrupt.h>  //dient zur behandlung der Interrupts
160
+
#include <stdint.h>     //definiert den Datentyp uint8_t
100
+
100
+
187
+
0
+
0
+
0
+
...
+
  
txbuffer
 
10
 
11
 
12
 
13
 
14
 
15
 
16
 
17
 
...
 
  
im Bereich rxbuffer stehen die vom Master geschriebenen Werte,
+
/**
im txbuffer die Werte, die der Master vom Slave lesen kann.
+
*  @defgroup twislave TWI-Slave
 +
*  @code #include "twislave.h" @endcode
 +
*
 +
*  @brief Betrieb eines AVRs mit Hardware-TWI-Schnittstelle als Slave.
 +
*  Zu Beginn muss init_twi_slave mit der gewuenschten Slave-Adresse als
 +
*  Parameter aufgerufen werden.
 +
*
 +
* Der Datenaustausch mit dem Master erfolgt ueber den Buffer i2cdata,
 +
* auf den von Master und Slave zugegriffen werden kann.
 +
* Dies ist fuer den Slave eine globale Variable (Array aus uint8_t).
 +
 
 +
* Der Zugriff durch den Master erfolgt aehnlich wie bei einem
 +
* normalen I2C-EEPROM.
 +
* Man sendet zunaechst die Bufferposition, an die man schreiben will,
 +
* und dann die Daten.
 +
* Die Bufferposition wird automatisch hochgezaehlt, sodass man mehrere
 +
* Datenbytes hintereinander schreiben kann, ohne jedesmal die
 +
* Bufferadresse zu schreiben.
 +
*
 +
* Um vom Master aus zu lesen, uebertraegt man zunaechst in einem
 +
* Schreibzugriff die gewuenschte Bufferposition und liest dann nach
 +
* einem repeated start die Daten aus. Die Bufferposition wird
 +
* automatisch hochgezaehlt, sodass man mehrere Datenbytes
 +
* hintereinander lesen kann, ohne jedesmal die Bufferposition zu
 +
* schreiben.
 +
*
 +
* Abgefangene Fehlbedienung durch den Master:
 +
* - Lesen ueber die Grenze des txbuffers hinaus
 +
* - Schreiben ueber die Grenzen des rxbuffers hinaus
 +
* - Angabe einer ungueltigen Schreib/Lese-Adresse
 +
* - Lesezugriff, ohne vorher Leseadresse geschrieben zu haben
 +
*
 +
*  @author uwegw
 +
*/
 +
 
 +
/*@{*/
 +
 
 +
//%%%%%%%% von Benutzer konfigurierbare Einstellungen %%%%%%%%
 +
/**@brief Groesse des Buffers in Byte (2..254) */
 +
#define i2c_buffer_size 10// I2C_REG_ANZAHL 254 Hier kann eingestellt werden wieviele Register ausgegeben werden
 +
 
 +
 
 +
//%%%%%%%% Globale Variablen, die vom Hauptprogramm genutzt werden %%%%%%%%
 +
/**@brief Der Buffer, in dem die Daten gespeichert werden.
 +
* Aus Sicht des Masters laeuft der Zugrif auf den Buffer genau wie
 +
*  bei einem I2C-EEPROm ab.
 +
* Fuer den Slave ist es eine globale Variable
 
*/
 
*/
 +
volatile uint8_t i2cdata[i2c_buffer_size];
  
  
#include "twislave.c"
+
/**@brief Initaliserung des TWI-Inteface. Muss zu Beginn aufgerufen werden, sowie bei einem Wechsel der Slave Adresse
#include "uart.c"
+
* @param adr gewuenschte Slave-Adresse */
 +
void init_twi_slave(uint8_t adr);
  
#define BAUD 9600 //Baudrate
+
//%%%%%%%% ab hier sind normalerweise keine weiteren Aenderungen erforderlich! %%%%%%%%//
#define SLAVE_ADRESSE 0x50 //Die Slave-Adresse
+
//____________________________________________________________________________________//
  
//Funktion für Warteschaleifen
 
//Parameter: Wartezeit in Milisekunden
 
#include <util/delay.h>
 
void warte (int loop)  //loop: wartezeit in ms
 
{
 
int i;
 
for(i=0;i<loop;i++) _delay_ms(1);
 
}
 
  
int main (void)
+
//Bei zu alten AVR-GCC-Versionen werden die Interrupts anders genutzt, daher in diesem Fall mit Fehlermeldung abbrechen
{
+
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
//TWI als Slave mit Adresse slaveadr starten
+
#error "This library requires AVR-GCC 3.4.5 or later, update to newer AVR-GCC compiler !"
init_twi_slave(SLAVE_ADRESSE);
+
#endif
  
//tybuffer mit Werten füllen, die der Master auslesen soll
+
//Schutz vor unsinnigen Buffergroessen
for(uint8_t i=0;i<buffer_size;i++)
+
#if (i2c_buffer_size > 254)
{
+
#error Buffer zu gross gewaehlt! Maximal 254 Bytes erlaubt.
txbuffer[i]=10+i;
+
#endif
}
+
  
//Serielle Schnittstelle aktivieren
+
#if (i2c_buffer_size < 2)
uart_init((UART_BAUD_SELECT((BAUD),F_CPU)));
+
#error Buffer muss mindestens zwei Byte gross sein!
uart_puts("I2C-Test\r\n");
+
#endif
uart_puts("Teste I2C-Slave mit Adresse "); uart_puti(SLAVE_ADRESSE);
+
 
uart_puts("\r\n");
+
 
uart_puts("\r\n"); //Leerzeile
+
 
 +
#endif //#ifdef _TWISLAVE_H
 +
////Ende von twislave.h////
  
//in einer Endlosschleife den Inhalt der Buffer ausgeben
 
while(1)
 
{
 
uart_puts("rxbuffer\r\n");
 
for(uint8_t i=0;i<buffer_size;i++)
 
{
 
uart_puti(rxbuffer[i]);
 
uart_puts("\r\n");
 
}
 
uart_puts("\r\n");//leerzeile
 
uart_puts("txbuffer\r\n");
 
for(uint8_t i=0;i<buffer_size;i++)
 
{
 
uart_puti(txbuffer[i]);
 
uart_puts("\r\n");
 
}
 
uart_puts("\r\n");//leerzeile
 
warte(500);
 
} //end.while
 
} //end.main
 
 
</pre>
 
</pre>
  
 +
== Andere Bibliotheken ==
  
 +
* [http://www.jtronics.de/avr-projekte/library-i2c/twi-slave-avr http://www.jtronics.de/avr-projekte/library-i2c/twi-slave-avr]
 +
* [http://www.ermicro.com/blog/?p=1239 http://www.ermicro.com/blog/?p=1239]
  
 
[[Kategorie:Microcontroller]]
 
[[Kategorie:Microcontroller]]
 
[[Kategorie:Quellcode C]]
 
[[Kategorie:Quellcode C]]

Aktuelle Version vom 19. Oktober 2012, 18:12 Uhr

Programm für einen AVR mit TWI (Hardware-I2C)-Schnittstelle als Slave. Manchmal stellt sich die Aufgabe, mehrere AVRs per I2C zu vernetzen. Ein Beispiel ist die Erweiterung eines bestehenden Systems um einen leistungsstärkeren Controller. Dies ist etwa beim Asuro oder Yeti denkbar, um z.B. mehr Ein/Ausgänge oder Speicherplatz zu bekommen.

Man kann aber beim Bau eines Roboters auch von Anfang an auf ein System von mehreren vernetzten Controllern setzen. So kann man verschiedene Aufgabe, die weitgehend unabhängig voneinader gleichzeitg erledigt werden müssen, besser verteilen. Denkbar ist z.B. ein Controller für die Motorsteuerung, einer für die Sensorik, einer für Ein-und Ausgabe (wie LCD und Bedientaster),... , und ein zentraler Controller, der die Richtung vorgibt und alle anderen Controller mit Befehlen versorgt. Ein konkretes Beispiel ist der Roboterbausatz Nibo (siehe: [1]) von nicai-systems, der einen Atmel ATmega128 als Hauptcontroller und zwei Atmel ATtiny44 als Controller für die Motorsteuerung und die IR-Sensorik einsetzt. Die Kommunikation läuft mit 400 kHz über den I2C-Bus mit dem ATmega128 als Master. Schließlich kann ein entsprechend programmierter AVR auch als Ersatz für handelsübliche I2C-Bauteile dienen. Der kleinste AVR mit Hardware-I2C, der ATmega48, ist mit 1,30€ (Reichelt) billiger als viele normale I2C-ICs. Er kann bei entsprechender Programmierung z.B. die Aufgaben von zwei PCF8574 (8bit-Portexpander, 1,25€) und einem PCF8591 (4fach AD-Wandler, 2,35€) übernehmen und außerdem noch als I2C-EEPROM mit 256 Bytes dienen.

Das System ist als eine Art Dualport-RAM konzipiert, Master und Slave teilen sich also einen Speicherbereich und können darüber Daten austauschen. Die Ansteuerung läuft wie bei den bekannten I2C-EEPROMs. Der Master schreibt erst die Adresse, die er lesen bzw- schreiben will. Bei einem Schreibzugriff überträgt er nun die Daten. Bei einem Lesezugriff wird ein repeated start im Lesemodus durchgeführt, dann können die Daten byteweise eingelesen werden. Die Adressen werden dabei automatisch weitergezählt, sodass mehrere Bytes in Folge verarbeitet werden können.

Getestet wurde der Code für zwei Mega32 mit dem AVR Studio. Beachte: im AVR-Studio ist default ein ATmega128 eingestellt, unter Projekt -> Configuration Options kann das Device geändert werden, außerdem ist die Taktfrequenz des Controllers einzustellen. Im Projekt wird für den Master die Dateien twimaster.c und i2cmaster.h hinzugefügt, und für den Slave die twislave.c und twislave.h. Wenn stattdessen ein makefile verwendet wird, müssen dort unter SRC die c-Dateien eingetragen werden.

Die Verbindung zwischen Master und Slave erfolgt über die Pins SCL und SDA, außerdem muss ein gemeinsamer GND vorhanden sein. Abschlusswiderstände (4,7kOhm) nicht vergessen, am besten beim Master, siehe auch I2C-Definitionen.


Das folgende Programm (twislave.c) steuert das TWI (Hardware-I2C)-Interface eines AVRs als Slave an. Es müsste auf allen AVRs der Mega-Reihe funktionieren, die über eine TWI-Schnittstelle verfügen.

Master

Ein Testprogramm für den Master. Es wird die I2C-Master-Bibliothek von Peter Fleury verwendet. Es wird geprüft, ob der Slave bereit ist, dann werden drei Bytes erst in den Slave geschrieben, und anschließend wieder ausgelesen.


// I2C-Master-Routinen von Peter Fleury verwenden 
// siehe http://homepage.hispeed.ch/peterfleury/avr-software.html#libs
// Hier sind auch die Dateien: i2cmaster.h und twimaster.c zu finden, die benötigt werden
// Letztes Update des Codes 5. April 2010 durch HannoHupmann

#include <avr/io.h>
#include "i2cmaster.h"  
#define SLAVE_ADRESSE 0x50

uint8_t byte1 = 42;
uint8_t byte2 = 43;
uint8_t byte3 = 44;

int main(void)
{
  i2c_init();         // init I2C interface


if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum schreiben?
{
  i2c_write(0x00);  // Buffer Startadresse setzen 	
  i2c_write(byte1); // Drei Bytes schreiben...
  i2c_write(byte2); 
  i2c_write(byte3); 
  i2c_stop();       // Zugriff beenden
}
else
{
  /* Hier könnte eine Fehlermeldung ausgegeben werden... */
}



if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum lesen?
{
  i2c_write(0x00); //Buffer Startadresse zum Auslesen
  i2c_rep_start(SLAVE_ADRESSE+I2C_READ); //Lesen beginnen

  byte1= i2c_readAck(); // Bytes lesen...
  byte2= i2c_readAck();
  byte3= i2c_readNak(); // letztes Byte lesen, darum kein ACK
  i2c_stop();           // Zugriff beenden
} 
else
{
  /* Hier könnte eine Fehlermeldung ausgegeben werden... */
}

if ((byte1 != 42)||(byte2 != 43)||(byte3 != 44))
{
  /* Die Bytes wurden nicht korrekt übertragen und wieder gelesen! */ 
}
    for(;;);	
}

// Code wurde von HannoHupmann auf einem Mega32 getestet


Slave

Hier soll nun der Code behandelt werden der für den Slave-Controller notwendig ist. Der vorgestellte Code schreibt die Register des Slaves an die Serielle Schnittstelle, mit einem Terminal Programm kann man sich die Ausgabe ansehen und überprüfen ob alles korrekt funktioniert. Der Code ist wieder an den von Peter Fleury angelehnt.

Der Slave sollte ohne Master folgende Ausgabe liefern:

i2cdata: 
10
11
12
13
usw. 

Die Register werden der Reihe nach mit ihrem Wert angezeigt. Wird der Master hinzugefügt sollte nun folgende Ausgabe im Terminal erscheinen:

i2cdata: 
42
43
44
13
usw. 

Slave Testprogramm

Ein Testprogramm für den Slave. Es verwendet zur Ausgabe die UART-lib von P. Fleury, die auf derselben Homepage wie die TWI-Master-Lib zu finden ist.

/*
Testprogramm für den Slave 
Der Buffer wird mit Werten gefüllt. Dann wird er fortlaufend über die serielle Schnittstelle ausgegeben.
Nun kann man dort sehen, wenn der Master einen Wert ändert
*/
#include <util/twi.h> 	    //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
#include <avr/interrupt.h>  //dient zur Behandlung der Interrupts
#include <stdint.h> 	    //definiert den Datentyp uint8_t
#include "twislave.h"
#include "uart.h"           //UART-Lib von P- Fleury
#include <stdlib.h>         //nötig für Zahlumwandlung mit itoa
#include <util/delay.h>


#define BAUD 9600 //Baudrate
#define SLAVE_ADRESSE 0x50 //Die Slave-Adresse

//Hilfsfunktion, um Zahlen über das UART auszugeben
void uart_puti( const int val )
{
    char buffer[8];
    uart_puts( itoa(val, buffer, 10) );

}

int main (void)
{
//TWI als Slave mit Adresse slaveadr starten
init_twi_slave(SLAVE_ADRESSE);

//i2cdatamit Werten füllen, die der Master auslesen und ändern kann
for(uint8_t i=0;i<i2c_buffer_size;i++)
	{
		i2cdata[i]=10+i;
	}

//Serielle Schnittstelle aktivieren
uart_init((UART_BAUD_SELECT((BAUD),F_CPU)));
uart_puts("I2C-Test\r\n");
uart_puts("Teste I2C-Slave mit Adresse "); uart_puti(SLAVE_ADRESSE);
uart_puts("\r\n");
uart_puts("\r\n"); //Leerzeile

//in einer Endlosschleife den Inhalt der Buffer ausgeben
while(1) 
{
	uart_puts("i2cdata:\r\n");
	for(uint8_t i=0;i<i2c_buffer_size;i++)
		{
			uart_puti(i2cdata[i]);
			uart_puts("\r\n");
		}
	uart_puts("\r\n");//leerzeile
_delay_ms(500);
} //end.while
} //end.main


Slave sourcecode

Die twislave.c für den Slave. Stand: 05.09.2012

#include <util/twi.h> 	    //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
#include <avr/interrupt.h>  //dient zur Behandlung der Interrupts
#include <stdint.h>         //definiert den Datentyp uint8_t       
#include "twislave.h"

//%%%%%%%% Globale Variablen, die vom Hauptprogramm genutzt werden %%%%%%%%
/*Der Buffer, in dem die Daten gespeichert werden. 
Aus Sicht des Masters läuft der Zugrif auf den Buffer genau wie bei einem I2C-EEPROm ab.
Für den Slave ist es eine globale Variable
*/
volatile uint8_t buffer_adr; //"Adressregister" für den Buffer

/*Initaliserung des TWI-Inteface. Muss zu Beginn aufgerufen werden, sowie bei einem Wechsel der Slave Adresse
Parameter adr: gewünschte 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 müssen verschiedene Bitmuster in TWCR geschreiben werden(siehe Tabellen im Datenblatt!). 
//Makros für 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);

//switch to the non adressed slave mode...
#define TWCR_RESET TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(1<<TWSTO)|(0<<TWWC);  


/*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;

switch (TW_STATUS) //TWI-Statusregister prüfen und nötige Aktion bestimmen 
{

// Slave Receiver 

case TW_SR_SLA_ACK: // 0x60 Slave Receiver, Slave wurde adressiert	
	TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach senden
	buffer_adr=0xFF; //Bufferposition ist undefiniert
break;
	
case TW_SR_DATA_ACK: // 0x80 Slave Receiver, ein Datenbyte wurde empfangen
	data=TWDR; //Empfangene Daten auslesen
	if (buffer_adr == 0xFF) //erster Zugriff, Bufferposition setzen
		{
			//Kontrolle ob gewünschte Adresse im erlaubten bereich
			if(data<i2c_buffer_size+1)
				{
					buffer_adr= data; //Bufferposition wie adressiert setzen
				}
			else
				{
					buffer_adr=0; //Adresse auf Null setzen. Ist das sinnvoll? TO DO!
				}				
			TWCR_ACK;	// nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
		}
	else //weiterer Zugriff, nachdem die Position im Buffer gesetzt wurde. NUn die Daten empfangen und speichern
		{
		
			if(buffer_adr<i2c_buffer_size+1)
				{
						i2cdata[buffer_adr]=data; //Daten in Buffer schreibe	
				}
			buffer_adr++; //Buffer-Adresse weiterzählen für nächsten Schreibzugriff
			TWCR_ACK;	
		}
break;


//Slave transmitter

case TW_ST_SLA_ACK: //0xA8 Slave wurde im Lesemodus adressiert und hat ein ACK zurückgegeben.
	//Hier steht kein break! Es wird also der folgende Code ebenfalls ausgeführt!
	
case TW_ST_DATA_ACK: //0xB8 Slave Transmitter, Daten wurden angefordert

	if (buffer_adr == 0xFF) //zuvor keine Leseadresse angegeben! 
		{
			buffer_adr=0;
		}	
		
	if(buffer_adr<i2c_buffer_size+1)	
		{
			TWDR = i2cdata[buffer_adr]; //Datenbyte senden
			buffer_adr++; //bufferadresse für nächstes Byte weiterzählen
		}
	else
		{
			TWDR=0; //Kein Daten mehr im Buffer
		}
	TWCR_ACK;
break;
case TW_SR_STOP:
            TWCR_ACK;
        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
default: 	
    TWCR_RESET;
break;
	
} //end.switch (TW_STATUS)
} //end.ISR(TWI_vect)

////Ende von twislave.c////

Slave header

Die zugehörige Headerdatei twislave.h für den Slave. Stand: 05.09.2012


#ifndef _TWISLAVE_H
#define _TWISLAVE_H

#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


/** 
 *  @defgroup twislave TWI-Slave
 *  @code #include "twislave.h" @endcode
 * 
 *  @brief Betrieb eines AVRs mit Hardware-TWI-Schnittstelle als Slave.
 *  Zu Beginn muss init_twi_slave mit der gewuenschten Slave-Adresse als
 *  Parameter aufgerufen werden.
 *
 * Der Datenaustausch mit dem Master erfolgt ueber den Buffer i2cdata, 
 * auf den von Master und Slave zugegriffen werden kann. 
 * Dies ist fuer den Slave eine globale Variable (Array aus uint8_t). 

 * Der Zugriff durch den Master erfolgt aehnlich wie bei einem
 * normalen I2C-EEPROM.
 * Man sendet zunaechst die Bufferposition, an die man schreiben will,
 * und dann die Daten. 
 * Die Bufferposition wird automatisch hochgezaehlt, sodass man mehrere
 * Datenbytes hintereinander schreiben kann, ohne jedesmal die 
 * Bufferadresse zu schreiben.
 *
 * Um vom Master aus zu lesen, uebertraegt man zunaechst in einem 
 * Schreibzugriff die gewuenschte Bufferposition und liest dann nach
 * einem repeated start die Daten aus. Die Bufferposition wird 
 * automatisch hochgezaehlt, sodass man mehrere Datenbytes
 * hintereinander lesen kann, ohne jedesmal die Bufferposition zu
 * schreiben.
 *
 * Abgefangene Fehlbedienung durch den Master:
 * - Lesen ueber die Grenze des txbuffers hinaus
 * - Schreiben ueber die Grenzen des rxbuffers hinaus
 * - Angabe einer ungueltigen Schreib/Lese-Adresse
 * - Lesezugriff, ohne vorher Leseadresse geschrieben zu haben
 * 
 *  @author uwegw
 */

/*@{*/

//%%%%%%%% von Benutzer konfigurierbare Einstellungen %%%%%%%%
/**@brief Groesse des Buffers in Byte (2..254) */
#define i2c_buffer_size 10// I2C_REG_ANZAHL 254 Hier kann eingestellt werden wieviele Register ausgegeben werden


//%%%%%%%% Globale Variablen, die vom Hauptprogramm genutzt werden %%%%%%%%
/**@brief Der Buffer, in dem die Daten gespeichert werden.
 * Aus Sicht des Masters laeuft der Zugrif auf den Buffer genau wie
 *  bei einem I2C-EEPROm ab.
 * Fuer den Slave ist es eine globale Variable
*/
volatile uint8_t i2cdata[i2c_buffer_size];


/**@brief Initaliserung des TWI-Inteface. Muss zu Beginn aufgerufen werden, sowie bei einem Wechsel der Slave Adresse
 * @param adr gewuenschte Slave-Adresse */
void init_twi_slave(uint8_t adr);

//%%%%%%%% ab hier sind normalerweise keine weiteren Aenderungen erforderlich! %%%%%%%%//
//____________________________________________________________________________________//


//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 (i2c_buffer_size > 254)
	#error Buffer zu gross gewaehlt! Maximal 254 Bytes erlaubt.
#endif

#if (i2c_buffer_size < 2)
	#error Buffer muss mindestens zwei Byte gross sein!
#endif



#endif //#ifdef _TWISLAVE_H
////Ende von twislave.h////

Andere Bibliotheken