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
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.
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.
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_tTaster;
7
8
intmain(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?
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.
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.
- 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.
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.
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.
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_tTaster;
7
8
intmain(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 !
############################## 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
unsignedchari2c_write(unsignedchardata)
3
{
4
uint8_ttwst;
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)return1;
16
return0;
17
18
}/* i2c_write */
Und noch den i2c_Stop
1
riefTerminatesthedatatransferandreleasestheI2Cbus
2
@paramvoid
3
@returnnone
4
*/
5
externvoidi2c_stop(void);
6
7
voidi2c_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
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.
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 !