Forum: Mikrocontroller und Digitale Elektronik AVR blockiert den I2C


von Florian G. (suffix)


Angehängte Dateien:

Lesenswert?

Servus,

folgendes Problem:
2 AtMega16 mit integrierter I2C-Steuerung miteinander verbunden. Der 
eine schickt, der andere empfängt. Funktioniert soweit auch ganz gut. Er 
reagiert nur, wenn er auch per Adresse selektiert ist und gibt die 
richtigen Daten aus. Jedoch gibt der Empfäger partout den Bus nicht mehr 
frei, dh die Taktleitung bleibt low. Das ist lt. Datenblatt das Zeichen, 
dass der Controller noch mit der Bearbeitung der Befehle beschäftigt ist 
und bitte in Ruhe gelassen werden möchte. Aber ich kann den nur per 
Reset wieder freimachen, ich kapiers echt nicht. Der Controller ist 
längst fertig.

Das ganze ist nur ein Test, deswegen ein sehr simpler Test und auch ohne 
Statuscontrolle. Lt. Datenblatt sollte der Code korrekt sein.

Im Anhang ist das Sende-file (main.asm) und der Empfängercode (rec.asm). 
Was muss ich da wo reinmachen, damit der Bus wieder freigegeben wird, 
bzw die Leitung wieder auf high gelassen wird?

von Läubi (Gast)


Lesenswert?

Ich glaub man muss das TWI einmal aus und wieder an machen oder der 
Master muss ein STOP senden.

von Florian G. (suffix)


Lesenswert?

wie meinst das? an und aus?

Der Master sendet ein Stop, wenn ich eine andere Adresse nehme, aufdie 
keiner reagiert, dann funktioniert das auch. Erst wenn ein Slave drauf 
anspringt, dann gibt der den Bus nichmehr frei.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Nach ende der Transmission muss der Master ein STOP senden.
Alternativ: Slave deaktiviert sein TWI und aktiviert es danach wieder 
(so auch im Fehlerfalle)

von Florian G. (suffix)


Lesenswert?

Hm, schaut euch bitte mal meine Codes an. Ist im Anhang des ersten 
Postings. Da wird doch in der richtigen Reihenfolge alles gesendet, und 
am Schluss noch eine Stop-Bedingung. Was ist denn da falsch?

von Axel R. (Gast)


Lesenswert?

'Nabend

habe mir mal den Code angesehen.
Beim Master scheint auf den ersten Blick alles ok zu sein.
Aber beim Empfänger:
hier benutzt Du den Interrupt.
im Label "rec:" bestätigst du die 0x60 (Slaveadressrec+W)
im label "rec1:" holst Du dir die Daten ab.
Das funktioniert ja auch, wie du oben geschrieben hast.
Nun musst Du, genau wie Du auf deinen Adressmatch geantwortet hast, auch 
den Datenempfang bestätigen.
1
...
2
...
3
rec:    cli        ;Interrupts abschalten
4
  ldi  r16,(1 << TWINT) | (1 << TWEN) | (1 << TWEA)
5
  out  TWCR,r16    ;Daten holen
6
7
rec1:   in   r16,TWCR    ;
8
  sbrs r16,TWINT    ;und kontrollieren
9
  rjmp rec1      ;
10
  in   r16,TWDR    ;nach r16
11
  out  PORTB,r16    ;dann PortB
12
  ldi  r16,(1 << TWINT) | (1 << TWEN) 
13
  out  TWCR,r16  
14
  sei        ;Interrupts an
15
  reti        ;Rücksprung
16
  .EXIT

Ein ACK brauchst Du nicht senden, da das eine Datenbyte ja eh das 
letzte, weil einzigstes, war.

Hoffe's hilft (und stimmt, habs jetzt nicht ausprobiert)

AxelR.

von Peter D. (peda)


Lesenswert?

Wenn der Slave SCL auf low hält, dann hat das seinen Grund, nämlich Du 
hast den Interrupt nicht bearbeitet.

Das stimmt auch, da Du nach dem ersten Interrupt jeden weiteren 
Interrupt sperrst.


Auch mußt Du die Daten lesen, bevor Du das Interruptflag löschst.

Und zu allererst mußt Du den Status lesen und prüfen, ob das überhaupt 
ein Datenbyte ist, welches den Interrupt ausgelöst hat.


Und schmeiß mal diesen ganzen cli/sei-Mist aus den Interrupts raus, das 
hat da nichts verloren (Datenblatt lesen).


Peter

von Axel R. (Gast)


Lesenswert?

