Forum: Mikrocontroller und Digitale Elektronik Aus einer while Schleife springen


von Huber M. (michael_h784)


Lesenswert?

Hallo,

ich habe folgendes vor. Ich möchte mit einem Taster zwei Kanäle hin und 
her schalten.Und dazwischen eine von mir definierte Wartezeit eingeben.

-wenn Taster nicht gedrückt
-schalte PA7 und PA3 aus
-warte
-schalte PA3 ein

-wenn taster gedrückt
-schalte PA3 und PA7 aus
-warte
-schalte PA7 ein

wenn ich keine zeitverzögerung haben wollte, ja gar kein Problem für 
mich wäre.
jetzt habe ich, aber bei meinem Code das Problem das ich aus der letzten 
while Schleife nicht mehr raus komme. wie kann ich das abändern. ?
1
/*
2
 * GccApplication10.c
3
 *
4
 * Created: 16.10.2016 11:55:50
5
 *  Author: huber
6
 */ 
7
8
#define F_CPU 1000000UL
9
#include <util/delay.h>
10
#include <avr/io.h>
11
12
#define  warte 4000
13
14
uint8_t x = 2;
15
16
17
int main(void)
18
{   DDRA = (1<<PA3)|(1<<PA7);
19
  PORTA = (1<<PA0);
20
    
21
  
22
  
23
  while(1)
24
    {  
25
    
26
    anfang();
27
    
28
        if ((PINA &(1<<PA0)))
29
        {   
30
      
31
      PORTA &= ~(1<<PA7);
32
      PORTA &= ~(1<<PA3);
33
      //_delay_ms(warte);
34
      while(1)
35
      {
36
        PORTA |= (1<<PA3);
37
        
38
        if (!(PINA & (1<<PA0)))
39
                {   
40
      
41
            PORTA &= ~(1<<PA3);
42
            PORTA &= ~(1<<PA7);
43
            //_delay_ms(warte);
44
            while(1)
45
            {
46
           PORTA |= (1<<PA7);
47
          
48
            }
49
      
50
               }
51
      }
52
      
53
        } 
54
        
55
    }
56
}

bei diesem Code klappt zwar das Umschalten mit Verzögerung, aber es 
verzögert sich auch die Zeit nach dem er gedrückt oder nicht gerdrückt 
wurde bis er in den eigentlichen Zustand fällt.Weil er halt die while 
Schleife erst zu ende durchläuft. Wie könnte ich denn das abändern.?

1
/* Hallo, an mich;
2
3
also mein code soll nachfolgendes machen.:
4
5
Taster nicht gedrückt
6
   schalte PA7 aus
7
   warte eine zeit
8
   schalte PA3 ein
9
   
10
   Taster gedrückt
11
   schalte PA3 aus
12
   warte eine zeit
13
   schalte PA7 ein
14
   
15
   jetzt habe ich folgendes Problem, wenn ich den taster drücke,
16
   geht er nicht sofort in den anderen zustand und umgekehrt. Wie kann ich das ändern.?
17
   
18
   Im prinzip möchte ich mit einem Taster zwischen zwei Kanäle umschalten. Und es soll dabei der jeweills gewhälte
19
   Kanal erst aus sein, und nach einer von mir definierten zeit dann einschalten. Und der nicht gewählte kanal natürlich aus.
20
 * GccApplication9.c
21
 *
22
 * Created: 16.10.2016 09:50:31
23
 *  Author: Daniela Weinhart
24
 */ 
25
#define  F_CPU 1000000UL
26
#include <util/delay.h>
27
#include <avr/io.h>
28
29
#define p  20
30
#define umschaltzeit 5000
31
32
uint8_t x=0;
33
34
int main(void)
35
{
36
  DDRA = (1<<PA3) | (1<<PA7);
37
  PORTA = (1<<PA0);
38
  
39
  void auf (void)
40
  {
41
    if ((PINA &(1<<PA0)))
42
    { _delay_ms(p);
43
      
44
      PORTA &= ~(1<<PA7);
45
      _delay_ms(umschaltzeit);
46
      x = 1;
47
      
48
       if (x == 1)
49
       {
50
        PORTA |= (1<<PA3);
51
       }
52
      
53
    }
54
  
55
    
56
  }
57
  
58
  void ab (void)
59
  {
60
    if (!(PINA &(1<<PA0)))
61
    {_delay_ms(p);
62
      
63
    PORTA &= ~(1<<PA3);
64
    _delay_ms(umschaltzeit);
65
    x = 2;
66
    
67
       if (x == 2)
68
       {
69
      PORTA |= (1<<PA7);
70
       }
71
      
72
    }
73
    
74
  }
75
  
76
  
77
    while(1)
78
    {
79
    auf();
80
      ab();
81
    
82
    }
83
}

grüsse Huber

: Verschoben durch User
von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

definiere ein Flag "Abbruchbedingung", welches auf "true" gesetzt wird, 
sobald die Abbruchbedingung zutrifft.

und deine Abfrage geht dann auf

while !(Abbruchbedingnung)
{
   mache irgendwas
}

von nicht"Gast" (Gast)


Lesenswert?

oder break

von nicht"Gast" (Gast)


Lesenswert?

Wegstaben V. schrieb:
> while !(Abbruchbedingnung)
> {
>    mache irgendwas
> }

ach, und das gibt einen Fehler.
Richtig ist:
1
bool Abbruchbedingnung = false;
2
while (!Abbruchbedingnung)
3
{
4
   mache irgendwas
5
   if (ichwillnichtmehr)
6
   {
7
      Abbruchbedingnung = true;      
8
   }
9
}
PS: Habs auch gleich mal Vervollständigt

von Mitlesa (Gast)


Lesenswert?

Huber M. schrieb:
> grüsse Huber

Das hat man dir schon mal gesagt .....

Ich hab "in der Schule" gelernt dass man in C nicht Funktionen
innerhalb von Funktionen definieren darf. Dass der Compiler das
geschachtelte frisst ist erstaunlich, aber das Verhaltens-Ergebnis
ist schlichtweg undurchsichtig ...... und vermutlich irritierend.

von Rolf M. (rmagnus)


Lesenswert?

Mitlesa schrieb:
> Ich hab "in der Schule" gelernt dass man in C nicht Funktionen
> innerhalb von Funktionen definieren darf. Dass der Compiler das
> geschachtelte frisst ist erstaunlich

Es gibt Compiler, die das als Erweiterung unterstützen, aber 
Standardkonform ist es nicht.

: Bearbeitet durch User
von Huber M. (michael_h784)


Lesenswert?

wenn ich wüsste wie ich es anders lösen könnte, würde ich es machen.

Im prinzip möchte ich mit einem Taster zwischen zwei Kanäle umschalten. 
Und es soll dabei der jeweills gewhälte
   Kanal erst aus sein, und nach einer von mir definierten zeit dann 
einschalten. Und der nicht gewählte kanal natürlich aus.

von Peter D. (peda)


Lesenswert?

Huber M. schrieb:
> -wenn Taster nicht gedrückt
> -schalte PA7 und PA3 aus
> -warte
> -schalte PA3 ein
>
> -wenn taster gedrückt
> -schalte PA3 und PA7 aus
> -warte
> -schalte PA7 ein

Das geht nicht.
Du mußt schon sinnvolle Regeln aufstellen.
Ein Taster kann nur gedrückt oder nicht gedrückt sein.
D.h. die 2.Zeile in beiden Bedingungen schaltet ständig alles aus. Und 
nach dem Delay geht einer für wenige µs kurz an, bis wieder die 2.Zeile 
erreicht wird.
Regeln für den MC müssen immer eindeutig sein. Erst dann kann man sie 
auch programmieren.

Generell fragt man Tasten nicht direkt im Code ab, sondern überläßt das 
einer Entprellroutine, die entprellt und die Tastenereignisse (Events) 
für das Main aufbereitet.
Für verzögerte Aktionen nehme ich einen Scheduler.
Man kann aber auch Statemachines nehmen, die z.B. in einem 10ms 
Zeitraster aufgerufen werden.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Huber M. schrieb:
> Im prinzip möchte ich mit einem Taster zwischen zwei Kanäle umschalten.
> Und es soll dabei der jeweills gewhälte Kanal erst aus sein, und nach
> einer von mir definierten zeit dann einschalten.
Kannst du das mal statt in Prosa in einem Timing-Diagramm darstellen? 
Sowas liest sich irgendwie leichter...

> wenn ich wüsste wie ich es anders lösen könnte, würde ich es machen.
Du hast den falschen Ansatz für die Lösung deiner Aufgabe. Stichworte 
für den richtigen Ansatz sind: Endlicher Automat, Zustandsautomat, FSM, 
State Machine

