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
Ja, beim Sleep muß man sehr auf die genaue Reihenfolge achten.
Die wird im sleep_mode(); Macro aber nicht garantiert, daher führt das
bei Benutzung regelmäßig zu Fehlfunktionen. Siehe auch der Warntext dazu
in sleep.h.
Hier ein funktionierendes Beispiel, der 2. Beitrag enthält den
verbesserten Code:
Beitrag "AVR Sleep Mode / Knight Rider"
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.
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.
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.
Johannes H. schrieb:> Wenn ich aber die Taste dann> wieder loslasse, wacht er auf.
Das macht ja nix und ist in meinem Beispiel auch so. Jede Flanke weckt
auf.
Aber es wird ja keine Aktion ausgeführt, d.h. die Variable "stop" ändert
sich nicht und damit wird sofort wieder "try_sleep" aufgerufen.
"flag" auf nen definierten Wert zu setzen hat's nicht gebracht.
@Peter: Aber was kann ich denn dann tun, um das Problem zu beheben? Im
Kommentar zu get_key_busy steht doch "until key released".
Prellt der Taster eigentlich auch, wenn man ihn loslässt?
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:
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.
Johannes H. schrieb:> Als quick&dirty-Möglichkeit habe ich das jetzt so probiert, geht:
Ne, das ist grausam.
Ohne den kompletten Code, kann man natürlich nicht Deinen Fehler finden.
Machs doch so, wie in meinem Beispiel.
Das try_sleep() muß immer wieder aufgerufen werden, bis die Preller
vorbei sind.
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
structcRGBLED_OUTPUT[1];
11
structcRGBTEMP[1];
12
13
#define MODES 13
14
#define BRIGHTNESSES 6
15
#define PWM_MAX 255
16
17
volatileuint8_tflag=1;
18
19
ISR(PCINT0_vect){
20
flag=1;
21
22
GIFR=0;// clear external interrupt flags
23
}
24
25
voidtry_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
intmain(void){
37
uint8_tmode_index=1;
38
uint8_tbrightness_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
Johannes H. schrieb:> Hier mal der komplette Code.
Schau Dir meinen PCINT an, der ist leer. Das Flag wird nur durch
Tasteneingaben geändert.
Aber bei Dir setzt jede Flanke das Flag zurück. Das kann nicht gehen.
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.
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.
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:
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.
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?
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
Hallo Peter,
Ich wollte noch mal nachfragen, ob Du schon Zeit hattest mal in den Code
reinzugucken.
Falls Dir das zu viel ist, kennst Du noch jemand anders aus dem Forum,
der sich mit Deinem Code etwas auskennt? Bzw. würde ich einfach im
zugehörigen Thread was posten.
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.
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.
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ß.