cli im Interrupt ist wahrlich nicht von nöten. Mcht er ja schon von 
Hause aus.
Das der Interrupt abgeschaltet wird, habe ich auch geshen. Hat mich aber 
nicht weiter interessiert, da das TWINT Flag auch ohne Int. kommt.
Das wird ja dann letztendlich gepollt.
Bisken kryptisch und ungewohnt erscheint mir die gemischte Nutzung von 
Interrupt und Polling allerdings auch.

>nämlich Du hast den Interrupt nicht bearbeitet.

macht er jetzt spätestens, wenn er das TWINT Flag setzt

von Florian G. (suffix)


Lesenswert?

okay, danke euch allen. es lag an der anweisung von axel, soe bearbeitet 
den letzten interrupt.

@peter: okay, sei/cli hat da nichts zu suchen. ich wollte damit nur 
folgendes verhindern: im slave receiver mode können u.a. folgende 
aktionen einen interrupt auslösen: Der Controller wurde angesprochen per 
eigener Adresse oder General Call und ACK ist gesendet worden. Daten 
sind empfangen worden, für die 2 Aktivierungsarten+nach ACK/NACK 
getrennt. JEDER dieser Aktionen würde ja dann den Interrupt auslösen. In 
meinem Slave-Programm werden ja folgende 3 Aktionen beantwortet:

1.Sla+Write empfangen
2.Daten empfangen
3.Stop erkannt.

Wenn man die Interrupts anlässt, springt der Controller nicht bei jeder 
Aktion zum Anfang des Service-Programms zurück? Wartet er bis reti 
erreicht ist? Ich habe heute extra nochmal das Datenblatt durchgesehen, 
aber leider nichts gefunden.

von Peter D. (peda)


Lesenswert?

Axel Rühl wrote:

> Das der Interrupt abgeschaltet wird, habe ich auch geshen. Hat mich aber
> nicht weiter interessiert, da das TWINT Flag auch ohne Int. kommt.
> Das wird ja dann letztendlich gepollt.

Ich sehe da nur:
1
loop:  rjmp  loop

Wenn also der Interrupt abgeschaltet ist, wird nichts gepollt.


Peter

von Peter D. (peda)


Lesenswert?

Florian Glaser wrote:

> getrennt. JEDER dieser Aktionen würde ja dann den Interrupt auslösen. In
> meinem Slave-Programm werden ja folgende 3 Aktionen beantwortet:
>
> 1.Sla+Write empfangen
> 2.Daten empfangen
> 3.Stop erkannt.


So gehts nicht.
Wenn ein Interrupt kommt, mußt Du ihn immer bearbeiten, auch wenn ein 
Status kommt, den Du nicht erwartest.
Ansonsten bleibt SCL = low und kein weiterer Interrupt kann kommen.


> Wenn man die Interrupts anlässt, springt der Controller nicht bei jeder
> Aktion zum Anfang des Service-Programms zurück? Wartet er bis reti
> erreicht ist? Ich habe heute extra nochmal das Datenblatt durchgesehen,
> aber leider nichts gefunden.

Siehe Status Register Bit 7:

The I-bit is cleared
by hardware after an interrupt has occurred, and is set by the RETI 
instruction to enable
subsequent interrupts. The I-bit can also be set and cleared by the 
application with the
SEI and CLI instructions, as described in the instruction set reference.


Peter

von Axel R. (Gast)


Lesenswert?

Peter Dannegger wrote:
> Axel Rühl wrote:
>
>> Das der Interrupt abgeschaltet wird, habe ich auch geshen. Hat mich aber
>> nicht weiter interessiert, da das TWINT Flag auch ohne Int. kommt.
>> Das wird ja dann letztendlich gepollt.
>
> Ich sehe da nur:
>
>
1
> loop:  rjmp  loop
2
>
>
> Wenn also der Interrupt abgeschaltet ist, wird nichts gepollt.
>
>
> Peter

erstmal springt er nach loop und häüngt dort.
Dann kommt der erste Interrupt
in der ISR wird der int abgeschaltet und das Flag gepollt
Die ISR wird nicht mehr verlassen, bis die Daten für die LEDs angekommen 
sind.
Jetzt werden die Interrupts wieder erlaubt und die ISR verlassen und 
sofort wieder bei Loppjmploop weitergearbeitet.

Funktioniert schon so, wenn auch etwas kryptisch un ungewöhlich, da 
nicht alle Stati ausgewertet werden - auch nicht beim Pollen des TWINT 
Flag.
Es kann schon sein, das zwischen der 60 und der 80(89) noch was anderes 
kommt. Dann hat er noch die alten Werte im TDR.

Ich würde mir da auch 'ne statemaschine basteln, klar. Aber wenn das 
Programm sonst nichts weiter zu tun hat.

Erinnert mich son bisken an Warteschleifen<->timer Diskussion.


