ATTiny45: Aufwachen mit PCINT klappt nicht

OP #4638491
Lesenswert?

Hallo,

Ich schicke den µC in den PowerDownModus und will ihn danach durch einen 
PinChangeInterrupt von 2 Tastern(also entweder der eine oder der andere)
wieder aufwecken.
In der while-Schleife werden die Taster über Peter Daneggers 
Entprellroutine abgefragt, deswegen deaktiviere ich den PCINT sobald er 
ausgelöst wurde und aktiviere den erst kurz bevor es ans Schlafen geht.
Das "flag" wird beim langen Drücken einer Taste auf 0 gesetzt.
Und noch während ich die Taste gedrückt halte wird bereits die ISR 
aufgerufen und danach reagiert meine Schaltung auf keinen Tastendruck 
mehr.

Habt ihr ne Idee, woran das liegen könnte?
Danke
der Hannes
1
.
2
.
3
uint8_t flag = 1;
4

5
ISR( PCINT0_vect){
6
  GIMSK &= ~(1<<PCIE);
7
  flag ^=1;
8
}
9
int main(void){
10
.
11
.
12
  /**** Configure external Interrupts ****/
13
  //GIMSK |= (1<<PCIE); // Enable Pin Change Interrupt
14
  PCMSK |= (1<<PCINT1)|(1<<PCINT2);
15
  
16
  sei();    // Enable global interrupts  
17
.
18
.
19
  while(1){
20
.
21
.
22
    if( flag == 0){
23
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
24
      GIMSK |= (1<<PCIE);
25
      sleep_mode();
26
    }
27
   }
28
}
Gast #4638542
Lesenswert?

Ich hab deinen Code nicht genau angeschaut. Aber das hier funktioniert:
1
bitSet(GIMSK, PCIE1);    //Turn on Pin Change interrupt
2
bitSet(PCMSK0, PCINT10); //Pin PCINT10
3

4
void sleepNow(){  
5
  cli();
6
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
7
  sleep_enable();
8
  sei();
9
  sleep_mode();
10
  sleep_disable();
11
}
12

13
ISR(PCINT1_vect){
14
  //flag handling
15
}
OP #4638723
Lesenswert?

Hallo Peter,
Das werde ich mal so in Deinem Beispielcode machen.

Allerdings hat es ja bereits schon funktioniert bei mir (mit 
sleepmode(); und set_sleep_mode();). Da hatte ich aber erst den 
LowLevelInterrupt INT0 benutzt und weil ich aber beide Taster zum 
Aufwecken nehmen will, auf PinChangeInterrupt gewechselt.

Ich schalte die "flag"-Variable um, wenn mit "get_key_long" ein langes 
Drücken festgestellt wurde.
OP #4639479
Lesenswert?

Hallo Peter,

Also das Problem ist, sobald das lange Drücken des einen Tasters erkannt 
wird, dann geht der Controller schlafen. Wenn ich aber die Taste dann 
wieder loslasse, wacht er auf.
Müsste ich da in Deiner debounce.c noch was ändern?

Ich habe Deine Routine 1:1 übernommen. An einer anderen Stelle im Code 
verwende ich sie auch nochmal und da wird nach einem kurzen Tastendruck 
schlafen gegangen. Da gibt es keine Probleme.
Gast #4639502
Lesenswert?

Toggle halt das Flag nicht
>  flag ^=1;
sondern setzte es in der ISR in einen definierten Zustand.
Wenn das Flag vor Aufruf der ISR durch irgendeinen Codeteil 0 wurde, 
dann ist's nachher <>0 und der Code in der While fühlt sich nicht mehr 
zuständig. Und dann ist halt Schluss, die ISR ist disabled.

Außerdem würde ich vor dem Enablen eines Interrupts
> GIMSK |= (1<<PCIE);
immer das entsprechende Interruptflag löschen, da sich der µC einen 
anstehenden Interrupt merkt und der kann noch von vorher, also nach dem 
Disablen des Interrupt stammen. Der wird dann sofort nach dem Enablen 
des entsprechenden Interrupts ausgeführt.
OP #4639931
Lesenswert?

Hallo Peter,
Das Problem ist ja nicht das Aufwachen, sondern dass der µC schlafen 
geht WÄHREND die Taste noch runtergedrückt ist. Wenn ich die also 
loslasse wacht er ja gleich wieder auf.
Aber das Ziel ist ja dass er nach dem Loslassen immer noch schläft.

Als quick&dirty-Möglichkeit habe ich das jetzt so probiert, geht:
1
if(flag == 0){
2
      _delay_ms(500);
3
      try_sleep();
4
}
Aber das ist natürlich nicht failsafe...
Gast #4639944
Lesenswert?

Johannes H. schrieb:
> Aber das Ziel ist ja dass er nach dem Loslassen immer noch schläft.

Das geht nicht mit PCINT, da jede Flanke den µC aufweckt.

