PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : I²C Bus mit C Programmieren



Green Hell
26.04.2006, 11:32
Hallo,
kann mir irgendjemand helfen?

Habe ein Board mit einem Atmel ATmega90CAN128 Controller und einem I²C-Bus und möchte über diesen einen Kompasssensor anschließen, der auf das Erdmagnetfeld ausgerichtet wird.

Ich habe aber bei der Suche in diesem Forum und bei google bloß ein programmbespiel in bascom gefunden.

Hat vielleicht irgendwer schon mal ein Proramm in der Richtung geschrieben oder ne idee, wo ich entsprechende beispiele oder beschreibungen dazu finden kann???

Gruß Green Hell

ogni42
26.04.2006, 14:38
Steht im Datenblatt nicht ein Beispiel für TWI (=I2C) Ansteuerung?

SprinterSB
26.04.2006, 18:38
Vielleicht auch bei Peter Fleury oder bei den Beispielen von WinAVR (examples/twidemo oder so).

Green Hell
27.04.2006, 10:32
ja danke, ich glaube das könnte mir helfen. Wenn nicht werd ich mich wohl dann noch mal melden.

Gruß Green Hell

bhm
27.04.2006, 11:13
moin moin,
ich habe ein paar Bibliotheken gesehen, aber ich habe die Links nicht hier. Wenn noch Bedarf besteht, kann ich nochmal nachsehen.
Ausserdem habe ich mir auch was Eigenes geschrieben. _Scheint_ zu funktionieren, aber nur im single-master-modus (arbitration ist nicht implementiert). Wenn daran Interesse besteht, kann ich die Sourcen mal verbreiten.
ciao .. bernd

Green Hell
27.04.2006, 14:42
Doch, klar besteht noch Interesse sowohl an den Links, als auch an dem Programmcode. Würde mir bestimmt weiterhelfen, da der bus bis jetzt noch nicht funktoniert.

Gruß Green Hell

bhm
28.04.2006, 10:40
ok, sollte ich am wochenende schaffen

bhm
09.05.2006, 16:26
ich hab's nicht vergessen, ich hab nur im Moment wenig Zeit ...

Green Hell
09.05.2006, 21:38
ja alles klar, hab in der zwischenzeit auch ein Programm hiengekriegt, das den i²c-bus benutzt und so einigermaßen (also noch mit einigen macken, deren Ursprung ich noch nicht kenne ](*,) ) funktioniert. Ich denk aber trotzdem das mir die Links und der Programmcode weiterhelfen können.

bhm
14.05.2006, 23:53
So, hier erstmal meine TWILib.
Es gibt 6 Routinen:
2 Initialisierroutinen für Master und Slave, Mastertransmitter, Masterreceiver, Slavetransmitter und Slavetransmitter. Funktion sollte klar sein.
Insbesondere die Slaveroutinen fehlen meist bei den Beispielen, die man im Netz findet (zB bei WinAVR), weil die Kommunkation da meist mit EEPROMs abläuft.
Die Routinen scheinen bei mir zu funktionieren, ich habe aber nicht alle mögliche Fehlereinflüsse (Slave funkt rein, oder hört nicht zu, oder ähnliches getestet. Leider habe ich den Multimastermodus noch nicht implementiert.
Ich bin natürlich für Kritik, Verbesserungen oder Begeisterung (vorzugsweise letzteres) offen.




/* *************************************
TWI Lib Version 5 oder so
bhm 2006
************************************** */

// bislang keinmultimaster support
// keine Garanie für irgendwas
// Bemerkungen, Fehlerreports usw willkommen


#ifndef _TWILIB_
#define _TWILIB_ 1

#include <avr/io.h> //Zugriff auf IO's des Controllers
#include <stdint.h>
#include <compat/twi.h>

void init_Master (void) {
TWSR &= ~(_BV(TWPS0) | _BV(TWPS1)); //0b11111100: Prescaler auf 1 setzen, Bit 1 und 0 auf 0
TWBR = 0x0A; //Bitrate auf 10 setzen, gibt 220kHz Bustakt bei 8MHz
}

void init_Slave (uint8_t SlvAdd) {
TWAR = SlvAdd; // eigene Address, hier ohne general call
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWINT); //slave receive, TWEA > ACK,
}


