Forum: Mikrocontroller und Digitale Elektronik Attiny13 Würfel INT0, Timer0, Power-Down, INT0 löst nur einmal aus


von Matze (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

Aus einem Attiny13 soll ein Würfel entstehen.
Ein problem ist jedoch noch das INT0 nur 1 mal auslöst.

Zunächst wartet der Controller auf den 1. Tastendurck,
dann wird ISR(INT0_vect) ausgeführt.
Soweit ist alles Ok,
Nun bekommt der Timer er seine Energie:
PRR&=~(1<<PRTIM0);        //Power to The Timer!
nun wird der Timer Interrupt zugellassen
TIMSK0|=(1<<TOIE0);
Danach wird der Timer gestartet,
TCCR0B|=((1<<CS02)|(1<<CS00));  //Prescaler=1024, Timer-Start
Da der Controller mit 128KHz läuft sollte er jede Sec um 128 Hochzählen
Folglich sollte er bei Normalem betrieb nach 2Sec überlaufen.

Nun Leuchten die LEDs.
Nach ca 2 Sec werden sie abgeschaltet.
Problem ist nun dass der Controller nie mehr aufwacht.
Ich sehe keinen Grund warum es kein 2. mal möglich sein sollte.
INT0 ist doch Low-Level Interrupt?

Kann mich jemand aufklären?

1
#include <avr/io.h>
2
#include <avr/sleep.h>
3
#include <avr/interrupt.h>
4
#include <stdlib.h>
5
6
int main(void)
7
{  
8
  sei();//Interrupts Enable
9
  GIMSK|=(1<<INT0);    //Timer0 Interrupt enable
10
  //Timer/ADC-Aus
11
  PRR|=((1<<PRTIM0)|(1<<PRADC));  //Timer0/ADC Aus
12
  BODCR|=0x03;          //BODS+BODSE = 1
13
  BODCR|=0x02;          //BODS=1 / BODSE=0
14
  ADCSRA &=~ ((1<<ADEN));      //AD-Wandler-Disable
15
  ACSR |= (1<<ACD);        //Analog-Comparator Disable
16
  ACSR &=~(1<<ACBG);        //BG-Refferenz nicht Nutzen
17
  //PB1 =Eingang, interner Pullup
18
  DDRB &=~ ((1<<PB1)|(1<<PB5));  //Eingang
19
  PORTB |= (1<<PB1);  //Pull-Up
20
  //PB0, PB2, PB3, PB4 = Ausgang
21
  DDRB |= ((1<<PB0) | (1<<PB2) | (1<<PB3) | (1<<PB4));
22
  while(1)
23
    {
24
    //Nun 30Sec Warten...
25
    //Nun in Power Down-Mode
26
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
27
    sleep_mode();
28
    }
29
}
30
31
ISR(INT0_vect) 
32
{
33
  GIMSK&=~(1<<INT0);    //Timer0 Interrupt Disable
34
  PRR&=~(1<<PRTIM0);        //Power to The Timer!
35
  while(!( PINB & ( 1<<PB1 ) ))
36
  {  
37
    unsigned short int a=rand()%6+1;
38
    PORTB&=~ ((1<<PB0)|(1<<PB2)|(1<<PB3)|(1<<PB4));  //Eingang
39
    switch (a)
40
    {
41
      case (1):
42
      PORTB|=(1<<PB0);
43
      break;
44
      case (2):
45
      PORTB|=(1<<PB2);
46
      break;
47
      case (3):
48
      PORTB|=(1<<PB0)|(1<<PB2);
49
      break;
50
      case (4):
51
      PORTB|=(1<<PB2)|(1<<PB4);
52
      break;
53
      case (5):
54
      PORTB|=(1<<PB2)|(1<<PB0)|(1<<PB4);
55
      break;
56
      case (6):
57
      PORTB|=(1<<PB2)|(1<<PB3)|(1<<PB4);
58
      break;
59
    }
60
  }
61
  //-->Timer Enable, Zeit, Starten
62
  TIMSK0|=(1<<TOIE0);        //IRQ-Enable
63
  TCCR0B|=((1<<CS02)|(1<<CS00));  //Prescaler=1024, Timer-Start
64
}
65
66
ISR(TIM0_OVF_vect)
67
{
68
  TCCR0B&=~((1<<CS02)|(1<<CS00));  //Timer-Stop
69
  PORTB&=~ ((1<<PB0)|(1<<PB2)|(1<<PB3)|(1<<PB4));  //Löchen
70
  TCNT0=0x00;        //Timer-Register zurück
71
  PRR|=(1<<PRTIM0);    //Timer Aus
72
  GIMSK|=(1<<INT0);    //INT0 Interrupt enable
73
}

von Uwe S. (de0508)


Lesenswert?

Hi,

Du produzierst ein Dead-Lock über die INT0 Anweisung
1
GIMSK&=~(1<<INT0);
, siehe Datenblatt "7.1 Sleep Modes".

Ist das klar ?

von Matze (Gast)


Lesenswert?

Stopp, hier stimmt was nicht:
1
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
2
    sleep_mode();
Steht nicht in Main, sondern in am Ende des ISR(TIM0_OVF_vect).

von Matze (Gast)


Lesenswert?

Uwe S. schrieb:
> Du produzierst ein Dead-Lock über die INT0 AnweisungGIMSK&=~(1<<INT0); ,
> siehe Datenblatt "7.1 Sleep Modes".
>
> Ist das klar ?

Ne ich verstehs nicht,
"
Bit 6 – INT0: External Interrupt Request 0 Enable
When the INT0 bit is set (one) and the I-bit in the Status Register 
(SREG) is set (one), the external
pin interrupt is enabled. The Interrupt Sense Control0 bits 1/0 (ISC01 
and ISC00) in the MCU
Control Register (MCUCR) define whether the external interrupt is 
activated on rising and/or falling
edge of the INT0 pin or level sensed. Activity on the pin will cause an 
interrupt request even
if INT0 is configured as an output. The corresponding interrupt of 
External Interrupt Request 0 is
executed from the INT0 Interrupt Vector."

Ich Disable ihn doch nur.
Nach dem Timerüberlauf wird er wieder Enabled
1
GIMSK|=(1<<INT0);    //INT0 Interrupt enable

von Uwe S. (de0508)


Lesenswert?

Hallo,

und genau diese Annahme
> Nach dem Timerüberlauf wird er wieder Enabled
ist falsch. Bitte lese das im Datenblatt nach.

: Bearbeitet durch User
von Matze (Gast)


Lesenswert?

Hab es nun Mehrfach durchgelesen, sehe in 7.1 keinen Grund für das 
Problem.

Das Bit INT0 in GIMSK dient zum Enablen/Disablen des Extreren 
Interrupts.
Wenn ich es rücksetze ist der Interrupt disabled, beim Setzen wird er 
Enabled.
Ich kann mir das Deaht-Lock nicht vorstellen, aber es scheint 
aufzutreten.

Auch wenn ich auf Enable/Disable INT0 verzichte löst er kein 2. mal aus.

von Thomas E. (thomase)


Lesenswert?

Matze schrieb:
> Ne ich verstehs nicht,

Ich auch nicht.

Uwe S. schrieb:
> und genau diese Annahme
>> Nach dem Timerüberlauf wird er wieder Enabled
> ist falsch. Bitte lese das im Datenblatt nach.

Und was ist daran falsch?

mfg.

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Miteinander,

Angenommen
1
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
2
    sleep_mode();
 setzt die Bits im Regster MCUCR SM[1:0]=10 und SE=1, dann gilt in der 
Tabelle 7.1 die markierte Zeile.
Und die wichtigste Info aus der Tabelle und dem erklärendem Text ist, 
dass es nur ZWEI Quelle für die Beendigung gibt und der Clock 
abgeschaltet ist.

Sprichwort "Wo kein Clock, da auch kein Timer!"

So genau sollte man das Datenblatt dann doch lesen können.

: Bearbeitet durch User
von Matze (Gast)


Lesenswert?

Uwe S. schrieb:
> Sprichwort "Wo kein Clock, da auch kein Timer!"

Ich verstehe nicht, das Problem ist dass der INT0 des Externen Pins INT0 
(PB1) der im Normalfall als LOW-Level-Interrupt konfiguriert ist nicht 
ausgelöst wird.
In der ROT mirkierten Zeile der Tabelle wird der Externe INT0 als 
Interruptquelle im Power-Down-Modus genannt.

Der Timer läuft, nach 2 Sec werden die LEDs abgeschaltet.
Wo soll da dass Problem sein?

von Uwe S. (de0508)


Lesenswert?

Matze, mit dem gezeigten Code aus dem ersten Post, läuft der Timer 
nicht.
Warum habe ich erläutert, wenn das nicht verständlich ist, könnte jemand 
Anderes das Problem "Dead-Lock" evtl. besser beschreiben ?

: Bearbeitet durch User
von Uwe K. (ukhl)


Lesenswert?

Mein Tip:
Komplett wegschmeißen und neu machen.
Der Mainloop ist furchtbar.
Die Interrupt-Routine macht viel zu viel. Auch gruselig.

Meckern ist immer einfach, deshalb auch ein paar anregungen.

Versuche es dann mal so:
- Timer einrichten und eine Variable für den Sleep hochzählen (INT).
- Im Main die Taste abfragen und darauf reagieren.
- Wird die Taste gedrückt, den Sleep-Zähler zurücksetzten.
- Im Main auch den Sleep-Zähler prüfen und gegebenenfalls "einschlafen".
- Tasten-Interrupt einrichten der nur das Aufwachen auslöst.
- Tasten-Interrupt-Routine muss vorhanden sein, kann aber leer bleiben.
- Nach den Aufwachen (direkt nach dem SLEEP Aufruf) nicht vergessen den 
Sleep-Zähler zurückzusetzen.
- Und vor dem SLEEP nicht vergessen die LEDs abzuschalten.

Versuche es mal. Ich unterstütze dich gern auf den Weg dahin. Habe auch 
schon mehrere ATtiny13 Würfel selbst gechrieben.

: Bearbeitet durch User
von Matze (Gast)


Lesenswert?

Uwe S. schrieb:
> Matze, mit dem gezeigten Code aus dem ersten Post, läuft der Timer
> nicht.
> Warum habe ich erläutert, wenn das nicht verständlich ist, könnte jemand
> Anderes das Problem "Dead-Lock" evtl. besser beschreiben ?

Ja, dass ist mir selbst schon aufgefallen.

Matze schrieb:
> Stopp, hier stimmt was nicht:    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
>     sleep_mode();
> Steht nicht in Main, sondern in am Ende des ISR(TIM0_OVF_vect).

Ursprünglich war der SLEEP-MODE auch schon im Timer-ISR, ich hatte es 
auf der Suche nur mal umgestellt, und bemerkt dass es keinen Sinn macht 
:)

