ATTiny per Taster wecken - Entprellung ?

Gast #3695317
Lesenswert?

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
int main(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_t i=0;i<1800;i++){
47
        _delay_ms(100);
48
     }
49
  }
50
}

Wird das so funktionieren, oder habe ich etwas grundlegendes übersehen ?

Gruß
Jan
Moderator Persönliche Seite #3695379
Lesenswert?

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.
#3695473
Lesenswert?

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.
Moderator Persönliche Seite #3695500
Lesenswert?

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
Gast #3696006
Lesenswert?

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
int main(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_t i=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 ?!
Gast #3696284
Lesenswert?

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.
Angehängte Dateien:

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