Forum: Mikrocontroller und Digitale Elektronik ATmega32 INT0 Problem


von Thomas B. (ver_b) Flattr this


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
momentan versuche ich, einen ENC28J60 in Betrieb zu nehmen. Der Code 
dafür steht soweit und funktioniert gut, solange ich den ENC ständig 
nach neuen Paketen frage.
Nun möchte ich allerdings den Packet Receive Interrupt des ENC nutzen. 
Daher habe ich den INT Ausgang des ENC mit INT0 des ATmega32 verbunden. 
Die entsprechende Initialisierung sieht so aus:
1
DDRD &= ~(1 << PD2); // INT0 as input
2
PORTD |= (1 << PD2); // Enable internal ~50k pull ups
3
MCUCR &= ~((1 << ISC00) | (1 << ISC01)); // INT0 Low Level Trigger
4
GICR |= (1 << INT0); // Enable INT0

Zum Test habe ich ausserdem eine LED Active-Low an die Interrupt Leitung 
angeschlossen.
Nutze ich den Interrupt nicht und polle wie gehabt, blinkt diese LED mit 
jedem Empfangenen Paket kurz auf, wie erwartet (mein Code löscht die 
Interrupt-Flags des ENC wieder).
Versuche ich jedoch den Interrupt mit aktivierten internen PullUps zu 
nutzen, wird die ISR nie ausgeführt, obwohl die LED ab dem ersten 
Empfangenen Paket leuchtet, also INT auf '0' liegt.
Deaktiviere ich die PullUps, wird die ISR ständig ausgeführt, sobald der 
Interrupt aktiviert wird.

Ein kleines Test Programm, welches in einer Endlosschleife den Zustand 
von PD2 (INT0) auf einem anderen Pin mit angeschlossener LED ausgibt, 
zeigt das erwartete Verhalten. INT geht mit dem ersten empfangenen 
Packet von '1' auf '0'.

Der gesamte Code befindet sich in diesem GitHub Repository:
https://github.com/xythobuz/avrNetStack

Der oben angegebene Initialisierungscode steht in 
"lib/drivers/enc28j60.c" ab Zeile 324.

Der Schaltplan als Eagle ".sch" und png findet sich in "Hardware" und 
hier im Anhang.

Also, warum wird die INT0 ISR nicht ausgeführt, obwohl INT0 auf '0' 
wechselt? Was mach ich falsch? Was kann ich tun?

Mit freundlichen Grüßen,
Thomas

von michael_h45 (Gast)


Lesenswert?

sind pins 30, 31 und 32 wirklich nicht angeschlossen? die müssen 
angeschlossen werden, auch wenn die analog-komponenten nicht benutzt 
werden.
siehe erlaubte pegel an den pins im datenblatt.

davor brauchst du dich gar nicht auf fehlersuche in der software machen.

von Thomas B. (ver_b) Flattr this


Lesenswert?

Verdammt, ich meinte mich zu erinnern die wären tatsächlich nur 
notwendig, wenn man den ADC nutzt. Ich melde mich zurück sobald ich die 
Pins angeschlossen habe...
Aber vielen lieben Dank für den Hinweis!

von Michael H. (michael_h45)


Lesenswert?

Thomas B. schrieb:
> Aber vielen lieben Dank für den Hinweis!
Nachdem das Pollen ja funktioniert, sollte das aber nicht der Grund 
sein.

Thomas B. schrieb:
> Zum Test habe ich ausserdem eine LED Active-Low an die Interrupt Leitung
> angeschlossen.
> Nutze ich den Interrupt nicht und polle wie gehabt, blinkt diese LED mit
> jedem Empfangenen Paket kurz auf, wie erwartet (mein Code löscht die
> Interrupt-Flags des ENC wieder).
Was passiert, wenn du nicht das PIND2 Bit sondern das Interrupt-Flag 
INTF0 in GIFR pollst?

Gibt es einen Grund, warum du auf Level und nicht auf Flanken-wechsel 
triggern willst? Der Interrupt liegt halt sonst an, bis der ENC den Pin 
wieder wegnimmt, egal wie oft du clearst.

