Forum: Mikrocontroller und Digitale Elektronik 2 Atmega8 über I²C / TWI verbinden


von Markus Schmitz (Gast)


Lesenswert?

Schönen guten Abend miteinander,

bin total der Anfänger und freue mich schon auf den Shit-Storm, der auf 
diesen Beitrag folgt. Habe aber folgendes Problem:

ich möchte 2 myAVR Boards mit je einem Atmega8A  über I²C (oder auch TWI 
genannt ?) miteinander verbinden.
Dabei soll das eine Board als Master arbeiten und das andere als Slave.
Habe jetzt schon mehrer Beispielprogramme probiert, doch keins 
funktioniert so auf Anhieb.

Hat irgendwer ein lauffähiges C-Programm, welches die Kommunikation 
zwischen Master und Slave aufbaut?


Vielen Dank und Gruß Markus

von Pepe (Gast)


Lesenswert?

Hi.
Programm hab ich keins, aber einige Tipps.

Erstmal vorab: Wenn Du totaler Anfänger bist, warum benutzt dann I2C? 
Ich würde mich dann lieber mit SPI beschäftigen. Braucht eine Verbindung 
mehr.
Kann aber wesentlich leichter debugged werden (elektrisch und in 
Firmware).
Und ist auch nur eine serielle Übertragung, aber ohne Adressierung, 
Half-Duplex, Open-Drain, etc...

I2C hat einige Fallstricke:
- Hast Du die PullUps auf beiden Leistungen? Sonst funktioniert zwar 
irgendwas aber sicherlich kein stabile Kommunikation.
- Fange mit möglichst kleiner Übertragungsfrequenz an.
- Hat der Slave die richtige Adresse ? Schickt der Master die richtige 
Adresse ?
- Bekommt Dein Master ein Ack am Ende des Pakets? Wenn nein, stimmt 
wahrscheinlich auf Slave-Seite was nicht.

So. Mehr gibt meine Glaskugel gerade nicht her. Mit so wenig Infos.

Vielleicht hilft es ja trotzdem. Kannst ja mal Deinen besten Source 
posten. Dann man dir sicher konkreter helfen.

Pepe.

von Matze (Gast)


Lesenswert?

Finde I2C hierfür auch eher aufwendig.

Hatte sowas schonmal, würde den UART nutzen.
Controller A (Master) schickte Controller B (PWM-IO-Erweiterung) PWM 
Tastverhältnisse.

von Markus Schmitz (Gast)


Lesenswert?

Pepe schrieb:
> Hi.
> Programm hab ich keins, aber einige Tipps.
>
> Erstmal vorab: Wenn Du totaler Anfänger bist, warum benutzt dann I2C?
> Ich würde mich dann lieber mit SPI beschäftigen. Braucht eine Verbindung
> mehr.
> Kann aber wesentlich leichter debugged werden (elektrisch und in
> Firmware).
> Und ist auch nur eine serielle Übertragung, aber ohne Adressierung,
> Half-Duplex, Open-Drain, etc...
>
> I2C hat einige Fallstricke:
> - Hast Du die PullUps auf beiden Leistungen? Sonst funktioniert zwar
> irgendwas aber sicherlich kein stabile Kommunikation.
> - Fange mit möglichst kleiner Übertragungsfrequenz an.
> - Hat der Slave die richtige Adresse ? Schickt der Master die richtige
> Adresse ?
> - Bekommt Dein Master ein Ack am Ende des Pakets? Wenn nein, stimmt
> wahrscheinlich auf Slave-Seite was nicht.
>
> So. Mehr gibt meine Glaskugel gerade nicht her. Mit so wenig Infos.
>
> Vielleicht hilft es ja trotzdem. Kannst ja mal Deinen besten Source
> posten. Dann man dir sicher konkreter helfen.
>
> Pepe.


Ja vielen Dank für die schnelle Rückmeldung. Würde mir gerne eine in 
euren Augen einfachere Kommunikationsschnittstelle aussuchen, aber i²C 
wurde mir von meiner LEERKraft so vorgegeben.

- Übertragungsfrequenz wurde auf 100kHz gesetzt bei einer F_CPU von 
3686,400 kHz. (3686400 Hz)

- Pull-Ups habe ich am Slave auf SDA und SCL gezogen.

- Slave hat die Adresse 0x50

Habe euch mal die Quelltexte vom Master und Slave angehangen.

