Forum: Mikrocontroller und Digitale Elektronik Auslesen eines Sensors mit I2C / AVR TWI


von Michael L. (eagle87)



Lesenswert?

Hallo,
ich versuche ein C-Programm zu schreiben mit dem ich einen Sensor über 
I2C bzw. TWI auslesen kann. Leider komm ich weder mit dem AVR TWI, noch 
mit dem Datenblatt des Sensors so richtig zu recht. Mein momentaner 
Ansatz liegt darin, über TWI Interrupts, in denen ich das TWI Status 
Register (TWSR) abfrage, die entsprechenden Funktionen aufzurufen. Da 
der Sensor wohl zuerst Initialisiert werden muss, habe ich erstmal mit 
dem MT-Mode angefangen. Bisher werden im Interrupt nur wenige 
Statuscodes wirklich behandelt, aber für einen einzelnen Sendevorgang 
sollte es ja ausreichen. Bei nichtbehandelten  Statuscodes lass ich 
einfach eine Meldung mit dem entsprechenden Code auf dem Display 
ausgeben.

Mein hauptsächliches Problem liegt gerade darin, dass mein Programm 
scheinbar zufällig eines der drei folgenden Punkte macht:
- entweder durchläuft wie es soll und 1 Byte sendet
- Einen Bus Error 0x00 ausgibt
- SLA+W has been transmitted NOT ACK has been received 0x20 ausgibt

Controller: ATmega324A
Datenblatt Sensor: 
http://www.produktinfo.conrad.com/datenblaetter/500000-524999/502376-da-01-de-DRUCKMODUL_I2C_DRMOD_12C_PD0B5.pdf
Zu dem Sensor gibt es auch noch eine Beschreibung für I2C. Die ist im 
Netz aber kaum noch zu finden und befindet sich deswegen mit im Anhang.


Achja Sensor und Mikrocontroller sind nicht direkt, sondern über einen 
Pegelwandler aus N-Kanal-Mosfets verbunden (Der Controller wird bei 5V 
und der Sensor bei 8V betrieben). Bei Bedarf kann ich dazu noch mehr 
Infos posten.

Gruß
Michael

von Stefan (Gast)


Lesenswert?

Versuche es zuerst ohne Interrupts, das schließt eine ganze Reihe 
möglicher Fehlerquellen aus.

Dann solltest Du mit einem Oszilloskop die Low Pegel auf beiden Seiten 
der Pegelwandler prüfen. Wenn der Sensor z.B. 0,5V liefert, dann ist zu 
erwarten, dass der Pegelwandler nochmal 0,3 Volt drauf legt udn dann 
bist du bei 0,8 Volt, was der AVR unter Umständen nicht mehr sauber als 
LOW erkennt.

Wenn das soweit gut aussieht, dann schau Dir die gesamte Kommunikation 
auf einem digitalen Oszilloskop oder LogikAnalyzer an.

Da I2C (in der Regel) beliebig langsam sein darf, kannst Du es auch mal 
"zu Fuß" ohne TWI versuchen und die Signale mit LED's anzeigen. So 
kannst Du jedes Bit einzeln sehen und prüfen.

von Stefan (Gast)


Lesenswert?

In der Codesammlung habe ich mal eine I2C Prozedur (für Master Modus) 
veröffentlicht, die ich persönlich einfacher anzuwenden finde, als die 
twi.h.

Beitrag "universelle I2C Master Prozedur für AVR"

von Stefan (Gast)


Lesenswert?

Sorry, ich meinte nicht, dass die twi.h schwierig ist, sondern die 
Beispiel-Programme von Atmel.

Wie dem auch sei, ich schätze, der Fehler liegt darin, dass Du in Deiner 
Interrupt Routine Dinge tust, die viel zu lange dauern, nämlich die 
Kommunikation mit dem LC-Display.

Was Dein Programm da tut, macht man besser in der main-Schleife (die bei 
Dir leer ist).

von Michael L. (eagle87)


Angehängte Dateien:

Lesenswert?

Hallo Stefan,
hab eben nochmal ein Programm ohne Interrrupts ausprobiert. Bei 400kHz 
Taktfrequenz hat das die ganze Zeit auch nicht funktioniert. Jetzt hab 
ich einfach mal 100kHz ausprobiert. Damit gelingt es sowohl ohne, als 
auch mit Interrupts ein Byte erfolgreich zu senden. Die 400kHz hatte ich 
bisher gewählt weils so im Datenblatt des Sensors steht. Im Anhang ist 
noch der Programmcode zu der Version ohne Interrupts. Ohne Interrupts 
weiß ich aber noch nicht so wirklich wie ich Bytes empfangen soll.

Die Low Pegel meines Signals hab ich noch nicht explizit gemessen, aber 
ich hab mir die Signale schon angeschaut und dabei ist mir nicht 
aufgefallen dass die Spannung zu hoch wäre.

Den Code aus deinem Link werd ich mir gleich mal genauer anschauen.

> Wie dem auch sei, ich schätze, der Fehler liegt darin, dass Du in Deiner
> Interrupt Routine Dinge tust, die viel zu lange dauern, nämlich die
> Kommunikation mit dem LC-Display.
Nach den Stellen, an denen was auf dem Display ausgegeben wird, kann 
doch eh kein neuer Interrupt mehr auslösen. Das dürfte also kein Problem 
machen.

Ansonsten ist mich noch nicht so ganz klar was ich genau zum Sensor 
schicken muss, damit mir dieser die Messwerte liefert. Ist das 
START_CYC_RAM (0x02) richtig oder muss ich den anders initialisieren?

Gruß
Michael

von Michael L. (eagle87)


Lesenswert?

Ok, bei deinem Beispielcode scheiterts an meinen C-Kenntnissen.
1
uint8_t[3] buffer;
Ich kenn nur: uint8_t buffer[3];
Wo ist der Unterschied?
1
// Schreibe 0 in Register 5.
2
buffer[0]=5;
3
buffer[1]=0;
Schreibst du damit nicht 5 in die 0. und 0 in die 1. Zelle des Arrays 
buffer?
1
i2c_communication(SLAVE,buffer,2,0,0);
Ist buffer nicht ein Array, bei dem man die Zelle auswählen muss?

Gruß
Michael

von Michael L. (eagle87)


Angehängte Dateien:

Lesenswert?

Ein bischen weiter bin ich nun gekommen. Mit einem 
Warteschleifenbasierten Programm schaffe ich es, dass nach dem 
Einschalten einmalig ein Messwert auf dem Display ausgegeben wird.

Doch obwohl ich den Lesevorgang in einer While 1 Schleife immer wieder 
aufrufe bleibt die ganze Zeit der gleiche Wert auf dem Display stehen. 
Auch wenn ich die zu messende Größe (Druck) verändere.

Ich vermute dass das Problem nicht beim TWI, sondern irgendwo bei der 
Art wie ich den Sensor anspreche liegt.

von Spess53 (Gast)


Lesenswert?

Hi

>Ich vermute dass das Problem nicht beim TWI, sondern irgendwo bei der
>Art wie ich den Sensor anspreche liegt.

Stimmt. Hast du auch nur ansatzweise mal gelesen, was auf S.6 deines 
Datenblatts steht? S.14-15 solltest du auch nicht ignorieren.

MfG Spess

von Stefan (Gast)


Lesenswert?

Mit dem Sensor habe ich mich noch nicht beschäftigt, deshalb kann ich 
dazu nicht allzu viel sagen.

Aber die Fragen zu meinem Codebeispiel kann ich Dir beantworten:

> uint8_t[3] buffer;
> Ich kenn nur: uint8_t buffer[3];
> Wo ist der Unterschied?

Beide sind alternative Schreibweisen für das gleiche Ergebnis.

// Schreibe 0 in Register 5.
buffer[0]=5;
buffer[1]=0;

Schreibst du damit nicht 5 in die 0. und 0 in die 1. Zelle des Arrays
buffer?

Nein. Hier wird davon ausgegangen, dass der Slave Register hat. Dann 
wird immer zuerst die Register-Nummer (5) gesendet, und dann die Bytes. 
Wobei das erste Byte nach der Register Nummer (0) in das benannte 
Register geschrieben wird und die folgenden Bytes (falls vorhanden) in 
die folgenden Register geschrieben werden.

Beim Empfang geht das so: Du sendest die Registernummer und empfängst 
anschließen ein oder mehrere Bytes. Da erste Byte, dass der Slave zurück 
sendet, kommt aus dem benannten Register, die folgenden Bytes kommen aus 
den folgenden Registern.

