Moin,
ich will einen ATTiny programmieren, der fast durchgehend schläft (Power
Down). Drückt man einen Taster soll er aufwachen und einen
Transistor/Relais anschalten, 3 Minuten an lassen, dann wieder
ausschalten und wieder schlafen gehen.
Ich habe das ganze nun relativ naiv implementiert, d.h.:
-Ohne Entprellung
-Ohne Timer, sondern mit delay, da der uC in den 3 Minuten sowieso
nichts anderes machen soll.
Hier der Code:
1
#define F_CPU 1200000UL
2
3
#include<avr/io.h>
4
#include<avr/sleep.h>
5
#include<avr/interrupt.h>
6
#include<util/delay.h>
7
8
#define BUTTON_PORT PB1
9
#define RELAIS_PORT PB4
10
11
#define RELAIS_ON (PORTB |= (1<<RELAIS_PORT))
12
#define RELAIS_OFF (PORTB &= ~(1<<RELAIS_PORT))
13
14
ISR(INT0_vect){
15
16
}
17
18
intmain(void)
19
{
20
//PB1 (Taste) als Eingang konfigurieren
21
DDRB&=~(1<<BUTTON_PORT);
22
//PB1 (Taste) - PullUp aktivieren
23
PORTB|=(1<<BUTTON_PORT);
24
25
//PB4 (Transistor) als Ausgang konfigurieren
26
DDRB|=(1<<RELAIS_PORT);
27
28
//Aktiviere INT0 (low level)
29
MCUCR&=~((1<<ISC01)|(1<<ISC00));
30
while(1)
31
{
32
RELAIS_OFF;
33
GIMSK|=(1<<INT0);
34
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
35
sleep_enable();
36
sei();
37
sleep_cpu();
38
/*
39
SCHLAF
40
*/
41
sleep_disable();
42
GIMSK&=~(1<<INT0);
43
cli();
44
45
RELAIS_ON;
46
for(uint16_ti=0;i<1800;i++){
47
_delay_ms(100);
48
}
49
}
50
}
Wird das so funktionieren, oder habe ich etwas grundlegendes übersehen ?
Gruß
Jan
Schlechte Idee.
Es ist natürlich klar, dass man einen Interrupt zum Aufwecken braucht,
aber die ISR sollte dann sofort hingehen und den Interrupt wieder
abschalten. Ansonsten bekommst du ziemlich schnell einen
"Interruptsturm".
Wie aufwändig man dann die tatsächliche Entprellung hernach noch
gestaltet, ist Geschmackssache, aber den Interrupt an einem Taster
sollte man wirklich nur zum Aufwecken benutzen.
Jörg Wunsch schrieb:> Btw., gibt es einen Grund, warum du die Einzel-Makros nimmst und nicht> gleich ein sleep_mode()?
Weil sleep_mode() eine Race Condition enthält, d.h. mit etwas Glück
landet man im Sleep-forever.
Bzw. beim Idle wundert man sich, warum Interrupts manchmal riesige
Latenzen haben. Viel Spaß beim Debuggen.
Das sleep_disable() ist allerdings völliger Humbug, auch wenn es Atmel
in jedem Datenblatt so schreibt.
Sleep wird nur durch den sleep_cpu() Befehl ausgeführt, ein Disable für
den restlichen Code ist daher völlig ohne Effekt.
set_sleep_mode(), sleep_enable() muß man auch nicht vor jedem
sleep_cpu() nochmal machen, einmal im Init nach dem Reset reicht völlig.
Die Bits ändern sich nicht durch Geisterhand.
Peter Dannegger schrieb:> Weil sleep_mode() eine Race Condition enthält, d.h. mit etwas Glück> landet man im Sleep-forever.> Bzw. beim Idle wundert man sich, warum Interrupts manchmal riesige> Latenzen haben. Viel Spaß beim Debuggen.
Kannst du die beiden obigen Punkte näher erläutern?
Peter Dannegger schrieb:> set_sleep_mode(), sleep_enable() muß man auch nicht vor jedem> sleep_cpu() nochmal machen, einmal im Init nach dem Reset reicht völlig.
Ja, wenn man es so herum implementiert, gebe ich dir völlig Recht.
Aber einfach nur das, was in sleep_mode() drin ist, als drei einzelne
Aufrufe direkt ersetzen, hat wenig Zweck.
Die Diskussion um die race condition kann man übrigens hier finden:
http://lists.gnu.org/archive/html/avr-gcc-list/2005-12/msg00085.html
Jörg Wunsch schrieb:> Aber einfach nur das, was in sleep_mode() drin ist, als drei einzelne> Aufrufe direkt ersetzen, hat wenig Zweck.
Macht er ja nicht.
Der feine Unterschied ist das sei() direkt vor dem sleep_cpu().
Moin und danke für die Antworten !
Ich deaktiviere den INT0 jetzt direkt wieder, um nicht den
angesprochenen "Interruptsturm" zu bekommen.
1
#define F_CPU 1200000UL
2
3
#include<avr/io.h>
4
#include<avr/sleep.h>
5
#include<avr/interrupt.h>
6
#include<util/delay.h>
7
8
#define BUTTON_PORT PB1
9
#define RELAIS_PORT PB4
10
11
#define RELAIS_ON (PORTB |= (1<<RELAIS_PORT))
12
#define RELAIS_OFF (PORTB &= ~(1<<RELAIS_PORT))
13
14
ISR(INT0_vect){
15
GIMSK&=~(1<<INT0);
16
}
17
18
intmain(void)
19
{
20
//PB1 (Taste) als Eingang konfigurieren
21
DDRB&=~(1<<BUTTON_PORT);
22
//PB1 (Taste) - PullUp aktivieren
23
PORTB|=(1<<BUTTON_PORT);
24
25
//PB4 (Transistor) als Ausgang konfigurieren
26
DDRB|=(1<<RELAIS_PORT);
27
28
//Aktiviere INT0 (low level)
29
MCUCR&=~((1<<ISC01)|(1<<ISC00));
30
31
//Aktiviere Sleep Mode
32
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
33
sleep_enable();
34
35
while(1)
36
{
37
RELAIS_OFF;
38
GIMSK|=(1<<INT0);
39
sei();
40
sleep_cpu();
41
cli();
42
43
RELAIS_ON;
44
for(uint16_ti=0;i<1800;i++){
45
_delay_ms(100);
46
}
47
}
48
}
Wenn ich den Taster nun aber nicht entprelle, was kann denn dann
passieren ? Ich rufe den Tasterstatus ja gar nicht mehr ab, sondern
schalte sofort 3Min ein, sobald der Controller wieder wach ist. Gibts da
Nachteile ?!
JH schrieb:> Gibts da> Nachteile ?!
Daß durch elektrostatische Entladungen (über Teppich laufen) die
Schaltung anspricht, ohne daß die Taste gedrückt wurde.
Okay, habe jetzt dann doch noch deine Entprellroutine für einen Taster
verwandt, da ich die Wartezeit eh mit einem Timer machen wollte.
Habe jetzt aber noch ein anderes Problem: Statt der ausgerechneten 3
Minuten wird der Ausgang für ca. 3m20s gesetzt. Das Problem tritt auch
auf, wenn ich statt des Timers einfaches naives delay wie oben verwende.
Kann das am Takt liegen, oder liegt der Fehler im Programm ?!
Den Takt habe ich auf 4.8MHz und die CKDIV Fuse aktiviert.
Hallo!
Warum nimmst Du für einen so übersichtlichen Algorithmus nicht einfach
nur den RESET-Kopf? Nach Deinen drei Minuten kannst du dann den µC in
den absoluten Tiefschlaf schicken. Die Reset-Beschaltung und die gefuste
Startup-Time sorgen für eine wirksame Entprellung.
Moin,
die Schaltung steht nun schon und die Entprellung funktioniert ja auch
ganz gut. Das oben beschriebene Problem mit der ungenauen Zeit besteht
jedoch immer noch :(
Weiß da jemand Rat ?
JH schrieb:> Statt der ausgerechneten 3> Minuten wird der Ausgang für ca. 3m20s gesetzt.
Das ist eine Abweichung von ca. 10% und liegt bei vielen ATtinys
durchaus im zulässigen Bereich.
Ändere einen der Werte (F_CPU, Endwert der Schleife, Delay-Wert) oder
kalibriere den RC-Oszillator.