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


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Nick (Gast)


Bewertung
-1 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht lesenswert
Nick schrieb:
> Wie kann man das ohne blocking Code implementieren?

Ganz einfach keinen blocking Code verwenden.

von Peter D. (peda)


Bewertung
1 lesenswert
nicht lesenswert
Z.B. mit einem Scheduler:

Beitrag "Wartezeiten effektiv (Scheduler)"

von Sebastian W. (wangnick)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht lesenswert
...
if(buttonpressed) {
  currentState = INIT
  SwitchLEDOFF
}
...

von Nick (Gast)


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

von Thomas W. (goaty)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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. (achs)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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. (stefanus)


Bewertung
1 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht lesenswert
Immer wieder schön, wie hier Hausaufgaben gemacht werden.

von Thomas W. (goaty)


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

von W.S. (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
1 lesenswert
nicht 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)


Bewertung
-1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht lesenswert

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.