Forum: Mikrocontroller und Digitale Elektronik I2C-Anfänger


von Maddin S. (schrank)


Lesenswert?

Hallo Leute!

Möchte gerne mittels I2C eine Verbindung zwischen zwei Atmegas (16 und 
32) aufbauen. Leider klappt momentan gar nichts:
Für den Master hab ich die lib von Peter Fleury verwendet. Ich hab daran 
nichts verändern, sollte eigentlich so klappen.
Beim Slave bin ich mir da gar net sicher: Der Quellcode ist von 
Roboternetz (Überschrift: TWI Slave mit avr-gcc). Bin mir net sicher ob 
des so klappt...
Mein Problem besteht momentan einfach darin, dass ich zwei Unbekannte 
habe, was die Fehlersuche recht schwierig für mich gestaltet.


Hat hier irgendjemand eine einfache Routine für mich, mit dem ich zwei 
AVR´s mittels I2C kommunizieren lassen kann?Also eine für Master und 
eine für den Slave.



Vielen Dank!

von Tobias K. (kurzschluss81)


Lesenswert?

Hast du an die Pullupwiderstände gedacht die du zwischen SDA (Daten) und 
VCC und CSL(Clock) und VCC schalten mußt?

von Maddin S. (schrank)


Lesenswert?

ja hab ich. wurden zu 2k2 dimensioniert.

von Maddin S. (schrank)


Lesenswert?