Ich dachte du beschreibst den Grund für das einmalige Auslösen des INT0.

Uwe S. schrieb:
> Hi,
>
> Du produzierst ein Dead-Lock über die INT0 AnweisungGIMSK&=~(1<<INT0); ,
> siehe Datenblatt "7.1 Sleep Modes".
>
> Ist das klar ?

Aber selbst in dem Zusammenhang versteh ich es nun noch nicht,
Dass disablen des EXT-INT (an sich) stoppt doch den doch Timer nicht?

von Uwe S. (de0508)


Lesenswert?

Uwe K,

das ist auch noch ein Fehler in deiner Programmskitze.

Der INT0 ist ein LEVEL Interrupt, man kann also nicht
> Tasten-Interrupt-Routine muss, kann aber leer bleiben.
Denn wir sofort nach dem verlassen der Interrupt-Routine, diese wieder 
aufgerufen.

Also, entweder INT0 Interrupt sperren, oder auf eine Flankenwechsel 
warten.

von Uwe S. (de0508)


Lesenswert?

Man,

Der Timer läuft nicht! da es in diesem Sleepmode keinen Takt gibt und 
dieser nicht als "Aufwach Quelle" angegeben ist, wie sollte er auch; er 
funktioniert jetzt gerade nicht.

Matze schrieb:
> Aber selbst in dem Zusammenhang versteh ich es nun noch nicht,
> Dass disablen des EXT-INT (an sich) stoppt doch den doch Timer nicht?

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Hallo,

