Es geht darum, dass wenn man auf einen Button drückt, dann soll eine LED dreimal blinken. Wenn man während dem Blinken erneut auf den Button drückt, dann muss die LED wieder 3x blinken. Das ganze soll ohne blocking Code implementiert werden. Wie man eine LED ohne blocking Code zum blinken bringt, habe ich bereits gefunden. https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay Die LED soll aber dreimal blinken. Jetzt könnte man eine Schleife verwenden, damit die LED 3x blinkt, aber dann hätte ich wieder implizit blocking Code drin. Wie kann man das ohne blocking Code implementieren?
Nick schrieb: > Wie kann man das ohne blocking Code implementieren? Ganz einfach keinen blocking Code verwenden.
Die einfachste Lösung ist eine Zustandsmaschine (state machine). Also sich einfach global merken, ob die LED gerade nicht blinkt, das erste Mal blinkt, das zweite Mal blinkt oder das dritte Mal blinkt. Und dann bei Ablauf der Wartezeit (millis() überprüfen wie in deinem Beispiel) die LED bedienen und den Zustand weiterschalten. LG, Sebastian
:
Bearbeitet durch User
Alles asynchron programmieren mit Statemachine. Jegliches delay/wait/etc braucht man damit fast nie mehr. Pseudocode:
1 | enum State{ |
2 | INIT, |
3 | ....
|
4 | }
|
5 | |
6 | State currentState = INIT; |
7 | |
8 | while(true) { |
9 | |
10 | |
11 | switch(currentState){ |
12 | case INIT: |
13 | if(someEvent or some time passed or..) { |
14 | currentState = LEDONSTATE |
15 | SwitchLEDON
|
16 | remember millis() |
17 | }
|
18 | case LEDONSTATE: |
19 | if(timePassed) { |
20 | currentState = NEXTSTATE |
21 | SwitchLEDOFF
|
22 | }
|
23 | |
24 | case... |
25 | }
|
26 | |
27 | }
|
(Mod: C-Tags eingefügt)
:
Bearbeitet durch Moderator
Vielen Dank für eure bisherigen Antworten. Das hat mir weiter geholfen. Eine Frage hätte ich noch. Wie realisiere ich den untenstehenden Punkt? Wenn man während dem Blinken erneut auf den Button drückt, dann muss die LED wieder von vorne 3x blinken.
... if(buttonpressed) { currentState = INIT SwitchLEDOFF } ...
Wo soll überall die Überprüfung rein,ob der Button gedrückt wurde? In jedem case?
Wie sagt der Meister: "Du sollst doch deinen Kopf benutzen". Schau dir das Prinzip an, versuch es zu verstehen, dann kannst du es selbst optimal umsetzen. Nicht einfach abschreiben.... Zur Frage: Ist dir überlassen, wo du den Event "Button Pressed" verarbeitest. Entweder du sagst, nur in einem bestimmten Zustand (z.B. LEDONSTATE) wird der Button berücksichtigt, oder du machst es außerhalb des Switch, dann setzt du einfach den Zustand/State zurück. Du kannst auch sehr schön mehrere Switch-Blöcke ineinander verschachteln um Sub-Statemachines zu machen. (Das ist alles super-primitiv-Variante ich weiß)
Noch eine Anmerkung:
1 | while(true) { |
Bei Arduino-Code kann diese Schleife entfallen. Stattdessen würde der enthaltene Code innerhalb der loop()-Funktion landen ... LG, Sebastian
Das Tolle an so einer Statemachine: Sie hat immer einen definierten Zustand. Kommt ein externer Stimulus, der zu einer Zustandsänderung führen soll, dann kann man einfach in einen anderen Zusatnd springen. Im Fall von "auf Knopfdruck von vorne blinken" halt in den Zustand, mit dem das Blinken beginnt. Optimal ist es übrigens, wenn man so eine Statemachine im Systick-Interrupt laufen lässt, dann hat man immer eine Zeitreferenz und das Ding nimmt kaum Rechenzeit in Anspruch.
Nick schrieb: > Wenn man während dem Blinken erneut auf den Button drückt, dann muss die > LED wieder von vorne 3x blinken. Das ist schlecht definiert. Was genau soll passieren? Das aktuelle blinken abbrechen? Das aktuelle erst beenden? Sagen wir Mal, du blinkt 1s an, 1s aus. Kurz vor Ende der ersten Sekunde erkennst Du einen Tastendruck. Soll dann insgesamt A) 6x geblinkt werden B) 4x geblinkt werden C) 3x geblinkt werden mit 2s an beim ersten mal
Sowas geht über einen regelmäßigen Timer-Interrupt sehr einfach (hier z.B. mit einem 100Hz Timer):
1 | static volatile uint8_t button_led; |
2 | #define LED_MSK 0x10
|
3 | #define LED_PORT PORTA
|
4 | |
5 | static void timer100hz(void) |
6 | {
|
7 | ...
|
8 | // button led blinking
|
9 | static uint8_t frac; |
10 | if ((frac += 3) >= 100) // 3Hz |
11 | {
|
12 | frac -= 100; |
13 | if (button_led & 1) |
14 | LED_PORT |= LED_MASK; |
15 | else
|
16 | LED_PORT &= ~LED_MASK; |
17 | button_led >>= 1; |
18 | }
|
19 | ...
|
20 | }
|
21 | |
22 | ...
|
23 | button_led = 0b10101; // start/restart 3 blinks |
24 | ...
|
25 | button_led = 0b101; // start/restart 2 blinks |
26 | ...
|
27 | button_led = 0b1111; // start/restart long blink |
28 | ...
|
29 | button_led = 0; // turn off |
Voll der overkill:
1 | struct Button { |
2 | Button(int p) : pin(p) {}; |
3 | enum { |
4 | NOTPRESSED, |
5 | PRESSED_, |
6 | RELEASE_
|
7 | } state; |
8 | |
9 | void update() { |
10 | unsigned now = millis(); |
11 | if (initialized) { |
12 | bool s = digitalRead(pin); |
13 | switch (state) { |
14 | case NOTPRESSED: |
15 | if (s == LOW) { |
16 | state = PRESSED_; |
17 | lastTime = now; |
18 | }
|
19 | break; |
20 | case PRESSED_: |
21 | if (s == LOW && now - lastTime > 50) { |
22 | state = RELEASE_; |
23 | lastTime = now; |
24 | Serial.println("Pressed"); |
25 | }
|
26 | else if (s == HIGH) state = NOTPRESSED; |
27 | break; |
28 | case RELEASE_: |
29 | if (s == HIGH && now - lastTime > 50) { |
30 | state = NOTPRESSED; |
31 | event = true; |
32 | Serial.println("Released"); |
33 | }
|
34 | break; |
35 | }
|
36 | }
|
37 | else { |
38 | state = (digitalRead(pin) == LOW ? PRESSED_ : NOTPRESSED); |
39 | initialized = true; |
40 | Serial.println("Button Init"); |
41 | }
|
42 | }
|
43 | |
44 | bool hasBeenPressed() { |
45 | bool event_ = event; |
46 | event = false; |
47 | return event_; |
48 | };
|
49 | |
50 | int pin; |
51 | bool initialized = false; |
52 | unsigned lastTime = millis(); |
53 | |
54 | bool event = false; |
55 | };
|
56 | Button button(6); |
57 | |
58 | void setup() { |
59 | pinMode(LED_BUILTIN, OUTPUT); |
60 | |
61 | pinMode(7, OUTPUT); |
62 | digitalWrite(7, LOW); |
63 | pinMode(6, INPUT_PULLUP); |
64 | |
65 | Serial.begin(9600); |
66 | }
|
67 | |
68 | enum State { |
69 | INIT, |
70 | IDLE_, |
71 | BLINK
|
72 | };
|
73 | State state = INIT; |
74 | |
75 | enum BlinkState { |
76 | LEDON, |
77 | WAIT, |
78 | LEDOFF, |
79 | WAIT2
|
80 | };
|
81 | BlinkState blinkState = LEDOFF; |
82 | |
83 | int lastTime; |
84 | int blinkCount = 0; |
85 | void loop() { |
86 | |
87 | button.update(); |
88 | |
89 | int now = millis(); |
90 | switch (state) { |
91 | case INIT: |
92 | lastTime = millis(); |
93 | Serial.println("Init"); |
94 | state = IDLE_; |
95 | break; |
96 | |
97 | case IDLE_: |
98 | if (button.hasBeenPressed()) { |
99 | Serial.println("Blink !"); |
100 | state = BLINK; |
101 | blinkCount = 0; |
102 | blinkState = LEDON; |
103 | }
|
104 | break; |
105 | |
106 | case BLINK: |
107 | switch (blinkState) { |
108 | case LEDON: |
109 | Serial.println("ON"); |
110 | digitalWrite(LED_BUILTIN, HIGH); |
111 | lastTime = millis(); |
112 | blinkState = WAIT; |
113 | blinkCount++; |
114 | break; |
115 | case WAIT: |
116 | if (now - lastTime > 1000) { |
117 | blinkState = LEDOFF; |
118 | }
|
119 | break; |
120 | case LEDOFF: |
121 | Serial.println("OFF"); |
122 | digitalWrite(LED_BUILTIN, LOW); |
123 | lastTime = millis(); |
124 | if (blinkCount == 3) state = IDLE_; |
125 | else blinkState = WAIT2; |
126 | break; |
127 | case WAIT2: |
128 | if (now - lastTime > 1000) { |
129 | blinkState = LEDON; |
130 | }
|
131 | break; |
132 | }
|
133 | |
134 | // Retrigger if button pressed again
|
135 | if (button.hasBeenPressed()) { |
136 | Serial.println("Retrigger"); |
137 | state = BLINK; |
138 | blinkCount = 0; |
139 | blinkState = LEDON; |
140 | }
|
141 | |
142 | break; |
143 | }
|
144 | }
|
Thomas W. schrieb: > Voll der overkill: Wieso overkill? Ich würde es vielleicht in mehre Prozeduren aufteilen, aber ansonsten gefällt mir der Code so. Er ist übersichtlich, nachvollziehbar und vor allem ausbaufähig.
Immer wieder schön, wie hier Hausaufgaben gemacht werden.
Da hat Uwe natürlich recht, das hab ich nicht bedacht. Vielleicht doch besser löschen ?
Nick schrieb: > Wie kann man das ohne blocking Code implementieren?
1 | #define tEvent unsigned char
|
2 | #define dword unsigned long
|
3 | |
4 | extern void Event_Init (void); |
5 | extern bool Event_avail (void); |
6 | extern tEvent Get_Event (void); |
7 | extern void Add_Event (tEvent aEvent); |
8 | extern bool Add_Delayed_Event (dword millisec, tEvent aEvent); |
9 | |
10 | extern void LED_einaus (bool ObLedEinschalten); |
11 | |
12 | #define tBlink 0x10
|
13 | |
14 | main(..) |
15 | { tEvent E; |
16 | int Blink; |
17 | ...
|
18 | elleweil:
|
19 | // Knopf auslesen und ggf. Blinken beginnen
|
20 | if (KnopfNeuGedrueckt()) |
21 | { Add_Delayed_Event(100,tBlink); // Event nach 100 ms auslösen |
22 | Blink = 5; |
23 | }
|
24 | |
25 | if (Event_avail()) |
26 | { E = Get_Event(); |
27 | if (E==tBlink) |
28 | { LED_einaus(E & 1); // Bit 0 = Blinkbit |
29 | E--; |
30 | if (E) Add_Delayed_Event(300,tBlink); |
31 | }
|
32 | |
33 | ... hier ggf. anderweitige Events auswerten |
34 | }
|
35 | ... hier ggf. anderes Zeugs erledigen |
36 | goto elleweil; |
So, jetzt brauchst du bloß noch eine Systemuhr in deiner Firmware und eine kleine Event-Verwaltung. Sowas habe ich schon vor ewig hier mal gepostet. W.S.
Übrigens: Solange man an der Priorität von Systick nichts verändert, nicht in ISRs aufrufen - they never come back...
Nachtrag: Mit E zu rechnen, ist falsch, dafür war Blink gedacht, hab's aber aus dem Stegreif geschrieben und verwechselt. W.S.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.