Stelle mal meinen Quellcode online, vllt findet ja jemand den Fehler.
Nochmal ne genau Beschreibung was ich in diesem Testprogramm machen 
will:
Habe zwei PollinFunkBoards, die ich über ein IDE-Kabel 
zusammengeschalten habe. Am 3.Stecker des Kabels hab ich die Pullups 
reingeschalten (jeweils 2k2). Diese Boards haben am PortD Pin6 eine LED. 
Ich möchte jetzt dem Slave ein Byte schicken, welches diese LED 
einschaltet (also 0x01000000). Der Slave soll die Adresse 0x5 haben. Die 
Master Lib ist von peter Fleury. Die Slave lib von 
[[http://www.roboternetz.de/wissen/index.php/TWI_Slave_mit_avr-gcc]] 
rauskopiert. Leider tut sichnach dem Hochladen auf den Controller gar 
nichts.

Hier mein Quellcode für den Master:
1
#include <avr/io.h>
2
#include "i2cmaster.h"
3
#define F_CPU 16000000UL
4
#include <util/delay.h>
5
6
7
8
9
10
int main(void)
11
{
12
13
     
14
15
     i2c_init();                             // initialize I2C library
16
17
   while(1)
18
   {
19
20
   i2c_start_wait(0b1010);     /*101 für Adresse 5 und der letzte Nuller für schreiben*/
21
     i2c_write(0x01);       /*Bufferadresse: möchte in rxbuffer[1] des Slaves schreiben*/
22
                    
23
     i2c_write(0b01000000);                        /*Schreibe 0b01000000 in rxbuffer[1],also Pin 6 wird gesetzt*/
24
     i2c_stop();
25
    } 
26
27
/*Ablauf so korrekt???*/
28
29
}

Hier die Routine für den Slave
1
#include <avr/io.h>
2
#include "twislave.h" /*Im Beispiel von Roboternetz als "twislave.c" eingebunden, bringt aber Fehler beim Compilieren*/
3
#define F_CPU 16000000UL
4
#include <util/delay.h>
5
6
7
8
9
10
int main()
11
{
12
DDRD=0b11111111;
13
init_twi_slave (0b101);/*Slave Adressse soll 0x5 sein*/
14
15
16
17
while(1)
18
{
19
20
PORTD=rxbuffer[1];/*Soll LED einschalten, die sich am PIND6 befindet*/
21
}
22
23
return 0;
24
25
}

Findet hier jemand einen Fehler!
Wäre echt super, wenn mir jemandhelfen würde, bin am verzweifeln!!

Vielen Dank!

von Maddin S. (schrank)


Lesenswert?

Keiner der mir weiter helfen kann?
Was ich bis jetzt rausgefunden habe:
Die Interruptroutine des Slaves wird nicht aufgerufen. Hab da einen 
Befehl für ne angeschlossene LED reingetan und LED leuchtet zu keinem 
Zeitpunkt.
Außderdem ist mir ein vermeindlicher Fehler bei der Slave lib 
aufgefallen:
TWAR= adr; laut Datenblatt darf das LSB aber nicht für die Adresse 
benutzt werden. Hab das umgeändert in: TWAR= (adr<<1);
trotzdem kein Erfolg:)

von Otto (Gast)


Lesenswert?

> Hier die Routine für den Slave

wenn das alles an Programm ist, kann der Slave nicht funktionieren.....

Wahrscheinlich hast Du zu wenig von der "Slave lib " kopiert.

Otto

von Maddin S. (schrank)


Lesenswert?

da hast du natürlich recht, dass das nicht alles ist:
ich hab beim slave hier eine twislave.h mit eingebunden in die ich den 
Code von roboternetz (siehe link oben) kopiert habe

von Maddin S. (schrank)


Lesenswert?

Hier mal die Routine für den Slave:
1
#ifndef _TWISLAVE_H
2
#define _TWISLAVE_H
3
4
#include <util/twi.h> //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
5
#include <avr/interrupt.h> //dient zur behandlung der Interrupts
6
#include <stdint.h> //definiert den Datentyp uint8_t
7
 
8
9
10
11
//%%%%%%%% von Benutzer konfigurierbare Einstellungen %%%%%%%%
12
13
#define buffer_size 8 //Größe der Buffer in Byte (2..254)
14
15
16
//%%%%%%%% Globale Variablen, die vom Hauptprogramm genutzt werden %%%%%%%%
17
18
/*Der Buffer, in dem die empfangenen Daten gespeichert werden. Der Slave funktioniert ähnlich  wie ein normales
19
 Speicher-IC [I2C-EEPROM], man sendet die Adresse, an die man schreiben will, dann die Daten, die interne Speicher-Adresse
20
 wird dabei automatisch hochgezählt*/
21
volatile uint8_t rxbuffer[buffer_size];
22
23
/*Der Sendebuffer, der vom Master ausgelesen werden kann.*/
24
volatile uint8_t txbuffer[buffer_size];
25
26
27
//%%%%%%%% Funktionen, die vom Hauptprogramm aufgerufen werden können %%%%%%%%
28
 
29
/*Initaliserung des TWI-Inteface. Muss zu Beginn aufgerufen werden, sowie bei einem Wechsel der Slave Adresse
30
Parameter: adr: gewünschte Slave-Adresse*/
31
void init_twi_slave (uint8_t adr);
32
33
34
35
//%%%%%%%% ab hier sind normalerweise keine weiteren Änderungen erforderlich! %%%%%%%%//
36
//____________________________________________________________________________________//
37
38
#include <util/twi.h> //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
39
40
41
//Bei zu alten AVR-GCC-Versionen werden die Interrupts anders genutzt, daher in diesem Fall mit Fehlermeldung abbrechen
42
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
43
  #error "This library requires AVR-GCC 3.4.5 or later, update to newer AVR-GCC compiler !"
44
#endif
45
46
//Schutz vor unsinnigen Buffergrößen
47
#if (buffer_size > 254)
48
  #error Buffer zu groß gewählt! Maximal 254 Bytes erlaubt.
49
#endif
50
51
#if (buffer_size < 2)
52
  #error Buffer muss mindestens zwei Byte groß sein!
53
#endif
54
55
56
volatile uint8_t buffer_adr; //"Adressregister" für den Buffer
57
58
/*Initaliserung des TWI-Inteface. Muss zu Beginn aufgerufen werden, sowie bei einem Wechsel der Slave Adresse
59
Parameter: adr: gewünschte Slave-Adresse
60
*/
61
void init_twi_slave (uint8_t adr)
62
{
63
  /**TWAR= adr; Adresse setzen. vermeindlicher Bug!!! Bit0 ist nicht Bestandteil der Adresse!!**/
64
  TWAR= (adr<<1);
65
  TWCR &= ~(1<<TWSTA)|(1<<TWSTO);
66
  TWCR|= (1<<TWEA) | (1<<TWEN)|(1<<TWIE);   
67
  buffer_adr=0xFF;  
68
  sei();
69
  
70
}
71
72
73
//Je nach Statuscode in TWSR müssen verschiedene Bitmuster in TWCR geschreiben werden(siehe Tabellen im Datenblatt!). 
74
//Makros für die verwendeten Bitmuster:
75
76
//ACK nach empfangenen Daten senden/ ACK nach gesendeten Daten erwarten
77
#define TWCR_ACK TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);  
78
//NACK nach empfangenen Daten senden/ NACK nach gesendeten Daten erwarten     
79
#define TWCR_NACK TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);
80
//switched to the non adressed slave mode...
81
#define TWCR_RESET TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(0<<TWWC);  
82
83
//Die Bitmuster für TWCR_ACK und TWCR_RESET sind gleich. Dies ist kein Fehler und dient nur der Übersicht!
84
85
86
/*ISR, die bei einem Ereignis auf dem Bus ausgelöst wird. Im Register TWSR befindet sich dann 
87
ein Statuscode, anhand dessen die Situation festgestellt werden kann.
88
*/
89
ISR (TWI_vect)  
90
{
91
uint8_t data=0;
92
PORTD=32;/*Test: rechte LED sollte leuchten*/
93
94
95
switch (TW_STATUS) //TWI-Statusregister prüfen und nötige Aktion bestimmen 
96
{
97
98
case TW_SR_SLA_ACK: // 0x60 Slave Receiver, wurde adressiert  
99
  TWCR_ACK; // nächstes Datenbyte empfangen, ACK danach
100
  buffer_adr=0xFF; //Bufferposition ist undefiniert
101
break;
102
  
103
case TW_SR_DATA_ACK: // 0x80 Slave Receiver,Daten empfangen
104
  data=TWDR; //Empfangene Daten auslesen
105
  if (buffer_adr == 0xFF) //erster Zugriff, Bufferposition setzen
106
    {
107
      
108
      //Kontrolle ob gewünschte Adresse im erlaubten bereich
109
      if(data<=buffer_size)
110
        {
111
          buffer_adr= data; //Bufferposition wie adressiert setzen
112
        }
113
      else
114
        {
115
        buffer_adr=0; //Adresse auf Null setzen. Ist das sinnvoll?
116
        }        
117
      TWCR_ACK;  // nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
118
    }
119
  else //weiterer Zugriff, Daten empfangen
120
    {
121
      rxbuffer[buffer_adr]=data; //Daten in Buffer schreiben
122
      buffer_adr++; //Buffer-Adresse weiterzählen für nächsten Schreibzugriff
123
      if(buffer_adr<(buffer_size-1)) //im Buffer ist noch Platz für mehr als ein Byte
124
        {
125
          TWCR_ACK;// nächstes Datenbyte empfangen, ACK danach, um nächstes Byte anzufordern
126
        }
127
      else   //es kann nur noch ein Byte kommen, dann ist der Buffer voll
128
        {
129
          TWCR_NACK;//letztes Byte lesen, dann NACK, um vollen Buffer zu signaliseren
130
        }
131
    }
132
break;
133
134
case TW_ST_SLA_ACK: //?!?
135
case TW_ST_DATA_ACK: //0xB8 Slave Transmitter, weitere Daten wurden angefordert
136
137
  if (buffer_adr == 0xFF) //zuvor keine Leseadresse angegeben! 
138
    {
139
      buffer_adr=0;
140
    }  
141
  TWDR = txbuffer[buffer_adr]; //Datenbyte senden 
142
  buffer_adr++; //bufferadresse für nächstes Byte weiterzählen
143
  if(buffer_adr<(buffer_size-1)) //im Buffer ist mehr als ein Byte, das gesendet werden kann
144
    {
145
      TWCR_ACK; //nächstes Byte senden, danach ACK erwarten
146
    }
147
  else
148
    {
149
      TWCR_NACK; //letztes Byte senden, danach NACK erwarten
150
    }
151
break;
152
153
case TW_ST_DATA_NACK: //0xC0 Keine Daten mehr gefordert 
154
case TW_SR_DATA_NACK: //0x88 
155
case TW_ST_LAST_DATA: //0xC8  Last data byte in TWDR has been transmitted (TWEA = “0”); ACK has been received
156
case TW_SR_STOP: // 0xA0 STOP empfangen
157
default:   
158
    TWCR_RESET; //Übertragung beenden, warten bis zur nächsten Adressierung
159
break;
160
161
  
162
} //end.switch (TW_STATUS)
163
} //end.ISR(TWI_vect)
164
165
166
#endif //#ifdef _TWISLAVE_H
167
////Ende von twislave.c////

von Maddin S. (schrank)


Lesenswert?

Alles was ich benötige ist eine Funktionierendes Beispiel um 2 uC per 
I2C kommunizieren zu lassen....
Man hört von i2c sehr viel interessante Sachen, gerade im 
Roboterbereich, aber funktionierende Routinen findet man (ich) nicht:(

von Otto (Gast)


Lesenswert?

Doch - in der Codesammlung - das Assemblerbeispiel z. B. funktioniert 
einwandfrei.

Das C-Besipiel: Beitrag "AVR TWI Master und Slave Funtionen in C" habe ich 
nicht getestet.

Evtl. versuchst Du es mal damit.

Gruss Otto

von Maddin S. (schrank)


Lesenswert?

Assembler möcht ich nicht nehmen, da ich neben dem Bus noch viele 
weitere Funktionen implemetieren muss.Aber Danke schön!

Im Moment kenn ich mich gar nimmer aus:
Dein vorgeschlagenes Beispiel, löst das den ein Interrupt beim Slave 
aus?





Die Verzweiflung zwingt mich jetzt ins Bett, vllt versteh ich morgen 
früh wieder mehr.

Trotzdem vielen Dank!

von Otto (Gast)


Lesenswert?

Du schriebst, dass Du keine Beispiele findest. Daher der Hinweis auf die 
Codesammlung. Evtl. vergleichst Du den anderen Slave mit Deinem, um den 
Fehler einzugrenzen.

Die Slave-Adresse 0x5 ist mir persönlich ein wenig suspekt.

Otto

von Maddin S. (schrank)


Lesenswert?

Bin jetzt einen kleinen Schritt weitergekommen:
Der Befehl "i2c_start_wait()" wartet ja anscheinend solange, bis sich 
der anzusprechende Slave meldet.
ich hab nach diesem Befehl eine Portausgabe gemacht und eine LED 
angesprochen. Diese leuchtet auch tatsächlich, wenn ich die richtige 
Adresse eingeben (aber auch wenn falsche adresse und sehr oft reset bei 
master und slave, wohl aber eher dann ein zufälliges verhalten).
Das heißt also: Der Slave reagiert schon. Nur die interruptroutine wird 
immer noch nicht aufgerufen...??

Welche Bedingungen müssen eigentlich herrschen, damit diese aufgerufen 
wird? Ausm Datenblatt werd ich momentan net schlau .



Vielen Dank!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.