ich sehe dein Verständnis Problem. Durch dieses Haupt-Programm
1
while(1)
2
    {
3
    //Nun 30Sec Warten...
4
    //Nun in Power Down-Mode
5
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
6
    sleep_mode();
7
    }
verweilt der Atmel µC fast Immer im Power Down Schlafmodus und kann nur 
durch den INT0 = GND aufwachen.
Dann führt er einmalig den Code in der INT0 Interrupt-Routine aus. Diese 
schaltet den INT0 Level-Interrupt aus, alles weitere ist nicht wichtig 
für die weitere Betrachtung.
Bei Verlassen der INT0 Interrupt-Routine verfällt er sofort wieder in 
den Power Down Schlafmodus aus dem er nie wieder aufwachen kann.
Da hilft nur noch ein Reset - den Reset-Pin = GND.

: Bearbeitet durch User
von Uwe K. (ukhl)


Lesenswert?

Uwe S. schrieb:
> Uwe K,
>
> das ist auch noch ein Fehler in deiner Programmskitze.
>
> Der INT0 ist ein LEVEL Interrupt, man kann also nicht
>> Tasten-Interrupt-Routine muss, kann aber leer bleiben.
> Denn wir sofort nach dem verlassen der Interrupt-Routine, diese wieder
> aufgerufen.
>
> Also, entweder INT0 Interrupt sperren, oder auf eine Flankenwechsel
> warten.

