Forum: Mikrocontroller und Digitale Elektronik STM32: LED 3 x blinken lassen ohne blocking Code zu verwenden


von Nick (Gast)


Lesenswert?

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?

von Sozialarbeiter (Gast)


Lesenswert?

Nick schrieb:
> Wie kann man das ohne blocking Code implementieren?

Ganz einfach keinen blocking Code verwenden.

von Peter D. (peda)


Lesenswert?

Z.B. mit einem Scheduler:

Beitrag "Wartezeiten effektiv (Scheduler)"

von Sebastian W. (wangnick)


Lesenswert?

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
von Thomas W. (goaty)


Lesenswert?

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
von Nick (Gast)


Lesenswert?

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.

von Thomas W. (goaty)


Lesenswert?

...
if(buttonpressed) {
  currentState = INIT
  SwitchLEDOFF
}
...

von Nick (Gast)


Lesenswert?

Wo soll überall die Überprüfung rein,ob der Button gedrückt wurde?
In jedem case?

von Thomas W. (goaty)


Lesenswert?

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ß)

von Sebastian W. (wangnick)


Lesenswert?

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

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


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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

von foobar (Gast)


Lesenswert?

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

von Thomas W. (goaty)


Lesenswert?

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
}

von Stefan F. (Gast)


Lesenswert?

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.

von Uwe G. (scd)


Lesenswert?

Immer wieder schön, wie hier Hausaufgaben gemacht werden.

von Thomas W. (goaty)


Lesenswert?

Da hat Uwe natürlich recht, das hab ich nicht bedacht.
Vielleicht doch besser löschen ?

von W.S. (Gast)


Lesenswert?

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.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Übrigens: Solange man an der Priorität von Systick nichts verändert, 
nicht in ISRs aufrufen - they never come back...

von W.S. (Gast)


Lesenswert?

Nachtrag: Mit E zu rechnen, ist falsch, dafür war Blink gedacht, hab's 
aber aus dem Stegreif geschrieben und verwechselt.

W.S.

von Moko (Gast)


Lesenswert?


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.