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
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.
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"
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).
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
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
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.
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
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.
@Spess53: die Seiten habe ich mir schon mehrfach durchgelesen. Was genau meinst du? @Stefan: Ok, danke für die Info.
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
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
Hä
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.)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.