Autor |
Atmega16 mit THMOD-I2C als Slave... keine Rückmeldung Suche nach: atmega16 (131) i2c (581) |
|
|
|
|
BID = 833303
1ncept10n Gerade angekommen
Beiträge: 8 Wohnort: regensburg
|
|
Guten Tag,
in einem Projekt ist ein Aufgabenteil zu lösen, indem eine Temperatur mit Hilfe vom Thermoelement Modul THMOD-IC2 gemessen werden soll.
Das ganze läuft wie im Namen bereits enthalten über ein IC2-Bus, der im Atmega16 über TWI realisiert wird.
In meinem Fall, so denk ich mir ist die Konfiguration als Master-Receiver sinnvoll.
Nun zum Problem: In jedem Source-Code den ich finde, wie auch in diesem Forum fängt es immer mit den selben Anweisungen an:
Code : |
#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <util/twi.h>
/* Für 100 kHz, hoffe das stimmt... */
#define F_CPU 4000000UL
TWBR = 12;
TWSR = 0;
DDRC &= ~((1<<DD0) | (1<<DD1));
PORTC |= (1<<DD0) | (1<<DD1);
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
while (!(TWCR & (1<<TWINT)));
/* Diese Schleife wird nie verlassen...
...
...
... */
|
|
Der Bus ist wie im Datenblatt beschrieben aufgebaut.. ich habe hier einfach für SDA und SCL jeweils einen 5,6 kOhm zu Vcc (5V) platziert.
Aus dem Dateblatt:
"When an event requiring the attention of the application occurs
on the TWI bus, the TWI Interrupt Flag (TWINT) is asserted..."
Ich frag mich eh wie und was hier ohne eine Adressierung passiert ?
Ich wäre sehr dankbar wenn mir hier jemand weiter helfen könnte.[/code] |
|
BID = 833368
1ncept10n Gerade angekommen
Beiträge: 8 Wohnort: regensburg
|
|
Mir ist aufgefallen das TWSTA im TWCR nicht gesetzt wurde, also probierte ich es mit:
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
Doch wieder verweilt er in dieser Schleife hier
while (!(TWCR & (1<<TWINT)));
Hat jemand eine Idee ? |
|
BID = 833370
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Hallo 1ncept10n,
mit dem Code kann man so erst mal nicht viel anfangen.
Was soll diese Zeile denn bewirken?
Eine Initialisierung als Master Receiver darf nur aus einem gesetzten Bit TWEN bestehen, sonst nichts weiter!
TWINT wird immer dann gesetzt, wenn auch eine konkrete Transaktion auf dem Bus vom Master ausgelöst werden soll, das ist hier ja noch gar nicht der Fall. TWEA bewirkt, dass nach einem empfangenen Byte ein ACK zurückgegeben wird, hier ebenfalls nicht sinnvoll.
Also: was willst du erreichen?
Der normale Fall sieht so aus:
Der Master beginnt als Master Transmitter und erzeugt zunächst eine Start-Kondition auf dem Bus, sendet dann die Adresse des Slaves (das letzte Bit der Transaktion gibt an, ob im Folgenden gelesen oder geschrieben werden soll). Der Slave sendet nach Empfang ein ACK.
Alle Bus-Transaktionen werden von der TWI-Hardware überwacht und alle möglichen Zustände, die dabei eintreten können sind nach einer abgeschlossenen Transaktion im TWI-Statusregister abgelegt.
Das ist alles bis ins kleinste Detail im Datenblatt beschrieben.
Du musst jetzt nur das im Datenblatt des Slaves angegebene Protokoll implementieren, dann klappt das auch.
Schreibe dazu am besten einen Satz von Funktionen zum Erzeugen von Start-, Stop-, Lesen- und Schreiben-Transaktionen.
Je nachdem, welche Anforderungen an die Stabilität gestellt werden, musst du auch berücksichtigen, dass der Slave mal nicht antwortet und entsprechend eine Timeout-Bedingung einbauen, denn wenn der Slave mal nicht reagiert, wartest du Ewigkeiten auf TWINT.
Alternativ macht man das asynchron mit Interrupts, denn sobald TWINT eine Änderung erfährt, wird ein Interrupt ausgelöst. Anhand des Statusregisters und des Status deiner eigenen Routinen kann man dann ermitteln, was als nächstes zu tun ist (endlicher Zustandsautomat).
Einen Satz Routinen kann ich dir geben, gib mir am besten per Mail deine Emailadresse.
_________________
|
BID = 833372
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Hier müsste der Ablauf etwa so aussehen:
Master <- Transmitter
Master: Start-Kondition
Master: Slave-Adresse 0x78 + 1; /* Bit0=1: lesen, Bit0=0: schreiben */
Slave: antwortet i.d.R. mit NACK, da es das letzte gelesene Byte war, kann hier aber auch ACK sein, ausprobieren!
Master <- Receiver
Master: lese Byte0, sende ACK
Master: lese Byte1, sende ACK
Master: lese Byte2, sende ACK,
Master: lese Byte3, sende NACK
Master: Stop-Kondition
Fertig.
_________________
|
BID = 833374
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Habe mal den alten Source rausgekramt, bitte schön.
Mein Tipp: implementiere ein Softwaremodul (am besten so nennen, wie den Temperaturchip), indem du die Kommunikation abwickelst.
Dort musst du dann twi.h inkludieren.
Beispiel:
/* Initialisierung, Angabe der Taktgeschwindigkeit in Hz, hier 100kHz */
i2c_switch_master(i2c_sclclock(100000));
Beispiel: Lese Daten von einem DS1377 (Echtzeit-Uhr)
static uint8_t
ds1377_read_data(void)
{
i2c_start();
i2c_transmit(DS1337_ADDR + 1);
register uint8_t ret = i2c_receive(NACK);
i2c_stop();
return ret;
}
So ähnlich wird deines auch aussehen .
_________________
|
BID = 833383
1ncept10n Gerade angekommen
Beiträge: 8 Wohnort: regensburg
|
Herzlichen Dank für deine Bemühungen !
Danke für die Routinen, sehr ordentlich programmiert!
Jetzt hab ich zwar verstanden wie es mit der Abfolge des Senden und Empfangen aussieht.. jedoch besteht das selbige Problem weiterhin:
void main()
{
/* LEDs zum testen */
DDRB = 0xFF;
PORTB = 0xB;
i2c_switch_master(i2c_sclclock(100000));
/* Aus der i2c_start */
/* Startkondition erzeugen */
TWCR = 1<<TWINT|1<<TWEN|1<<TWSTA;
/* Warten, bis TWINT gesetzt ist */
while(!(TWCR & (1<<TWINT)));
PORTB = 0x00;
}
Die LEDs leuchten dauerhaft.. TWINT bleibt immer 0.
|
BID = 833389
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Benutze bitte direkt die Aufrufe wie i2c_start() usw, das macht es für mich leichter.
Beim Mega16 sind die Pins nicht auf 4,5 sonder 0,1; ändere mal in i2c_switch_master() den IO-Teil auf
/* IO einrichten */
DDRC &= ~(1<<0|1<<1);
PORTC |= (1<<0|1<<1);
Daran wird es vermutlich nicht liegen, aber dennoch machen.
Ich vermute immer noch, dass die Hardware nicht korrekt angeschlossen ist.
Wenn der Slave nicht reagiert, kann der TWI-Bus das ganze Programm blockieren.
_________________
|
BID = 833392
1ncept10n Gerade angekommen
Beiträge: 8 Wohnort: regensburg
|
Danke,
ouh das habe ich übersehen, schon geändert.. ohne Auswirkungen.
Stromlaufplan habe ich hochgeladen...
Betriebsspannung am Modul ist 16V (Datenblatt: 6...24V)
Ansonsten kann man nicht viel falsch machen denk ich...
USART und alles andere habe ich bereits aus Verzweiflung abgeklemmt..
EDIT: Bilddatei nochmal hochgeladen.
[ Diese Nachricht wurde geändert von: 1ncept10n am 9 Jun 2012 22:04 ]
|
BID = 833427
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Auf dem Bild hast du SCL und SDA vertauscht.
Ist das auch so implementiert oder hast du dich versehen?
Edit:
Da ich den Rahmen des Projekts sowie dein Elektrotechnik-Wissen nicht kenne, frage ich vorsichtshalber mal nach: beide Bausteine, also µC und Sensor, liegen hoffentlich auf gleichem GND-Potential und alle ICs sind mit 100nF gestützt?
_________________
[ Diese Nachricht wurde geändert von: DonComi am 10 Jun 2012 6:14 ]
|
BID = 833428
Her Masters Voice Inventar
Avatar auf fremdem Server ! Hochladen oder per Mail an Admin
Beiträge: 5308 Wohnort: irgendwo südlich von Berlin
|
vielleicht vergessen JTAG abzuschalten? Der liegt doch beim M16 auf Port C wenn ich mich recht erinnere.
|
BID = 833450
1ncept10n Gerade angekommen
Beiträge: 8 Wohnort: regensburg
|
Ach das tut mir leid, ich hab den Plan zum Posten erstellt und auch noch die 2 Pins vertauscht.. Auf dem Steckbrett ist aber alles in Ordnung...
JTAGEN ist aus, danke.
Ich hab jetzt den ATmega16 getauscht und die Start-Kondition läuft
Wirklich eigenartig... ich hab alle Timer, USART, ADC usw am laufen gehabt ... naja, wie auch immer es läuft.
Ich werde jetzt mit Hilfe den Routinen von dir versuchen etwas auszulesen.
Melde mich dann, vielen Dank !
|
BID = 833517
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Nein, JTAG liegt darüber, beginnt also bei PC2.
Was allerdings schon häufig passiert ist, dass der Watchdog aktiviert war und regelmäßig ein Reset ausgeführt hat. Ist hier aber nicht der Fall, das Verhalten wäre anders.
P.S.: wenn ich mich nicht täusche, kann man sich die IO-Initialisierung auch sparen.
Das Einzige Interessante, was dann bleibt, ist, dass man sich externe Pullups spart. Allerdings sollten die für hohe Geschwindigkeiten deutlich kleiner als 10kΩ.
_________________
|
BID = 833520
1ncept10n Gerade angekommen
Beiträge: 8 Wohnort: regensburg
|
Ich bin nun wieder an einem Punkt angelangt, an dem ich nicht weiterkomme...
Einmal mit folgender TWDR: 0x78 + 1
register uint8_t received_data;
DDRB = 0xFF;
PORTB = 0xB;
i2c_switch_master(i2c_sclclock(100000));
i2c_start();
USARTWriteChar(TWSR & 0xF8); /* Status: 0x08 */
i2c_transmit(0x78 + 1);
USARTWriteChar(TWSR & 0xF8); /* Status: 0x48 */
received_data = i2c_receive(NACK); /* Verbleibt hier... */
USARTWriteChar(TWSR & 0xF8);
i2c_stop();
USARTWriteChar(TWSR & 0xF8);
PORTB = 0x0A; [/code]/* LED */
USARTWriteChar(received_data);
Und nach einem Tutorial dieser TWDR: 0x78 << 1
register uint8_t received_data;
DDRB = 0xFF;
PORTB = 0xB;
i2c_switch_master(i2c_sclclock(100000));
i2c_start();
USARTWriteChar(TWSR & 0xF8); /* Status: 0x08 */
i2c_transmit(0x78 << 1 | TW_WRITE);
USARTWriteChar(TWSR & 0xF8); /* Status: 0x20 */
received_data = i2c_receive(NACK);
USARTWriteChar(TWSR & 0xF8); /* Status: 0x30 */
i2c_stop();
USARTWriteChar(TWSR & 0xF8); /* Status: 0x30 */
PORTB = 0x0A; /* LED */
USARTWriteChar(received_data); /* received_data = 0xF0 */
Wobei ( 0x78 << 1 ) der Ausgabe 0xF0 entspricht !?
Ich habe es mit NACK und ACK probiert.. selbes Ergebnis
Edit: i2c_stop() und register nachgetragen. ( Kein Unterschied )
[ Diese Nachricht wurde geändert von: 1ncept10n am 10 Jun 2012 18:08 ]
|
BID = 833533
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Das mit der Adresse ist immer so eine Sache...
Die ersten 7 Bits, die übertragen werden, entsprechen der Adresse. Das letzte Bit dem Lese- oder Schreibbit.
Nun muss die Adresse ein Bit nach links geschoben werden und das LSB entsprechend gesetzt oder gelöscht werden.
Es gibt demnach zwei gängige Methoden, die Adresse eines Slaves anzugeben.
1. Man gibt tatsächlich eine Adresse an, die max. 7 Bit groß ist. Man muss sie selbst eine Stelle nach links schieben und das Lese-/Schreibbit anhängen, oder
2. Es werden zwei Adressen bekanntgegeben, einmal die Schreibadresse und einmal die Leseadresse. Hier also Schreibadresse 0xF0 und Leseadresse 0xF1.
Da die TWI-Geschichte nun offenbar funktioniert, musst du das im Datenblatt angegebene Protokoll implementieren. Dort werden nacheinander vier Bytes gelesen.
Im Datenblatt fehlt eigentlich ein Timingdiagramm einer gesamten Lesetransaktion.
Da musst du einfach probieren, bist aber auf dem richtigen Weg.
Du musst im Datenblatt gucken, wie du die empfangenen Bytes in die gewünschten Daten umrechnen musst.
_________________
[ Diese Nachricht wurde geändert von: DonComi am 10 Jun 2012 19:17 ]
|
BID = 833536
1ncept10n Gerade angekommen
Beiträge: 8 Wohnort: regensburg
|
Danke,
nun ich finde den Fehler einfach nicht...
Habe jetzt eben zum Lesen i2c_transmit(0xF1) verwendet.
Ergebnis:
/* A START condition has been transmitted */
/* SLA+R has been transmitted; NOT ACK has been received */
Doch kommt er aus dem i2c_receive(NACK) nicht raus..
Mit 0xF0 bleibt die Adresse bis zur Rückgabe im TWDR...
Jetzt weiß ich wirklich nich mehr weiter...
|