Forum: Mikrocontroller und Digitale Elektronik Aufbau Statemaschine


von Achim S. (achims)


Lesenswert?

Hallo
Innerhalb einer statemaschine erfolgt die Verzweigung mit switch und 
case.
Wieviel mal kann man case nehmen? So oft wie nötig? Wie ist es mir der 
Übersichtlichkeit und Fehlersuche? Wie kann man eine Hierarchie 
vernünftig machen?
Leider habe ich bei go.. sehr wenig dazu gefunden. Vielleicht kann mir 
das jemand erklären.
achim

von cppler (Gast)


Lesenswert?

Dann hoffe ich mal das Du das
1
break;
 nicht vergessen hast.
Schaue in ein beliebiges C-Buch und dann weißt Du die Antwort auf Deine 
Frage.
Wenn Du objektorientiert arbeitest dann packe Deine Übergänge in die 
jeweiligen Klassen, ist übersichtlicher, wartbarer und einfacher zu 
erweitern.
Ansonsten weiß keiner welches eigentliche Problem Du hast ?

von Achim S. (achims)


Lesenswert?

Keine Angst, break habe nicht ich vergessen. Leider hält sich mein C 
Buch an dieser Stelle ziemlich raus. Entsprechende (speziel) Bücher kann 
ich mir leider nicht leisten. Noch mal, damit es klar wird.
Bei einer statemschine erfolgt die verzweigung innerhalb der switch / 
case und endet jeweils mit braek (so richtig?). Wieviel mal kann man 
case auswerten, ohne das es unübersichtlich wird (alles bei C)? Wie kann 
ich am besten eine Hierarchie machen? Bei meinem C-Buch (c von a bis z) 
steht nichts drin.
achim

von Claus M. (energy)


Lesenswert?

switch-case taugt höchtens was für ganz kleine state-machines. Für mehr 
als 3 states würde ich mit funktionspointern arbeiten und für jeden 
state eine eigene funktion anlegen.

von cppler (Gast)


Lesenswert?

Ganz ehrlich ich empfehle Dir den Stroustrup zu kaufen und mit C++ 
anzufangen.
Kostet um die 50,- Euro.
Es gibt auch massenhaft Tutorials im WWW:
C++:
http://www.online-tutorials.net/c-c++-c/c++-tutorial-teil-1/tutorials-t-1-58.html
http://www.cplusplus.com/doc/tutorial/
http://www.cprogramming.com/tutorial.html
FSM:
http://unitygems.com/fsm1/

Wenn Du's unbedingt in C machen willst/mußt dann tue Dir den Gefallen 
und nimm für die States sinnvolle Namen, z.B.:
1
...
2
3
#define MOTORAN 0
4
#define MOTORAUS 1
5
#define LEDROTAN 2
6
#define LEDGRUENAN 3
7
#define WAITFORUSER 4
8
9
...
10
11
switch(actualstate)
12
{
13
 case MOTORAN:{
14
               startmotor();
15
               actualstate = LEDROTAN;
16
               }
17
 break;
18
 case MOTORAUS:{
19
               stopmotor();
20
               actualstate = LEDGRUENAN;
21
               }
22
 break;
23
 case LEDROTAN:{
24
                setledrot();
25
                actualstate = WAITFORUSER;
26
               }
27
 break;
28
 case LEDGRUENAN:{
29
                 setledgruen();
30
                 actualstate = WAITFORUSER;
31
                 }
32
 break;
33
 case WAITFORUSER:{
34
                   actualstate = userinteraction();
35
                  }
36
 break;
37
 default: stopall();
38
 break;
39
}
40
41
...

Ob das übersichtlich ist bleibt geschmackssache, schau Dir die C++ 
Tutorien aml durch und versuche den Unterschied zu verstehen.
Wenn Du nicht genügend Platz hast Dein Projekt in OO umzusetzen nimm 
einfach aussagefähige Statebeschreibungen.

von Robert L. (lrlr)


Lesenswert?

>switch(actualstate)
> case MOTORAN:{
>              startmotor();

interessante taktik ;-)

von Leo H. (Gast)


Lesenswert?

Claus M. schrieb:
> Für mehr
> als 3 states würde ich mit funktionspointern arbeiten und für jeden
> state eine eigene funktion anlegen.

Hast du dazu mal ein Beispiel?

von cppler (Gast)


Lesenswert?

Claus M. schrieb:
> switch-case taugt höchtens was für ganz kleine state-machines. Für mehr
> als 3 states würde ich mit funktionspointern arbeiten und für jeden
> state eine eigene funktion anlegen.

Das stimmt so nicht, in jeder Fensterapplikation können schon sehr große 
Konstrukte stehen, für jeden Menueintrag/Schaltfläche eben ein case.
Das ist auch nichts anderes als eine Statemachine.
Es wird halt unübersichtlicher, vor allem wenn man unsinnige 
Bezeichnungen wählt.
Das mit den Funktionspointern entspricht ja meiner Empfehlung das in 
Klassen zu kapseln und darin dann die Übergänge zu realisieren.
Nur hat OO halt noch mehr Vorteile.

von cppler (Gast)


Lesenswert?

Robert L. schrieb:
>>switch(actualstate)
>> case MOTORAN:{
>>              startmotor();
>
> interessante taktik ;-)

Jaja "shoot yourself in the foot" :-P
Aber das Prinzip ist dem TO hoffentlich klar geworden ;-)

von Achim S. (achims)


Lesenswert?

Hallo
Das Prinzip ist mir klar. Verwende jetzt bis zu 8x case. Das mit 
Funktion werde ich testen. Habe bisher alles in c gemacht. Konnte mich 
leider nicht mit C++ anfreunden.
Bleibt nur noch die Frage offen, wie ich am besten eine

               /  Auswahl 1
Haupsteuerung /
              \  Auswahl 2

machen kann.
achim

von cppler (Gast)


Lesenswert?

Achim Seeger schrieb:
> Das mit
> Funktion werde ich testen.

Er meinte Funktionspointer !
Bleibe erstmal beim case bis Du Dich in OO eingearbeitet hast.
Dann machst Du für jeden Zustand ein eigenes Objekt welches dann jeweils 
zum nächsten Zustandsobjekt wechselt.

von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>Das Prinzip ist mir klar. Verwende jetzt bis zu 8x case.