Was ist schon Perfekt. Noch besser den PCINT1 verwenden.
Vor dem Schlafen einschalten, danach wieder ausschalten.

von Matze (Gast)


Lesenswert?

Uwe S. schrieb:
> verweilt der Atmel µC fast Immer im Power Down Schlafmodus und kann nur
> durch den INT0 = GND aufwachen.
> Dann führt er einmalig den Code in der INT0 Interrupt-Routine. Diese
> schaltet dann den INT0 Level-Interrupt aus.
> Bei verlassen der INT0 Interrupt-Routine verfällt er sofort wieder in
> den Power Down Schlafmodus aus dem er nie wieder aufwachen kann.
> Da hilft nur noch ein Reset - das Reset-Pin = GND.

Du hast schon recht, der Timer läuft im Oben angegebenen Programm nicht, 
es sollte so aussehen.

Dies ändert am Problem aber garnichts, der Timer läuft.
Dies zeigt sich durch das Rücksetzen der LEDs, nur aufwachen tut er nie 
wieder.
1
#include <avr/io.h>
2
#include <avr/sleep.h>
3
#include <avr/interrupt.h>
4
#include <stdlib.h>
5
6
int main(void)
7
{  
8
  sei();//Interrupts Enable
9
  GIMSK|=(1<<INT0);    //Timer0 Interrupt enable
10
  //Timer/ADC-Aus
11
  PRR|=((1<<PRTIM0)|(1<<PRADC));  //Timer0/ADC Aus
12
  BODCR|=0x03;          //BODS+BODSE = 1
13
  BODCR|=0x02;          //BODS=1 / BODSE=0
14
  ADCSRA &=~ ((1<<ADEN));      //AD-Wandler-Disable
15
  ACSR |= (1<<ACD);        //Analog-Comparator Disable
16
  ACSR &=~(1<<ACBG);        //BG-Refferenz nicht Nutzen
17
  //PB1 =Eingang, interner Pullup
18
  DDRB &=~ ((1<<PB1)|(1<<PB5));  //Eingang
19
  PORTB |= (1<<PB1);  //Pull-Up
20
  //PB0, PB2, PB3, PB4 = Ausgang
21
  DDRB |= ((1<<PB0) | (1<<PB2) | (1<<PB3) | (1<<PB4));
22
  while(1)
23
    {
24
    
25
    }
26
}
27
28
ISR(INT0_vect) 
29
{
30
//  GIMSK&=~(1<<INT0);      //INT0 Interrupt Disable
31
  PRR&=~(1<<PRTIM0);        //Power to The Timer!
32
  while(!( PINB & ( 1<<PB1 ) ))
33
  {  
34
    unsigned short int a=rand()%6+1;
35
    PORTB&=~ ((1<<PB0)|(1<<PB2)|(1<<PB3)|(1<<PB4));  //Eingang
36
    switch (a)
37
    {
38
      case (1):
39
      PORTB|=(1<<PB0);
40
      break;
41
      case (2):
42
      PORTB|=(1<<PB2);
43
      break;
44
      case (3):
45
      PORTB|=(1<<PB0)|(1<<PB2);
46
      break;
47
      case (4):
48
      PORTB|=(1<<PB2)|(1<<PB4);
49
      break;
50
      case (5):
51
      PORTB|=(1<<PB2)|(1<<PB0)|(1<<PB4);
52
      break;
53
      case (6):
54
      PORTB|=(1<<PB2)|(1<<PB3)|(1<<PB4);
55
      break;
56
    }
57
  }
58
  //-->Timer Enable, Zeit, Starten
59
  TIMSK0|=(1<<TOIE0);        //IRQ-Enable
60
  TCCR0B|=((1<<CS02)|(1<<CS00));  //Prescaler=1024, Timer-Start
61
}
62
63
ISR(TIM0_OVF_vect)
64
{
65
  TCCR0B&=~((1<<CS02)|(1<<CS00));  //Timer-Stop
66
  PORTB&=~ ((1<<PB0)|(1<<PB2)|(1<<PB3)|(1<<PB4));  //Löchen
67
  TCNT0=0x00;        //Timer-Register zurück
68
  PRR|=(1<<PRTIM0);    //Timer Aus
69
//  GIMSK|=(1<<INT0);    //INT0 Interrupt enable
70
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
71
  sleep_mode();
72
}

