PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Suche Programmbeispiel für TWI/I2C Bus in Assembler



sking86
26.01.2010, 17:55
Moin, ich bin noch relativ neu in der Mikrocontroller-Welt und benötige mal etwas Hilfe.

Hab ein mega32 uc und wollte mit diesem über ein I2C Bus mit einem PCF8574 kommunizieren lassen. Hab die ganze Prozedur mit dem Busverkehr verstanden doch irgendwie bekomme ich das ganze nicht zum laufen.

1.Wollte fragen ob nicht jemand mal ein Fertiges I2C Programm in Assembler hat bei dem ich mal reinschauen kann was ich anders mache?

2.Der PCF hat die Hex Adresse 72. Das ganze sind ja nu 7 Bit. Und das letzte Bit soll ja so weit ich es verstanden habe das Read/Write Bit sein. Wenn ich jetzt schreiben möchte muss ich doch noch ne Null an die Adresse ranhängen so das das komplette Byte zum verschicken im TWDR dann 0xE4 (bzw 11100100) sein müsste. Oder etwa nicht?

Wäre super wenn Ihr mir da weiter helfen könntet.

bax
26.01.2010, 18:56
Hi,

wenn Du die Adresse als 0x72 angibst ist bit 0 schon das R/W-bit.

0x72 gehört laut Datenblatt zum PCF8574A, paßt das?

Rajko

sking86
26.01.2010, 21:15
Es ist ein PCF8574AP. Habe A0 auf 5V und A1 und A2 auf GND.

Die SDA und SCL liegen ja auf Port C (PC0, PCC1). Müssen diese Ports noch auf In- oder Output gestellt werden?

Ich initialisiere die Startsequenz (TWINT, TWSTA und TWEN bekommen ein "1" signal) danach kommt die erste "wait" Schleife in der gewartet wird bis TWINT ausgelöst wird. Da hängt das Programm dann in einer Endlosschleife, weil TWINT nicht von der Hardware auf 1 gesetzt wird. Woran kann es liegen das das Interrupt-Flag nicht ausgelöst wird?


Wie sollte ich die Bus-Frequenz wählen für meinen PCF? 100kHz gewählt. Das ganze habe ich vor der Startsequenz in TWBR geschrieben (0x48).

bax
27.01.2010, 13:13
Wenn noch nicht mal der Start-Zustand erreicht wird kann das fast nur schaltungstechnische Gründe haben. Hast Du die notwendigen Pullup-Widerstände auf den zwei Leitungen? Führen beide Leitungen H-Pegel bevor Du den TWI startest?

Die Ports selber mußt Du normal nicht anfassen, die werden durch TWEN auf TWI umgeschaltet. Es gibt den Hinweis, daß man die Ports auf Eingang mit aktiviertem internen Pullup stellen kann, um sich unter Umständen die externen Pullups sparen zu können. Bei 100kHz sind die internen Pullups aber gefühlsmäßig zu hochohmig, je nach Länge und Kapazität des Busses.

Rajko

sking86
27.01.2010, 16:11
Tausend dank. Funktioniert super.

Hatte das Kapitel mit den Pull-ups ehrlich gesagt völlig übersprungen, weil ich mich auf die Pull-up Widerstände im uC verlassen wollte. Das waren ja glaub ich so 10k. Hab das ganze jetzt mal mit 4,7k probiert und es läuft. Bin echt super erleichtert. Das mit der Bus-Kapazität habe ich auch schon mal irgendwo gelesen doch wenn es jetzt funktioniert wahrscheinlich übertrieben Hirnschmalz in die Berechnung der Kapazität zu legen.

Von meiner Seite nochmal vielen Dank. Damit kann nun ich wieder ruhig schlafen.

Zwei Fragen wären da aber noch von meiner Seite.

1. Ich wollte für mein Projekt mehrere PCFs benutzen und ein Mega32 aus Master. Wenn ich nun die Kommunikation mit einem PCF eingeleitet habe und ich möchte mitten in meinem Programm einen anderen PCF ansprechen muss ich doch nicht jedesmal den BUS Stoppen und neu initialisieren oder? Reicht es nicht wenn ich ab dem Step mit dem Senden der Slaveadresse weitermache.