Das ist voll OK, selbst das Dreifache und mehr ist OK. Nur dass man in 
die einzelnen case Zweigen keine Abläufe packt, die über mehr als 1/2 
Bildschirmseite gehen. Dort kommt nur ein aussagekräftiger 
Funktionsaufruf rein.

>Das mit
>Funktion werde ich testen. Habe bisher alles in c gemacht. Konnte mich
>leider nicht mit C++ anfreunden.

Ist auch besser so, du musst erstmal mit C programmieren lernen. Wenn du 
das mal sicher beherrscht, kannst du über C++ nachdenken.

>Bleibt nur noch die Frage offen, wie ich am besten eine

>               /  Auswahl 1
>Haupsteuerung /
>              \  Auswahl 2

>machen kann.

Was meinst du damit? Verschachtelte Menus?

von cppler (Gast)


Lesenswert?

Falk Brunner schrieb:
> Ist auch besser so, du musst erstmal mit C programmieren lernen. Wenn du
> das mal sicher beherrscht, kannst du über C++ nachdenken.

Frag mal KarlHeinz was er dazu meint ...
Das Kind ist jetzt zwar schon in den Brunnen gefallen, aber trotzdem mit 
OO anzufangen macht wesentlich mehr Sinn.
Auch bei µCs !
Dabei kommt es nicht darauf an das C ein subset von C++ ist !

von Frank M. (frank_m35)


Lesenswert?

Ich verwende für state machines immer:
1
typedef enum
2
{
3
  WELCOME_STATIC = 0  ,
4
  WELCOME_SOUND    ,
5
  WELCOME_BACKTOHOME  ,
6
  WELCOME_ANIMATION
7
} WELCOME_STATES;
8
9
WELCOME_STATES  welcomeState  = WELCOME_STATIC;

und dann in der state machine
1
switch ( welcomeState )
2
  {
3
  case WELCOME_STATIC:      
4
    ...
5
    welcomeState = WELCOME_SOUND;
6
    break;
7
  
8
  case WELCOME_SOUND:
9
    ...
10
    welcomeState = WELCOME_BACKTOHOME;
11
    break;
12
13
  ...
14
  }

Dadurch wird es strukturierte als mit losen Defines.

Deine offenen Fragen verstehe ich auch nicht, vielleicht solltest du 
dazu auch ein Codeschnispel posten wie du es momentan gelöst hast oder 
was noch fehlt.

von Falk B. (falk)


Lesenswert?

@ cppler (Gast)

>> Ist auch besser so, du musst erstmal mit C programmieren lernen. Wenn du
>> das mal sicher beherrscht, kannst du über C++ nachdenken.

>Frag mal KarlHeinz was er dazu meint ...
>Das Kind ist jetzt zwar schon in den Brunnen gefallen, aber trotzdem mit
>OO anzufangen macht wesentlich mehr Sinn.

Kaum. Das Kind muss erstmal laufen lernen. Ich kenn den OP ein wenig, 
wir haben mehrfach per Email kommuniziert. Er muss erst noch einige 
Grundlagen der allgemeinen Programmierung lernen. Das geht besser mit C. 
C++ überfordert die Leute.

>Auch bei µCs !
>Dabei kommt es nicht darauf an das C ein subset von C++ ist !

Darum geht es nicht.

von Achim S. (achims)


Lesenswert?

Hallo
Danke Falk. Ein C-Buch ist gut und ich verwende es. Die Grundlagen 
stehen drin. Aber das eigentliche programmieren und die notwendige 
Übersicht fehlen dazu. Ich versuche die Sachen zu lösen. Dazu nehme ich 
mir ein Stück Papier und schreibe was ich erreichen will, dann versuche 
ich den weg zu machen, so mit notwendigen Variablen und Schema. Je 
komplizierter die Programme werden, um so schlimmer. Da hilft manchmal 
nur Funktion und Fehler zu machen. Aus Fehlern lernt man. Dann bleibt 
noch Falk und Karl Heinz und einige andere.
Zu der Umschaltung. Habe in der Literatur den Hinweis gefunden eine 
Hierarchie zu machen. Beispiel: Ich verwende an einer Ampel zwei 
verschiedene Funktionen, den Regelbetrieb und den Notbetrieb. 
Regelbetrieb ist klar mit den Folgen Rot, Grün usw. Beim Notbetrieb wird 
nur Gelb blinkend geschaltet. Die Umschaltung erfolgt dabei mit einem 
Taster. Der Start erfolgt mit Gelb Blinkend, Beim Umschalten erfolgt 
erst Rot auf beiden danach normale Folge, beim Rückschalten auf Not 
erfolgt der übergang wenn beide rot sind. Code haben ich für die 
einzelnen Funktionen und ein Teil/Versuch beides zu machen. Leider mit 
vielen Fehlern. Versuche jetzt den Ablauf als Tabelle zu machen. Habe 
jetzt sogar 2 Tabellen, doch wie kann man die zusammen machen? Ist der 
Begriff - verschachtelte Menüs -an dieser Stelle richtig? Dachte dabei 
an zwei verschiedenen Abläufen, die an einer Stelle gekoppelt sind, wenn 
die Bedingungen erfüllt sind.
achim

von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>Danke Falk. Ein C-Buch ist gut und ich verwende es. Die Grundlagen
>stehen drin.

Schön, aber das allein richt nicht.

> Aber das eigentliche programmieren und die notwendige
>Übersicht fehlen dazu.

Genau das ist das Problem. Aus den Informationen im Buch und anderswo 
muss anwendungsbereits Wissen in deinem Kopf werden. Das nennt man 
Lernprozess ;-)

> Ich versuche die Sachen zu lösen. Dazu nehme ich
>mir ein Stück Papier und schreibe was ich erreichen will, dann versuche
>ich den weg zu machen, so mit notwendigen Variablen und Schema.

Richtiger Ansatz.

> Je
>komplizierter die Programme werden, um so schlimmer. Da hilft manchmal
>nur Funktion und Fehler zu machen.

Ja.

> Aus Fehlern lernt man.

Hoffentlich ;-)

> Dann bleibt
>noch Falk und Karl Heinz und einige andere.

Fragen ist nicht schlimm, im Gegenteil. Es ist richtig. Natürlich nur, 
wenn man einen ausreichenden Eigenanteil zur Problemlösung mitbringt.