Hier noch ein Beispiel mit PCINT und zwei Tastern, die auch noch 
entprellt werden und selbst gedrückt keine hohe Stromaufnahme 
verursachen.
Beitrag "Re: EIN-AUS mit Taster per Interrupt, ATtiny25 o.ä."

Wichtig ist, daß nur beim Drücken einer Taste eine Aktion ausgelöst 
wird. Beim Loslassen wird der µC zwar kurz wach, legt sich aber gleich 
wieder schlafen.
OP #4640298
Lesenswert?

Hier mal der komplette Code. Eigentlich dachte ich, dass ich es genau so 
wie Du in Deinem Beispiel mache.
Wenn das flag 0 ist wird try_sleep aufgerufen.
1
#include <util/delay.h>
2
#include <avr/interrupt.h>
3
#include <avr/io.h>
4
#include <avr/sleep.h>
5

6
#include "includes/config.h"
7
#include "includes/debounce.h"
8
#include "includes/light_ws2812.h"
9

10
struct cRGB LED_OUTPUT[1];
11
struct cRGB TEMP[1];
12

13
#define MODES 13
14
#define BRIGHTNESSES 6
15
#define PWM_MAX 255
16

17
volatile uint8_t flag = 1;
18

19
ISR( PCINT0_vect){
20
  flag = 1;
21

22
  GIFR = 0; // clear external interrupt flags
23
}
24

25
void try_sleep()
26
{
27
  if( get_key_busy( 1<<KEY1 ))
28
    return;          // still busy
29
  set_sleep_mode( SLEEP_MODE_PWR_DOWN);    // prepare power down
30
  GIMSK |= (1<<PCIE);      // awake interrupt on
31
  sleep_cpu();
32
  GIMSK &= ~(1<<PCIE);
33

34
}
35

36
int main(void){
37
  uint8_t mode_index = 1;
38
  uint8_t brightness_index = BRIGHTNESSES;  
39
  
40
  /**** Set Timer & Buttons for Debounce Functionality ****/
41
  KEY_DDR &= ~ALL_KEYS;
42
  KEY_PORT |= ALL_KEYS;
43

44
  TCCR0B = (1<<CS02)|(1<<CS00);         // divide by 1024
45
  TCNT0 = (uint8_t)(int16_t)-(F_CPU / 1024 * 10e-3 + 0.5);  // preload for 10ms
46
  TIMSK |= 1<<TOIE0;                   // enable timer interrupt  
47
  
48
  /**** Configure external Interrupts ****/
49
  PCMSK |= (1<<PCINT1)|(1<<PCINT2);
50
  
51
  
52
  /**** Power Reduction ****/
53
  PRR = (1<<PRUSI) | (1<<PRADC) | (1<<PRTIM1);
54
  
55
  sei();    // Enable global interrupts  
56
  
57
  initColors(); //can be deleted if direct initialization works
58
  
59
  TEMP[0]= colorStatic(0);
60
  ws2812_setleds(TEMP,1);  
61
  
62
  /************************************************************************/
63
  /* MAIN Action ;)                                                                     */
64
  /************************************************************************/
65
  while(1){
66
    
67
    if( get_key_short(1<<KEY0) ){
68
      if (mode_index == MODES)mode_index = 1;
69
      else mode_index += 1;            
70
    }
71
    
72
    if (get_key_long(1<<KEY0)) mode_index =0;
73
    
74
    if( get_key_short(1<<KEY1) ){
75
      if (brightness_index == 1)brightness_index = BRIGHTNESSES;
76
      else brightness_index -= 1;      
77
    }
78
    
79
    if (get_key_long(1<<KEY1)) flag ^= 1;
80
        
81
    switch (mode_index){
82
      case 0: LED_OUTPUT[0] = colorFade();break;
83
      case 1: if( flag == 1){
84
            LED_OUTPUT[0] = stroboscope();
85
             _delay_ms(10);
86
          }
87
          else LED_OUTPUT[0] = colorStatic(13);
88
          break;             
89
      case 2: LED_OUTPUT[0] = colorStatic(0); ws2812_setleds(LED_OUTPUT,1);try_sleep(); break;
90
      default: break;
91
    }
92
        
93
    if(mode_index >= 3){      
94
      if(flag == 1){
95
        LED_OUTPUT[0] = colorPulse(mode_index - 2);
96
        _delay_ms(20);
97
      }    
98
      else LED_OUTPUT[0] = colorStatic(mode_index-2);// Cycle colors starting from color "1" µC can be switched off here and woken up with interrupt
99
      
100
    }
101
    LED_OUTPUT[0].r  /= brightness_index;
102
    LED_OUTPUT[0].g  /= brightness_index;
103
    LED_OUTPUT[0].b  /= brightness_index;
104
    ws2812_setleds(LED_OUTPUT,1);
105
    
106
    if(flag == 0){
107
      _delay_ms(500);
108
      try_sleep();
109
    }
110
  }    
111
}
OP #4647643
Lesenswert?

Hallo,
So bin jetzt wieder zurück aus dem Urlaub.