Ein üblicher und leicht erweiterbarer Code sieht also so aus, dass die 
Hauptschleife ständig (schnellstmöglich ohne delay()!) durchlaufen wird 
und darin dann lediglich abgefragt wird, ob z.B. wegen einer 
abgelaufenen Zeit oder wegen äusseren Eingaben (z.B. Taster, Sensoren) 
Aktionen nötig sind. Dazu braucht es aber als "Infrastruktur" einen 
Zeitgeber, der z.B. in einem Interrupt einen ms-Zähler (oder 
Hundertstelsekunden oder Zehntelsekunden...) hochzählt.
Beim Arduino gibt es dafür die Funktion millis(), die die Millisekunden 
seit Power-On zurückgibt. Und dann kann man das so angehen:
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/using-millis-for-timing
Und wenn du diesen Ansatz mit den Zeitdifferenzen und der schnell 
durchlaufenen Hauptschleife hast, dann findest du dann auch ganz schnell 
den Weg zur State Machine...

von Huber M. (michael_h784)


Lesenswert?

Moing;

wäre das eine Möglichkeit die in die richtige Richtung geht? Hier wird 
es ja auch langsam, bzw. wo setze ich dann am besten das delay oder 
nochmal irgendwie mit Timer intterrupt?
1
/*
2
 * GccApplication46.c
3
 *
4
 * Created: 17.10.2016 10:40:32
5
 *  Author: Huber
6
 */ 
7
8
#define F_CPU 1000000UL
9
#include <util/delay.h>
10
#include <avr/io.h>
11
#include <avr/interrupt.h>
12
13
volatile uint8_t counter = 0; 
14
volatile uint8_t Flag = 0;
15
16
ISR(EXT_INT0_vect)
17
{
18
  Flag = 1;
19
}
20
int main(void)
21
{ DDRA = (1<<PA3)|(1<<PA7);
22
  PORTA = (1<<PA0);
23
 
24
 MCUCR |= (1<<ISC00);// jede änderung löst ISR aus
25
 GIMSK |= (1<<INT0);
26
 sei();
27
 
28
 
29
 void zaehler (void)
30
 {
31
   if (Flag == 1)
32
   {
33
     counter++;
34
     Flag = 0;
35
   }
36
 }
37
 
38
 void statemachine(void)
39
 {
40
  switch (counter)
41
  {
42
    
43
    case 1: PORTA &= ~(1<<PA3);counter = 2; break;
44
    case 2: /*_delay_ms(5000)*/; counter = 3; break;
45
    case 3: PORTA |= (1<<PA3);counter = 4; break;
46
    case 4:counter = 0; break;
47
    case 5:    ; break;
48
    case 6:    ; break;
49
    case 7:    ; break;
50
  } 
51
 }
52
 
53
 
54
 
55
 
56
 
57
 
58
  while(1)
59
  {
60
   statemachine();
61
   zaehler();
62
  }
63
64
}

: Bearbeitet durch User
von Nico W. (nico_w)


Lesenswert?

Ließ dir einfach mal das hier durch. Ich habe mich da vor ein paar 
Monaten auch einmal durchgewühlt und finde den Artikel sehr gut 
beschrieben.

https://www.mikrocontroller.net/articles/Statemachine

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Nico W. schrieb:
> finde den Artikel sehr gut beschrieben.
Das Konzept selber ist dort hübsch dargestellt.

Trotzdem sollte dabei im Hinterkopf behalten werden: die von mir 
erwähnte Zeitbasis ist dort beim Ampel-Beispiel einfach über das 
delay(1000) gemacht. Diese aktive Rechenzeitvernichtung mit dem delay() 
verhindert dann auch wirksam, dass z.B. ein Anforderungsknopf einer 
Fußgängerampel mit eingebunden werden kann. Denn der Fußgänger wird 
nicht mindestens 1 Sekunde lang auf den Anforderungstaster drücken. 
Sondern bestenfalls kurz mal draufhauen...

von Nico W. (nico_w)


Lesenswert?

Lothar M. schrieb:
> Trotzdem sollte dabei im Hinterkopf behalten werden: die von mir
> erwähnte Zeitbasis ist dort beim Ampel-Beispiel einfach über das
> delay(1000) gemacht.

Was hindert dich daran das alles über einen Interrupt alle 1000ms oder 
so aufzurufen?

von Huber M. (michael_h784)


Lesenswert?

ok, das ist jetzt die Statemaschine mit der Ampel. Und auch in meinem 
buch ist es ebenfalls mit delay in der Schleife. Nun meine Frage mit 
welchen Interrupt kann ich das delay ersetzen zb.TCNT. und wie setzte 
ich das dann ein. bzw. welchen verendet man dazu vielleicht komme ich 
dann von alleine drauf.
1
#define F_CPU 80000000UL
2
#include <avr/delay.h>
3
#include <avr/io.h>
4
5
enum ampel{rot=1,rot_gelb,gelb, gruen};
6
char zustand = 1;
7
8
void ampel1(ampel)
9
{
10
  switch(ampel)
11
  {
12
    case rot     : PORTB = (1<<PB0);          break;
13
    case rot_gelb: PORTB = (1<<PB0)+(1<<PB1); break;
14
    case gelb    : PORTB = (1<<PB1);          break;
15
    case gruen    : PORTB = (1<<PB2);         break;
16
  }
17
}
18
19
void ampel2(ampel)
20
{
21
  switch(ampel)
22
  {
23
    case rot     : PORTD = (1<<PD0);          break;
24
    case rot_gelb: PORTD = (1<<PD0)+(1<<PD1); break;
25
    case gelb    : PORTD = (1<<PD1);          break;
26
    case gruen    : PORTD = (1<<PD2);          break;
27
  }
28
}
29
30
void state_machine(void)
31
{
32
  enum ampel ampel_1, ampel_2;
33
  
34
  switch (zustand)
35
  {
36
    case 1: ampel1(ampel_1 = rot);
37
            ampel2(ampel_2 = gruen);
38
        zustand = 2;
39
        break;
40
        
41
    case 2: ampel1(ampel_1 = rot);
42
            ampel2(ampel_2 = gelb);
43
            zustand = 3;
44
            break;  
45
    
46
    case 3: ampel1(ampel_1 = rot);
47
            ampel2(ampel_2 = rot);
48
            zustand = 4;
49
            break;    
50
    
51
    case 4: ampel1(ampel_1 = rot_gelb);
52
            ampel2(ampel_2 = rot);
53
            zustand = 5;
54
            break;
55
    
56
    case 5: ampel1(ampel_1 = gruen);
57
            ampel2(ampel_2 = rot);
58
            zustand = 6;
59
            break;
60
        
61
    case 6: ampel1(ampel_1 = gelb);
62
            ampel2(ampel_2 = rot);
63
            zustand = 7;
64
            break;  
65
        
66
      case 7: ampel1(ampel_1 = rot);
67
              ampel2(ampel_2 = rot);
68
              zustand = 8;
69
              break;  
70
        
71
      case 8: ampel1(ampel_1 = rot);
72
              ampel2(ampel_2 = rot_gelb);
73
              zustand = 1;
74
              break;    
75
          
76
  }
77
}
78
79
int main (void)
80
{
81
  DDRB = 0x07;
82
  DDRD = 0x07;
83
  
84
  while (1)
85
  {
86
    state_machine();
87
    _delay_ms(3000);
88
  }
89
  
90
}

: Bearbeitet durch User
von Nico W. (nico_w)


Lesenswert?

Nicht das erste Beispiel nehmen. Das letzte!
1
// 1ms bei 16MHz
2
#define TICK_TIME 16000
3
4
void timer_init() {
5
  TCCR1A = MASK(CS10);
6
  OCR1A = TICK_TIME; // 1ms bei 16MHz
7
8
  TIMSK1 = MASK(OCIE1A); // enable interrupt
9
}
10
11
ISR(TIMER1_COMPA_vect) {
12
  OCR1A = (OCR1A + TICK_TIME) & 0xFFFF;
13
  state_machine();
14
}

Ganz grob so. Der Code ist jetzt schnell dahin getippt. Fehler dürfen 
selbst gefunden und korrigiert werden.

Tick ist hier 1ms. Also dementsprechend den Zähler mal 1000 nehmen oder 
irgendwie anders über Prescaler oder so auf eine Sekunde erweitern. Je 
wie man das will und braucht.
1
// Tabelle fuer state machine
2
 
3
ampel_state_t state_table[8] = {
4
 
5
// AMPEL1 AMPEL2   Induktionsschleife ? 
6
// |         |       |   Wartezeit in s <- den hier mal 1000 z.B. um von ms auf s zu kommen
7
// |         |       |   |
8
// |         |       |   |   naechster Zustand     Name
9
//----------------------------------------------------------------------
10
{ROT     , GRUEN   , 1, 10,  OSTWEST_GELB},        // OSTWEST_GRUEN
11
{ROT     , GELB    , 0,  1,  ALLE_ROT_1},          // OSTWEST_GELB
12
{ROT     , ROT     , 0,  3,  NORDSUED_ROTGELB},    // ALLE_ROT_1
13
{ROTGELB , ROT     , 0,  1,  NORDSUED_GRUEN},      // NORDSUED_ROTGELB
14
{GRUEN   , ROT     , 0, 10,  NORDSUED_GELB},       // NORDSUED_GRUEN
15
{GELB    , ROT     , 0,  1,  ALLE_ROT_2},          // NORDSUED_GELB
16
{ROT     , ROT     , 0,  3,  OSTWEST_ROTGELB},     // ALLE_ROT_2
17
{ROT     , ROTGELB , 0,  1,  OSTWEST_GRUEN}};      // OSTWEST_ROTGELB