Uwe K. schrieb:
> Versuche es mal. Ich unterstütze dich gern auf den Weg dahin. Habe auch
> schon mehrere ATtiny13 Würfel selbst gechrieben.

Danke, finde es so wie es ist schon sinvoll, du würdest also statt der 
Timer-ISR, das Timer-Interrupt-Flag abfragen?

Habe eine Aufgabe nochnie so unterschätzt.

von Peter D. (peda)


Lesenswert?

Uwe S. schrieb:
> Du produzierst ein Dead-Lock über die INT0 AnweisungGIMSK&=~(1<<INT0);

Nö, sondern anders.

Wenn der INT0_vect endet, geht er zur Mainloop zurück und die macht 
sleep. Damit stoppt der Timer und Du kriegst nie nen Timerinterrupt.

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

Uwe S. schrieb:
> Bei Verlassen der INT0 Interrupt-Routine verfällt er sofort wieder in
> den Power Down Schlafmodus aus dem er nie wieder aufwachen kann.
> Da hilft nur noch ein Reset - den Reset-Pin = GND.

Ja. jetzt sehe ich es auch.

Matze schrieb:
> ISR(TIM0_OVF_vect)
> {
>   TCCR0B&=~((1<<CS02)|(1<<CS00));  //Timer-Stop
>   PORTB&=~ ((1<<PB0)|(1<<PB2)|(1<<PB3)|(1<<PB4));  //Löchen
>   TCNT0=0x00;        //Timer-Register zurück
>   PRR|=(1<<PRTIM0);    //Timer Aus
> //  GIMSK|=(1<<INT0);    //INT0 Interrupt enable
>   set_sleep_mode(SLEEP_MODE_PWR_DOWN);
>   sleep_mode();
> }

Was soll das denn? Wie willst du da wieder rauskommen? In der ISR sind 
die Interrupts abgeschaltet. Da gibt es keinen INT0.

mfg.

