Hallo,
verwende den Atmega 649A und will auf 2 Pins den PinChange Interrupt
aktivieren. Das Datenblatt dazu:
http://www.atmel.com/Images/doc8284.pdf
Ich will den PCINT12 und PCINT14 (Pins 14 und 16) aktivieren, alle
anderen sollen nicht aktiv sein.
Zuerst setze ich das PCINT1 Bit im EIMSK Register, damit die Interrupts
PCINT8-PCINT15 aktiviert sind. Den globalen Interrupt enable führe ich
mit sei() aus.
Seite 64: Register EIFR, Bit 5:
Wenn nun auf einen dieser Pins (PCINT8-15) eine Flanke auftritt, wird
dieses Bit gesetzt (1) und die ISR ausgeführt, richtig? Wenn die ISR
ausgeführt ist, wird dieses Bit automatisch wieder gelöscht?! Warum
steht dann da im letzten Satz, dass man dieses Flag auch löschen kann,
wenn man eine 1 reinschreibt, es also setzt?!! Verstehe ich da was
falsch oder ist das ein Fehler im Text?
Vielen Dank für die Hilfe!!
Manuel schrieb:> steht dann da im letzten Satz, dass man dieses Flag auch löschen kann,> wenn man eine 1 reinschreibt, es also setzt?!! Verstehe ich da was> falsch oder ist das ein Fehler im Text?
Weil die ISR auch angesprungen werden muss, damit das BIt gelöscht wird.
Manchmal ist man aber gar nicht daran interessiert, dass es eine ISR
gibt. Alles was man will ist, dass die Hardware das betreffende Bit
setzt. Und dann muss es eine Möglichkeit geben, es auch wieder zu
löschen.
Und ja. Das ist schon richtig. Diese Bits werden durch einschreiben
einer 1 gelöscht.
PCMSK1&=~((1<<PCINT8)|(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11)|(1<<PCINT13)|(1<<PCINT15));// disable the other Interrupts of this group
8
9
sei();// Enable Interrupt
Ist das so korrekt, wenn ich will, dass NUR PCINT12 und PCINT14 aktiv
sind, das heißt, alle anderen können keine Interrupts auslösen? Ich
frage nach, weil diese Gruppe (PCINT8-PCINT15) eine GEMEINSAME ISR
haben.
Wird eigentlich ein Interrupt Flag auch gesetzt, wenn ein Interrupt
disabled ist? Wenn cih z.B. irgendwo später iom Programm den PCINT12 so
disable:
1
PCMSK1&=~(1<<PCINT12);
wird dann trotzdem ein Flag gesetz, die ISR aber nicht ausgeführt? Oder
wird dann auch das Flag nicht gesetzt? Wenn es troptzdem gesetzt wird,
kann es dann passieren, dass wenn ich den Interrupt wieder enable:
1
PCMSK1|=(1<<PCINT14);
dass dann sofort ein Interrupt ausgelöst wird, auch wenn gerade kein Pin
Change stattgefunden hat?
Vielen Dank!!!
Manuel schrieb:> Wird eigentlich ein Interrupt Flag auch gesetzt, wenn ein Interrupt> disabled ist?
Ja.
Diese Flags registrieren das Auftreten des Ereignisses.
Unabhängig davon, ob die Interrupts dafür auch aktiviert sind.
> dass dann sofort ein Interrupt ausgelöst wird, auch wenn gerade kein Pin> Change stattgefunden hat?
Ganz genau.
Jetzt hast du einen 2.ten Grund gefunden, warum man diese Bits händisch
löschen können muss.
OK vielen Dank :)
Trotzdem sind jetz noch ein paar Unklarheiten offen:
1.) Ich habe deinen ersten Beitrag nicht ganz verstanden: wenn ich das
Flag löschen will, schreib ich eine 1 rein, damit die ISR ausgeführt
wird und dadurch das Flag gelöscht wird? Was ist aber wenn ich nicht
will dass die ISR ausgeführt wird? Warum kann ich nicht einfach eine 0
rein schreiben, ohne dass die ISR ausgeführt wird?
2.) Meiner Meinung nach müsste nun nach deinen Erklärungen mit folgender
Zeile ein Interrupt ausgelöst und die ISR ausgeführt werden (zusätzlich
zu vorigem Code):
1
EIFR|=(1<<PCIF1);
Funktioniert aber nicht, warum?
3.) Ich hatte das Problem, dass die ISR ungewollt aufgerufen wurde. Erst
danach ist mir eingefallen, dass es einer der anderen Pins sein könnte,
da ja alle eine gemeinsame ISR haben. Erst danach habe ich folgende
Zeile im obigen Code hinzugefügt:
1
PCMSK1&=~((1<<PCINT8)|(1<<PCINT9)|(1<<PCINT10)|(1<<PCINT11)|(1<<PCINT13)|(1<<PCINT15));// disable the other Interrupts of this group
ich wollt nun trotzdem noch auf nummer sicher gehn und jedesmal, wenn
ich einen der beiden Interrupts wieder freigeben (werden innerhalb des
Codes auch deaktiviert) vorher das dazugehörige Flag löschen, damit
nicht sofort und ungewollt ein Interrupt ausgelöst wird, weil das Flag
gerade gesetz ist. Wie mache ich das, wenn ich nicht wioll dass dabei
die ISR aufgerufen wird? Bzw. ist das überhaupt sinnvoll?
Dankesehr :)
und noch eine Frage:
4.) Gibt es überhaupt eine Möglichkeit, innerhalb der ISR
herauszufinden, welcher der 8 Interrupts (PCINT8-15) den Interrupt
ausgelöst hat?
Manuel schrieb:> 1.) Ich habe deinen ersten Beitrag nicht ganz verstanden: wenn ich das> Flag löschen will, schreib ich eine 1 rein, damit die ISR ausgeführt> wird und dadurch das Flag gelöscht wird?
Von welchen Bits reden wir eigentlich?
Ich rede von den Bits im EIFR. Das sind die Bits, welche von der
Hardware gesetzt werden, wenn ein bestimmtes Ereignis (in diesem Fall
eben Pin Change) eintritt. Wird von der Hardware ein Pin Change
festgestellt, wird das zugehörige Bit im EIFR auf 1 gesetzt.
Damit ist noch nichts ausgesagt, was weiter passiert.
Diese Bits registrieren nur, dass das Ereignis stattgefunden hat.
Das muss auch so sein, denn es muss einen Mechanismus geben, der genau
dieses registriert, wenn die zugehörige ISR nicht ausgeführt werden
kann, zb weil gerade eine Timer-ISR am arbeiten ist. Die wird ja
deswegen nicht unterbrochen, sondern arbeitet fertig ab, nach dem Return
von dieser ISR werden die Interrupts global wieder freigegeben und dann
bemerkt die CPU, dass eines dieser Flags in EIFR gesetzt ist und
entscheidet weiter ob das zugehörige Mask Bit gesetzt ist und wenn ja,
wird die ISR aufgerufen.
Du hast also 2 Mechanismen am Werk
* der eine ist dafür zuständig, das Auftreten eines Ereignisses zu
registrieren. Das sind die Bits im EIFR, oder allgemeiner ausgedrückt
sind das die Bits, die im Datenblatt immer irgendwie etwas mit "Interupt
Flag" im Namen haben. Das 'Flag' ist der Hinweis darauf, dass das die
'Merker' sind, die sich das AUftreten eines Ereignisses merken. Das sind
auch die Bits, die mittels einschreiben einer 1 gelöscht werden.
* der andere Mechanismus ist der Mechanismus, der darüber entscheidet,
ob eine ISR aufgerufen wird.
Auch das ist bei allen Interrupts systemweit gleich. Zum einen gibt es
ein Bit, das spezifisch für einen bestimmten Interrupt ist. Zum anderen
gibt es die globale Interrupt Freigabe.
Damit eine ISR aufgerufen wird, müssen also 3 Bedingungen zutreffen, die
man mit 2 Schalgworten zusammenfassen könnte: Das Ereignis muss
eingetreten sein UND die entsprechenden ISR müssen freigegeben sein.
* Das entsprechende Ereignis muss aufgetreten sein, sprich das
zugehörige 'Interupt Flag' Bit muss gesetzt sein. Logisch, gab es keinen
Timer Overflow, wird auch kein Timer Overflow Interrupt ausgelöst.
* Das spezifische Interupt Freigabe Bit für diesen Interrupt muss
gesetzt sein. Auch logisch. Nur weil ein Timer Overflow aufgetreten ist,
bedeutet das ja nicht, dass mich der interessiert. Ich muss also
explizit sagen: Ich will auf Timer Overflows in Form einer ISR
reagieren.
* Die globale Interrupt Freigabe muss gesetzt sein (sei()). Auch
logisch. Es muss einen Mechanismus geben, mit dem man alle Interrupts
auf einen Schlag abschlaten kann, ohne dann beim Wiedereinschalten durch
die ganze Neukonfiguration aller Interrupts gehen zu müssen. Ein
Hauptschalter sozusagen.
> Was ist aber wenn ich nicht> will dass die ISR ausgeführt wird?
Dann gibst du den Interrupt eben nicht frei.
Aber deswegen tritt ja das Ereignis immer noch auf, auch wenn du nicht
in Form einer ISR darauf reagierst.
> Warum kann ich nicht einfach eine 0> rein schreiben, ohne dass die ISR ausgeführt wird?
Ich denke, wir reden im Moment von verschiedenen Bits.
Es sind ausschliesslich die 'Interupt Flags' die durch einschreiben
einer 1 gelöscht werden. Das sind aber die Merker-Bits. Die, in denen
das Auftreten eines Ereignisses registriert wird.
> 2.) Meiner Meinung nach müsste nun nach deinen Erklärungen mit folgender> Zeile ein Interrupt ausgelöst und die ISR ausgeführt werden (zusätzlich> zu vorigem Code):>
1
>EIFR|=(1<<PCIF1);
2
>
> Funktioniert aber nicht, warum?
Weil du so das Bit löscht und nicht setzt.
Diese Bits im Flag Register werden durch das Auftreten des zugehörigen
Ereignisses gesetzt. Von der Hardware. Wenn du sie gewollte setzen
willst, dann steht es dir frei selber im Programm durch Beschreiben der
Port Register ein derartiges Ereignis auszulösen. Aber abgesehen davon
kannst du die nicht setzen. Du kannst sie nur löschen, wenn du willst.
Hi
>4.) Gibt es überhaupt eine Möglichkeit, innerhalb der ISR>herauszufinden, welcher der 8 Interrupts (PCINT8-15) den Interrupt>ausgelöst hat?
Geht nur wenn du dir den Zustand vom vorherigen Interrupt gemerkt hast.
Dann reicht ein Ex-Or.
MfG Spess
Hmmm, danke für die ausfürliche Antwort.
Wenn ich nun wie oben im Code beschrieben nur die bedien externen
Interrupts PCINT12 und PCINT14 aktiviert (1 gesetzt) habe und die
anderen 6 dieser Gruppe (8-15) deaktiviert (0 gesetzt) habe, kann ich
davon ausgehen, dass die (gemeinsame) ISR nur von ereignissen an PCINT
12 und 14 aufgerufen wird? Alle anderen setzten zwar das PCIF1 im EIFR,
lösen aber die ISR NICHT aus? Ist das korrekt?
Aber wenn die deaktivierten auch eine 1 ins EIFR schreiben, woher weiß
der Controller dann, welcher INterrupt das war? Es gibt ja nur 1 Bit im
EIFR (PCIF1) für 8 Interruptquellen?
Warum sollte man dann das Interrupt Flag eigentlich löschen, wenn die
ISR sowiso nicht ausgelöst wird?
Und wie ist das mit dem Erkennen des Interrupts in der ISR? habe ich da
eine Chance zu erfahren, von welchem der 8 Interrupts die ISR ausgelöst
wurde (angenommen alle wären aktiviert)?
Tut mir leid, aber ich denke ich steh noch auf der Leitung... Vielen
Dank für die Hilfe!
Manuel schrieb:> Hmmm, danke für die ausfürliche Antwort.>> Wenn ich nun wie oben im Code beschrieben nur die bedien externen> Interrupts PCINT12 und PCINT14 aktiviert (1 gesetzt) habe und die> anderen 6 dieser Gruppe (8-15) deaktiviert (0 gesetzt) habe, kann ich> davon ausgehen, dass die (gemeinsame) ISR nur von ereignissen an PCINT> 12 und 14 aufgerufen wird? Alle anderen setzten zwar das PCIF1 im EIFR,> lösen aber die ISR NICHT aus? Ist das korrekt?
Nicht ganz.
Das PCMSK3 Register sitzt noch vor dem EIFR Register.
D.h. damit wählst du aus, welche Pins überhaupt bis zum EIFR
durchkommen.
> Warum sollte man dann das Interrupt Flag eigentlich löschen, wenn die> ISR sowiso nicht ausgelöst wird?
Wenn dich dieses Flag im ganzen restlichen Programm nirgends
interessiert, brauchst du dich auch nicht darum kümmern.
>> Und wie ist das mit dem Erkennen des Interrupts in der ISR? habe ich da> eine Chance zu erfahren, von welchem der 8 Interrupts die ISR ausgelöst> wurde (angenommen alle wären aktiviert)?
Nur weil du einen Pin-change aktiviert hast, bedeutet das ja nicht, dass
der Pin seine normale reguläre Funktion verliert. Du kannst
selbstverständlich nach wie vor das PINx Register auslesen und daraus
deine Schlüsse ziehen.
Warum PCMSK3??? Meinst du PCMSK1? Jetz verstehe ich gar nichts mehr :(
Mit PCMSK1 stelle ich PCINT12 und 14 auf High und die anderen 6 auf
Low...
Was muss ich denn im PCMSK3 tun, damit die ISR definitiv nur von diesen
beiden (PCINT12 und 14) ausgeläöst wird????
Manuel schrieb:> Warum PCMSK3??? Meinst du PCMSK1? Jetz verstehe ich gar nichts mehr :(
Ja, mein ich.
Mein Fehler.
Hab beim Scrollen durch das Datenblatt nicht aufgepasst.
Wobei:
Im MOment bin ich mir jetzt selbst auch unsicher, wie das wirklich
läuft. Die Beschreibung ist da etwas seltsam
1
• Bit 7:0 – PCINT15:8: Pin Change Enable Mask 15:8
2
Each PCINT15:8-bit selects whether pin change interrupt is enabled
3
on the corresponding I/O pin. If PCINT15:8 is set and the PCIE1 bit
4
in EIMSK is set, pin change interrupt is enabled on the corresponding
5
I/O pin. If PCINT15:8 is cleared, pin change interrupt on the
6
corresponding I/O pin is disabled.
Das fällt der Beschreibung nach in den Zustaändigkeitsbereich
Auswerten des Interupt Flag Bits
und nicht
Setzen des Interupt Flag Bits
Leider finde ich im Datenblatt keine Zeichnung, in der die Anordnung und
der Einflussbereich der diversen Flags ersichtlich ist.
Im Zweifelsfall: mit einem Testprogramm ausprobieren und ergründen wie
die Zusammenhänge wirklich sind.
Karl Heinz schrieb:> Leider finde ich im Datenblatt keine Zeichnung, in der die Anordnung und> der Einflussbereich der diversen Flags ersichtlich ist.
Doch.
Hier in der Figure 12-1 sieht man es.
Die PCMSK(x) Register beeinflussen die Verarbeiteung, an deren Ende das
Setzen der PCIF (also der Flag Register) steht.
Passt also schon. Mittel PCMSKx wählst du aus, welche Eingangsleitung
überhaupt zugelassen sind, das zugehörige Flag Bit zu setzen.
Edit:
IMHO ist die Beschreibung dir Registerfunktionalität nicht ganz
glücklich gewählt.
Hi
Wenn PCINT12 und PCINT14 einen Interrupt auslösen sollen müssen
PCIE1 in EIMSK sowie PCINT14 und PCINT12 in PCMSK1 gesetzt sein.
Bei einem PinChange wird dann PCIF1 in EIFR gesetzt und ggf. PCINT1
ausgelöst.
MfG Spess
Hi
>Das bedeutet, dass das PCIF1 in EIFR nun doch nur durch Pinchanges an>PCINT12 und 14 gesetzt wird und nicht durch alle 8?
Ja. Sonst hätten die PCMSK-Register ja keinen Sinn.
MfG Spess