void waitTWINT (void) {
uint32_t m=0,mmax=F_CPU; //zZt einfach eine große Zahl

while (bit_is_clear(TWCR, TWINT)) {
if (m++ > mmax) return; // bricht waitTWINT ab, TW_Status enthält dann TW_NO_INFO
}
}

// *********** MasterTransmitter *************
// Daten in *Data, lenmax #Daten senden, MT #gesendete Daten
uint8_t MT (uint8_t SlvAdd, uint8_t *Data, uint8_t lenmax) {
uint8_t len=1;

TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); //sende start
waitTWINT();
if ((TW_STATUS != TW_START) && (TW_STATUS != TW_REP_START)){ // chk Start | repeated start
goto ENDE;
}

TWDR = SlvAdd + TW_WRITE; //Addresse des Slaves angeben, SLA+W
TWCR = _BV(TWINT) | _BV(TWEN); // sende sla_w
waitTWINT();
if (TW_STATUS != TW_MT_SLA_ACK) { // chk sla_ack
goto STOP;
}

do {
TWDR = *Data++; // Datum laden
TWCR = _BV(TWINT) | _BV(TWEN); // sende Datum
waitTWINT();
if (TW_STATUS != TW_MT_DATA_ACK) { // chk data_ack
goto STOP;
}
} while (len++<lenmax);

STOP:
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); // sende stop, kein warten auf TWINT

ENDE:
return len-1;
}

// *********** MasterReceiver *************
// empfangene Daten nach *Data, Speicher muss bereitgestellt sein,
// lenmax #Daten empfangen, MR #empfangene Daten
uint8_t MR (uint8_t SlvAdd, uint8_t *Data, uint8_t lenmax) {
uint8_t len=1;

TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); //sende start
waitTWINT();
if ((TW_STATUS != TW_START) && (TW_STATUS != TW_REP_START)){ // chk Start | repeated start
goto ENDE;
}

TWDR = SlvAdd + TW_READ; //Addresse des Slaves angeben, SLA+r
TWCR = _BV(TWINT) | _BV(TWEN); // sende sla_r
waitTWINT();
if (TW_STATUS != TW_MR_SLA_ACK) { // chk sla_ack
goto STOP;
}

while (1) { // endlos, verlassen via break in if TW_Status
if (len<lenmax) {
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWINT); //receive, ACK,
} else {
TWCR = _BV(TWEN) | _BV(TWINT); //receive, NACK,
}
waitTWINT();
if (TW_STATUS != TW_MR_DATA_ACK) break; // LOOP VERLASSEN
*Data++ = TWDR; len++; // Daten verarbeitet
}

if (TW_STATUS == TW_MR_DATA_NACK) {
*Data++ = TWDR; len++; // Daten verarbeitet
goto STOP;
}

STOP:
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); // sende stop, kein warten auf TWINT
ENDE:
return len-1;
}


// *********** SlaveReceiver *************
// empfangene Daten nach *Data, Speicher muss bereitgestellt sein,
// lenmax #Daten empfangen, SR #empfangene Daten
uint8_t SR (uint8_t *Data, uint8_t lenmax) {
uint8_t len=1; // lenmax >= 1, min ein Byte empfangen!

// auf SLA warten
while (bit_is_clear(TWCR, TWINT)); // beliebig lange warten oder pollen

// jetzt geht's los
if (TW_STATUS != TW_SR_SLA_ACK) { // chk SLA+W
goto STOP;
}

while (1) { // endlos, verlassen via break in if TW_Status
if (len<lenmax) {
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWINT); //slave receive, TWEA > ACK,
} else {
TWCR = _BV(TWEN) | _BV(TWINT); //slave receive, TWEA > NACK,
}
waitTWINT();
if (TW_STATUS != TW_SR_DATA_ACK) break; // LOOP VERLASSEN
*Data++ = TWDR; len++; // Daten verarbeitet
}

