STM32: LED 3 x blinken lassen ohne blocking Code zu verwenden

Gast #6565216
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?
#6565250
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
#6565256
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)
#6565311
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ß)
(Firma: Code Mercenaries GmbH) #6565349
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.
Gast #6565480
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
Gast #6565491
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
#6565588
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
}
Gast #6566219
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren