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


Zeile 39: Zeile 39:
 
</pre>
 
</pre>
  
Die twislave.c für den Slave. Stand: 23.03.07
+
Die twislave.c für den Slave. Stand: 25.03.07
 
<pre>
 
<pre>
 
#ifndef _TWISLAVE_H
 
#ifndef _TWISLAVE_H
Zeile 64: Zeile 64:
 
letze Änderungen:  
 
letze Änderungen:  
 
23.03.07 Makros für TWCR eingefügt. Abbruch des Sendens, wenn der TXbuffer komplett gesendet wurde.  
 
23.03.07 Makros für TWCR eingefügt. Abbruch des Sendens, wenn der TXbuffer komplett gesendet wurde.  
+
24.03.07 verbotene Buffergrößen abgefangen
 +
 
 +
 
 +
Abgefangene Fehlbedienung durch den Master:
 +
- Lesen über die Grenze des txbuffers hinaus
 +
- Schreiben über die Grenzen des rxbuffers hinaus
 +
- Angabe einer ungültigen Schreib/Lese-Adresse
 +
- Lesezuggriff, ohne vorher Leseadresse geschrieben zu haben
 +
 
 +
 
 
  */  
 
  */  
 
   
 
   
Zeile 72: Zeile 81:
 
//%%%%%%%% von Benutzer konfigurierbare Einstellungen %%%%%%%%
 
//%%%%%%%% von Benutzer konfigurierbare Einstellungen %%%%%%%%
  
#define buffer_size 8 //Größe der Buffer in Byte (1..255)
+
#define buffer_size 8 //Größe der Buffer in Byte (2..254)
  
  
Zeile 94: Zeile 103:
  
  
//%%%%%%%% ab hier sind normalerweise keine weiteren Änderungen erforderlich! %%%%%%%%
+
//%%%%%%%% ab hier sind normalerweise keine weiteren Änderungen erforderlich! %%%%%%%%//
//_____________________________________________________________________________________________
+
//____________________________________________________________________________________//
  
 
#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
Zeile 102: Zeile 111:
 
//Bei zu alten AVR-GCC-Versionen werden die Interrupts anders genutzt, daher in diesem Fall mit Fehlermeldung abbrechen
 
//Bei zu alten AVR-GCC-Versionen werden die Interrupts anders genutzt, daher in diesem Fall mit Fehlermeldung abbrechen
 
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
 
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
#error "This library requires AVR-GCC 3.4.5 or later, update to newer AVR-GCC compiler !"
+
#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
 
#endif
  
  
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
Zeile 113: Zeile 131:
 
void init_twi_slave (uint8_t adr)
 
void init_twi_slave (uint8_t adr)
 
{
 
{
TWAR= adr; //Adresse setzen
+
TWAR= adr; //Adresse setzen
TWCR|= (1<<TWEA) | (1<<TWEN)|(1<<TWIE);
+
TWCR &= ~(1<<TWSTA)|(1<<TWSTO);
TWCR &= ~(1<<TWSTA)|(1<<TWSTO);
+
TWCR|= (1<<TWEA) | (1<<TWEN)|(1<<TWIE);
buffer_adr=0xFF;   
+
buffer_adr=0xFF;   
sei();
+
sei();
 
}
 
}
  
Zeile 130: Zeile 148:
 
//switched to the non adressed slave mode...
 
//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);   
 
#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!
 +
  
 