> Der oben angegebene Initialisierungscode steht in
> "lib/drivers/enc28j60.c" ab Zeile 324.
Zu deinem Code ab Zeile 203:
Da dürfte ein unterminiertes #if und eine }-Klammer zu viel stehen, 
oder?

von Thomas B. (ver_b) Flattr this


Lesenswert?

> Zu deinem Code ab Zeile 203:
> Da dürfte ein unterminiertes #if und eine }-Klammer zu viel stehen,
> oder?
Stimmt, blödes Copy & Paste in letzter Sekunde, ohne danach zu 
kompilieren. Danke :)

> Gibt es einen Grund, warum du auf Level und nicht auf Flanken-wechsel
> triggern willst? Der Interrupt liegt halt sonst an, bis der ENC den Pin
> wieder wegnimmt, egal wie oft du clearst.
Ich lösche ja das Interrupt Flag im ENC, dann geht INT wieder auf '1'. 
Daher sollte das egal sein.

> Nachdem das Pollen ja funktioniert, sollte das aber nicht der Grund
> sein.
> ...
> Was passiert, wenn du nicht das PIND2 Bit sondern das Interrupt-Flag
> INTF0 in GIFR pollst?
Hier hab ich mich etwas unklar ausgedrückt, glaube ich. Das Pollen hatte 
in diesem Fall mit beiden Bits nichts zu tun. Stattdessen polle ich beim 
ENC über SPI, ob Pakete empfangen wurden. Wenn ich stattdessen das PIND2 
Bit Polle funktioniert es genauso. Das INTF0 Flag zeigt keine Regung. 
Daher halte ich deine Idee mit den freien Pins immer noch für plausibel. 
Zumindest dann, wenn fehlende AVCC und AGND dafür sorgen können, dass 
INT0 nicht funktioniert, PD2 jedoch schon.

von Karl H. (kbuchegg)


Lesenswert?

Thomas B. schrieb:

>> Gibt es einen Grund, warum du auf Level und nicht auf Flanken-wechsel
>> triggern willst? Der Interrupt liegt halt sonst an, bis der ENC den Pin
>> wieder wegnimmt, egal wie oft du clearst.
> Ich lösche ja das Interrupt Flag im ENC, dann geht INT wieder auf '1'.
> Daher sollte das egal sein.

Wenn das nicht mal ein Trugschluss ist.

Die ISR wird angesprungen, wenn im Mega das Interrupt Request Flag 
gesetzt ist. Das wird zwar beim Betreten der ISR gelöscht, da aber dein 
Pin immer noch auf 0 ist, wird es gleich wieder gesetzt - und damit 
kommt dann auch gleich nach der ISR der nächste ISR Aufruf.

Entweder du löscht es von Hand oder du machst das, was du eigentlich 
wirklich willst und stellst den Interrupt auf flankengetrieben. Es 
bringt nichts, wenn man seltsame Einstellugen macht um die man sich dann 
erst recht rundherum programmieren muss.



Leg halt den ganzen ENC Kram mal zur Seite, mach dir ein Testprogramm, 
bei dem du nur den externen Interrupt in Betrieb nimmst. Bei soviel Code 
lenkt das doch alles nur vom eigentlichen Problem ab. Irgendwo ist da 
ein Wurm drinnen und ich denke nicht, dass du hier jemanden findest, der 
erst mal hunderte Lines of Code durchsucht um zu sehen, ob du irgendwo 
was verbockt hast, was sich über seltsame Wege auf den INT0 auswirkt.

von Michael H. (michael_h45)


Lesenswert?

Thomas B. schrieb:
>> Gibt es einen Grund, warum du auf Level und nicht auf Flanken-wechsel
>> triggern willst? Der Interrupt liegt halt sonst an, bis der ENC den Pin
>> wieder wegnimmt, egal wie oft du clearst.
> Ich lösche ja das Interrupt Flag im ENC, dann geht INT wieder auf '1'.
> Daher sollte das egal sein.
Ich traue mich wetten, dass deine ISR 2 mal aufgerufen wird.
Deine SPI-Transaktion zum löschen wird nur gestartet und danach sofort 
aus der ISR zurückgesprungen. Bis die SPI alle Bits draußen hat, den CS 
wieder gesetzt hat und der ENC die Daten verarbeitet hat, liegt der 
Interrupt weiter an und wird nochmal angesprungen.

>> Was passiert, wenn du nicht das PIND2 Bit sondern das Interrupt-Flag
>> INTF0 in GIFR pollst?
> Hier hab ich mich etwas unklar ausgedrückt, glaube ich. Das Pollen hatte
> in diesem Fall mit beiden Bits nichts zu tun. Stattdessen polle ich beim
> ENC über SPI, ob Pakete empfangen wurden. Wenn ich stattdessen das PIND2
> Bit Polle funktioniert es genauso. Das INTF0 Flag zeigt keine Regung.
Das ist doch schon mal eine Spur...
Ich würde als nächstes PIND2 pollen und beim Auslösen alle interessanten 
Register erst in Variablen sichern und dann per UART ausgeben lassen: 
GICR, GIFR, MCUCR.

> Daher halte ich deine Idee mit den freien Pins immer noch für plausibel.
> Zumindest dann, wenn fehlende AVCC und AGND dafür sorgen können, dass
> INT0 nicht funktioniert, PD2 jedoch schon.
Ich ehrlich gesagt nicht =)

von Thomas B. (ver_b) Flattr this


Lesenswert?

> Ich ehrlich gesagt nicht =)
Stimmt. AVCC, AGND und ARef sind jetzt verbunden, half nichts.

> Entweder du löscht es von Hand oder du machst das, was du eigentlich
> wirklich willst und stellst den Interrupt auf flankengetrieben.
Okay, ich lösche das Flag nun von Hand.

> Ich traue mich wetten, dass deine ISR 2 mal aufgerufen wird.
> ...
> Bis die SPI alle Bits draußen hat, den CS
> wieder gesetzt hat und der ENC die Daten verarbeitet hat, liegt der
> Interrupt weiter an und wird nochmal angesprungen.
Sobald der Interrupt auftritt lösche ich erst das Flag im ENC, 
anschliessend das Flag im AVR, dann deaktiviere ich INT0. Anschliessend 
beginnt die Abarbeitung des Interrupts, gefolgt von der Aktivierung des 
INT0.
Sollte dann wieder '0' an INT0 anliegen, wurde während der Verarbeitung 
ein neues Paket empfangen. Würde ich nur die Flanken erkennen, könnte 
ich so auch die Flanke verpassen und dann ewig warten. Deshalb triggere 
ich auf Low-Level. Warum das ganze? Ich will während der INT0 ISR 
weiterhin den Timer und den UART Interrupt laufen lassen.

> Ich würde als nächstes PIND2 pollen und beim Auslösen alle interessanten
> Register [...] per UART ausgeben
Aktiviere ich die internen Pull-Ups geht PIND2 mit dem ersten 
empfangenen Paket auf '0'. Ab dann haben die Register folgenden Inhalt:
MCUCR = 0x00
GICR  = 0x40
GIFR  = 0x00

Deaktiviere ich die internen Pull-Ups wird ab dem ersten Empfangenen 
Paket ununterbrochen die ISR ausgeführt. Also deaktiviere ich die ISR 
zum Test. Nun haben alle 3 Register 0x00 als Inhalt.

> Leg halt den ganzen ENC Kram mal zur Seite, mach dir ein Testprogramm,
> bei dem du nur den externen Interrupt in Betrieb nimmst.
Ja, das mache ich jetzt. Aber ich fürchte, dann wird es funktionieren, 
und es ist tatsächlich irgend eine "Race-Condition" in meinem Code... :)

Noch mal vielen Dank für eure Hilfe. Wollt ihr euch nicht auch so einen 
flattr Knopf zulegen?