Gruß
AxelR.

von Peter D. (peda)


Lesenswert?

Axel Rühl wrote:

> erstmal springt er nach loop und häüngt dort.
> Dann kommt der erste Interrupt
> in der ISR wird der int abgeschaltet und das Flag gepollt
> Die ISR wird nicht mehr verlassen, bis die Daten für die LEDs angekommen
> sind.
> Jetzt werden die Interrupts wieder erlaubt und die ISR verlassen und
> sofort wieder bei Loppjmploop weitergearbeitet.

Nö:
1
ldi    r16,(1 << TWINT) | (1 << TWEN) | (1 << TWEA)
2
out    TWCR,r16      ;Daten holen

Es wird also TWIE gelöscht und damit sind weitere I2C-Interrupts 
gesperrt.


Peter

von Bernhard S. (bernhard)


Lesenswert?

Hallo Florian,

hierzu habe ich mal ein kleines Beispiel für einen MASTER und einen 
SLAVE erstellt, 2 BYTE senden und empfangen, villeicht hilft es:


Beitrag "TWI / I2C einf. MASTER SLAVE Beispiel(Assembler) ATmega8"


Bernhard

von Axel R. (Gast)


Lesenswert?

Peter Dannegger wrote:
> Axel Rühl wrote:
>
>> erstmal springt er nach loop und häüngt dort.
>> Dann kommt der erste Interrupt
>> in der ISR wird der int abgeschaltet und das Flag gepollt
>> Die ISR wird nicht mehr verlassen, bis die Daten für die LEDs angekommen
>> sind.
>> Jetzt werden die Interrupts wieder erlaubt und die ISR verlassen und
>> sofort wieder bei Loppjmploop weitergearbeitet.
>
> Nö:
>
>
1
> ldi    r16,(1 << TWINT) | (1 << TWEN) | (1 << TWEA)
2
> out    TWCR,r16      ;Daten holen
3
>
>
> Es wird also TWIE gelöscht und damit sind weitere I2C-Interrupts
> gesperrt.
>
>
> Peter

Das TWINT Flag wird aber trotzdem gesetzt, auch wenn der Interrupt 
gesperrt ist. Dieses wird gepollt.
Es ist vom Programmablauf nicht nötig, das ein neuer Interrupt ausgelöst 
wird - und auch nicht erwünscht.

So sehe ich das...

von Peter D. (peda)


Lesenswert?

Axel Rühl wrote:

>>> Jetzt werden die Interrupts wieder erlaubt und die ISR verlassen und
>>> sofort wieder bei Loppjmploop weitergearbeitet.
...
> Das TWINT Flag wird aber trotzdem gesetzt, auch wenn der Interrupt
> gesperrt ist. Dieses wird gepollt.

Du solltest Dich mal entscheiden, wird der Interupt wieder erlaubt oder 
gepollt.

In Loppjmploop wird jedenfalls nichts gepollt und wegen TWEA=0 auch kein 
Interupt mehr ausgeführt.

Aber egal, ob pollen oder Interrupt, man muß TWINT immer zurücksetzten 
(also setzten), sonst hängt der I2C forever.



> Es ist vom Programmablauf nicht nötig, das ein neuer Interrupt ausgelöst
> wird - und auch nicht erwünscht.

Dann muß man aber das TWI ganz abschalten (TWEN = 0), ansonsten hängt 
der I2C, wenn der Master wieder drauf zugreift und der Interrupt nicht 
bearbeitet wird.


Peter

von Florian G. (suffix)


Lesenswert?

Danke euch allen.

Zu der ganzen Interrupt-Diskussion: Mit der ersten Instruktion im 
Service-Programm wird in der Tat TWIE 0 gesetzt und hat somit keine 
Wirkung mehr. Aber da ja in SREG I beim betreten des Service-Programms 
sowieso 0 gesetzt wird, ist das egal.

Das TWINT-Flag heißt zwar TWI-Interrupt, wird aber einfach nur nach 
jeder Aktion auf dem Bus gelöscht. Erst zusammen mit TWIE=1 löst es auch 
wirklich einen Interrupt aus, benutzen kann man es nach wie vor zur 
Steuerung. So richtig?

Die ganze Statusüberprüfung werde ich noch einbauen.

von Axel R. (Gast)


Lesenswert?

habe mich entschieden:
erst wird der int erlaubt (bis0x60), dann wird in der ISR verweilt und 
weiter das TWINT Flag gepollt und ordnungsgemäß gelöscht. Nach 
Vollendeter Datenübertragung wird der TWI Interrupt - jetzt gebe ich Dir 
ENDLICH Recht -  NICHT wieder erlaubt und der Kerl bleibt in loppjmploop 
schleife hängen!
Beim zweiten Versuch, irgentwas an den LEDs zu schalten, hängt der I2C 
Bus, da weder das TWI abgeschaltet wurde, noch das TWINT Flag 
zurückgesetzt werden kann, da ein setzen dieses nicht bemerkt wird (Kein 
Int aktiv und Loopjmploop).