>Zu der Umschaltung. Habe in der Literatur den Hinweis gefunden eine
>Hierarchie zu machen. Beispiel: Ich verwende an einer Ampel zwei
>verschiedene Funktionen, den Regelbetrieb und den Notbetrieb.
>Regelbetrieb ist klar mit den Folgen Rot, Grün usw. Beim Notbetrieb wird
>nur Gelb blinkend geschaltet. Die Umschaltung erfolgt dabei mit einem
>Taster. Der Start erfolgt mit Gelb Blinkend, Beim Umschalten erfolgt
>erst Rot auf beiden danach normale Folge, beim Rückschalten auf Not
>erfolgt der übergang wenn beide rot sind. Code haben ich für die
>einzelnen Funktionen und ein Teil/Versuch beides zu machen. Leider mit
>vielen Fehlern. Versuche jetzt den Ablauf als Tabelle zu machen. Habe
>jetzt sogar 2 Tabellen, doch wie kann man die zusammen machen?

Steht alles im Artikel statemachine und wie es der Zufall will, auch 
mit einem Ampelbeispiel.

> Ist der
>Begriff - verschachtelte Menüs -an dieser Stelle richtig?

Für deine Ampel? Nein, natürlich nicht. Das ist ein ganz normaler Ablauf 
mit bedingter Verzweigung (Wenn das -> tu dies etc.)

>Dachte dabei
>an zwei verschiedenen Abläufen, die an einer Stelle gekoppelt sind, wenn
>die Bedingungen erfüllt sind.

Ja, aber das ist dennoch eine State Machine. So ein einfaches Beispiel 
auseinanderzureißen ist nicht sinnvoll. Das ist erst bei DEUTLICH 
größeren state machines sinnvoll.

Poste vollständigen Code als Anhang, darüber kann man klar reden. Alles 
andere ist bisweilen zu abstrakt und allgemein. Zeichne dir ein 
Zustandsdiagramm mit den Übergängen, das kann man dann leicht in ein 
C-Programm umsetzen.

von Peter D. (peda)


Lesenswert?

Vergiß das mit den Funktionspointern, das ist für Dich nur eine 
zusätzliche Fehlerquelle.
Sind viele aufeinander folgende Case, macht der Compiler schon selber 
eine Sprungtabelle daraus. Du verlierst also keinerlei Performance.

Man kann mehrere Statemaschinen ineinander verschachteln, z.B. eine für 
das Hauptmenü und dann weitere für die Untermenüs.

von Achim S. (achims)


Lesenswert?

Hallo Falk
Dein Zufall ist einfach. Lerne an deinen Beispielen und passe es an und 
verändere es. Am konkreten Beispiel kann man am meisten Fehler machen 
und am besten lernen. Ansonsten ist es sehr interessantes Thema mit 
vielen Möglichkeiten.
Mit dem Code kann ich nicht so schnell dienen. Komme erst heute abend 
wieder an "meinen" Rechner. Im Moment besteht er aus vielen versuchen 
und Irrtümern mit viel Spagetti dazwischen. Reiche es nach (noch 
sortieren und ändern).
Bedingte Verzweigungen habe ich bereits drin, auch mit einer Änderung 
der state Nr. Bin auch noch an der Zeiteinstellung und den Tabellen 
dran.
Danke
achim

von Achim S. (achims)


Lesenswert?

Sorry, Peter habe es erst jetzt gesehen.
In der Literatur war so was drin, aber ohne konkretes zu sagen. Unter 
einem Menü oder Steuerung stellt man sich eine anzeige mit was drauf 
vor, was man ändern oder auswählen kann.
Aber eine Verschachtelte statemaschine, sorry keine Peilung. Ist 
eigentlich auch die Anfangsfrage gewesen. Wenn der Aufwand grösser ist, 
als der Nutzen, löst sich das Problem von allein. Zu diesem Thema habe 
ich nichts, auch keinen Ansatz.
achim

von Stefan (Gast)


Lesenswert?

> Wieviel mal kann man case auswerten, ohne das es unübersichtlich wird.

Dem Compiler ist es ziemlich egal. Wieviel Du noch übersichtlich 
findest, kannst nur Du sagen.

Die Abfrage von Ereignissen und Aktionen kannst Du in 
Prozeduren/Funktionen auslagern wie im obigen Beispiel von cppler. 
Sprechende namen für die States helfen sehr, um die Übersichtlichkeit zu 
verbessern. Außerdem kannst Du den Zustandsautomaten möglicherweise in 
mehrere Unter-Automaten aufsplitten. Wenn man das übertreibt, wird's 
aber wieder unübersichtlicher, als ein größerer.

Letztendlich kommt es auf DEINE Vorlieben an. Oder die deines Prüfers, 
falls es darum geht, eine gute Bewertung für die Arbeit zu bekommen.

von Falk B. (falk)


Lesenswert?

@Achim Seeger (achims)

>und am besten lernen. Ansonsten ist es sehr interessantes Thema mit
>vielen Möglichkeiten.

In denen man sich vor allem an Anfang schnell verlieren kann.

>Bedingte Verzweigungen habe ich bereits drin, auch mit einer Änderung
>der state Nr. Bin auch noch an der Zeiteinstellung und den Tabellen
>dran.

Mach erstmal nur EINE Variante. Mit switch, ohne Tabelle. Wenn das gut 
läuft geht es weiter.

von Achim S. (achims)


Lesenswert?

Wie versprochen stelle ich den ersten Code rein. Sorry, musste es noch 
"schön" machen.
1
#include <nibo/niboconfig.h>      // Laden der Header Dateien
2
#include <nibo/leds.h>
3
#include <nibo/delay.h>
4
#include <nibo/iodefs.h>
5
6
#define Rot      1
7
#define Rot_Orange  2
8
#define Gruen    3
9
#define Gelb    4
10
11
int state = 1;
12
13
void uprg1()
14
  {
15
    leds_set_status(2, 2);      // setzt LED 2 auf rot ein
16
    leds_set_status(2, 7);      // setzt LED 7 auf rot ein  
17
  }
18
19
void uprg2()
20
  {
21
    leds_set_status(0, 6);      // setzt LED 6 auf aus
22
    leds_set_status(2, 7);      // setzt LED 7 auf rot ein
23
    leds_set_status(3, 3);      // setzt LED 3 auf orange ein  
24
}
25
26
void uprg3()
27
  {
28
    leds_set_status(0, 2);      // setzt LED 2 auf aus
29
    leds_set_status(0, 3);      // setzt LED 3 auf aus
30
    leds_set_status(1, 4);      // setzt LED 4 auf grün ein  
31
  }
