Atmel AVR Sleep Pin Change Interrupt - Verständnisfrage

OP #7556100
Lesenswert?

Guten Abend,

ich habe hier einen Code der zwar wunderbar funktioniert, ich aber gerne wüsste warum.

Geht um den Power Down Sleep Mode eines Atmel Attiny44 µC, der bei einem Pinchange an einem der beiden Pins aufwachen soll.

Folgende ISR Routine wird beim Pinchange aufgerufen:

1
ISR (PCINT1_vect) {
2

3
 arbeiten = true;
4

5
}

Folgende Funktionen konfigurieren den Sleep Mode und schicken den µC ins Bett:

1
void setup_sleep() {
2
  
3
  cli();
4
  GIMSK |= (1<<PCIE1); 
5
  PCMSK1 |= ((1<<PCINT8)|(1<<PCINT9));
6
  sei();
7
}
8

9
void goto_sleep() {
10
  
11
  ADCSRA &= ~_BV(ADEN); //ADC ausschalten
12
  
13
  MCUCR |= (1 << SM1) & ~(1 << SM0); //Sleep Modus = Power Down
14
  MCUCR |= _BV(SE); //Sleep Enable setzen
15
  sleep_cpu(); // Schlafe
16
  MCUCR &= ~(1<<SE); // Sleep Disable Bit setzen
17
}

Wie gesagt, das funktioniert auch super wenn ich goto_sleep(); aufrufe.

Nun meine Frage: Woher weiß der µC welche ISR er beim Pinchange aufrufen soll? Warum weiß er dass PCINT1_vect hierfür zuständig ist?

Vielen Dank :)

Gast #7556105
Lesenswert?

Borsty B. schrieb:

Woher weiß der µC welche ISR er beim Pinchange aufrufen soll? Warum weiß er dass PCINT1_vect hierfür zuständig ist?

Schau dir im Datenblatt die Beschreibung der Register GIMSK und PCMSK1 an.

Auch das Einleitende Kapitel zu "Interrupts" sind hilfreich.

Du hast nicht angegeben, um welchen Mikrocontroller es geht.

#7557850
Lesenswert?

Steve van de Grens schrieb:

Im Datenblatt Kapitel 10, 10.1, 11, 11.2.2, 11.2.4

Hängt vom Alter des Datenblatts ab, bei meinen gespeicherten sind es andere Kapitel. 😀 (Die Kapitel sind bei der Automotiv-Variante andere).

@Borsty B. Suche nach "Sleep Modes" und nach den Registernamen GIMSK (General Interrupt Mask Register) und evtl. PCMSK1. Die Sleep Modes nennen die möglichen Interruptquellen fürs Aufwachen aus den verschiedenen Sleep-Zuständen. In GIMSK wird definiert, welchen Interrupt du verwenden willst (bei dir ist es PCINT1 weil PCIE1 gesetzt wird) und dementsprechend gibt es in deinem Programm auch die ISR (PCINT1_vect) und die muss es auch geben. PCMSK ist eine Maske, die diejenigen Pins definiert, bei denen der Interrupt ausgelöst werden darf/soll. In deinem Programm sind es die Pins PCINT8 (PB0) und PCINT9 (PB1).

Du hast das Programm wohl nicht selber geschrieben, sonst müsste das ja alles klar sein?

#7557881
Lesenswert?

Borsty B. schrieb:

Nun meine Frage: Woher weiß der µC welche ISR er beim Pinchange aufrufen soll?

Woher weiß der, was der bei irgend einem Assemblerbefehl ausführen soll? Weil dem das halt ins Innerste eingebrannt wurde.

Warum weiß er dass PCINT1_vect hierfür zuständig ist?

Das wiederum weiß der µC überhaupt nicht, das steht im Programmcode, den der Compiler erzeugt.

Oliver

#7557897
Lesenswert?

Borsty B. schrieb:

Nun meine Frage: Woher weiß der µC welche ISR er beim Pinchange aufrufen soll? Warum weiß er dass PCINT1_vect hierfür zuständig ist?

Weil diese Registereinstellungen

1
GIMSK |= (1<<PCIE1);
2
PCMSK1 |= ((1<<PCINT8)|(1<<PCINT9));

dem Controller in Hardware sagen das er hierbei immer auf dieses Ereignis schauen und reagieren soll. Auch die ISR ist damit mittels Vector Adresse hinterlegt. Und weil letzteres so ist, läuft der Controller undefiniert Amok wenn jemand einen Interrupt per Register konfiguriert aber die dazugehörige ISR vergisst zu schreiben. Kannste dir mal im Hinterkopf abspeichern. Biste nicht der Erste und wirst nicht der Letzte bleiben. Lies mal das Interrupt Kapitel im Manual. Ist sehr hilfreich.

(Firma: Gast) #7557933
Lesenswert?

Veit D. schrieb:

Und weil letzteres so ist, läuft der Controller undefiniert Amok wenn jemand einen Interrupt per Register konfiguriert aber die dazugehörige ISR vergisst zu schreiben.

Nöö... Bei AVRs (zumindest in C und C++ (Gcc)) wird die Vektortabelle mit __bad_interrupt geflutet

Ausschnitt:

1
00000000 <__vectors>:
2
       0:  0c 94 5f 00   jmp  0xbe  ; 0xbe <__dtors_end>
3
       4:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
4
       8:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
5
       c:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
6
      10:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
7
      14:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
8
      18:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
9
      1c:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
10
      20:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
11
      24:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
12
      28:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
13
      2c:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
14
      30:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
15
      34:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
16
      38:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
17
      3c:  0c 94 87 00   jmp  0x10e  ; 0x10e <__bad_interrupt>
18
      40:  0c 94 96 05   jmp  0xb2c  ; 0xb2c <__vector_16>
1
0000010e <__bad_interrupt>:
2
     10e:  0c 94 00 00   jmp  0  ; 0x0 <__vectors>

Von Amok kann also keine Rede sein! Es tut vielleicht nicht das was man sich erwartet hat, ist aber doch gut definiert/reproduzierbar.

#7558474
Lesenswert?

Klaus H. schrieb:

Ja, wollte ich auch schon erwähnen. Sieht eher aus wie ein Soft-Reset. Aber das wiederholt sich dann, denn die fehlende ISR ist ja dann noch immer nicht da 😀.

Lässt sich per

1
ISR(BADISR_vect) { ...

auch anders belegen, wenn man da ein anderes Verhalten möchte... z.B. was zum Debuggen, einfach ignorieren, oder Endlosschleife die den Watchdog auslösen lässt...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren