Forum: Mikrocontroller und Digitale Elektronik For Schleife mit user action unterbrechen


von King Julian (Gast)


Lesenswert?

Ich habe eine for-Schleife mit integriertem Delay die für eine Ausgabe 
auf einem Display verantwortlich ist.
1
uint8_t k, j;
2
  for(k = 0; k < 23; k+=2){  
3
    for(j=0; j < 7; j++){
4
      DisplaySet(k, 1<<j);
5
      wait_ms(200);
6
    }
7
    for(j=0; j < 3; j++){
8
      DisplaySet(k+1, 1<<j);
9
      wait_ms(200);
10
    }
11
  }

Wenn ich nun die Möglichkeit haben möchte, diese Schleife mit einem User 
Input zu unterbrechen respektive in eine andere State der Statemachine 
wechseln möchte, wie löse ich das elegant?

Grundsätzlich komme ich ja wohl nicht darum herum meine Buttons anstatt 
zu pollen über Interrupts einzulesen?

von Falk B. (falk)


Lesenswert?

@ King Julian (Gast)

>Wenn ich nun die Möglichkeit haben möchte, diese Schleife mit einem User
>Input zu unterbrechen respektive in eine andere State der Statemachine
>wechseln möchte, wie löse ich das elegant?

ohne wait_ms(200);

Siehe Multitasking

>Grundsätzlich komme ich ja wohl nicht darum herum meine Buttons anstatt
>zu pollen über Interrupts einzulesen?

Doch, Interrupts sind nicht zwingend nötig.

von Nop (Gast)


Lesenswert?

King Julian schrieb:
> Wenn ich nun die Möglichkeit haben möchte, diese Schleife mit einem User
> Input zu unterbrechen respektive in eine andere State der Statemachine
> wechseln möchte, wie löse ich das elegant?

