Forum: Mikrocontroller und Digitale Elektronik Externe Interrupts (Pin Change) mit Atmega 649A


von Manuel (Gast)


Lesenswert?

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!!

von Karl H. (kbuchegg)


Lesenswert?

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.

von Manuel (Gast)


Lesenswert?

Folgenden Code verwende ich:
1
EIMSK |= (1<<PCIE1);  // Pin Change Interrupt Enable1 (PCINT15 - PCINT8)
2
EIMSK &= ~(1 << PCIE0);  // disable PCINT7 - PCINT0
3
  
4
  
5
PCMSK1 |= (1<<PCINT12);  //Pin Change 12 interrupt enable
6
PCMSK1 |= (1<<PCINT14);  //Pin Change 14 interrupt enable
7
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!!!

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

> Folgenden Code verwende ich:

Sieht gut aus

von Manuel (Gast)


Lesenswert?

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 :)

von Manuel (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

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

von Manuel (Gast)


Lesenswert?

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!

von Karl H. (kbuchegg)


Lesenswert?

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.

von Manuel (Gast)


Lesenswert?

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????

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

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

von Hans Peter (Gast)


Lesenswert?

Das bedeutet, dass das PCIF1 in EIFR nun doch nur durch Pinchanges an 
PCINT12 und 14 gesetzt wird und nicht durch alle 8?

von spess53 (Gast)


Lesenswert?

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

von Manuel (Gast)


Lesenswert?

OK, das wurde dann ursprünglich falsch interpretiert... Danke euch 
beiden für die Hilfe!!!

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.