32
33
void uprg4()
34
  {
35
    leds_set_status(0,4);      // setzt LED 4 auf aus
36
    leds_set_status(3,3);      // setzt LED 3 auf orange ein  
37
  }
38
39
void uprg5()
40
  {
41
    leds_set_status(0,4);      // setzt LED 4 auf aus
42
    leds_set_status(0,3);      // setzt LED 3 auf aus
43
    leds_set_status(2,2);      // setzt LED 2 auf rot ein
44
    leds_set_status(3,6);      // setzt LED 6 auf orange ein  
45
  }
46
47
void uprg6()
48
  {
49
    leds_set_status(0,7);      // setzt LED 7 auf aus
50
    leds_set_status(0,6);      // setzt LED 6 auf aus
51
    leds_set_status(1,5);      // setzt LED 5 auf grün ein  
52
  }
53
54
void uprg7()
55
  {
56
    leds_set_status(0,5);      // setzt LED 5 auf aus
57
    leds_set_status(3,6);      // setzt LED 6 auf orange ein  
58
  }
59
60
void stateMaschine()
61
{
62
  switch (state)
63
  {    
64
    case 1:              // nur beim start aktiv
65
    uprg1();
66
    state = 2;
67
    break;
68
    
69
    case 2:
70
    uprg2();
71
    state = 3;
72
    break;
73
    
74
    case 3:  
75
    uprg3();
76
      if (!get_input_bit(IO_INPUT_1))     // Abfrage S3 Port
77
      delay(20);                          // Pause Entprellung 20 ms
78
      if (!get_input_bit(IO_INPUT_1))     // Abfrage S3 bei loslassen
79
      while (!get_input_bit(IO_INPUT_1))  // Wiederholung Schleife
80
        { state = 4 ; }                   // ohne klammer geht nicht
81
    break;
82
    
83
    case 4:
84
    uprg4();
85
    state = 5;
86
    break;
87
    
88
    case 5:
89
    uprg5();
90
    state = 6;
91
    break;
92
    
93
    case 6:
94
    uprg6();  
95
    state = 7;
96
    break;
97
    
98
    case 7:
99
    uprg7();
100
    state = 2;
101
    break;  
102
  }
103
}
104
105
int main()                // Start des Programms
106
{
107
  leds_init();            // LED`s initiiert
108
  bot_init();
109
  while(1==1)             // Endlosschleife
110
  {                       // Klammer Anfang
111
    // LED Anweisung: Farbe / LED
112
    // 0 = aus, 1 = grün, 2 = rot, 3 = orange    
113
    stateMaschine();
114
    delay(1000);          // Pause 1000 ms
115
  }                       // Klammer Ende
116
  return 0;               // Programmschleife von vorn
117
}                         // Ende Programm

Er ist so wie angegeben lauffähig. Es erfolgt ein Verzweigung und Aufruf 
von void und eine Abfrage des Tasters S3. Wie (Wo) kann ich am besten 
die Umschaltung zu Gelb blinken einbauen bzw. eine verschachtelte State 
machine?
achim

von Frank M. (frank_m35)


Lesenswert?

So ganz verstanden habe ich deine Ampel nicht:
wozu 7 LEDs, eine Ampel hat doch Rot, Gelb, Grün, also drei LEDs
was meinst du mit "Beim Umschalten erfolgt
erst Rot auf beiden danach normale Folge, beim Rückschalten auf Not
erfolgt der übergang wenn beide rot sind", also was ist beiden? Wie 
sieht deine Ampel denn aus?


generell etwas zum Programm:

Würdest du anstatt 0, 1, 2, 3 deine Defines für rot, grün, ... (Define 0 
für aus hast du noch vergessen) verwenden, so könntest du dir die 
Kommentare hinter den Funktionsaufrufen sparen, da die Zeilen dann 
selbsterklärend werden.

Deine Funktionsnamen sind kryptisch und tragen nichts zum 
Programmverständnis bei. Entweder du gibst ihnen aussagekräftige Namen, 
oder, da die Funktionen eh nur aus zwei drei Zeilen bestehen, lässt sie 
ganz weg und packst die Zeilen in die State Machine, wobei du hinter 
case 1: case 2: etc jeweils ein Kommentar für seine Funktion schreibst.

Da die State Machine selbst kryptisch ist (aufgrund der Verwendung von 
Zahlen) könntest du hier auch anstatt Zahlen aussagekräftige Defines 
verwenden (dadurch kannst du die zuvor genannten Kommentare dann wieder 
weg lassen, da es selbsterklärend wird) oder, meiner Meinung nach 
besser, mit dem typedef enum wie ich in einem Post weiter oben 
aufgeführt habe.

In Mikrocontrollern verwendet man ungern so harte Pausen, da sie schnell 
alles sehr unflexibel machen. Kennst du Timer, hast du damit Erfahrung? 
Falls nein, dann vergiss sie erst mal wieder und bringe erst deine State 
Machine fertig, und nimm dir als nächste Aufgabe Timer anstatt Pausen zu 
verwenden.

Ein bisschen schöner Einrücken, damits leserlicher wird. Denn Case 3 
sieht unleserlich und auch falsch aus (sofern ich verstanden habe was du 
damit bezwecken willst). Wenn man es richtig einrückt und mit den 
geschweiften Klammern nicht so sparsam ist wird's offensichtlich.

Warum fragst du den Taster in der State Machine ab? Sollte er nicht 
immer in den Notbetrieb wechseln können, egal in welchem Zustand die 
Ampel sich momentan befindet?

Zum Gelb blinken:
Du nennst einen Zustand in deiner State Machine 'gelb_blinken' bei dem 
du dann eben den Ausgang jedes mal invertierst und den Taster noch 
überprüfst. Nur wenn der der Taster gedrückt wird kommt er weiter, 
ansonsten bleibt deine 'state' Variable unverändert, d.h. die LED blinkt 
dann mit 1Hz da der Zustand ständig aufgerufen wird (mit einer 1s Pause 
dazwischen) bis der Taster eben gedrückt wird.

von Achim S. (achims)


Lesenswert?