: Bearbeitet durch User
von Huber M. (michael_h784)


Lesenswert?

Nico W. schrieb:
> Ganz grob so. Der Code ist jetzt schnell dahin getippt. Fehler dürfen
> selbst gefunden und korrigiert werden.

ok das ist das was ich brauche. Jetzt habe ich einen 8 bit Timer mit 
Timer 0 im Normal Modus. Bei dem im sec. Takt an PB1 eine led toggled. 
Aus meinem buch.Da kann ich schneller etwas nachlesen. Und zum späteren 
Zeitpunkt mit der Statemaschine vereinigen. Ich habe das eigentlich 
alles soweit verstanden, wie ich glaube. Allerdings blinkt es nicht. 
Kann aber keinen fehler entdecken
1
#define F_CPU 8000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
#define statusled (1<<PB1) // LED an PB1
6
7
uint8_t timer_overflow_counter = 0;
8
9
void init_timer_0 (void)
10
{
11
  TCCR0A = 0x00;                      // weil kein bit gesetzt werden muss für gewüschte Funktion
12
  TCCR0B = (1<<CS01) + (1<<CS00);     // Prescaler = 64 -> 8Mhz/64 = 125000
13
  TIMSK0 = (1<<TOIE0);              // Timer0 overflow interrupt enable
14
  TCNT0 = 131;                        // Timer mit 131 vorladen
15
}
16
17
int main(void)
18
{
19
  DDRB = 0xFF;        // PORTB Richtungsregister = Ausgang
20
  
21
  init_timer_0();     // Timer 0 initialisieren
22
  
23
  sei();              // Interrupts einschalten
24
  
25
  while(1)
26
  {
27
    asm ("NOP");    // Nichts tun
28
  }
29
}
30
31
ISR(TIMER0_OVF_vect)    // Timer Interrupt wird alle 1ms ausgeführt
32
{
33
  TCNT0 = 131;        // Timer0 erneut mit 131 vorladen
34
  
35
  if (timer_overflow_counter <= 999)   // Zählvariable 999 mal hochzählen
36
  timer_overflow_counter ++;
37
  
38
  else                                // Den else-Zweig beim 1000. Durchlauf (1 Sekunde) einmal ausführen
39
  {
40
    PORTB ^= statusled;             // Status LED toggeln
41
    timer_overflow_counter = 0;     // Zähler zurücksetzen
42
  }
43
}

von Route_66 H. (route_66)


Lesenswert?

Huber M. schrieb:
> uint8_t timer_overflow_counter = 0;

Huber M. schrieb:
> if (timer_overflow_counter <= 999)   // Zählvariable 999 mal
> hochzählen

Huber M. schrieb:
> Kann aber keinen fehler entdecken

Immer noch nicht???

Selbst meine dreijährige Enkelin weiß, dass sie mit Ihren Fingern nicht 
alle Fussballer auf dem Platz zählen kann!

: Bearbeitet durch User
von Paul B. (paul_baumann)


Lesenswert?

Huber M. schrieb:
> Kann aber keinen fehler entdecken

Ich habe zwar keine Ahnung von C, vermute aber, daß in den Datentyp 
"uint8_t" nur maximal 255 reinpasst. 999 wäre zu "dick" dafür.



MfG Paul

von Thomas E. (picalic)


Lesenswert?

Route 6. schrieb:
> dass sie mit Ihren Fingern nicht
> alle Fussballer auf dem Platz zählen kann!

Kann man doch - mit den Fingern einer Hand kann man alle 22 Spieler und 
sogar den Schiedrichter und ein paar Linienrichter mitzählen. Für 999 
Marathonläufer braucht man allersdings schon die Finger beider Hände, 
ein Arbeiter vom Sägewerk, dem zwei Finger fehlen, könnte halt nur bis 
255 Marathonläufer zählen...

von Route_66 H. (route_66)


Lesenswert?

Thomas E. schrieb:
> Kann man doch - mit den Fingern einer Hand kann man alle 22 Spieler und
> sogar den Schiedrichter und ein paar Linienrichter mitzählen.

Das weiß aber meine dreijährige Enkelin nicht, und auf diesem 
Wissensstand wollte ich dem TO seinen Denfehler aufzeigen.

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.