Ich hab's grad nochmal probiert: Auch wenn die ISR leer ist, wacht der 
µC nach dem Loslassen des Tasters gleich wieder auf.
Bzw. sogar wenn ich auf "get_key_long" triggere und den Taster die ganze 
Zeit gedrückt halte, geht der µC in den Schlafmodus und wacht im 
nächsten Moment wieder auf.
#4647751
Lesenswert?

Johannes H. schrieb:
> Auch wenn die ISR leer ist, wacht der
> µC nach dem Loslassen des Tasters gleich wieder auf.

Du must das Programm natürlich so schreiben, daß er nur die 
Tasteneingabe  auswertet und immer wieder schlafen geht, solange das 
Flag gesetzt ist.
Der Name try_sleep wurde schon mit Absicht so gewählt, d.h. es muß nicht 
beim ersten Versuch (Flanke, Preller) klappen.
Genau so ist es in meinem Beispiel gelöst.
OP #4648696
Lesenswert?

Hallo Peter,

Jetzt wird's misteriös bei mir.
Nachdem es partout nicht klappen wollte, hab ich die try_sleep mal so 
auskommentiert, dass der µC aus dem Schlafzustand nicht mehr aufwachen 
sollte:
1
void try_sleep()
2
{
3
  //if( get_key_busy( 1<<KEY1 )||get_key_busy(1<<KEY0) )
4
    //return;          // still busy
5
  set_sleep_mode( SLEEP_MODE_PWR_DOWN);    // prepare power down
6
  //GIMSK |= (1<<PCIE);      // awake interrupt on
7
  sleep_cpu();
8
  //GIMSK &= ~(1<<PCIE);
9

10
}

Aber: nach erneutem Drücken geht's einfach weiter.

Wie kann das sein? Das PCINT-Bit im GIMSK wird ja gar nicht gesetzt.

> Du must das Programm natürlich so schreiben, daß er nur die
> Tasteneingabe  auswertet und immer wieder schlafen geht, solange das
> Flag gesetzt ist.
Aber die Abfrage geht doch nach "if( flag == 0)" direkt zum 
"try_sleep"...
Allerding hast Du in Deiner Abfrage noch "get_key_short( 1<<KEY0 );". 
Das verstehe ich noch nicht ganz.
OP #4649884
Lesenswert?

Hallo Peter,

Also zum einen hatte ich vergessen während der Initialisierung 
"sleep_enable" zu setzen (da hat "sleep_cpu" natürlich nichts 
bewirkt)...
Zum anderen kommt von "get_key_busy" IMMER nicht Null zurück, d.h. der 
µC wird nie schlafen geschickt.

Hast Du nen Idee, wie sich das beheben lässt?
OP #4650208
Lesenswert?

Hallo Peter,

Ich hab noch mal in Deinem Knightriderbeispiel geschaut und mal den Code 
der initialen "get_key_busy" verwendet, also
1
return (~KEY_PIN | key_state) & key_mask;

Damit gibt die Funktion nun auch nur einen Wert ungleich 0 zurück, wenn 
der Schalter tatsächlich gedrückt wird, allerdings treten nun noch 
andere Problem auf. Ich vermute, weil die VerODERung mit "key_press" 
fehlt.

Kannst Du mir bitte nochmal helfen, warum Deine aktualisierte Variante
1
return ((KEY_PIN ^ key_state) | key_press) & key_mask;
 bei mir nicht funktioniert?
#4656292
Lesenswert?

Kannst Du mal das "nicht funktioniert" beschreiben.

Ich weiß nicht, wie lange Deine Mainloop dauert, es werden ja einige 
Unterfunktionen aufgerufen.
Die long/short Unterscheidung funktioniert nur dann einwandfrei, wenn 
die Mainloop immer kürzer als die short-Zeit dauert.
Wenn das nicht geht, muß man diese Unterscheidung mit in den 
Timerinterrupt legen.
OP #4656468
Lesenswert?

Hallo Peter,
Naja, ich hab zwischen einigen Unterfunktionen 10 bzw. 20 ms delay 
eingebaut.

Die long/short Unterscheidung hat ja ohne Probleme funktioniert.
Durch die "get_key_busy"-Abfrage sind erst die Probleme gekommen, weil 
dessen Rückgabewert immer ungleich 0 ist und "try_sleep" dadurch gleich 
wieder verlassen wird.
#4657948
Lesenswert?

So ich hab nochmal in Deinen Code geschaut.
Der Unterschied ist, daß ich das low-activ gleich in die Definition 
gepackt habe.
1
#define KEY_PIN      ~PINB    // Input low active
Damit ist dann KEY_PIN high aktiv.
Und damit ergibt
1
(KEY_PIN ^ key_state)
0, wenn der entprellte Zustand gleich dem (high)-Eingang ist, also das 
Entprellen abgeschlossen ist.
Ich finde das schöner, da dann das ~ nur einmal in der Definition stehen 
muß.

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