Hallo Frank
das Programm entwickel ich langsam. Vorher habe ich die anweisungen der 
LED direkt hinter die case geschrieben. In diesem wollte ich die 
Funktionrn über void testen.
Meine Ampel besteht jeweils aus 3 verschiednen LED. Die LED haben die 
Farbe rot, grün und orange. Orange verwende ich für gelb. Daher auch die 
Zahlen. Ich habe einen Start Zustand definiert, beide auf Rot. danach 
geht eine Ampel auf grün, bleibt so lange grün bis die umschaltung durch 
den taster erfolgt. Wenn ich die Funktionen einer echten Ampel zu Grunde 
lege, erfolgt eine Umschaltung nicht willkürlich innerhalb des 
Programmes. Die Farben beider Ampeln müssen auf rot schalten und erst 
dann auf gelb(orange) blinken gehen. Die Funktion des Tasters wird dann 
anders genutzt.
case3 habe ich bereits eingerückt, leider hat das Prg es nicht ganz 
korrekt genommen. An einem Timer arbeite ich bereits. Bin leider mit dem 
Ablauf noch nicht zufrieden. Es geht dabei um unterschiedliche Zeiten, 
z.B. grün 6 s, wechsel rot-gelb-grün je 1s, wobei die Angabe der Zeiten 
extra erfolgen kann. Die Umschaltung zum Notbetrieb ist hier noch nicht 
drin.
achim

von Frank M. (frank_m35)


Lesenswert?

Achso, du hast zwei Ampeln :-D das wird aus deinem Programm nicht 
ersichtlich, denn welche LED gehört zu welcher Ampel?

Und so lange das so kryptisch bleibt wird das nichts.

So kurz aus dem Ärmel geschüttelt würd ich es so machen: (nicht 
vollständig, d.h. du musst es schon noch schöner wie im Beispiel machen, 
dass nicht plötzlich mehrer LEDs gleichzeitig leuchten)
1
switch ( ampelState )
2
  {
3
  case Ampel_Init:      
4
    beide Ampeln rot schalten
5
    ampelState = Ampel_Wait
6
7
  case Ampel_Wait:
8
    counter hochzaehlen, falls groesser als 10 Alle LEDs abschalten und ampelState=Ampel_Error
9
    taster abfragen, falls gedrueckt ampelState=Ampel1_Gruen
10
    Pause 1s  
11
12
  case Ampel_Error:
13
    Orangene LED invertieren
14
    taster ueberpruefen, falls gedrueckt, Ampel1_Gruen
15
    Pause 1s
16
17
  case Ampel1_Gruen:
18
    Ampel1 auf gruen schalten
19
    Ampel2 auf rot schalten
20
    ampelState = Ampel1_GruenWait
21
22
  case Ampel1_GruenWait:
23
    taster abfragen, falls gedrueckt ampelState = Ampel1_Orange
24
25
  case Ampel1_Orange:
26
    Ampel1 auf orange
27
    Pause 500ms
28
    ampelState = Ampel1_rot
29
30
  case Ampel1_rot:
31
    Ampel1 auf rot
32
    Ampel2 auf rotorange
33
    Pause 500ms
34
    ampelState = Ampel2_gruen
35
  
36
  case Ampel2_gruen:
37
    Ampel2 auf gruen
38
    Ampel1 auf rot
39
    ampelState = Ampel2_gruenWait
40
41
  case Ampel2_gruenWait:
42
   taster abfragen, falls gedrueckt ampelState = Ampel2_orange
43
44
  case Ampel2_orange
45
    Ampel2 auf orange
46
    Pause 500ms
47
    ampelState = Ampel2_rot
48
49
  case Ampel2_rot
50
    Ampel2 auf rot
51
    Ampel1 auf rot_orange
52
    Pause 500ms
53
    ampelState = Ampel1_gruen
54
  }
55
56
57
int main()                // Start des Programms
58
{
59
  leds_init();            // LED`s initiiert
60
  bot_init();
61
  ampelState = Ampel_Init;
62
  while(1==1)             // Endlosschleife
63
  {                       // Klammer Anfang
64
    stateMaschine();
65
  }                       // Klammer Ende
66
  return 0;               // Programmschleife von vorn
67
}
Nicht optimal, aber vielleicht das was du willst?


Deine leds_set_status Routine ist noch ein kryptische, versuche so eine 
wie im Beispiel:
http://www.mikrocontroller.net/articles/Statemachine
zu ersetllen, d.h. dass du gezielt Ampel1 auf eine Farbe setzen kannst 
und nicht Ampel2 durch LED7 ansprechen musst, sondern durch Ampel2 auf 
Rot,
bspw:
1
#define Rot      1
2
#define Rot_Orange  2
3
#define Gruen    3
4
#define Gelb    4
5
6
void Ampel1(int Farbe)
7
{
8
 alle LEDs aus
9
 switch(Farbe)
10
 {
11
 case Rot:
12
   LED1 an
13
 case Rot_Orange:
14
   LED1 an
15
   LED2 an
16
 case Gruen:
17
   LED3 an
18
 case Gelb:
19
   LED2 an
20
 } 
21
}

von Enum (Gast)


Lesenswert?

Und, wie oben schonmal angedeutet: Nimm ein enum statt einem Haufen 
#defines für die States, wenn möglich.
Der Compiler generiert daraus zwar exakt den gleichen Code, aber: Bei 
einem Enum kann dich der Compiler WARNEN, wenn du einen State vergessen 
hast.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

@ Achim Seeger (achims)

>Wie versprochen stelle ich den ersten Code rein. Sorry, musste es noch
>"schön" machen.

Warum nicht als Anhang? Egal.

>void uprg1()
>  {
>    leds_set_status(2, 2);      // setzt LED 2 auf rot ein
>    leds_set_status(2, 7);      // setzt LED 7 auf rot ein
>  }

Hier ist schon das erste Problem. Warum nichtsagende Zahlen anstatt 
aussagekräftiger defines bei den Funktionsaufrufen? Somit musst du 
überall noch einen Kommentar schreiben, damit man es versteht, und der 
Kommentar kann falsch sein, weil du mal was geändert hast, aber nicht 
den Kommentar angepasst hast. Ebenso bei den case, Nummern sind 
nichtssagend, symbolische Namen per enum oder define sind 100mal besser.