von Karl H. (kbuchegg)


Lesenswert?

Thomas B. schrieb:

> Sollte dann wieder '0' an INT0 anliegen, wurde während der Verarbeitung
> ein neues Paket empfangen. Würde ich nur die Flanken erkennen, könnte
> ich so auch die Flanke verpassen

nein, könntest du nicht.

Die Leute von Atmel haben schon mitgedacht.
Genau deshalb gibt es die Flags, in denen das Auftreten eines INterrupt 
Ereignisses erst mal nur registriert wird.
Dieses Flag wird zurückgesetzt, sobald die Bearbeitung des Interrupts 
beginnt. Danach kann bis zum 'Return aus Interrupt' keine weitere ISR 
gestartet werden, aber das Flag reagiert selbstverständlich sofort 
wieder auf die nächste Flanke und speichert so, dass eine 
Interruptbearbeitung notwendig ist.

Du machst dir da selbst zu viel Arbeit für nichts.

> Ich will während der INT0 ISR
> weiterhin den Timer und den UART Interrupt laufen lassen.

Klingt für mich eher nach einem grundsätzlich schlechten Programmaufbau.
Timer und UART ISR sind traditionell eher kurz mit wenig Arbeit. 
Netzwerkpacket abholen hingegen klingt nach viel Arbeit und sollte nicht 
zur Gänze in einer ISR abgehandelt werden. DIe ISR setzt für die 
Hauptschleife ein Job-Flag und überlasst es der Hauptschleife das Packet 
zu holen.

von Thomas B. (ver_b) Flattr this


Lesenswert?

> Du machst dir da selbst zu viel Arbeit für nichts.
Verdammt...

> Klingt für mich eher nach einem grundsätzlich schlechten Programmaufbau.
Oh Mann, ich glaub du hast recht...

> DIe ISR setzt für die Hauptschleife ein Job-Flag und überlasst es der
> Hauptschleife das Packet zu holen.
Dann kann ich mir den Interrupt aber gleich ganz sparen und einfach 
schauen ob PD2 Low ist, oder?

Vielen Dank für deine Bemühungen mit meiner Doofheit :)

von Karl H. (kbuchegg)


Lesenswert?

Thomas B. schrieb:
>> Du machst dir da selbst zu viel Arbeit für nichts.
> Verdammt...
>
>> Klingt für mich eher nach einem grundsätzlich schlechten Programmaufbau.
> Oh Mann, ich glaub du hast recht...
>
>> DIe ISR setzt für die Hauptschleife ein Job-Flag und überlasst es der
>> Hauptschleife das Packet zu holen.
> Dann kann ich mir den Interrupt aber gleich ganz sparen und einfach
> schauen ob PD2 Low ist, oder?

Da der ENC den Pin ja sowieso auf Low zieht und dort hält bis zu es 
zurücksetzt: Im Prinzip ... ja

Ich finde: externe Interrupts werden an dieser Stelle sowieso 
überschätzt. Ihr Einsatz ist dann gerechtfertig, wenn das Pulssignal so 
kurz ist, dass man es durch Polling übersehen kann. Aber den Fall hast 
du ja sowieso nicht.

Ist irgendwie dasselbe wie 'externer Interrupt bei Tasten'. Weil "auf 
Tastendruck muss ja sofort reagiert werden". Programmiert man 
vernünftig, schafft man es als Mensch nicht, eine Taste so schnell zu 
drücken, dass das Programm ein Polling übersieht. Dafür braucht man 
keinen externen Interrupt. Macht alles nur komplizierter, weil man 
einplanen muss, dass der INterrupt überall und zu jeder Zeit kommen 
kann. Pollt man in der Hauptschleife dann hat man eine geordnete Abfolge 
von Abfragen und Reaktionen.

(*) und dann gibts natürlich noch den Fall, dass der Interrupt den µC 
aus dem Sleep holen muss. Auch da ist ein Interrupt ok. Aber auch den 
Fall hast du nicht.

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.