2. Hab beim PCF die Funktion des Interrupt Aus- oder Eingangs nicht so ganz verstanden? Wann und wozu ich diesen überhaupt benötige? Dient der als eine art Erweiterung der uC Eingänge meines Mega`s?

bax
28.01.2010, 09:04
1. jein... Um einen neuen Slave anzusprechen ist es am saubersten, wenn Du einen Stop-Befehl absetzt und dann mit einem Start weitermachst. Du kannst aber auch ohne vorheriges Stop einen sogenannten "repeated Start" machen, das mußt Du aber dann in der Auswertung des Statusbytes berücksichtigen, es wird dann als 0x10 statt 0x08 gemeldet.

Die komplette Initialisierung mußt Du nicht jedes Mal wiederholen.

2. Den /INT-Ausgang des PCF kannst Du auf einen (INT-)Eingang des mega32 verdrahten um Dir signalisieren zu lassen, daß sich ein Eingang am PCF verändert hat. Dann kannst Du das auslesen ereignisgesteuert machen, sonst mußt Du halt bei Bedarf oder zyklisch pollen.

Rajko

sking86
31.01.2010, 14:43
Ich habe bisher ja immer den Master Transmitter Mode benutzt und den PCF als Ausgang verwendet.
Jetzt wollte ich die ganze Sache umdrehen und den Master Receiver Mode benutzten wobei mit der PCF als Eingang dient. Leider bekomme ich das noch nicht so wirklich hin.

Habe die Startsequenz eingeleitet und die Adresse des PCF angesprochen. Bis dahin funktioniert alles. Bei der Statusabfrage nach dem senden der Adresse soll ich ja jetzt nicht mehr $18 sondern $40 benutzten. Und da hakt dann auch die ganze Sache.

1. Wie bekomme ich die Statusbits auf $40?
2. Wie kann ich dann die Daten vom Slave zum Master übertragen (ins TWDR)? Werden die dann automatisch ins TWDR geschrieben?

Danke schonmal für die Antwort

bax
31.01.2010, 15:57
Bit 0 der Adresse hast Du auf 1 gesetzt zum lesen?

Kannst Du Dir den anliegenden Statuscode anzeigen lassen, zur Not provisorisch binär per paar Led's an freien Ports?

Oder poste mal Deinen Code.

Wenn alles richtig läuft landen die Daten des Slaves automatisch im TWDR, ja.

Rajko

sking86
02.02.2010, 17:31
Initialisierung läuft jetzt. $40 steht jetzt im Statusregister und ich wollte jetzt anfangen Daten vom TWDR zu lesen. Hab die Textpassage in der Doku zum Mega 32 nur nicht so richtig verstanden.

"Received data can be read from the TWDR Register when the TWINT Flag
is set high by hardware. This scheme is repeated until the last byte has been received. After the last byte has been received, the MR should inform the ST by sending a NACK after the last received data byte."

Soll ich nach dem der Status ($40) erkannt wurde nochmal auf das TWINT Flag warten und dann einfach mit "in r16, TWDR" die Daten aus dem Datenregitster holen?

So würde ich mir das ganze jetzt nach dem lesen des Manuals vorstellen.
(beginnt da dem Punkt wo die Adresse erfolgreich gesendet wurde)

in r16, TWSR
andi r16, 0xF8
cpi r16, 0x40
brne error

rcall wait ;warten auf TWINT Flag

in r20, TWDR ;einlesen der Daten in r20
ldi r16, (1<<TWINT)
out TWCR, r16 ;senden eines NACK`s an den ST

rcall wait ;warten auf TWINT Flag

in r16, TWSR
andi r16, 0xF8
cpi r16, 0x50 ;NACK wurde vom ST empfangen
brne error


ldi r16, (1<<TWINT)|(1<<TWEN)|(1<<TWSTO)
out TWCR, r16

r20 würde dann zur Weiterverarbeitung zur Verfügung stehen.

bax
02.02.2010, 20:43
Na das sieht doch schon gut aus.

Nachdem du Status 0x40 erhalten hast mußt Du nur auch wieder das TWINT auf 1 setzen, damit es weiter geht.

Wenn Du die einzelne Kommunikation nur auf das einmalige lesen eines Bytes von diesem PCF beschränken willst, mußt Du dabei gleichzeitig das TWEA auf 0 setzen. Das zeigt dem PCF an, daß dies die letzte Abfrage (in dem Fall die erste und die letzte gleichermaßen) ist.

Sollen die Daten eines PCF laufend eingelesen werden, um z.B. auf einen Pinwechsel zu warten, setzt Du das TWEA auf 1, dann läßt sich der PCF immer wieder auslesen. Willst Du dann z.B. auf einen anderen PCF wechseln muß das TWEA vor(!) dem letzten empfangenem Byte wieder auf 0 gesetzt werden.

Nach der 0x40 kehrt sich also die 'Richtung' vom ACK/NACK um, es wird nun vom ATmega erzeugt und zeigt dem PCM an, ob weiterhin ausgelesen wird.

In dem Zusammenhang sind dann auch die folgenden Statuswerte zu sehen, wenn Du vorher festgelegt hast, daß ein ACK gesendet wird, bekommst Du die 0x50, und mußt anschließend weiter auslesen. Nach Du ein NACK erzeugt hast bekommst Du nach dem nächsten empfangenen Byte die 0x58, erst jetzt darfst Du mit TWSTO die Verbindung beenden.