Es gibt allerdings auch Slaves, die gar keine Register haben. Die senden 
und empfangen typischerweise einzelne Bytes oder eine bestimmte Anzahl 
von Bytes. Z.B. kenne ich einen A/D Wandler, dem man einen Befehl als 
einzelnes Byte sendet und beim Lesen immer zwei Bytes liefert (16bit 
Integer).

i2c_communication(SLAVE,buffer,2,0,0);

Ist buffer nicht ein Array, bei dem man die Zelle auswählen muss?

Da erste Byte im buffer ist die Register Nummer, die folgenden Bytes 
(falls vorhanden) sind Daten, die in das benannte und die folgenden 
Register geschrieben werden.

Aber alls das könnte bei Deinem Chip auch ganz anders sein. Entscheidend 
ist, dass meine Prozedur n Bytes (in diesem Fall 2) aus dem Buffer 
sende. Wie der Slave diese beiden Bytes interpretiert, hängt ziemlich 
vom SLave selbst ab. Eine Norm gibt es da nicht. Oben habe ich Dir 
beschrieben, wie sich die meisten Slaves verhalten. Ob das auf Deinen 
Sensor zutrifft, kannst Du dem Datenblatt entnehmen.

von Michael L. (eagle87)


Lesenswert?

@Spess53:
die Seiten habe ich mir schon mehrfach durchgelesen. Was genau meinst 
du?

@Stefan:
Ok, danke für die Info.

von Spess53 (Gast)


Lesenswert?

Hi

>@Spess53:
>die Seiten habe ich mir schon mehrfach durchgelesen. Was genau meinst
>du?

Nachdem du den IC adressiert hast erwartet er ein Kommando, damit er 
weiß, was du von ihm willst.

MfG Spess

von Michael L. (eagle87)


Lesenswert?

Hi,
> Nachdem du den IC adressiert hast erwartet er ein Kommando, damit er
> weiß, was du von ihm willst.

Ich schick dem Sensor ja vor der while-1-Schleife mehrere Befehle um ihn 
zu konfigurieren. In der Schleife kommen dann die Read-Anforderungen. 
Ein extra Commando das besagt dass der Sensor gleich Messwerte schicken 
soll gibt es ja nicht und nach dem Adressieren im MR-Mode kann ich 
sowiso kein Commando senden.

Gruß
Michael

von Karl H. (kbuchegg)


Lesenswert?

1
void TWI_read(void)
2
{
3
  TWCR|=(1<<TWINT);
4
  while(!(TWCR&(1<<TWINT)));        //Warte auf Empfang
5
  if((TWSR&0xF8)!=TW_MR_DATA_ACK) TWI_error(TW_MR_DATA_ACK);//0x50
6
  pressure[0]=TWDR;
7
8
  TWCR|=(1<<TWINT);
9
  TWCR&=(~((1<<TWINT)|(1<<TWEA)));      //Enable Acknowledge Bit löschen
10
  while(!(TWCR&(1<<TWINT)));        //Warte auf Empfang
11
  if((TWSR&0xF8)!=TW_MR_DATA_NACK) TWI_error(TW_MR_DATA_NACK);//0x58
12
  pressure[1]=TWDR;
13
}

Wenn mich mein Gedächtnis nicht trügt, dann funktioniert TWI ja so, dass 
der Master IMMER ein Byte senden muss.
Während dieses Byte vom Master zu Slave rübergetaktet wird, wird 
gleichzeitig ein entsprechendes Byte auf der anderen Leitung vom Slave 
zum Master getaktet.
D.h. der Master steuert und taktet die ganze Übertragung, indem er ein 
Byte zum Slave schickt.

In deinem Code hier fehlt mir aber das Senden des Bytes. Daher werden da 
auch keine Taktimpulse beim Auslesen generiert. Wie also sollen die 
Daten vom Slave zum Master kommen?

Oder überseh ich da was?

(Das kann doch nicht so schwer sein. Ich bin sicher, dass es in der 
Codesammlung jede Menge I2C bzw. TWI Routinen fix fertig gibt.)

von Klaus (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn mich mein Gedächtnis nicht trügt, dann funktioniert TWI ja so, dass
> der Master IMMER ein Byte senden muss.

Es trügt. Was du beschreibst ist SPI nicht TWI aka I2C.

MfG Klaus

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.