von Matze (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Was soll das denn? Wie willst du da wieder rauskommen? In der ISR sind
> die Interrupts abgeschaltet. Da gibt es keinen INT0.
>
> mfg.

Ja, das ist die Idee, natütlich springt er nicht zwischen 2 gleich 
Priorisierten Interrupts!

Nutze nun eine globale volatile variable in ihn nur in den Sleep-Modus 
zu schicken wenn der Timer abgelaufen ist.

So funktioniert es.

Der Controller selbst braucht beim Würfeln 100uA,
während der Timer läuft 30uA, im Power-Down nur 100nA.

Schön sparsam :)

Danke an Euch.
1
#include <avr/io.h>
2
#include <avr/sleep.h>
3
#include <avr/interrupt.h>
4
#include <stdlib.h>
5
6
volatile uint8_t sleepen; 
7
8
int main(void)
9
{  
10
  sleepen=1;
11
  sei();//Interrupts Enable
12
  GIMSK|=(1<<INT0);    //Timer0 Interrupt enable
13
  //Timer/ADC-Aus
14
  PRR|=((1<<PRTIM0)|(1<<PRADC));  //Timer0/ADC Aus
15
  BODCR|=0x03;          //BODS+BODSE = 1
16
  BODCR|=0x02;          //BODS=1 / BODSE=0
17
  ADCSRA &=~ ((1<<ADEN));      //AD-Wandler-Disable
18
  ACSR |= (1<<ACD);        //Analog-Comparator Disable
19
  ACSR &=~(1<<ACBG);        //BG-Refferenz nicht Nutzen
20
  //PB1 =Eingang, interner Pullup
21
  DDRB &=~ ((1<<PB1)|(1<<PB5));  //Eingang
22
  PORTB |= (1<<PB1);  //Pull-Up
23
  //PB0, PB2, PB3, PB4 = Ausgang
24
  DDRB |= ((1<<PB0) | (1<<PB2) | (1<<PB3) | (1<<PB4));
25
  while(1)
26
    {
27
    if(sleepen==1)
28
    {
29
      sleepen=0;
30
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
31
      sleep_mode();
32
    }
33
    
34
    }
35
}
36
37
ISR(INT0_vect) 
38
{
39
  PRR&=~(1<<PRTIM0);        //Power to The Timer!
40
  while(!( PINB & ( 1<<PB1 ) ))
41
  {  
42
    unsigned short int a=rand()%6+1;
43
    PORTB&=~ ((1<<PB0)|(1<<PB2)|(1<<PB3)|(1<<PB4));  //Eingang
44
    switch (a)
45
    {
46
      case (1):
47
      PORTB|=(1<<PB0);
48
      break;
49
      case (2):
50
      PORTB|=(1<<PB2);
51
      break;
52
      case (3):
53
      PORTB|=(1<<PB0)|(1<<PB2);
54
      break;
55
      case (4):
56
      PORTB|=(1<<PB2)|(1<<PB4);
57
      break;
58
      case (5):
59
      PORTB|=(1<<PB2)|(1<<PB0)|(1<<PB4);
60
      break;
61
      case (6):
62
      PORTB|=(1<<PB2)|(1<<PB3)|(1<<PB4);
63
      break;
64
    }
65
  }
66
  //-->Timer Enable, Zeit, Starten
67
  TIMSK0|=(1<<TOIE0);        //IRQ-Enable
68
  TCCR0B|=((1<<CS02)|(1<<CS00));  //Prescaler=1024, Timer-Start
69
}
70
71
ISR(TIM0_OVF_vect)
72
{
73
  sleepen=1;
74
  TCCR0B&=~((1<<CS02)|(1<<CS00));  //Timer-Stop
75
  PORTB&=~ ((1<<PB0)|(1<<PB2)|(1<<PB3)|(1<<PB4));  //Löchen
76
  TCNT0=0x00;        //Timer-Register zurück
77
  PRR|=(1<<PRTIM0);    //Timer Aus
78
}

von Uwe S. (de0508)


Lesenswert?

Hi Matze,

Du hast noch einen Designfehler, solange der Zustand des Eingangs INT0 
aktiv low ist, wird die Interrupt-Service-Routine INT0_vect auch 
angesprungen.
Entweder auf einen Flankenwechsel per Software triggern oder den INT0 
Interrupt sperren.

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.