Vielen Dank schonmal für eure Hilfe

Gruß Markus



1
// Master 
2
#include <avr/io.h>
3
#include "i2cmaster.h"
4
#define SLAVE_ADRESSE 0x50
5
6
uint8_t Taster; 
7
8
int main(void)
9
{
10
  DDRB  = 0b00000000;    //Alles Eingänge
11
  PORTB = 0b00000001;    //Pull-Up an PINB.1 setzen
12
  
13
  while (1)
14
  {
15
    i2c_init();         // init I2C interface
16
17
    if (bit_is_clear (PINB, 0))
18
    {
19
      Taster = 0b00000001;
20
    }
21
    else
22
    {
23
      Taster = 0b00000000; 
24
    }
25
    
26
    if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum schreiben?
27
    {
28
      i2c_write(0x00);  // Buffer Startadresse setzen
29
      i2c_write(Taster);  // Ein Bytes schreiben...
30
      i2c_stop();      // Zugriff beenden
31
    }
32
  }
33
}

##############################################################
SLAVE
1
#include <util/twi.h>       //enthält z.B. die Bezeichnungen für die Statuscodes in TWSR
2
#include <stdint.h>       //definiert den Datentyp uint8_t
3
#include "twislave.h"
4
5
#define SLAVE_ADRESSE 0x50 //Die Slave-Adresse
6
7
int main (void)
8
{
9
  DDRB  = 0b00000001;    //PB0 = Ausgang
10
  PORTB = 0b00000000;    // LED aus !
11
  PORTC = 0b00110000;    // Pull-Up für TWI an PORTC4 und 5
12
  
13
  //TWI als Slave mit Adresse slaveadr starten
14
  
15
  
16
  while(1)
17
  {
18
    init_twi_slave(SLAVE_ADRESSE);
19
    //i2cdatamit Werten füllen
20
    for(uint8_t i=0;i<i2c_buffer_size;i++)
21
    {
22
      i2cdata[i]=10+i;
23
    }
24
  
25
    if (i2cdata[0] = 0b00000001)
26
    {
27
      PORTB |= 0b00000001;  //setze die LED    
28
    }
29
    else
30
    {
31
      PORTB |= 0b00000000;    // rücksetze die LED  
32
    }        
33
  } 
34
} //end.main

von Pepe (Gast)


Lesenswert?

Als Basis doch ganz gut.

Beim Slave fehlt aber noch das komplette Empfangen der Daten.
Also kann das ja nicht gehen.

Um es einfach aufzubauen, musst Du prüfen, ob beim Slave ein Paket 
angekommen ist und dann einlesen. Und der Slave muss ein ACK 
zurückgeben, sonst bleibt die Kommunikation hängen.
Und ich gehe mal davon aus, dass in i2c_write im Master auch das ACK 
überprüft wird.

von Thomas W. (Gast)


Lesenswert?

Markus Schmitz schrieb:
> - Slave hat die Adresse 0x50

> if(!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum
> schreiben?

Guck dir mal den Aufbau des Adressierungsbytes an. Slave Adresse und 
Adressbyte sind zwei verschiedene Dinge. Die Adresse hat 7 Bit, die 
Datenrichtungssteuerung (R/W) ein weiteres.

von Mitlesa (Gast)


Lesenswert?

- Zeige deine vollständigen Programme. Wir müssen sonst hellsehen
ob deine Initialisierungen richtig sind.

- Nimm die Init-Aufrufe aus der Schleife heraus, jedesmal
neu zu initialisieren ist Käse, das braucht nur einmal.

von Wolfgang (Gast)


Lesenswert?

Pepe schrieb:
> Vielleicht hilft es ja trotzdem. Kannst ja mal Deinen besten Source
> posten.

Und vielleicht auch mal ein Bild der Signalverläufe von SDA und SCL ...
(DSO oder LA)

Markus Schmitz schrieb:
> Würde mir gerne eine in
> euren Augen einfachere Kommunikationsschnittstelle aussuchen, aber i²C
> wurde mir von meiner LEERKraft so vorgegeben.

Ist dir schon mal die Idee gekommen, dass diese Vorgabe vielleicht 
didaktische Gründe haben könnte. Beim Lernen kommt es oft nicht auf 
schnell und billig drauf an.

Du sollst dich wahrscheinlich mit dem Kommunikationsprotokoll, sowohl 
aus Sicht des Masters als auch der des Slaves auseinandersetzen und wie 
man sieht, gibt es da noch deutliche Verständnislücken, sowohl beim 
I²C-Protokoll als auch bei Fehleranalysemethoden.

von Narkus Schlitz (Gast)


Lesenswert?

Markus Schmitz schrieb:
> 3686,400 kHz. (3686400 Hz)

Ich rechne Dir das mal in Megaherz um:

3,68640 * 10^0 MHz

Mitlesa schrieb:
> neu zu initialisieren ist Käse,

Da ich gerne Käse esse, kommt mir das mit dem vielen Käse ganz gelegen.

Wolfgang schrieb:
> Beim Lernen kommt es oft nicht auf
> schnell und billig drauf an.

auf billig schon, denn die Schulen haben in der Regel wenig Geld.

Wolfgang schrieb:
> Du sollst dich wahrscheinlich mit dem Kommunikationsprotokoll, sowohl
> aus Sicht des Masters als auch der des Slaves auseinandersetzen

Ich weis nicht, was eine Lehrkraft damit bezwecken möchte. Aber ich 
bleibe beim Käse, denn Käse esse ich gerne.

von Mitlesa (Gast)


Lesenswert?

Narkus Schlitz schrieb:
> .............

Wenn das so weiter geht, dann wird das nichts, lieber
Knarkus Schwitz

von Markus S. (markusschmitz)


Lesenswert?

Mitlesa schrieb:
> - Zeige deine vollständigen Programme. Wir müssen sonst hellsehen
> ob deine Initialisierungen richtig sind.
>
> - Nimm die Init-Aufrufe aus der Schleife heraus, jedesmal
> neu zu initialisieren ist Käse, das braucht nur einmal.


Hallo zusammen,

schonmal vielen Dank für eure Hilfe.

Hier mal der komplette Code vom Master:
1
 // Master 
2
#include <avr/io.h>
3
#include "i2cmaster.h"
4
#define SLAVE_ADRESSE 0x50  // Die Slave Adresse
5
6
uint8_t Taster; 
7
8
int main(void)
9
{
10
  DDRB  = 0b00000000;    //Alles Eingänge
11
  PORTB = 0b00000001;    //Pull-Up an PINB.1 für Taster setzen
12
  DDRC  = 0b11111111;  
13
  PORTC = 0b00110000; // Pull-Up von SDA und SCL am MAster
14
   
15
  i2c_init();         // init I2C interface
16
  
17
  while (1)
18
  {
19
    if (bit_is_clear (PINB, 0)) // wenn Taster 1 gedrückt wird
20
    {
21
      Taster = 0b00000001;    // schreibe Bit-Folge in Var_Taser uint8_t
22
    }
23
          
24
        else                        // wenn Taster 1 NICHT gedrückt
25
        {
26
          Taster = 0b00000000;  //schreibe Nullen in Var_Taster
27
        }
28
    
29
    
30
    if (!(i2c_start(SLAVE_ADRESSE+I2C_WRITE))) //Slave bereit zum schreiben?
31
    {
32
      i2c_write(0x00);  // Buffer Startadresse setzen
33
      i2c_write(Taster);  // Ein Bytes schreiben...
34
      i2c_stop();      // Zugriff beenden
35
    }
36
  }
37
}

Zur i2c_init()
1
#include <inttypes.h>
2
#include <compat/twi.h>
3
4
#include "i2cmaster.h"
5
6
7
/* define CPU frequency in Mhz here if not defined in Makefile */
8
#ifndef F_CPU
9
#define F_CPU 3686400
10
#endif
11
12
/* I2C clock in Hz */
13
#define SCL_CLOCK  100000 //76800 // Durch diese Busfrequenz ergibt sich ein Wert von 16 an TWBR ! und nicht 10,432 !
14
15
16
/*************************************************************************
17
 Initialization of the I2C bus interface. Need to be called only once
18
*************************************************************************/
19
void i2c_init(void)
20
{
21
  /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
22
  
23
  TWSR = 0 ;  // Prescaler auf 1 gestellt ...00 und das Statusregister für Fehler auf 00000... gesetzt !
24
  TWBR = ((F_CPU/SCL_CLOCK)-16)/2;  // wie oben erwähnt wird der Wert 16 in TWBR  geschrieben
25
                  // um eine Busfrequenz von 76800 Hz zu erhalten
26
27
}/* i2c_init */

Zur i2c_start und I2c_write
1
/*************************************************************************  
2
  Issues a start condition and sends address and transfer direction.
3
  return 0 = device accessible, 1= failed to access device
4
*************************************************************************/
5
unsigned char i2c_start(unsigned char address)
6
{
7
    uint8_t   twst;
8
9
  // send START condition
10
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
11
12
  // wait until transmission completed
13
  while(!(TWCR & (1<<TWINT)));
14
15
  // check value of TWI Status Register. Mask prescaler bits.
16
  twst = TW_STATUS & 0xF8;
17
  if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;
18
19
  // send device address
20
  TWDR = address; // 80 dez 
21
  TWCR = (1<<TWINT) | (1<<TWEN);
22
23
  // wail until transmission completed and ACK/NACK has been received
24
  while(!(TWCR & (1<<TWINT)));
25
26
  // check value of TWI Status Register. Mask prescaler bits.
27
  twst = TW_STATUS & 0xF8;
28
  if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
29
30
  return 0;
31
32
}/* i2c_start */
33
34
############################## i2c_write = setzt eigentlich nur eine 0!
35
/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */
36
#define I2C_WRITE   0

i2c_write = hier werden die Daten gesendet
1
/
2
unsigned char i2c_write( unsigned char data )
3
{  
4
    uint8_t   twst;
5
    
6
  // send data to the previously addressed device
7
  TWDR = data;
8
  TWCR = (1<<TWINT) | (1<<TWEN);
9
10
  // wait until transmission completed
11
  while(!(TWCR & (1<<TWINT)));
12
13
  // check value of TWI Status Register. Mask prescaler bits
14
  twst = TW_STATUS & 0xF8;
15
  if( twst != TW_MT_DATA_ACK) return 1;
16
  return 0;
17
18
}/* i2c_write */

Und noch den i2c_Stop
1
rief Terminates the data transfer and releases the I2C bus 
2
 @param void
3
 @return none
4
 */
5
extern void i2c_stop(void);
6
7
void i2c_stop(void)
8
{
9
    /* send stop condition */
10
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
11
  
12
  // wait until stop condition is executed and bus released
13
  while(TWCR & (1<<TWSTO));
14
15
}/* i2c_stop */

Soweit ist das mein Master-Programm. In diesem sehe ich soweit keine 
großen Probleme. Seht ihr noch Fehler ?

Muss der errechnete Wert von SCL_Frequenz ein gerader Wert sein ? Wenn 
ich als SCL_Clock 100000hz deklariere kommt ja ein errechneter Wert von 
10,432 raus, der ja in TWBR geschrieben wird um den SCL_Takt zu 
definieren. Der Wert sollte ja laut Atmel größer 10 sein um eine stabile 
Kommunikation zu erhalten. Rundet Atmel(C) dann auf auf 11 oder ab auf 
10 ? oder geht das wegen der Kommazahl nicht ?

Habe mir dann für TWBR einen Wert von 16 eingetragen und ausgerechnet 
welche SCL_Clock Frequenz eingestellt werden muss. In diesem Fall ist 
TWBR ja gerade und als SCL_Clock muss dann 76800Hz eingegeben werden, 
damit die 16 als Wert für TWBR herrauskommt. !

Verstehe ich das so richtig ? Könnt ihr mir bitte helfen ?!

Vielen Dank und Gruß Markus

von Thomas W. (Gast)


Lesenswert?

Markus Schmitz schrieb:
> Hier mal der komplette Code vom Master:

Das mit der Adresse ist immer noch Unfug oder die 0x50 ist nicht die 
I2C-Adresse des Slaves. In jedem Fall solltest du deinen Variablen und 
Konstanten Namen geben, die nicht zu Verwirrung führen.

von Markus Schmitz (Gast)


Lesenswert?

Hallo zusammen,

habe es zum Laufen bekommen.

"Musterlösung" folgt später.....

Vielen Dank für eure Hilfe!

von Markus Schmitz (Gast)


Angehängte Dateien:

Lesenswert?

Hier das fertige Programm für Master und Slave !!

Fehler war das nur ein = benutzt wurde und nicht wie es richtig ist ==
!!
if (i2cdata[0] = 0b00000001)

Jetzt läuft alles mit 2 Atmega 8 !

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.