Forum: Mikrocontroller und Digitale Elektronik Menu einer Uhr erstellen


von Fabian L. (fabls)


Lesenswert?

Guten Tag Zusammen,

ich habe folgendes Problem und habe auch lange an einem ähnlichen 
Beispiel gesucht und leider nichts gefunden, vielt kann mir da jemand 
helfen!

Ich benutze einen STM32F1 und am Ende des Projektes soll es eine Uhr 
werden.

Ich habe 4 Taster angedacht:

Taster +
Taster -
Taster Return
Taster- Menu/Enter: durch diesen Taster soll man ins "Menu" kommen, 
sprich er fängt bei Stunden an es ändern zu können und mit den Tasten + 
und - kann man den Wert von Stunden erhöhen und verkleinern. Durch 
erneutes drücken der Menu/Enter-Taste soll es bestätigt werden und mit 
der Taste + oder - sollte man den nächsten Wert verändern können, sprich 
Minute.
Mit der Return Taste soll das Menu sofort verlassen werden.

Die Taster habe ich alle vor das Entprellen geschützt.

Nun weis ich leider nicht wie man bei einer solchen Routine vorgehen 
soll.
Da ich leider nicht so viel Erfahrung in Sachen Programmierung habe, 
würde ich mich sehr über Tipps und Tricks von euch freuen!!

Ich bedanke mich jetzt schon ganz herzlich auf wertvolle Beiträge.


Mit freundlichen Grüßen
Fabian

von Peter D. (peda)


Lesenswert?

Dazu nimmt man eine Statemaschine.
Hier mal ein Beispiel mit 2 Tasten und kurz/lang Unterscheidung:

Beitrag "Jumbo-LED Uhr"

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

mal dir erst mal ein Zustandsdiagramm auf. Knubbel für bestimmte 
Zustände, und Pfeile zwischen den Knubbeln, um von einem Zustand zum 
nächsten zu gelangen. Die Knubbel und die Pfeile beschriftest du mit 
"sinnigem"

ich gebe mal vor:

erster Knubbel = Startzustand, Uhr ist im Anzeigemodus (was auch immer 
sie dort macht)

vom Startzustand führen 3 Kanten wieder direkt als "Schlaufe" in den 
Startzustand zurück: "Taste + gedrückt", "Taste - gedrückt", "Taste 
return gedrückt". Nur eine einzige Kante "Taste Menue gedrückt" führt zu 
einem weiteren Knubbel "Taste Menue WURDE gedrückt"

und so weiter.

Du wirst z.B. irgendwann z.B. einen Zustand "erhöhe den Minutenzähler - 
Modulo 60" haben, d.h. der Minutenzähler wird immer um 1 erhöht, bis er 
bei 60 wieder auf 0 "zurück" geht und wieder von vorne los zählt"

Dahinter verbirgt sich natürlich einiger Programmieraufwand, z.B. eine 
Variable zu erhöhen [=Variable lesen, neu berechnen = +1 modulo 60, und 
Variable zurück speichern], Aber die konkrete 
"Programmierbefehl-Umsetzung" ist für die erste Gestaltung des Ablaufs 
erst mal unwichtig.

Interessant ist halt auf jeden Fall, immer alle Dinge im Auge zu 
behalten, was irgendwo irgendwie passiert. z.B. hast du ja auch eine 
Anzeige, welche aktualisiert werden möchte. Durch was das "automatisch" 
erfolgt, mag jemand anderes verraten.

Den Zustandsgraphen mußt du dann "nur noch" später in eine 
Programmiersprache deiner Wahl formulieren, und fertig ist das Programm 
;-)

von Karl H. (kbuchegg)


Lesenswert?

Fabian L. schrieb:

> Nun weis ich leider nicht wie man bei einer solchen Routine vorgehen
> soll.

Du hast dir ja schon den prinzipiellen Ablauf klar gemacht. Von daher 
sollte das nicht mehr weiter schwer sein.

Grundsätzlich gibt es 2 Möglichkeiten:
Der eine ist er sequentielle Durchlauf, der andere der ereignis 
gesteuerte Ablauf.

Nehmen wir mal den sequentiellen Durchlauf, da er meistens etwas 
einfacher zu verstehen ist.
Du nimmst einfach nur deinen bereits bekannten Bedienablauf her und 
programmierst den im Grunde runter.

Dazu brauchst du ein paar Hilfsfunktionen, wie zb Tastenerkennung und 
Auswertung. Die sollten im Vorfeld schon mal stehen und stabil sein.