if (TW_STATUS == TW_SR_DATA_NACK) { // Datum mit NACK beantwortet
*Data = TWDR; len++; // im Prinzip noch ein Datum verfügbar
}

STOP: // (TW_STATUS == TW_SR_STOP) einzig anderer möglicher Status, von ARBs abgesehen
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWINT); //slave receive, TWEA > ACK,
return len-1;
}

// *********** SlaveTransmitter *************
// Daten in *Data, lenmax #Daten senden, ST #gesendete Daten
uint8_t ST (uint8_t *Data, uint8_t lenmax) {
uint8_t len=0; // lenmax >= 1, min ein Byte senden!

// auf SLA warten
while (bit_is_clear(TWCR, TWINT)); // beliebig lange warten oder pollen

// jetzt geht's los
if (TW_STATUS != TW_ST_SLA_ACK) { // chk SLA+R
goto STOP;
}

do {
TWDR = *Data++; len++;
if (len<lenmax) {
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWINT);
} else {
TWCR = _BV(TWEN) | _BV(TWINT);
}
waitTWINT();
} while (TW_STATUS == TW_ST_DATA_ACK);

STOP:
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWINT); //slave receive, TWEA > ACK,
return len;
}

#endif



Bei den versprochenen Links habe ich immernoch nicht die Zeit gefunden ...

ciao ... bernd

Green Hell
15.05.2006, 20:47
ja danke ich werd mal ausprobieren, ob die sache bei meinem board ebenfalls funktioniert
gruß Green Hell

simple
17.07.2006, 15:59
Hallo zusammen,

ich bin Neuling auf dem Gebiet der uC. Seit neustem beschäftige ich mich mit dem TWI. Der Code von bhm hat mir schon seehr viel geholfen, allerdings hab ich ihn teilweise noch nicht ganz durchschaut. Wer hat Lust, mir folgende Zeilen (den Knackpunkt der Funktion) zu erklären:

Auszug aus Master-Receiver von bhm's TWI-Lib


while (1) { // endlos, verlassen via break in if TW_Status
if (len<lenmax) {
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWINT); //receive, ACK,
} else {
TWCR = _BV(TWEN) | _BV(TWINT); //receive, NACK,
}
waitTWINT();
if (TW_STATUS != TW_MR_DATA_ACK) break; // LOOP VERLASSEN
*Data++ = TWDR; len++; // Daten verarbeitet
}


Meine Fragen dazu:
- was hat es mit der Längengeschichte (Variablen len und lenmax) auf sich
- gesamte funktionsweise der while-Schleife

Toll wäre eine kurzer, verständlicher Abriss über die Schritte, die in der while-Schleife passieren. Das würde mir sehr weiterhelfen und zum Gesamtverstädnis beitragen.

Vielen Dank im voraus für Euere Hilfe!

PicNick
17.07.2006, 16:20
Schau,
"len" sind die empfangenen Bytes, am Anfang "1"
"lenmax" die gegebene Anzahl, wieviele es werden dürfen
Data* zeigt auf den datenbuffer

while(1) heißt, von sich aus hört die Schleife nicht auf.

if (len < lenmax) // noch nicht genug bytes
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWINT);
wenn ein Byte kommt, ACK zurückschicken

sonst
TWCR = _BV(TWEN) | _BV(TWINT);
auch empfangen, aber KEIN Ack zurück

Dann warten, was passiert

Beim empfang MIT ACK
*Data++ = TWDR // Daten speichern (pointer+1)
len++ // mitzählen

Beim empfang OHNE ACK (also letztes Byte) --> break, schleife exit.


