www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik TWI mit zwei ATmega8 funktioniert nicht :-(


Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi :-)

Ich bin gerade dabei einen kleinen Test mit dem I²C-Bus zur Einarbeitung 
durchzuführen. Leider klappt das nicht so wie ich mir dachte. Der Slave 
will einfach nicht hören.

bei Master und Slave habe ich einfach SDA und SCL miteinander verbunden 
und jeweils über einen Pull-Up Widerstand von 4,7kOhm nach Vcc gelegt.

Am Master hängen 4 unterschiedlichfarbige Status-LEDs an PortB 0-3 und 
eine dicke rote Error-LED an PortB 4. Ausserdem ein hardwareentprellter 
Taster an PinD 2 (INT0) welcher bei fallender Flanke den Interrupt 
auslöst um mein Programm auszuführen.

Am Slave hängen am PortD 0-7 insgesamt 8 gelbe LEDs nebeneinander. So 
kann ich  einen 8-Bit-Wert schön parallel ausgeben.

Das Testprogramm soll erstmal einen 8-Bit Wert an den Slave schicken der 
diesen dann an seinem PortD ausgibt und anschließend hochzählen. Soweit 
bin ich jedoch noch garnicht gekommen da der Slave nichtmal den 
Interrupt auszulösen scheint. Irgendwie tut er überhaupt nichts und ist 
taub. Habe ich im Code vielleicht irgendwas vergessen? Der Master erhält 
nach dem Senden der Slave-Adresse ein NOT ACK bzw eben kein ACK vom 
Slave worauf sich schließen lässt dass sich dieser nicht angesprochen 
fühlt. Die ersten beiden Status-LEDs und die Error-LED an meinem Master 
leuchten. Am Slave tut sich nichts..

Master:
.include "m8def.inc"        ; Deklarationen für ATmega8

.cseg                ; Programm-Flash
    rjmp  init        ; Reset-Einsprung

.org  0x001            ; INT0 Interrupt
    rjmp  send

.org  0x013            ; Interrupteinsprünge übergehen


init:  ldi    R16, LOW(RAMEND)  ; Stapel anlegen
    out    SPL, R16
    ldi    R16, HIGH(RAMEND)
    out    SPH, R16


    ; INT0 Interrupt
    ldi    R16, (1 << ISC01)  ; Fallende Flanke an INT0 generiert Interrupt
    out    MCUCR, R16

    ldi    R16, (1 << INT0)  ; INT0 Interrupt aktiviern
    out    GICR, R16


    ; Two-Wire-Interface
    ldi    R16, 10        ; Bitratenfaktor für 10kHz Bustakt bei 1Mhz
    out    TWBR, R16


    ; Status LED Ausgang
    ldi    R16, 0b00011111    ; PB0-3 als Ausgang
    out    DDRB, R16

    sei              ; Interrupts aktivieren

    clr    R17          ; Zu benutzende Register leeren


loop:  rjmp  loop        ; Endlosschleife



send:  ; Start Condition senden
    ldi    R16, (1 << TWINT) | (1 << TWSTA) | (1 << TWEN)
    out    TWCR, R16

    ; Warten bis ausgeführt
send1:  in    R16, TWCR
    sbrs  R16, TWINT
    rjmp  send1

    sbi    PORTB, 0      ; Rote Status-LED an

    in    R16, TWSR      ; TWI-Statusregister checken
    cpi    R16, 0x08      ; Code 0x08 = A START condition has been transmitted
    brne  error        ; Springe zu error wenn ungleich


    ; 7-Bit Slave Adresse + R/W senden (R=High, W=Low)
    ldi    R16, (0b1111111 << 1) | (0 << 1)
    out    TWDR, R16

    ; Daten senden
    ldi    R16, (1 << TWINT) | (1 << TWEN)
    out    TWCR, R16

    ; Warten bis ausgeführt
send2:  in    R16, TWCR
    sbrs  R16, TWINT
    rjmp  send2

    sbi    PORTB, 1      ; Grüne Status-LED an

    in    R16, TWSR      ; TWI-Statusregister checken
    cpi    R16, 0x18      ; Code 0x18 = SLA+W has been transmitted; ACK has been received
    brne  error        ; Springe zu error wenn ungleich



    ; Zählerdaten senden
    out    TWDR, R17      ; Zählerdaten ins Ausgangsregister laden
    inc    R17          ; Zähler inkrementieren

    ; Daten senden
    ldi    R16, (1 << TWINT) | (1 << TWEN)
    out    TWCR, R16

    ; Warten bis ausgeführt