Aber dann geht das dahin. Im einfachsten Fall steckst du die ganze 
Einstellerei in eine Funktion, denn dann bedeutet "Abbrechen der 
Eingabe" einfach nur: vorzeitiger Aussteig aus der Funktion

in etwa so
1
void SetupClock()
2
{
3
  uint8_t gedrueckteTaste;
4
  uint8_t StundeLokal;
5
  uint8_t MinuteLokal;
6
7
  //
8
  // erst mal die "richtigen" Werte lokal in die Funktion übernehmen
9
  // dann kann man die Ändern ohne die "echte Uhr" durcheinander
10
  // zu bringen. Das ist insofern zb wichtig, weil der Benutzer
11
  // bei der Eingabe der Minuten immer noch aus der ganzen Einstellung
12
  // aussteigen kann und sich dann die Stunden auch nicht verändern
13
  // sollen.
14
  // Nur wenn der ganze Einstellvorgang als ganzes durchgelaufen ist,
15
  // dann gilt die neu eingestellte Zeit und dann wird die richtige
16
  // Uhr aus den eingestellten Werten neu gestellt.
17
  //
18
  StundeLokal = StundeGlobal;
19
  MinuteLokal = MinuteGlobal;
20
21
  StundeLokal ausgeben
22
  MinuteLokal ausgeben
23
24
  // Stunden einstellen
25
  // die Einstellung wird durch Drücken von Return oder Enter beendet
26
27
  do
28
  {
29
    gedrueckteTaste = hole_nächsten_Tastendruck();
30
31
    if( gedrueckteTaste == Taste_+ )
32
    {
33
      StundeLokal++;
34
      if( StundeLokal == 24 )
35
        StundeLokal = 0;
36
37
      neue StundeLokal ausgeben
38
    }
39
40
    else if( gedrueckteTaste  == Taste_- )
41
    {
42
      if( StundeLokal > 0 )
43
        StundeLokal--
44
      else
45
        StundeLokal = 23;
46
47
      neue StundeLokal ausgeben
48
    }
49
50
    else if( gedrueckteTaste == Taste_Return )
51
      return;
52
53
  } while( gedrueckteTaste != Taste_Enter );
54
55
  // dasselbe nochmal für die Minuten
56
  do
57
  {
58
    gedrueckteTaste = hole_nächsten_Tastendruck();
59
60
    ....
61
62
  } while( gedrueckteTaste != Taste_Enter );
63
64
  // Die Einstellung ist bis hier her durchgelaufen, d.h der Benutzer
65
  // will tatsächlich genau die eingestellte Zeit setzen und ist nicht
66
  // vorher mit Return ausgestiegen
67
  //
68
  // aktiviere jetzt die so eingestellte Zeit, indem die
69
  // 'richtigen' Stunden und Minuten Werte aus den eingestellten
70
  // WErten aktualisiert werden
71
72
  StundeGlobal = StundeLokal;
73
  MinuteGLobal = MinuteLokal;
74
}

das ist natürlich nur eine Skizze. Mangels Wissen über dein 
Komplettsystem hab ich ein paar Annahmen treffen müssen, um wenigstens 
die grundlegende Idee skizzieren zu können.
Wie du konkret die Tastenabfrage machst, bzw. wie du konkret auf deiner 
Anzeige was ausgibst, das musst du selber wissen. Betrachte den Code 
also mehr als eine grundsätzliche Idee, denn als Code den du mit 
Copy&Paste übernehmen kannst.

: Bearbeitet durch User
von Fabian L. (fabls)


Lesenswert?

Vielen Dank für die vielen Beiträge!

Ich schaue es mir an und versuche es alles umzusetzen!

von Fabian L. (fabls)


Lesenswert?

Hallo zusammen,

erst mal vielen dank an Karl Heinz, dein Beitrag war für mich sehr 
wertvoll!!

Nun bin ich an dem Problem gestoßen, dass ich zwar die Stunden 
problemlos umstellen kann, jedoch weis ich dann nicht wie ich die 
Minuten umstellen kann.

Also die do-while schleife hat bei mir nicht funktioniert, da die 
schleife immer einmal abgearbeitet wird und da ich die Funktion in main 
bei der while(1) reingepackt habe, kann ich die Uhrzeit bzw. die Stunden 
jederzeit ändern.

Zugegeben ich habe es ohne:

gedrueckteTaste = hole_nächsten_Tastendruck();

programmiert, da ich nicht wusste wie ich es umsetzen sollte. Mag sein 
das es daran liegt.

