Forum: Compiler & IDEs ATTiny per Taster wecken - Entprellung ?


von JH (Gast)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Uwe (de0508)


Lesenswert?

Hallo Jan,

vor dem aktivieren eines Taster Interrupts würde ich noch das Flag INTF0 
löschen.

Warum? steht im Datenblatt des unbekannten AVR µC?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Btw., gibt es einen Grund, warum du die Einzel-Makros nimmst und nicht
gleich ein sleep_mode()?

von Peter D. (peda)


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.

von Fred (Gast)


Lesenswert?

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?

von Sandmännchen (Gast)


Lesenswert?


von Peter D. (peda)


Lesenswert?

Fred schrieb:
> Kannst du die beiden obigen Punkte näher erläutern?

Siehe sleep.h, Zeilen 73..104

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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

von Peter D. (peda)


Lesenswert?

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().

von JH (Gast)


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 ?!

von Peter D. (peda)


Lesenswert?

JH schrieb:
> Gibts da
> Nachteile ?!

Daß durch elektrostatische Entladungen (über Teppich laufen) die 
Schaltung anspricht, ohne daß die Taste gedrückt wurde.

von JH (Gast)


Angehängte Dateien:

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.

von Jörg E. (jackfritt)


Lesenswert?

Und warum steht bei F_CPU 600khz? Oder habe ich mich verguckt?

: Bearbeitet durch User
von JH (Gast)


Lesenswert?

Naja 4.8MHz durch 8 aufgrund der CKDIV8 Fuse ergibt bei mir 600kHz..

von Jörg E. (jackfritt)


Lesenswert?

Ah ok jetz habe ichs. Zu schnell überflogen.

von Route_66 H. (route_66)


Lesenswert?

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.

von JH (Gast)


Lesenswert?

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 ?

von Konrad S. (maybee)


Lesenswert?

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.

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.