Forum: Mikrocontroller und Digitale Elektronik Ausschalten von LED nach dreimaligem Taster drücken


von Svenja S. (lavy7)


Lesenswert?

Hallöchen alle miteinander :)

Ich möchte gerne eine LED "steuern", indem ich drei mal auf den Taster 
drücke und diese an geht. Nach erneutem dreimaligen Drücken soll sie 
wieder ausgehen und immer so weiter.
Das Einschalten funktioniert soweit ganz gut, jedoch weiß ich nicht, wie 
ich in dieser Art die LED wieder ausschalten soll. Ich hab es damit 
probiert, die Variable var wieder zu dekrementieren, jedoch kommt dabei 
nicht wirklich etwas gescheites raus.

Hier mein Code um die LED einzuschalten:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
int main(void)
4
{  
5
  PORTD = (1<<PD2); 
6
  DDRB |= (1<<PB0);
7
  uint8_t var = 0;
8
  uint8_t taster_vorher = 0;
9
  uint8_t taster_jetzt = 0;
10
11
  while(1)
12
  {
13
    if ( (PIND & (1<<PD2)) )
14
    {
15
      taster_jetzt = 1;
16
    }
17
    else
18
    {
19
      taster_jetzt = 0;
20
    }
21
    if ( taster_vorher == 0  &&  taster_jetzt == 1 )
22
    { 
23
      var++;
24
    }  
25
    if(var == 3)
26
    {
27
      PORTB |= (1<<PB0);
28
    }
29
     taster_vorher = taster_jetzt;  
30
  }
31
 }
Entprellt habe ich mittels Kondensatoren.

Liebe Grüße,
Lavy!

von Udo (Gast)


Lesenswert?

Du hast keinen Befehl die LED auszuschalten:
PORTB &= ~(1<<PB0);

Führe eine Variable ein, die den Status der LED widerspiegelt, ändere 
die abfrage auf 3 durch eine Modulo-Divisions-Abfrage auf 0 und toggle 
dann die LED anhand deren Status.


Nebenbei: Willst du einen Triple-Klick erkennen, oder nur die Anzahl der 
Klicks zählen und dann reagieren? Weil für ersteres müsstest du die 
Klicks
nach einer gewissen Zeit resetten...

von fop (Gast)


Lesenswert?

Svenja S. schrieb:
> if(var == 3)
>     {
>       PORTB |= (1<<PB0);
>     }

hier schaltest Du immer ein, wenn 3 Mal drücken erreicht wurde. Wie wäre 
es, wenn Du den Zustand der Led umkehrst, wenn 3 Mal drücken erreicht 
wurde ?
Ok, da lauert noch ein kleiner Fallstrick : die Led fängt an zu glimmen 
nach 3 Mal drücken. Wenn man erst mal weiss warum, ist es klar. Der 
Zähler steht noch auf 3 Mal gedrückt, und die Led wird in jedem 
Durchlauf an oder aus geschaltet. Bisher hast Du sie immer wieder 
eingeschaltet, was bei einer bereits eingeschalteten Led nichts macht, 
also nicht auffällt.

Die Idee von Udo ist deshalb nicht so prickelnd, weil jede Variable 
einen maximalen Wert hat. Bei C geht das Zählen danach ohne weitere 
Warnungen wieder von vorne los. Wenn also jemand es schafft 256 mal zu 
drücken, bevor Dein Programm neu gestartet wird, passiert was 
Unerwartetes weil 256 nunmal nicht ohne Rest durch 3 teilbar ist. 
Überhaupt will ja niemand wissen, wie oft der Taster seit Programmstart 
gedrückt wurde. Das macht die Sache nur unnötig kompliziert so mit 
Modulo und so. Einfach jedesmal bis 3 zählen und gut. Klappt schon im 
Kindergarten...

Achtung Spoiler :

.

.

.

.
1
    if(var == 3)
2
    {
3
      PORTB ^= (1<<PB0);
4
      var = 0;
5
    }

von Svenja S. (lavy7)


Lesenswert?

Hey super, vielen Dank!
Manchmal ist die Lösung zu einfach um darauf zu kommen ^^'

Mal aus Interesse: Ich verwende einen ATMega8. Gibt es dort vielleicht 
eine interne Funktion, die man dafür ausnutzen könnte?
Also um dann aus dem Code quasi einen Dreizeiler zu machen?

Liebe Grüße
Lavy :)

von EAF (Gast)


Lesenswert?

fop schrieb:
> if(var == 3)
>     {
>       PORTB ^= (1<<PB0);
>       var = 0;
>     }

Für die meisten/modernen AVR reicht hier  PINB = (1<<PB0); statt  PORTB 
^= (1<<PB0);

von fop (Gast)


Lesenswert?

Du kannst dafür den Timer/Counter 2 im CTC Modus mit externem 
Takteingang nutzen. Die Initialisierung passt eventuell nicht in 3 
Zeilen, wenn es dann mal läuft, läuft es ohne eine einzige Codezeile.

