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:
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
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.
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!
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?
> 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.
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.
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 =)
> 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?
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.
> 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 :)
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.