send3:  in    R16, TWCR
    sbrs  R16, TWINT
    rjmp  send3

    sbi    PORTB, 2      ; Gelbe Status-LED an



    ; Stop Condition senden
    ldi    R16, (1 << TWINT) | (1 << TWSTO) | (1 << TWEN)
    out    TWCR, R16

    ; Warten bis ausgeführt
send4:  in    R16, TWCR
    sbrs  R16, TWINT
    rjmp  send4

    sbi    PORTB, 3      ; Blaue Status-LED an

    reti



error:  sbi    PORTB, 4      ; Rote Error-LED an
    rjmp  error

Slave:
.include "m8def.inc"        ; Deklarationen für ATmega8

.equ  adress = 0b1111111      ; TWI-Adresse

.cseg                ; Programm-Flash
    rjmp  init        ; Reset-Einsprung

.org  0x011
    rjmp  rec          ; TWI Interrupt

.org  0x013            ; Interrupteinsprünge übergehen


init:  ldi    R16, LOW(RAMEND)  ; Stapel anlegen
    out    SPL, R16
    ldi    R16, HIGH(RAMEND)
    out    SPH, R16


    ; TWI und Interrupt aktivieren
    ldi    R16, (1 << TWEN) | (1 << TWIE)
    out    TWCR, R16

    ; Slave Adresse 0001111 + TWGCE
    ldi    R16, (adress << 1) | (1 << TWGCE)
    out    TWAR, R16
  

    ; Status LEDs Ausgang
    ldi    R16, 0b11111111    ; PortD als Ausgang
    out    DDRD, R16

    sei              ; Interrupts aktivieren



loop:  in    R16, TWCR
    sbrc  R16, TWINT      ; Überspringe wenn TWINT-Bit low
    sbi    PORTD, 1      ; Status-LED 2 an

    rjmp  loop        ; Endlosschleife



rec:  sbi    PORTD, 0      ; Status-LED 1 an

/*
    in    R16, TWSR      ; Statusregister einlesen
    out    PORTD, R16
*/

    reti

Autor: ecslowhand (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nur mal kurz über den Empfänger-Code geschaut, grundsätzlich:

In der Interruptroutine musst Du als erstes mal die verschiedenen 
Statuscodes des TWSR auswerten. Diese sind im Datasheet beschrieben.

Erkennt der Slave beispielsweise, dass seine SLA-Adresse gesendet wurde, 
steht im TWSR beispielsweise 0x60.
Wenn im TWSR dann 0x80 steht, hast der µC ein Byte empfangen.

Halte Dich mal an das Datasheet, dort sind ja auch Routinen angegeben.

Ansonsten findest Du im Netz sicherlich Beispielcodes.

LG EC

Autor: Anderer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi, danke schonmal für die Antworten.

Ich weiß dass ich in der Empfänger Interruptroutine die Statuscodes zur 
weiteren Verarbeitung erstmal auswerten muss :-) Aber dazu kommt es erst 
garnicht. Der Interrupt wird überhaupt nicht ausgeführt, der Slave 
reagiert garnicht erst.

Habe mir auch das Beispiel hier obendrüber mal angeschaut, alles 
plausibel aber helfen tuts mir leider nicht wirklich :-( Einen Fehler in 
der Schaltung kann ich auch nicht wirklich entdecken. Irgendwo im 
Programm ein Fehler? Ich hab leider auch kein digitales 
Speicheroszilloskop.

mfg PoWl

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sicher, dass die LEDs auch richtig rum am Port hängen? Nicht, dass die 
Low-Side angeschlossen sind. Dann schaltest Du sie mit sbi... nämlich 
aus.

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein die LEDs funktionieren alle, hab ich schon getestet :-)

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Got it. Tja das passiert wenn man das Manual nicht von vorne bis hinten 
hin wort für wort durchließt.
    ; TWI und Interrupt aktivieren
    ldi    R16, (1 << TWEN) | (1 << TWIE)
    out    TWCR, R16

Da muss noch das TWEA Bit rein damit das ACK überhaupt gesendet wird... 
schwupp, schon funktioniert es :-)

Danke trotzdem für die Hilfen, ich werde mich melden wenn es wieder 
probleme gibt.

mfg PoWl

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Teil bringt mich noch zur verzweiflung. Nachdem ein erster Transfer 
geklappt hat, dafür ein zweiter aber nicht habe ich mal weitere 
Untersuchungen angestellt. Irgendwie ist da der Wurm drin. Entweder der 
Master sendet die Stop-Condition nicht richtig raus oder der Slave 
erkennt sie irgendiwe nicht?! Nach einschalten der Versorgungsspannung 
drücke ich auf meinen magischen INT0 Taster. Prompt leuchten alle 4 
bunten StatusLEDs am Master die mir anzeigen dass er mit seiner 
senderoutine fertig ist. Am Slave jedoch nur die beiden ersten gelben 
Status LEDs die mir zeigen dass die Adresse empfangen und ACK gesendet 
und dass die daten empfangen und ack gesendet worden sind. Jedoch wurde 
die stop condition nicht erkannt. Findet da jemand den fehler?

Master:
.include "m8def.inc"        ; Deklarationen für ATmega8

.cseg                ; Programm-Flash
    rjmp  init        ; Reset-Einsprung

.org  0x001            ; INT0 Interrupt
    rjmp  send

.org  0x013            ; Interrupteinsprünge übergehen


init:  ldi    R16, LOW(RAMEND)  ; Stapel anlegen
    out    SPL, R16
    ldi    R16, HIGH(RAMEND)
    out    SPH, R16


    ; INT0 Interrupt
    ldi    R16, (1 << ISC01)  ; Fallende Flanke an INT0 generiert Interrupt
    out    MCUCR, R16

    ldi    R16, (1 << INT0)  ; INT0 Interrupt aktiviern
    out    GICR, R16


    ; Two-Wire-Interface
    ldi    R16, 10        ; Bitratenfaktor für 10kHz Bustakt bei 1Mhz
    out    TWBR, R16


    ; Status LED Ausgang
    ldi    R16, 0b00011111    ; PB0-3 als Ausgang
    out    DDRB, R16

    sei              ; Interrupts aktivieren

    clr    R17          ; Zu benutzende Register leeren
    ldi    R17, 100      ; Startwert zum Test

loop:  rjmp  loop        ; Endlosschleife



send:  cbi    PORTB, 0
    cbi    PORTB, 1
    cbi    PORTB, 2
    cbi    PORTB, 3
    cbi    PORTB, 4

    sei



    ; Start Condition senden
    ldi    R16, (1 << TWINT) | (1 << TWSTA) | (1 << TWEN)
    out    TWCR, R16

    ; Warten bis ausgeführt
send1:  in    R16, TWCR
    sbrs  R16, TWINT
    rjmp  send1

    sbi    PORTB, 0      ; Rote Status-LED an

    in    R16, TWSR      ; TWI-Statusregister checken
    cpi    R16, 0x08      ; Code 0x08 = A START condition has been transmitted
    brne  error        ; Springe zu error wenn ungleich



    ; 7-Bit Slave Adresse + R/W senden (R=High, W=Low)
    ldi    R16, (127 << 1) | (0)
    out    TWDR, R16

    ; Daten senden
    ldi    R16, (1 << TWINT) | (1 << TWEN)
    out    TWCR, R16

    ; Warten bis ausgeführt
send2:  in    R16, TWCR
    sbrs  R16, TWINT
    rjmp  send2

    sbi    PORTB, 1      ; Grüne Status-LED an

    in    R16, TWSR      ; TWI-Statusregister checken
    cpi    R16, 0x18      ; Code 0x18 = SLA+W has been transmitted; ACK has been received
    brne  error        ; Springe zu error wenn ungleich



    ; Zählerdaten senden
    out    TWDR, R17      ; Zählerdaten ins Ausgangsregister laden
    inc    R17          ; Zähler inkrementieren

    ; Daten senden
    ldi    R16, (1 << TWINT) | (1 << TWEN)
    out    TWCR, R16

    ; Warten bis ausgeführt
send3:  in    R16, TWCR
    sbrs  R16, TWINT
    rjmp  send3

    sbi    PORTB, 2      ; Gelbe Status-LED an

    in    R16, TWSR      ; TWI-Statusregister checken
    cpi    R16, 0x28      ; Code 0x28 = Data byte has been transmitted; ACK has been received
    brne  error        ; Springe zu error wenn ungleich



    ; Stop Condition senden
    ldi    R16, (1 << TWINT) | (1 << TWSTO) | (1 << TWEN)
    out    TWCR, R16

    sbi    PORTB, 3      ; Blaue Status-LED an

    reti



error:  sbi    PORTB, 4      ; Rote Error-LED an
    reti

Slave:
.include "m8def.inc"        ; Deklarationen für ATmega8

.equ  adress = 127        ; TWI-Adresse

.cseg                ; Programm-Flash
    rjmp  init        ; Reset-Einsprung

.org  0x011
    rjmp  twis        ; TWI Interrupt

.org  0x013            ; Interrupteinsprünge übergehen