>void stateMaschine()
>{
>  switch (state)
>  {
>    case 1:              // nur beim start aktiv
>    uprg1();
>    state = 2;
>    break;

Hier das Gleiche. uprg1 ist nichtssagend. leds_reset() ist da deutlich 
besser. Nomen est omen!

>    case 2:
>    uprg2();
>    state = 3;
>    break;

Eine Einrückung macht es leserlicher.

>    case 3:
>    uprg3();
>      if (!get_input_bit(IO_INPUT_1))     // Abfrage S3 Port
>      delay(20);                          // Pause Entprellung 20 ms

MÖÖP! Hier nicht!

>      if (!get_input_bit(IO_INPUT_1))     // Abfrage S3 bei loslassen
>      while (!get_input_bit(IO_INPUT_1))  // Wiederholung Schleife
>        { state = 4 ; }                   // ohne klammer geht nicht
>    break;

MÖÖÖP MÖÖÖP! Endlose Warteschleifen auch nicht!

>int main()                // Start des Programms
>{
>  leds_init();            // LED`s initiiert
>  bot_init();
>  while(1==1)             // Endlosschleife

Ein while(1) reicht.

>  {                       // Klammer Anfang

So ein Kommentar ist hyperliquide

>    // LED Anweisung: Farbe / LED
>    // 0 = aus, 1 = grün, 2 = rot, 3 = orange
>    stateMaschine();

Was denn nun? Englisch oder Deutsch? statemachine oder Zustandsautomat? 
Denglisch ist bäh.

>    delay(1000);          // Pause 1000 ms
>  }                       // Klammer Ende

OK, kann man fürs erste machen, Timer ist aber besser.

>Er ist so wie angegeben lauffähig.

Und blockierend. -> Durchgefallen!
Lies den Artikel statemachine, dort wird EXPLIZIT gezeigt und 
geschrieben, dass in der Statemachine NIE gewartet wird. Die 
Tastenerkennung kann und muss man anders machen.

>Es erfolgt ein Verzweigung und Aufruf
>von void

NEIN! VERDAMMT NOCHMAL! Gewöhn dir diese Unsinnssprache ab!

> und eine Abfrage des Tasters S3. Wie (Wo) kann ich am besten
>die Umschaltung zu Gelb blinken einbauen bzw. eine verschachtelte State
>machine?

Du brauchst keine verschachtelte statemachine. Du brauchst nur 
gescheite, NICHT blockierende Tastenauswertung. Der Rest ist dann 
einfach.

Wie das geht? Siehe Anhang. Ist aber nur hingeschrieben, die Tippfehler 
sind noch drin. Ist zwar keine perfekte Entprellung, aber immer noch 
viel besser als der jetzige Zustand. Ich habe auch versucht, dir zu 
zeigen, dass Symbole deutlich besser als reine Zahlen sind.

von Enum (Gast)


Lesenswert?

Enum schrieb:
> Der Compiler generiert daraus zwar exakt den gleichen Code,

Nachtrag: Am AVR nicht unbedingt, da kann es sein, dass aus dem enum ein 
16-Bit-int wird... => Feintuning für später, ggfs. per Compilerswitch 
umschaltbar.

von Frank M. (frank_m35)


Lesenswert?

Wenn du Falks Code zur Tastenabfrage verwendest (welche sicherlich zu 
bevorzugen ist), so darfst du natürlich keine Pause in der State Machine 
mehr machen, da Falks Code davon lebt, dass die Tastenroutine alle 10ms 
gestartet wird.
Das verkompliziert natürlich Pausen vorerst für dich und Falk hat es 
auch in seinem Beispielcode auch nicht implementiert, wäre aber schon 
ein Schritt in Richtung Timern.

Deine Methode die Taste abzufragen ist schlampig und unzuverlässig, aber 
ich würde es für den ersten Versuch so lassen, denn dann kannst du 
Pausen vorerst in der State Machine lassen, was man zwar nicht macht, 
aber man hat nie das perfekte Ergebnis von Anfang an.

Wenn es dann so funktioniert würde ich versuchen die Pausen aus der 
State Machine raus zu nehmen (wie das geht steht im Artikel) und die 
Tastenroutine von Falk zu implementieren.

Wenn das funktioniert würde ich die 10ms Pause in der While Schleife 
raus nehmen und die Zeit über Timer steuern.

von Robert B. (goldcap) Benutzerseite


Lesenswert?

Hallo,

bei diesem Projekt habe ich den Interpreter als Statemachine 
implementiert.

Die folgende Struktur wird benutzt um das Regelwerk abzubilden.
1
// Die Kommandostruktur
2
struct state {
3
  int           lineid;
4
  int           oldstate;
5
  int           nextstate;
6
  uint16_t      needed_mode;
7
  uint16_t      flags;
8
  const char    *text;          // string to compare with, string must be located in flash
9
  int           (*func)(int);   // function pointer, executed when expression is true
10
  int16_t       arg;            // argument to be used for the function unless the mode is NEED_NUM
11
};
Alle Daten wurden soweit möglich ins Flashmemory gepackt.

http://www.mikrocontroller.net/articles/Terminal_mit_Kommandointerpreter

von Achim S. (achims)


Lesenswert?

Hallo
Danke für eure Meinung zum Thema. Geht nicht gleich zu streng mit mir 
um. Vermute, das ihr auch mal angefangen habt. Noch dazu, das es wenig 
Info gibt, die ich auch verstehe.
Zur Tasterauswertung. Es ist nur eine einfache Sache. Reicht zum ersten 
Test aus. In anderen Projekten habe ich die Auswertung von Peter drin, 
wodurch selbst die Länge zum auswerten ist (mit Timer). Nicht alles 
gleich sofort. Es erfolgt keine Blockierung der Auswertung. Habe es 
getestet und einen anderen state reingeschrieben. An dem Timer arbeite 
ich bereits. Es ist mir klar, das man so nicht macht. Ein 
Programmdurchlauf dauert jetzt wenigstens 1s. Zur Auswertung der Taste 
ist das zu lang. Ist mir klar. Nach dem Vorbild von Frank wird es 
geändert. Bitte, alles nacheinander. Kommt wieder das gesagte, aus 
Fehlern lernen.
Die Bezeichnungen erscheinen jetzt kompliziert. Habe sie vom Hersteller 
übernommen. Da ich damit umgehe sind sie mir geläufig. Kann man später 
alles noch ändern (optimieren). Jetzt muss erst mal der Timer laufen.
achim

von Frank M. (frank_m35)


Lesenswert?

Ich denke als erst sollte deine State Machine laufen. Wenn du es mit so 
harten Pausen wie in deinem ersten Beispiel und in meiner Version 
machst, brauchst du auch gar keine Tastenentprellung wenn du nur auf das 
Drück-Ereignis des Taster reagierst (danach kommen ja wieder lange 
Pausen und dein Programm reagiert auf keine Tasten für ein paar Sekunden 
lang, daher ist eine Entprellung vorerst nicht notwendig, was die Sache 
weiter vereinfacht)

Auch solltest du, wenn du weiterhin Hilfe von uns willst, die 
Kryptischen Bezeichnungen entfernen. Du magst es so nachvollziehen 
können, wir und jeder andere aber nicht. Somit ist eine weitere Hilfe 
für uns unmöglich.

Wenn deine State Machine soweit funktioniert würde ich weiterhin ohne 
Timer die Sache von Falk implementieren.

Wenn das fertig ist, Timer.

Wie willst du denn den Timer bei dir überhaupt dann einsetzen? Man kann 
die Sachen nämlich auch mit einem Timer blockierend und somit falsch 
implementieren :-)

von Achim S. (achims)


Lesenswert?

Hallo Frank
als erstes will delay durch einen Timer ersetzen. Basis ist dabei 1ms. 
Als nächstes soll nach Falk innerhalb jeder case eine extra Zeit drin 
sein. Dabei geht es um unterschiedliche Zeiten z.B. Umschaltung 
rot-gelb-grün und grün Pause 6s und dabei besonders um die 
Zeiteinstellung. Dann soll die gesamte stop Funktion mit rot auf beiden 
Ampeln erfolgen bis zum übergang auf gelb blinkend. Da brauche ich aber 
noch Zeit um das alles zu machen und zu begreiffen.
Wenn ich delay erst mal durch einen Timer ersetze und dann erweitere 
dürfte es zu keiner Blockade kommen, hoffe ich.
achim

von Falk B. (falk)


Lesenswert?

@ Achim Seeger (achims)

>Zur Tasterauswertung. Es ist nur eine einfache Sache. Reicht zum ersten
>Test aus.

Nein, reicht es nicht! Denn damit killst du an einer zentralen Stelle 
die Leistung des ganzen Konzepts! Und es ist ja nicht wirklich schwer, 
es gleich richtig zu machen! DU hast dich doch mit dem Thema 
Multitasking lange ebschäftigt! Alles wieder vergessen?

>In anderen Projekten habe ich die Auswertung von Peter drin,
>wodurch selbst die Länge zum auswerten ist (mit Timer). Nicht alles
>gleich sofort. Es erfolgt keine Blockierung der Auswertung. Habe es
>getestet und einen anderen state reingeschrieben. An dem Timer arbeite
>ich bereits. Es ist mir klar, das man so nicht macht. Ein
>Programmdurchlauf dauert jetzt wenigstens 1s. Zur Auswertung der Taste
>ist das zu lang. Ist mir klar. Nach dem Vorbild von Frank wird es

Mein Gott, wenn du es nicht schaffst, so einen simplen Namen wie den 
meinen (F A L K) mit VIER Buchstaben richtig zu wiederholen, sehe ich 
für deine (Hobby)programiererzukunft schwarz! Kann es sein, dass du 
generell Schwierigkeiten beim Lernen von abstrakten Sachverhalten hast? 
Das ist eine ernste Frage, keine Polemik.

>geändert. Bitte, alles nacheinander. Kommt wieder das gesagte, aus
>Fehlern lernen.

Nö, du ignorierst KONSEQUENT die Beispiele, die es schon nahezu 
idiotensicher erklären. Den Artikel statemachine finde ich 
eigentlich schon recht anschaulich, wenn gleich ich das nicht endgültig 
beurteilen kann, denn ich weiß ja wie es funktioniert. Richtigerweise 
müssten mehrere Leute, welche eben das Thema noch nicht verstanden 
haben, diesen Artikel lesen und danach kann man prüfen, wie gut sie ihn 
verstanden haben. Wenn ich mich aber dich so ansehe, ist der Artikel 
scheinbar grottenschlecht. Das kann ich aber nicht so recht glauben.

>Die Bezeichnungen erscheinen jetzt kompliziert. Habe sie vom Hersteller
>übernommen. Da ich damit umgehe sind sie mir geläufig. Kann man später
>alles noch ändern (optimieren).

FALSCH! Man muss es GLEICH gut lesber hinschreiben, sonst verzettelt man 
sich in seinem eigenen, undurchsichtigen Geschreibsel! Und kommunizieren 
kannst du deinen Code dann auch nicht. Deine Beispiel oben ist nicht 
besonders anschaulich lesbar (ok, es gibt deutlich schlimmere, aber das 
ist nicht der rechte Massstab).

>Jetzt muss erst mal der Timer laufen.

Mein Gott, das ist in einer Viertelstunde vergessen. Es gibt ÜBERALL 
komplette Beispiele! U.a. in dem dir bekannten Artikel Multitasking.
UNd selbst wenn man mit einem einfachen Delay arbeitet, kann man es 
DEUTLICH besser machen. Ich hab dir das Beipiel gezeigt. 
Multitasking und statemachine hängen sehr eng zusammen.

von Peter D. (peda)


Lesenswert?

Achim Seeger schrieb:
> stateMaschine();
>     delay(1000);          // Pause 1000 ms

Das ist nicht Dein Ernst?
Da muß man ja auf der Taste einschlafen, eh sie erkannt wird.
Ein Mensch drückt typisch nur etwa 300ms lang. Dir gehen also 3 
Tastendrücke verloren.

So geht das auf keinen Fall.
Eine Statemaschine hat keinerlei Delays, sie läuft immer mit voller 
Geschwindigkeit durch.

Delays macht man mit Timern, irgendwann setzt der Timer ein Flag und die 
Statemaschine testet bei jedem Durchgang dieses Flag und macht dann die 
gewünschte Aktion.

Nebenbei, Tasten Entprellen macht man vorzugsweise nicht in der 
Statemaschine, sondern im Timerinterrupt.
Die Statemaschine testet auch wieder nur das Ereignis-Flag: Taste wurde 
gedrückt.

Eine Statemaschine wartet niemals und nirgends.

von Achim S. (achims)


Lesenswert?

Hallo Peter
das ist einer meiner ersten Versuche dazu. Habe eine delay eingesetzt 
für den ersten Lauf. So bald der Timer geht und die Zeit in jeder case 
ist, gibt es die delay nicht mehr. Für mich war in dieser Stelle erst 
einmal die Funktion wichtig. Damit kann ich auch verschiedene andere 
Sachen testen. Die Tastenabfrage von dir habe ich im Multitasking drin, 
im Interrupt, diese soll auch hier rein. Vorher muss einiges anderes 
gehen.
Hab ein bisschen Geduld mit mir.
achim

von Achim S. (achims)


Lesenswert?

Hallo
Zur Entprellung verwende ich dieses Prg.
1
void nibo_timer2()                                    // Timer 1ms
2
  {
3
  TCNT2 = 0;
4
  OCR2=249;
5
  TCCR2=(1<<WGM21)|(1<<CS21)|(1<<CS20);
6
  TIMSK |= (1<<OCIE2);
7
  }
8
9
ISR (TIMER2_COMP_vect)                              // ISR wait1=1ms,
10
  {
11
  static uint8_t ct0,ct1,rpt;
12
  uint8_t i;
13
  if(  wait<=5)                                    // Einstellung Takt
14
    {            // 9=langsam
15
    wait++;          // 5=relativ schnell
16
    }                                                  // erhöht
17
  else                                              // wenn dann ...
18
    {
19
    wait=0;                                        // setzt wait auf 0
20
    wait1=0xFF;                                 // Signal alle wait1
21
    i=key_state ^~KEY_PIN;
22
    ct0=~(ct0&i);        // Tasten abfragen
23
    ct1=ct0^(ct1&i);
24
    i&=ct0&ct1;
25
    key_state^=i;
26
    key_press|=key_state&i;
27
    if((key_state & REPEAT_MASK)==0)
28
    rpt=REPEAT_START;
29
    if(--rpt==0){
30
    rpt=REPEAT_NEXT;
31
    key_rpt|=key_state & REPEAT_MASK;
32
       }
33
    }
34
  }
35
  
36
  uint8_t get_key_press(uint8_t key_mask)    // Taste press
37
    {
38
    cli();
39
    key_mask &=key_press;
40
    key_press^=key_mask;
41
    sei();
42
    return key_mask;
43
    }
44
  
45
  uint8_t get_key_rpt(uint8_t key_mask)    // Taste rpt
46
    {
47
    cli();
48
    key_mask &=key_rpt;
49
    key_rpt^=key_mask;
50
    sei();
51
    return key_mask;
52
    }
53
54
  uint8_t get_key_short(uint8_t key_mask)    // Taste short
55
    {
56
    cli();
57
    return get_key_press(~key_state & key_mask);
58
    }
59
  
60
  uint8_t get_key_long(uint8_t key_mask)    // Taste long
61
    {
62
    return get_key_press(get_key_rpt(key_mask));
63
    }

Das kommt rein. Damit habe ich auch einen Timer
achim

von Falk B. (falk)


Lesenswert?

@Achim Seeger (achims)

>Zur Entprellung verwende ich dieses Prg.

Brauchbar. Aber deine Einrückung des Quelltextes ist sehr eigen, ich 
finde sie nicht günstig. Die öffnenen und schließenden Klammern sollten 
die gleiche Ebene wir die Funktionsnamen bzw. Strukturköpfe 
(for/while/switch) haben. Ahhh, wahrscheinlich hast du echte Tabulatoren 
drin, aber mit einer anderen Einstellung als die Forensoftware. Darum 
sollte man beim Editor besser einstellen, dass die Tabs in echte 
Leerzeichen umgewandelt werden, dann passiert sowas nicht. Siehe

http://www.mikrocontroller.net/articles/Strukturierte_Programmierung_auf_Mikrocontrollern#Formatierung_des_Quelltextes

Ausserdem sind in dem Quelltext ein paar böse C-Fallstricke drin. if 
Anweisungen ohne  Klammern für den "then" Block sind GEFÄHRLICH!
Gerade Anfänger sollten IMMER Klammern für den then/else Block setzen!
Eine saubere Formatieruing des Quelltextes ist SEHR wichtig! Ein paar 
Leerzeichen erhöhen die Lesbarkeit bei Zuweisungen und Vergleichen.

// setzt wait auf 0
uint8_t get_key_short(uint8_t key_mask)    // Taste short

Solche Kommentare kann man sich sparen!

1
void nibo_timer2()                                    // Timer 1ms
2
{
3
  TCNT2  = 0;
4
  OCR2   = 249;
5
  TCCR2  = (1<<WGM21) | (1<<CS21) | (1<<CS20);
6
  TIMSK |= (1<<OCIE2);
7
}
8
9
ISR (TIMER2_COMP_vect)                             // ISR wait1=1ms,
10
{
11
  static uint8_t ct0,ct1,rpt;
12
  uint8_t i;
13
  if(wait <= 5)                                    // Einstellung Takt
14
  {                  // 9=langsam
15
    wait++;          // 5=relativ schnell
16
  }                                                // erhöht
17
  else                                             // wenn dann ...
18
  {
19
    wait  = 0;                                        
20
    wait1 = 0xFF;                                 
21
    i= key_state ^ ~KEY_PIN;
22
    ct0 = ~(ct0 & i);        // Tasten abfragen
23
    ct1 = ct0 ^ (ct1 & i);
24
    i  &= ct0 & ct1;
25
    key_state ^= i;
26
    key_press |= key_state & i;
27
    if((key_state & REPEAT_MASK)==0) 
28
    {
29
      rpt=REPEAT_START;
30
    }
31
    if(--rpt==0)
32
    {
33
      rpt=REPEAT_NEXT;
34
      key_rpt |= key_state & REPEAT_MASK;
35
    }
36
  }
37
}
38
  
39
uint8_t get_key_press(uint8_t key_mask)
40
{
41
  cli();
42
  key_mask &=key_press;
43
  key_press^=key_mask;
44
  sei();
45
  return key_mask;
46
}
47
  
48
uint8_t get_key_rpt(uint8_t key_mask)
49
{
50
  cli();
51
  key_mask &= key_rpt;
52
  key_rpt  ^= key_mask;
53
  sei();
54
  return key_mask;
55
}
56
57
uint8_t get_key_short(uint8_t key_mask)
58
{
59
  return get_key_press(~key_state & key_mask);
60
}
61
  
62
uint8_t get_key_long(uint8_t key_mask)
63
{
64
  return get_key_press(get_key_rpt(key_mask));
65
}


>Das kommt rein. Damit habe ich auch einen Timer

Gut!

von Achim S. (achims)


Lesenswert?

Sorry, Kopierfehler.
Kante den Grund noch nicht. Im text sieht es gut aus bei mir.
Ist nur ein Teil von einer Menüsteuerung mit nur einem Knopf.
achim

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.