von LostInMusic (Gast)


Lesenswert?

>Also um dann aus dem Code quasi einen Dreizeiler zu machen?

Dahin kommst Du, wenn Du in Deinem Programm die Zeilen 21 bis 
(einschließlich) 28 änderst in...
1
    if ( taster_vorher == 0  &&  taster_jetzt == 1 )
2
    { 
3
    KeyPress
4
    }

...und in die Funktion "KeyPress" den entsprechenden Code auslagerst:
1
    var++;
2
    if(var == 3)
3
    {
4
    LEDState = !LEDState;
5
    var = 0;
6
    }

Plus noch an geeigneter Stelle eine Zeile, die sich darum kümmert, dass 
die boolesche Variable LEDState mit Pin PB0 auf dem Port PORTB verknüpft 
wird. Bekommst Du sicher selber hin.

Den Inhalt von "KeyPress" könnte man nun als "Anwendung" interpretieren 
und alles andere als "Betriebssystem" (beide Begriffe in einem sehr 
weiten Sinn). Wie Du siehst, ist die "Anwendung" dann tatsächlich nur 
ein ganz einfacher Vierzeiler.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Svenja S. schrieb:
> Ich verwende einen ATMega8

EAF schrieb:
> Für die meisten/modernen AVR reicht hier  PINB = (1<<PB0); statt  PORTB
> ^= (1<<PB0);

Meiner Erinnerung nach ist der ATMega8 zu alt dafür. Ein ATMega88 
hingegen könnte das.

von EAF (Gast)


Lesenswert?

Frank M. schrieb:
> Meiner Erinnerung nach ist der ATMega8 zu alt dafür.
Durchaus!
Allerdings, kam diese Salamischeibe quasi gleichzeitig mit meinem 
Posting.

Ansonsten, würde ich mich mit solchen Antiquitäten nur unter Zwang 
beschäftigen

Beitrag #6785732 wurde von einem Moderator gelöscht.
von Svenja S. (lavy7)


Lesenswert?

Nochmal vielen Dank an alle :)

Hab es jetzt auch mit Interupts probiert und funktioniert alles wie es 
soll!

Ich wünsche euch noch eine schöne Woche :)

LG
Lavy

von Guido K. (Firma: Code Mercenaries GmbH) (thebug)


Lesenswert?

So was programmiert sich komfortabler als State-Machine (finiter 
Automat).

Also kein linearer Algorithmus, der hin und her hüpft, sondern eine 
Funktion, die periodisch aufgerufen wird und dann abhängig vom Status 
die Eingangswerte auswertet und ggf. den Status wechselt.

Wenn man nur ein/aus hätte, dann würde der "aus" Status, also State 0, 
prüfen, ob die Taste gedrückt wurde. Wenn das so ist, schaltet er die 
Lampe ein und wechselt zum nächsten Status (1), der dann jedes mal beim 
Aufruf prüft, ob die Taste wieder losgelassen wurde, dann wechselt er 
zum Status 2, der guckt, ob die Taste wieder gedrückt wurde, dann die 
Lampe ausschaltet, zum Status 3 wechselt, der auf das Loslassen wartet 
und zum Status 0 zurück kehrt.

Solche State-Machines kann man beliebig komplex machen. Am Besten ruft 
man die in einem Interrupt auf, wenn etwas passiert ist, oder periodisch 
über einen Timer. Die einzelnen States sind in einem switch/case was die 
Funktionen klein, handlich und übersichtlich macht.

ggf. mal was über finite Automaten lesen. Das ist ein sehr praktisches 
Werkzeug.

von Peter D. (peda)


Lesenswert?

Ich würde zum Einschalten fünfmal drücken, dann reicht zum Ausschalten 
einmal drücken.

von EAF (Gast)


Lesenswert?

Da fehlt noch ein "Zeitfenster".
Oder darf das drei mal drücken auch mit 1 Wochen Abstand zwischen den 
Drücken funktionieren?

von Egonwalter M. (heiner1234)


Lesenswert?

Peter D. schrieb:
> Ich würde zum Einschalten fünfmal drücken, dann reicht zum Ausschalten
> einmal drücken.

+1 ;-)

Du musst Dir aber dann merken (oder aufschreiben), dass Du schon 5mal 
gedrückt hast ;-)

von LostInMusic (Gast)


Lesenswert?

>Solche State-Machines kann man beliebig komplex machen.

Oh ja! Besser ist es aber, sie so einfach wie möglich zu machen :-)

von Dirk (Gast)


Lesenswert?

Hallo Svenja,

hat dieses 3 malige betätigen einen Hintergrund?
Es macht nach nur dann Sinn wenn Du die Taste 3x innerhalb eines 
Zeitraumes mit Timeout betätigst.

Alternativ wäre die Länge eines Tastendrucks eine Lösung.

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.