init:  ldi    R16, LOW(RAMEND)  ; Stapel anlegen
    out    SPL, R16
    ldi    R16, HIGH(RAMEND)
    out    SPH, R16


    ; Slave Adresse 1111111 + TWGCE
    ldi    R16, (adress << 1) | (1 << TWGCE)
    out    TWAR, R16

    ; TWI und Interrupt aktivieren
    ldi    R16, (1 << TWEA) | (1 << TWEN) | (1 << TWIE)
    out    TWCR, R16
  

    ; Status LEDs Ausgang
    ldi    R16, 0b11111111    ; PortD als Ausgang
    out    DDRD, R16

    sei              ; Interrupts aktivieren



loop:  rjmp  loop        ; Endlosschleife



twis:  in    R16, TWSR
    cpi    R16, 0x60      ; 0x60 = Own SLA+W has been received; ACK has been returned
    brne  twis1        ; Springe wenn ungleich

    ; Data byte will be received and ACK will be returned
    ldi    R16, (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE)
    out    TWCR, R16

    sbi    PORTD, 0      ; Gelbe Status-LED 1 an

    rjmp  twis3


twis1:  in    R16, TWSR
    cpi    R16, 0x80      ; 0x80 = Previously addressed with own SLA+W; data has been received; ACK has been returned
    brne  twis2        ; Springe wenn ungleich

    sbi    PORTD, 1      ; Gelbe Status-LED 2 an

;    in    R16, TWDR      ; Daten an PortD ausgeben
;    out    PORTD, R16

    rjmp  twis3


twis2:  in    R16, TWSR
    cpi    R16, 0xA0      ; 0xA0 = A STOP condition or repeated START condition has been received while still addressed as Slave
    brne  twis3        ; Springe wenn ungleich

    ; Switched to the not addressed Slave mode; own SLA will be recognized; GCA will be recognized if TWGCE = 1
    ldi    R16, (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE)
    out    TWCR, R16  

    sbi    PORTD, 2      ; Gelbe Status-LED 3 an


twis3:    reti            ; Kehre zurück

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Leider kann ich den Post nicht mehr editieren. Mir ist aufgefallen dass 
wenn ich das erste mal die Übertragung starte eine zweite nicht möglich 
ist. Die Start Condition wird vom Master irgendwie nicht gesendet bzw. 
das TWINT-flag wird einfach nicht gesetzt?! Wenn ich den Slave nach der 
ersten Übertragung jedoch resette dann geht es wieder. Wenn ich den 
Slave resette nachdem ich die zweite übertragung gestartet habe (also 
einfach 2 mal hintereinander auf den taster drücken) dann läuft das 
programm im master weiter und sendet die SLA + R/W. Danach geht die 
Error LED an was bedeutet dass der Slave kein ACK sendet.

Ich habe das Masterprogramm mal geringfügig modifiziert.. testweise. 
Egal ob ich die Stop-conidition am ende nun sende oder nicht, der slave 
reagiert gleich. Er erkennt sie einfach nicht. Irgendwo ist da der Wurm 
drin aber ich bin schon ganz verzweifelt.. ich finde keinen Fehler 
mehr.. möglicherweise hab ich ja was übersehen, nur was :-S

mfg PoWl

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tut mir leid dass ich hier soviele postings mache.. kann leider 
irgendwie nicht editieren!?

Ich habe mal im slave einen zähler eingebaut der die interrupts zählt 
und am PortD ausgibt.

dann habe ich mal im master die 4 schritte Start-Condition senden, SLA + 
W senden, Daten senden, Stop Condition senden schrittweise 
auskommentiert. Zuerst habe ich die letzten 3 auskommentiert, d.h. der 
master sendet nur eine startcondition und macht garnix. Wie zu erwarten 
führt der slave kein interrupt aus. Die LEDs an Port D bleiben aus.

als nächstes habe ich nur die letzten beiden schritte im master 
auskommentiert, es wird jetzt also die startcondition gesendet und 
SLA+W. Der Interruptzähler im Slave zeigt an Port D nun einen interrupt 
an, wie zu erwarten. Am Master leuchten natürlich die ersten beiden 
bunten Status-LEDs.

dann habe ich mal den letzten schritt auskommentiert. D.h. es werden 
jetzt startcondition, SLA+W und das Datenbyte gesendet. 
Unerwarteterweise leuchteten nun sämtliche LEDs an Port D, d.h. es 
würden 255 Interrupts ausgelöst. Wieso denn so viele? Und wieso denn 
genau 255? Ich nehme mal an das liegt am Master aber ich kann im code 
auch keinen fehler entdecken der soviele datenbytes hintereinander 
sendet. Ist meine TWI hardware kaputt oder habe ich irgendwas grob 
falsch gemacht?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.