Nach der Schleife wird auch noch das letzte (nicht "geackte" Byte gespeichert

Und STOP abgesetzt. aus.


Der Witz dabei ist, daß der Master receiver das letzte geholte byte NICHT "Acked", damit der Slave weiß, das war das letzte.
(Der steht sonst nämlich da wie die Kuh vor dem neuen Tor)


logo ?

EDIT: Ich weiß, ich hab auch schon elegantere Schleifen gesehen, aber was soll's

simple
17.07.2006, 16:28
Danke PicNick für die super schnelle und ideotensichere Antwort. C-Code beherrsche ich schon, mir ging es eher um das Verständnis der einzelnen Registerwerte, d.h. was macht der uC wenn der und der Wert drin steht .... - das ist noch meine Schwachstelle und das muss ich noch lernen/üben.

Aber deine Erklärung ist super. VIELEN DANK !!!

PicNick
17.07.2006, 16:44
Ah so-

Die TWCR Register sind für sich allein nicht eindeutig, was er macht, hängt davon ab, welchen zustand die TWI grad hat. also

Als Master sagt man am Anfang START INT/ EN / STA, das ist neutral

ist das ok,
Schickt man mit adresse->TWDR und INT/ EN die Adresse weg

schreibt man die Slaveaddresse + W (also die "gerade" adresse)
ist man dann Master transmitter
da geht es immer Daten --> TWDR und INT/ EN

schreibt man die Slaveaddresse + R (also die adresse + 1)
ist man dann Master receiver
da geht es immer umgekehrt, INT/ EN , TWDR --> Daten
sagt man auch EA/ dazu, schickt er ein ACK zurück, sonst eben nicht

(dazwischen natürlich immer warten, ob ein INT zurückkommt)

bhm
20.07.2006, 22:07
Schau,
....
logo ?

EDIT: Ich weiß, ich hab auch schon elegantere Schleifen gesehen, aber was soll's
Hallo PickNick,
gute Erklärung, hätte ich nicht besser gekonnt ;-)

Das mit der eleganten Programmierung liegt wahrschenlich daran, dass ich eher aus der Pascal-Welt komme und natürlich auch bei C Pascal denke.
Insofern ist


while (1) {
..
if (irgendwas) { break; }
}

schon ein Zugeständniss!
Eigentlich müsste es sauber strukturiert so aussehen ;-)


int ende;
ende =0;
while (ende==0) {
..
if (irgendwas) { ende =1; }
}

aber das scheint selbst mir etwas -hm- geschwätzig. Aber ob *(buf++) oder *buf++ oder *(&(*buf)++) oder doch buf[i++] besser oder gar eleganter ist werde ich wohl nie lernen.

Meine Anforderung an meine C-Programme ist, dass sie a) einigermaßen funktionieren (ist oft genug nicht der Fall) und b) ich sie nach 3 Monaten noch oder wieder verstehe (scheint hier gelungen zu sein :-) ).

ciao bernd

PicNick
21.07.2006, 09:09
Deine Anforderungen klingen vernünftig, ich möcht' fast sagen, das ist genau das, worauf's ankommt.
Ich bin überzeugt, wenn ein Programmierer in die Hölle kommen sollte, muß er dort alle seine Programme nach-dokumentieren. Und das tut man sich dann leichter :mrgreen:

bhm
21.07.2006, 09:27
Ich bin überzeugt, wenn ein Programmierer in die Hölle kommen sollte, muß er dort alle seine Programme nach-dokumentieren. Und das tut man sich dann leichter :mrgreen:
uff! gut, dass ich kein Programmierer bin (jedenfalls keiner der das für Geld macht ;-)) und auch nicht in die Hölle komme :twisted: (das ist jedenfalls der Plan)

ciao .. bernd

dark_flash01
22.11.2006, 13:56
Wie sieht denn nun zum Beispiel eine typische main.c mit den Funktionen drin aus für Master und Slave?!

Genauer gesagt möchte ich mittels TWI die ADC eingänge eines Anderen Mega32 an den Master übermitteln. Der Master soll also den Slave immer wieder abfragen. Wie stelle ich das an?
Kann mir bitte jemand helfen?
Grüße Sebastian

--