Mein Problem:
Ich würde gerne mit der Taste-Menu/Enter (ist nur eine Taste) mit 
einmaligem betätigen in die Funktion void SetupClock()gelangen und durch 
nochmaliges betätigen dieser Taste zwischen Stunde & Minuten wechseln.


Ist mit Sicherheit was simples, doch leider versuche ich es schon seit 
Freitag ohne Erfolg :(

Danke im Voraus und lieben Gruß
Fabian

von Karl H. (kbuchegg)


Lesenswert?

Fabian L. schrieb:

> Zugegeben ich habe es ohne:
>
> gedrueckteTaste = hole_nächsten_Tastendruck();
>
> programmiert, da ich nicht wusste wie ich es umsetzen sollte. Mag sein
> das es daran liegt.

Das dachte ich mir schon.

Das ist allerdings einer der wensentlichen Punkte in der 
Menüprogrammierung: die UNterscheidung zwischen
* mach etwas SOLANGE eine Taste gedrückt ist
* mach etwas, beim NIEDERDRÜCKEN einer Taste.

Das sind 2 paas Schuhe.
Das eine ist recht trivial und bedeutet nichts anderes als: du drückst 
auf eine Taste und das Garagentor fährt zu. Lässt du die Taste los, 
bleibt das Tor stehen. SOLANGE die Taste gedrückt ist, bewegt sich das 
Tor.

Das andere ist erstaunlicherweise gar nicht so einfach und es bedeutet 
schlicht und ergreifend: mit dem Antippen eines Tasters wird ein Vorgang 
ausgelöst. Der Vorgang des Tastendrückens ist es, welcher die Aktion 
anstösst. Du drückst eine Taste und eine LED schaltet sich ein. Du 
drückst nochmal auf dieselbe Taste und die LED schaltet sich wieder aus.

Das sind 2 verschiedene Dinge und wenn du dich an Menüsteuerungen 
versuchst, dann brauchst du als aller erstes eine robuste und 
zuverlässige Erkennung eines Tastendrucks! (Also nicht eine Erkennung ob 
eine Taste gedrückt ist, sondern eine Erkennung, dass eine Taste 
gedrückt wurde).
IM ersten Fall reicht es, das 'gedrückt sein' festzustellen. Im zweiten 
Fall durchläuft die Tastenerkennung aber den Zyklus vom nicht-gedrückten 
Taster - den Vorgang des Niederdrückens - den Status des gedrückt seins 
- den Vorgang des loslassens - den Status des losgelassenen Tasters, bis 
ein Tastendruck komplett abgehandelt ist.


Hier
Entprellung
die Komfortroutinen vom PeDa machen das problemlos. Es gibt auch noch 
andere Routinen, aber die PeDa Routinen spielen alle Stücke die du 
jemals brauchen wirst.

: Bearbeitet durch User
von Fabian L. (fabls)


Lesenswert?

> Das sind 2 verschiedene Dinge und wenn du dich an Menüsteuerungen
> versuchst, dann brauchst du als aller erstes eine robuste und
> zuverlässige Erkennung eines Tastendrucks! (Also nicht eine Erkennung ob
> eine Taste gedrückt ist, sondern eine Erkennung, dass eine Taste
> gedrückt wurde).
> IM ersten Fall reicht es, das 'gedrückt sein' festzustellen. Im zweiten
> Fall durchläuft die Tastenerkennung aber den Zyklus vom nicht-gedrückten
> Taster - den Vorgang des Niederdrückens - den Status des gedrückt seins
> - den Vorgang des loslassens - den Status des losgelassenen Tasters, bis
> ein Tastendruck komplett abgehandelt ist.
>

Genau das habe ich vergeblich versucht, doch leider bin ich folglich 
dran gescheitert.

> Hier
> Entprellung
> die Komfortroutinen vom PeDa machen das problemlos. Es gibt auch noch
> andere Routinen, aber die PeDa Routinen spielen alle Stücke die du
> jemals brauchen wirst.

Die Komfortroutine von PeDa habe ich mir schon oft angeschaut und 
versucht es zu verstehen, doch leider kann ich es nicht am STM32F1 
umsetzen, wenn ich es zu 80% NICHT verstehe :(

von Karl H. (kbuchegg)


Lesenswert?

Fabian L. schrieb:

> Die Komfortroutine von PeDa habe ich mir schon oft angeschaut und
> versucht es zu verstehen, doch leider kann ich es nicht am STM32F1
> umsetzen, wenn ich es zu 80% NICHT verstehe :(

Entschuldige. Daran hab ich nicht mehr gedacht, dass du ja einen STM32F1 
hast.

Nun das Prinzip ist ja recht einfach.
Erst mal brauchst du einen Timer, der dir eine ISR in regelmässigen 
Zeitabständen aufruft. Ohne diese Zutat geht recht wenig.

D.h. wenn du das noch nie gemacht hast, dann ist jetzt der beste 
Zeitpunkt, den Umgang mit einem Timer zu lernen.

IN der ISR wird ganz einfach der am Port festgestellte Zustand der 
Tasten mit dem zuletzt im Programm als gültig angesehenen Tastenzustand 
verglichen.
Sind sie gleich, dann ist die Taste entweder gerade gedrückt oder gerade 
losgelassen. Welcher Fall vorliegt kann man ja recht einfach am 
Portpegel feststellen.

Der interessante Fall liegt aber vor, wenn programmintern eine 
entsprechende Variable aussagt, dass die Taste momentan nicht gedrückt 
ist, tatsächlich ergibt aber die aktuelle Nachschau in der ISR, dass die 
Taste jetzt aber gedrückt ist.
Dieser Zustand kann nur dann eintreten, wenn just in diesem Moment 
gerade jemand auf die Taste gedrückt hat. Denn davor waren ja die beiden 
Dinge gleich (programmintern besagte die VAriable, dass die Taste nicht 
gedrückt ist, und eine Nachschau um Port bestätigt dieses und auch 
umgekehrt).

D.h. genau dieser Wechsel ist dein Tastendruck, den man in der ISR 
registrieren muss (zb in einer Variablen merken), so dass das restliche 
Programm dann auch auf die Abfrage:
"Ist die Taste Enter gedrückt worden?"
dann auch genau diese Auskunft nur ein einziges mal kriegt: Jawohl, ist 
sie.


Die PeDa Routinen leisten dann noch mehr, in dem sie da eine Entprellung 
drüber legen und auch noch so Dinge wie Autorepeat einfügen können.


Aber den Teil musst DU unter Kontrolle kriegen. Einen Tastendruck 
auszuwerten, egal wie, ist Grundtechnik. Das ist, technisch gesprochen, 
nichts anderes als eine Flankenerkennung.
1
int main()
2
{
3
  bool EnterPressed = false;
4
5
  uint8_t TastenZustandEnterAlt;
6
7
  ....
8
9
  while( 1 )
10
  {
11
12
    // Feststellen und Erkennen eines Tastendrucks
13
14
    TastenzustandEnter = Status vom Pin jetzt in diesem Moment
15
16
    if( TastenzustandEnter != TastenzustandEnterAlt )
17
    {
18
       // Ha. Da hat sich was verändert! Der Portpin ist in einem
19
       // anderen Zustand.
20
       // In welchem?
21
       //
22
       if( TstenzustandEnter == gedrückt )
23
         EnterPressed = true;
24
25
       TastenzustandEnterAlt = TastenzustandEnter;
26
     }
27
28
     // einen eventuell festgestellten Tastendruck dann auch auswerten
29
     if( EnterPressed == true )
30
     {
31
       EnterPressed = false;
32
33
       mache die Aktion, die bei einem Tastendruck gemacht werden soll
34
       zb eine LED umschalten
35
     }
36
  }
37
}

Das ist so ungefähr das Grundgerüst für die ersten SChritte in die 
Tastendrück-Erkennung.
Da ist noch keine Entprellung drinnen, aber zumindest die 
Flankenerkennung. Sieh zu, dass du dieses Verfahren verstehst (erst mal 
abseits deines eigentlichen Programms, mach dir ein Testprogramm dafür), 
bring ihn auf deinem µC zum laufen und verlagere dann den ganzen Teil 
der Tastenerkennung in eine Timer-ISR, so dass die Nachschau auf dem 
Portpin so ungefähr alle 15 bis 20 Millisekunden gemacht wird. Das ist 
zwar noch keine ganz saubere Entprellung, aber für nicht allzu schlechte 
Taster sollte es reichen.

Das ist dein Teil-Ziel. Und das ist ein wichtiges Ziel um 
Tastenauswertung so zu machen, dass man dann als nächstes ein Menü 
darauf aufbauen kann. Ehe du diesen Teil nicht im Griff hast, hat es 
keinen bis kaum Sinn, am Menü weiter zu arbeiten.

: Bearbeitet durch User
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.