Mit einem "break"? (;

> Grundsätzlich komme ich ja wohl nicht darum herum meine Buttons anstatt
> zu pollen über Interrupts einzulesen?

Du wirst ja ohnehin einen Systemtimer haben. In dessen ISR kannst Du die 
Buttons pollen und das Debouncing machen. Wenn Du die Buttons direkt auf 
Interrupts legst, wird das Debouncing komplizierter.

Aus der ISR schiebst Du den einen Tastendruck in eine lockless queue. 
Die kannst Du dann abfragen, ob was drin ist, in den betreffenden 
Zustand springen und dort den Tastendruck aus der Queue holen.

von Programmierer (Gast)


Lesenswert?

Kommt halt wie immer, auf die Gesammtumstände drauf an.

Eine Möglichkeit wäre etwa:

1
uint8_t wait_event_ms(volatile uint8_t *eptr, uint16_t timeout_ms)
2
{
3
  while (timeout_ms--) {
4
    wait_ms(1);
5
    uint8_t event = *eptr;
6
    if (event)
7
      return event;
8
  }
9
  return 0; // Kein Event aufgetreten
10
}
11
12
...
13
14
  volatile uint8_t event = 0;
15
16
  uint8_t k, j;
17
  for(k = 0; k < 23 && !event; k+=2){  
18
    for(j=0; j < 7 && !event; j++){
19
      DisplaySet(k, 1<<j);
20
      wait_event_ms(&event, 200);
21
    }
22
    for(j=0; j < 3 && !event; j++){
23
      DisplaySet(k+1, 1<<j);
24
      wait_event_ms(&event, 200);
25
    }
26
  }

So in der Art. Die Variable 'event' wird z.B. aus einem 
Interrupt-Handler beschrieben.

Alternativen und spielweisen gibt es davon unendlich viele. Z.b. könnte 
man aus wait_event_ms() auch einen entsprechenden Event-Handler direkt 
aufrufen, oder, oder, oder.

von Programmierer (Gast)


Lesenswert?

Achso, besser wäre:

1
  volatile uint8_t event;
2
3
  ...
4
5
  uint8_t leave = event;
6
  uint8_t k, j;
7
  for(k = 0; k < 23 && !leave; k+=2){  
8
    for(j=0; j < 7 && !leave; j++){
9
      DisplaySet(k, 1<<j);
10
      leave = wait_event_ms(&event, 200);
11
    }
12
    for(j=0; j < 3 && !leave; j++){
13
      DisplaySet(k+1, 1<<j);
14
      leave = wait_event_ms(&event, 200);
15
    }
16
  }

von KingJulian (Gast)


Lesenswert?

Also ich hab nun meinen Code umgestellt um etwas näher am Multitasking 
zu sein, und zwar hab ich meine Funktion, nennen wir sie mal foo(), mit 
einer Statemachine versehen. Das heisst ich habe die Funktion in 
einzelnen Unterfunktionen gegliedert von denen keine mehr die 
timeconstraints verletzen sollte.

Das Problem das ich nun habe ist, an der Stelle an der ich zuvor foo() 
einmal aufgerufen habe, muss ich foo() nun x-mal aufrufen (für alle 
Iterationen eben), aber wie teile ich dem darüber liegenden Code mit 
wenn aller Iterationen durchlaufen sind?

Ich hoffe das ist einigermassen Verständlich formuliert?

von Markus L. (rollerblade)


Lesenswert?

> Das Problem das ich nun habe ist, an der Stelle an der ich zuvor foo()
> einmal aufgerufen habe, muss ich foo() nun x-mal aufrufen (für alle
> Iterationen eben)
Wie meinst Du das? Ich würde die Funktion timergesteuert alle 200ms 
aufrufen und in der Funktion die Statusvariablen k und j entsprechend 
modifizieren. Die beiden können dann natürlich nicht mehr lokal 
deklariert werden, da sie anfangs initialisiert und später immer wieder 
wiederverwendet werden sollen.
Eine Schleife (Iterationen) gibt es dann keine mehr, einen wait-Aufruf 
dann schon zweimal nicht mehr.

> aber wie teile ich dem darüber liegenden Code mit
> wenn aller Iterationen durchlaufen sind?
Versteh ich nicht. Ich würde die Funktion pauschal immer aufrufen, auch 
wenn sie dann feststellen sollte, daß es nix zu tun gibt. Wieso soll das 
der darüber liegende Code festellen? Der ruft ja wohl auch noch ganz 
andere Sachen auf. Der braucht die Funktionsweise der Funktion nicht zu 
kennen, denn dann müßte er sie potentiell alle kennen, die er aufruft. 
Das wäre dann Spaghetti-Code, unübersichtlich, fehleranfällig. Die 
Funktion kann ja ein beliebiger Handler für einen Timer-Event sein, und 
der darüber liegende Code - also der Aufrufer - wäre die Event-Loop, die 
nur den Typ Event-Handler kennt.

: Bearbeitet durch User
von stromtuner (Gast)


Angehängte Dateien:

Lesenswert?

>>bi sik biliyomus gibi yaziyonya anani sikesim geliyo gavat

Na, da hat sich einer aber eloquent artikluiert.
Herzlichen Glückwunsch!

StromTuner

von Manfred (Gast)


Lesenswert?

King Julian schrieb:
> wait_ms(200);

Du hast offenbar Zeit, "wait_ms(200);". Sollte reichen, in eine andere 
Routine zu springen und die Tasten abzufragen (aka pollen). Dein wait 
muß dann natürlich auf einen absoluten Timer umgebaut werden.

von KingJulian (Gast)


Lesenswert?

Markus L. schrieb:
>> Das Problem das ich nun habe ist, an der Stelle an der ich zuvor
> foo()
>> einmal aufgerufen habe, muss ich foo() nun x-mal aufrufen (für alle
>> Iterationen eben)
> Wie meinst Du das? Ich würde die Funktion timergesteuert alle 200ms
> aufrufen und in der Funktion die Statusvariablen k und j entsprechend
> modifizieren. Die beiden können dann natürlich nicht mehr lokal
> deklariert werden, da sie anfangs initialisiert und später immer wieder
> wiederverwendet werden sollen.
> Eine Schleife (Iterationen) gibt es dann keine mehr, einen wait-Aufruf
> dann schon zweimal nicht mehr.

Vielleicht ist das Problem nicht mehr ganz dasselbe wie im 
ursprünglichen Post. Also was ich machen will, ist im Prinzip eine LED 
faden ohne dabei andere zeitkritische Funktionen zu vernachlässigen. Das 
Faden ist aber auch nur ein Teil einer darüberliegenden Statemachine.
1
void MatrixEffectFade(void){
2
  typedef enum {  state_setFadeInOut, state_fade, state_complete } effectStates;
3
  static effectStates state = state_setFadeInOut;
4
  static uint8_t fadingIterations;
5
  int8_t fadeInOut[10][11];
6
  uint8_t i, j = 0;
7
  switch(state){
8
    case state_setFadeInOut:
9
      /* Set the matrix to define which LED to fade in respectively out */
10
      //Read actual matrix and save in currentFrame
11
      getCurrentMatrix();
12
      for(; i < 10;i++){
13
        for(; j < 11; j++){
14
          //If LED currently not set but set in new frame, set fadeInOut to 1
15
          if(currentFrame[i][j] == 0 && newFrame[i][j] != 0){
16
            //Fade in
17
            fadeInOut[i][j] = 1;
18
          //If LED currently set but not set in new frame, set fadeInOut to -1
19
          } else if(currentFrame[i][j] != 0 && newFrame[i][j] == 0){
20
            //Fade out
21
            fadeInOut[i][j] = -1;
22
          } else {fadeInOut[i][j] = 0;}
23
        }
24
      }
25
      fadingIterations = 0;
26
      state = state_fade;
27
      break;
28
      
29
    case state_fade:
30
      /* Fade the whole matrix one fading step */
31
      for(i=0; i < 10;i++){
32
        for(j=0; j < 11; j++){
33
          //If fading is over set all fading LEDs to target value 0 or maxBrightness
34
          if(fadingIterations == FADING_STEPS && fadeInOut[i][j] != 0){  //
35
            newFrame[i][j] = fadeInOut[i][j] == 1 ? getMaxBrightness() : 0;
36
          } else {
37
            newFrame[i][j] += fadeInOut[i][j]*(getMaxBrightness()/FADING_STEPS);
38
          }
39
        }
40
      }
41
      computeMatrix(newFrame);
42
      fadingIterations++;    
43
      break;
44
      
45
    case state_complete:
46
      state = state_setFadeInOut;
47
      break;
48
    default: break;
49
  }
50
}

>> aber wie teile ich dem darüber liegenden Code mit
>> wenn aller Iterationen durchlaufen sind?
> Versteh ich nicht. Ich würde die Funktion pauschal immer aufrufen, auch
> wenn sie dann feststellen sollte, daß es nix zu tun gibt. Wieso soll das
> der darüber liegende Code festellen? Der ruft ja wohl auch noch ganz
> andere Sachen auf. Der braucht die Funktionsweise der Funktion nicht zu
> kennen, denn dann müßte er sie potentiell alle kennen, die er aufruft.
> Das wäre dann Spaghetti-Code, unübersichtlich, fehleranfällig. Die
> Funktion kann ja ein beliebiger Handler für einen Timer-Event sein, und
> der darüber liegende Code - also der Aufrufer - wäre die Event-Loop, die
> nur den Typ Event-Handler kennt.

Damit meine ich, wenn die oben gelistete Statemachine im State 
state_complete angelangt ist, müsste die höherliegende Statemachine in 
einen anderen State wechseln, sonst beginnt das Faden ja wieder von 
vorne.

von Axel S. (a-za-z0-9)


Lesenswert?

King Julian schrieb:
> Wenn ich nun die Möglichkeit haben möchte, diese Schleife mit einem User
> Input zu unterbrechen respektive in eine andere State der Statemachine
> wechseln möchte, wie löse ich das elegant?

Indem du keine solche Schleife schreibst.

So wie es aussieht, passiert nur alle 200ms etwas in dieser Schleife. 
Statt das nun in ein statische Gerüst zu zwängen, solltest du lieber 
eine Funktion schreiben, die die Schleifenvariablen als lokale Variablen 
hält und bei jedem Aufruf genau einen Interationsschritt macht. Diese 
Funktion mußt du dann nur einmal alle 200ms aufrufen. Z.B. aus einem 
Timerinterrupt. Oder - wenn das Timing paßt - aus deiner Hauptschleife.

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.