Also geht der Spass genau ein einziges mal :-))

Und wenn zwischendurch noch was anderes aufm Bus passiert, nicht mal 
das.

Ich denke, jetzt ham was alle begriffen - auch ich.

Viele Grüße
AxelR.

von Florian G. (suffix)


Lesenswert?

Ja, das ist mir jetzt auch klar geworden, korrigiere ich denn ma ;)

von Florian G. (suffix)


Lesenswert?

falls du übrigens micht gemeint hast mit recht geben: ich habe die ganze 
zeit nur beobachtet, was ihr über die interrupts diskutiert. In meinem 
vorherigen Post war ich bereits deiner Meinung und das Problem wurde mir 
klar, dass man TWIE wieder anschalten muss, vor dem Rücksprung :)

von Peter D. (peda)


Lesenswert?

Florian Glaser wrote:

> klar, dass man TWIE wieder anschalten muss, vor dem Rücksprung :)


Du hättest es garnicht erst ausschalten müssen, denn Du bist ja noch im 
Interrupt und kannst Dich daher nicht selbst unterbrechen.

Generell würde ich pollen in einem Interrupt aber vermeiden, da ja nicht 
klar ist, wann und ob überhaupt das Bit wieder gesetzt wird.

Pollen in Interrupts ist also eine gute Chance, auf ewig hängen zu 
bleiben.

Eine Ausnahme wäre pollen auf Timerablauf oder ADC-fertig, die müssen ja 
nach der definierten Zeit kommen.


Peter

von Florian G. (suffix)


Lesenswert?

Moment.

Habe ich das richitg verstanden: Jedesmal, wenn ich TWCR lade und nicht 
explizit 1<<TWIE mache, wird es null?

Und was meint ihr denn genau mit pollen? Was soll ich vermeiden, und 
warum kann man da auf ewig hängen bleiben? könnt ihr mir des pls 
erklären?

edit: falls du mit pollen twint setzen meinst, das muss sein. anders 
kriegt man ja keine aktion an die TWI-Steuerung raus.

von Florian G. (suffix)


Lesenswert?

noch jemand am lesen`?

von Rahul, der Trollige (Gast)


Lesenswert?

>Jedesmal, wenn ich TWCR lade und nicht
>explizit 1<<TWIE mache, wird es null?

Wenn ich die Frage (bzw. den ersten Teil der Frage) richtig verstehe:
Alle Interrupts werden während der Ausführung einer ISR ausgeschaltet.
Die Interrupts können zwar auftreten (wird durch Setzen der 
entsprechenden Interrupt-Flags gemacht), aber sie werden zu diesem 
Zeitpunkt nicht angesprungen. Das passiert frühestens nach Verlassen der 
aktuellen ISR.

>Und was meint ihr denn genau mit pollen?
Mit Pollen/Polling ist die Abfrage eines Zustandes gemeint. Es ist das 
("manuelle") Gegenstück zur Interrupt-Programmierung.
Das was der eingebaute Interrupt-Controller macht, macht man in diesem 
Fall in Software.

von suffix (Gast)


Lesenswert?

kay, die 2te sache ist mir jetzt klar.

zum ersten: dass alle Interrupts während der ISR gesperrt sind, weiß 
ich.

Meine Frage war lediglich, wenn ich in ein Register 2-3 Bits per obigem 
Lade-Befehl 1 setze, ob dann alle anderen Bits in diesem Register 
automatisch 0 werden, also setzt zb

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

TWSTA, TWSTO und TWIE = 0?

das war meine frage :)

von AxelR. (D1RTO) (Gast)


Lesenswert?

>Meine Frage war lediglich..alle anderen Bits..automatisch 0 werden

Ja, dem ist so!
Du könntest das TWCR einlesen mit "in"
deine Bits in einem deiner Temp register setzen
mit deinem "einleseregister" verodern
und mit "out" ins TWCR zurückschreiben.

von Florian G. (suffix)


Lesenswert?

super, danke!

von Axel R. (Gast)


Lesenswert?

 in  r16,TWCR
 out TWCR, r16

sollte hierbei schon reichen.

Das TWINT Bit ist bereits gesetzt, um es zu löschen musst Du es setzen.
TWEN ist gesetzt und soll es bleiben, glieches gilt für TWEA.
gibt also keine Notwendigkeit (meistens jedenfalls) an den Bits was zu 
ändern.

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.