sking86
03.02.2010, 14:53
Danke für deine schnelle Antwort.

Hab mal probiert das ganze umzusetzen. Kann bis jetzt nur sagen, dass das Programm komplett durchläuft. Kann noch nicht sagen ob die Daten des PCF richtig eingelesen werden, weil ich unter der Woche kaum Zeit habe. Muss das am Wochenende prüfen.

Kannst du nochmal schauen ob ich das so richtig verstanden habe und ob ich an der richtigen stelle das Datenregister (TWDR) auslese. Bin mir da ein wenig unsicher.

Danke

__________________________________________________ ___________

in r16, TWSR
andi r16, 0xF8
cpi r16, 0x40
brne error

ldi r16, (1<<TWINT) | (1<<TWEN) | (0<<TWEA)
out TWCR, r16

in r20, TWDR

rcall wait

in r16, TWSR
andi r16, 0xF8
cpi r16, 0x58
brne error

ldi r16, (1<<TWINT) | (1<<TWEN) | (1<<TWSTO)
out TWCR, r16
ret

wait:
in r16, TWCR
sbrs r16, TWINT
rjmp wait
ret
__________________________________________________ ___________

sking86
03.02.2010, 15:02
Hab noch was vergessen. Wollte das ganze nutzen wenn der Interrupt des PCF ausgelöst wird. bzw wenn sich was ein den Eingängen tut, weil ich da vorerst ein paar Schalter anschließen wollte.

INT (PCF) hab ich an INT2 (Mega) angeschlossen.

Sollte doch funktionieren, wenn ich das als ganz normale ISR ablaufen lasse, oder ?

mit
.org 0x006
rjmp input_isr

bax
03.02.2010, 18:12
Das wäre noch zu früh, erst wenn Du den Status 0x50 / 0x58 hast, ist ein Datenbyte im TWDR.
INT2 mußt Du im GICR-Register noch 'freischalten', damit die ISR ausgeführt wird.
__________________________________________________ ___________

in r16, TWSR
andi r16, 0xF8
cpi r16, 0x40
brne error

ldi r16, (1<<TWINT) | (1<<TWEN) | (0<<TWEA)
out TWCR, r16

rcall wait

in r16, TWSR
andi r16, 0xF8
cpi r16, 0x58
brne error

in r20, TWDR

ldi r16, (1<<TWINT) | (1<<TWEN) | (1<<TWSTO)
out TWCR, r16
ret

wait:
in r16, TWCR
sbrs r16, TWINT
rjmp wait
ret
__________________________________________________ ___________

sking86
07.02.2010, 13:39
Hey, ich hänge noch ein wenig beim Interrupt des PCFs.

Hab das ganze so gemacht, dass meine Routine bei einer steigenden Flanke an INT2 abläuft. Funktioniert auch bestens wenn ich INT2 mit 5V verbinde.

Mein Problem liegt jetzt beim PCF. Bei mir sind alle I/O des PCF auf 5V. Da im Datenblatt stand, dass der INT ausgelöst wird wenn an einem Port eine Flanke steigt oder fällt, hab ich mir gedacht einen Port über einen Taster auf GND zu legen. Wenn ich den Taster drücke habe ich GND am I/O Port des PCFs. Nur leider passiert nichts an INT.

An INT sind bei mir 0V, obwohl ich eigentlich dachte, dass man da 5V hat und bei Änderung der Ports INT auf logisch 0 geht und nicht auf logisch 1.

Was mache ich falsch?

bax
07.02.2010, 16:47
Irgendwie ist Deine Antwort in sich nicht schlüssig, findest Du nicht auch?

Richtig ist, daß der INT auf logisch 0 / 0V geht, wenn sich an den Pins was ändert. Darum heißt der Pin ja auch INT mit Strich drüber.
Dementsprechend mußt Du die fallende Flanke auswerten.

Dann gibts da noch den Hinweis im Datenblatt, daß das ein open-drain-output ist, also brauchst Du einen Pullup auf der Leitung. Kann auch der interne im mega32 sein.

Rajko

sking86
07.02.2010, 19:01
Sorry, hab mich ein bisschen unklar ausgedrückt.

Hab das mit der hohen Flanke am Mega nur mal gemacht um zu schauen ob meine ISR funktioniert.

Tatsache ist jedoch, dass ich keine 5V am INT Ausgang meines PCFs bekomme. Da müsste ich doch schon 5V haben sobald ich VDD und VSS angeschlossen habe. Hab ich jedoch nicht.

Weißt du dafür ne Antwort oder ist mein PCF kaputt?

Danke schonmal.