/*ISR, die bei einem Ereignis auf dem Bus ausgelöst wird. Im Register TWSR befindet sich dann  
 
/*ISR, die bei einem Ereignis auf dem Bus ausgelöst wird. Im Register TWSR befindet sich dann  
Zeile 136: Zeile 157:
 
ISR (TWI_vect)   
 
ISR (TWI_vect)   
 
{
 
{
uint8_t data=0; //Speicher für das gerade empfangene Datenbyte
+
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  
Zeile 149: Zeile 171:
 
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
  {
 
buffer_adr= data; //Bufferposition wie adressiert setzen
 
      TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
 
  }
 
else //weiterer Zugriff, Daten empfangen
 
      {
 
rxbuffer[buffer_adr]=data; //Daten in Buffer schreiben
 
buffer_adr++; //Buffer-Adresse weiterzählen für nächsten Schtreibzugriff
 
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
+
//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. Ist das sinnvoll?
 +
}
 +
TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
 
}
 
}
else   //es kann nur noch ein Byte kommen, dann ist der Buffer voll
+
else //weiterer Zugriff, Daten empfangen
 
{
 
{
+
rxbuffer[buffer_adr]=data; //Daten in Buffer schreiben
TWCR_NACK;//letztes Byte lesen, dann NACK, um vollen Buffer zu signaliseren
+
buffer_adr++; //Buffer-Adresse weiterzählen für nächsten Schreibzugriff
 +
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
 +
}
 +
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;
 
break;
  
 
+
case TW_ST_SLA_ACK: //?!?
case TW_ST_SLA_ACK:
+
 
case TW_ST_DATA_ACK: //0xB8 Slave Transmitter, weitere Daten wurden angefordert
 
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  
 
TWDR = txbuffer[buffer_adr]; //Datenbyte senden  
 
buffer_adr++; //bufferadresse für nächstes Byte weiterzählen
 
buffer_adr++; //bufferadresse für nächstes Byte weiterzählen
 
 
if(buffer_adr<(buffer_size-1)) //im Buffer ist mehr als ein Byte, das gesendet werden kann
 
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
+
TWCR_ACK; //nächstes Byte senden, danach ACK erwarten
}
+
}
 
else
 
else
{
+
{
TWCR_NACK; //letztes Byte senden, danach NACK erwarten
+
TWCR_NACK; //letztes Byte senden, danach NACK erwarten
}
+
}
 
+
+
 
break;
 
break;
 
  
 
case TW_ST_DATA_NACK: //0xC0 Keine Daten mehr gefordert  
 
case TW_ST_DATA_NACK: //0xC0 Keine Daten mehr gefordert  
Zeile 193: Zeile 222:
 
case TW_ST_LAST_DATA: //0xC8  Last data byte in TWDR has been transmitted (TWEA = “0”); ACK has been received
 
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
 
case TW_SR_STOP: // 0xA0 STOP empfangen
default:  
+
default:
//Übertragung beenden, warten bis zur nächsten Adressierung
+
    TWCR_RESET; //Übertragung beenden, warten bis zur nächsten Adressierung
    TWCR_RESET;
+
 
break;
 
break;
  

Version vom 25. März 2007, 21:39 Uhr

Programm für einen AVR mit TWI (Hardware-I2C)-Schnittstelle als Slave.

Noch nicht 100% getestet! Es können noch Fehler enthalten sein. Bei mir (uwegw) funktioniert das System problemlos. Es müssten aber noch diverse Sonderfälle wie falsch adressierte Bufferzugriffe abgefangen werden... Also insgesamt eher Alpha-Status!

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.

Schließlich kann ein entsprechend programmierter AVR auch als Ersatz für handelsübliche I2C-Bauteile dienen. Der kleinste AVR mit Hardware-I2C, der Mega8, ist mit 1,70€ (Reichelt) billiger als viele normale I2C-ICs. Ein Mega8 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 ein TWI-Schnittstelle verfügen.

Das System ist als ein Art Dualport-RAM konzipiert, der Master und der Slave teilen sich also einen Speicherbereich und können darüber Daten austauschen.


Ein Codeschnipsel 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 die ersten drei Bytes aus dem txbuffer des Slaves gelesen und in byte0..2 abgespeichert.

#include "i2cmaster.c"  //I2C-Master-Routinen von Peter Fleury verwenden (siehe http://homepage.hispeed.ch/peterfleury/avr-software.html#libs)
#define SLAVE_ADRESSE 0x50
uint8_t byte0;
uint8_t byte1;
uint8_t byte2;

      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

            byte0= i2c_readAck();
            byte1= i2c_readAck();
            byte2= i2c_readAck();
         i2c_stop();
      } 

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

#ifndef _TWISLAVE_H
#define _TWISLAVE_H

/*
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
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. 
rxbuffer und txbuffer sind globale Variablen (Array aus uint8_t). 
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)
Status: Testphase, keine Garantie für ordnungsgemäße Funktion! 
letze Änderungen: 
23.03.07 Makros für TWCR eingefügt. Abbruch des Sendens, wenn der TXbuffer komplett gesendet wurde. 
24.03.07 verbotene Buffergrößen abgefangen


Abgefangene Fehlbedienung durch den Master:
- Lesen über die Grenze des txbuffers hinaus
- Schreiben über die Grenzen des rxbuffers hinaus
- Angabe einer ungültigen Schreib/Lese-Adresse
- Lesezuggriff, ohne vorher Leseadresse geschrieben zu haben


 */ 
 



//%%%%%%%% von Benutzer konfigurierbare Einstellungen %%%%%%%%

#define buffer_size 8 //Größe der Buffer in Byte (2..254)


//%%%%%%%% Globale Variablen, die vom Hauptprogramm genutzt werden %%%%%%%%

/*Der Buffer, in dem die empfangenen Daten gespeichert werden. Der Slave funktioniert ähnlich  wie ein normales
 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*/
volatile uint8_t rxbuffer[buffer_size];

/*Der Sendebuffer, der vom Master ausgelesen werden kann.*/
volatile uint8_t txbuffer[buffer_size];


//%%%%%%%% Funktionen, die vom Hauptprogramm aufgerufen werden können %%%%%%%%
 
/*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);



//%%%%%%%% ab hier sind normalerweise keine weiteren Änderungen erforderlich! %%%%%%%%//
//____________________________________________________________________________________//

#include <util/twi.h> //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR


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


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);
//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!


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

case TW_SR_SLA_ACK: // 0x60 Slave Receiver, wurde adressiert	
	TWCR_ACK; // nächstes 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 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. Ist das sinnvoll?
				}				
			TWCR_ACK;	// nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
		}
	else //weiterer Zugriff, Daten empfangen
		{
			rxbuffer[buffer_adr]=data; //Daten in Buffer schreiben
			buffer_adr++; //Buffer-Adresse weiterzählen für nächsten Schreibzugriff
			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
				}
			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 für nächstes Byte weiterzählen
	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
		}
	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; //Übertragung beenden, warten bis zur nächsten Adressierung
break;

	
} //end.switch (TW_STATUS)
} //end.ISR(TWI_vect)


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