Forum: Mikrocontroller und Digitale Elektronik Code wird nicht Richtig ausgeführt


von Lukas W. (lukas_we)


Lesenswert?

Hallo,

wie dem Betreff schon zu entnehmen ist habe ich ein Problem beim 
ausführen meines Programmcodes aber hier erstmal ein paar Daten worum es 
sich handelt.

Mikrocontroller: Atmega328PB
Programmieradapter: AVR-Dragon
Programmierumgebung: Atmel Studio 7

Beim Programmcode handelt sich es um eine Statemachine die durch ein 
Flag welches in der Mainloop geprüft wird ob es gesetzt ist aufgerufen 
wird. Das Flag wird in einem Timerinterrupt alle 50ms gesetzt. 
Angestoßen wird das ganze dadurch das die Variable "State" auf 
"Ausfahren" gesetzt wird. Nachdem alles im case Ausfahren abgearbeitet 
ist sollte es beim nächsten mal wenn die Statemachine aufgerufen wird in 
den Ausgefahren case springen was aber nicht passiert. Um der ganzen 
Sache auf den Grund zu gehen habe ich angefangen das ganze zu Debuggen. 
Beim Debuggen habe ich festgestellt dass wenn ich einen Breakpoint im 
Ausfahren case erstelle das Programm beim nächsten mal aufrufen der 
Statemachine in den Ausgefahren case springt. Ohne Breakpoint wird der 
Programmcode dann wieder nicht richtig ausgeführt egal ob im Debugmodus 
oder normal Programmiert. Woran könnte das liegen ?

Mit freundlichen Grüßen.
Lukas W.
1
switch(State)
2
{
3
  case Warten:
4
  break;
5
        
6
  case Ausfahren:
7
    motor_up();
8
    State = Ausgefahren;
9
  break;
10
        
11
  case Ausgefahren:
12
    if(getHall_up())
13
    {
14
      motor_stop();
15
      State = Warten;
16
    }
17
  break;
18
}

von jo mei (Gast)


Lesenswert?

Lukas W. schrieb:
> Ohne Breakpoint wird der
> Programmcode dann wieder nicht richtig ausgeführt egal ob im Debugmodus
> oder normal Programmiert. Woran könnte das liegen ?

"Wilde Sprünge" deuten erst mal auf einen vom Compiler optimierten
Code hin den man grundsätzlich nicht richtig debuggen kann.

YMMV

Auch in der Make-Version "Debug" kann der Compiler zum Optimieren
aufgerufen worden sein. Prüfe daher erst ob dein Build ohne
Compiler-Optimierung eingestellt wurde.

von leo (Gast)


Lesenswert?

Lukas W. schrieb:
> ... Das Flag wird in einem Timerinterrupt alle 50ms gesetzt.

Beim Flag fehlt "volatile"?

>   case Warten:
>   break;

Das kannst du streichen.

leo

von MaWin (Gast)


Lesenswert?

leo schrieb:
> Das kannst du streichen.

Nö, das ist zum Verständnis genau so richtig. Der Compiler optmiert das 
schon.

von Lukas W. (lukas_we)


Lesenswert?

Danke schon mal für die Antworten das ausschalten der Codeoptimierung 
hat leider nichts gebracht (Projekteinstellungen->Toolchain->AVR/GNU C 
Compiler->Optimization und Optimization Level auf None(-Q0)). Die 
Variable "State" hatte ich schon als Volatile deklariert.

von Max D. (max_d)


Lesenswert?

Wo setzt du denn den State auf Ausfahren ?
Eventuell setzt diese Routine ja wiederholt deine Variable...

von Einer K. (Gast)


Lesenswert?

Lukas W. schrieb:
> oder normal Programmiert. Woran könnte das liegen ?

Mir fehlt der Kontext um das sehen/testen zu können.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

stell dir bitte einmal die Frage was jemand Fremdes mit dem switch-case 
Code alleine anfangen kann? Kürze das Programm soweit zusammen wie es 
möglich ist und zeige es uns. Dann erst kann das Forum helfen.

von Dirk B. (dirkb2)


Lesenswert?

Lukas W. schrieb:
> Das Flag wird in einem Timerinterrupt alle 50ms gesetzt.

Schafft es denn der Motor in 50 ms auszufahren (d.h. den Zustand Warten 
zu erreichen)?

von Stefan F. (Gast)


Lesenswert?

Zum Debuggen musst du die Optimierungsstufe -O0 oder -Og benutzen.

von Wolfgang (Gast)


Lesenswert?

Lukas W. schrieb:
> Die Variable "State" hatte ich schon als Volatile deklariert.

Und hast du sie auch passend initialisiert?

von Egon D. (Gast)


Lesenswert?

Lukas W. schrieb:

> Ohne Breakpoint wird der Programmcode dann wieder
> nicht richtig ausgeführt egal ob im Debugmodus
> oder normal Programmiert. Woran könnte das liegen ?

Daran, dass Dein (Denk-)Modell nicht stimmt.

Die Zustände der Mechanik und die Zustände des steuernden
Automaten sind erstmal nicht identisch: Wenn die Mechanik
betriebsmäßig ZWEI Zustände kennt, z.B. "Eingefahren" und
"Ausgefahren", muss der steuernde Automat VIER Zustände
kennen: "Eingefahren", "Ausfahren", "Ausgefahren" und
"Einfahren". Das liegt einfach daran, dass der steuernde
Automat wesentlich schneller ist als die gesteuerte
Mechanik, so dass "Übergänge" der Mechanik in "Zustände"
des steuernden Automaten abgebildet werden müssen.


Die nächste Falle lauert schon: Mit vier Endlagenschaltern
können zwar vier verschiedene Zustände codiert werden --
von diesen sind aber nur drei betriebsmäßig zulässig:
- "00" = "Aktor in Bewegung",
- "10" = "vordere Endlage",
- "01" = "hintere Endlage",
- "11" = unzulässig (--> Fehler).

Man braucht also zur korrekten Behandlung erstens einen
Timeout und muss zweitens die Vorgeschichte berücksichtigen,
um softwaremäßig "Einfahren" und "Ausfahren" unterscheiden
zu können.

Ungefähr darauf wollte Dirk wohl mit seiner kurzen Frage
hinweisen, nehme ich an.

von M.K. B. (mkbit)


Lesenswert?

Zeig bitte ein minimales Beispiel. Ich vermute du hast ein Problem mit 
parallel laufenden Prozessen (main, interrupt).

Beitrag #6187056 wurde vom Autor gelöscht.
von Dirk B. (dirkb2)


Lesenswert?

Egon D. schrieb:
> Ungefähr darauf wollte Dirk wohl mit seiner kurzen Frage
> hinweisen, nehme ich an.

Ich wollte darauf hinaus, dass da sowas wie

[c]
if (Taste_gedrueckt())
    State = Ausfahren;
else
    State = Warten;
[\c]

in der ISR steht

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

Dirk B. schrieb:
> Egon D. schrieb:
>> Ungefähr darauf wollte Dirk wohl mit seiner kurzen Frage
>> hinweisen, nehme ich an.
>
> Ich wollte darauf hinaus, dass da sowas wie
>
1
 if (Taste_gedrueckt())
2
     State = Ausfahren;
3
 else
4
     State = Warten;
>
> in der ISR steht

Als "non-Windows" geht es besser ;-)

Sprich [/c] statt [\c] am Ende

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Carl D. schrieb:
> Als "non-Windows" geht es besser ;-)
>
> Sprich [/c] statt [\c] am Ende

?‍♂️ Auf der Tablet-Tastatur ist der \ direkt neben [

von Carl D. (jcw2)


Lesenswert?

Dirk B. schrieb:
> Carl D. schrieb:
>> Als "non-Windows" geht es besser ;-)
>>
>> Sprich [/c] statt [\c] am Ende
>
> ?‍♂️ Auf der Tablet-Tastatur ist der \ direkt neben [

Auf meiner auch, dann drücke ich vorher auf die Taste "123" und schon 
paßt's ;-)

von Dirk B. (dirkb2)


Lesenswert?

Carl D. schrieb:
> Dirk B. schrieb:
>> Carl D. schrieb:
>>> Als "non-Windows" geht es besser ;-)
>>>
>>> Sprich [/c] statt [\c] am Ende
>>
>> ?‍♂️ Auf der Tablet-Tastatur ist der \ direkt neben [
>
> Auf meiner auch, dann drücke ich vorher auf die Taste "123" und schon
> paßt's ;-)

Ja, ich demnächst auch.

von Lukas W. (lukas_we)


Lesenswert?

Danke für die Antworten. Die Statemachine war schonmal weiter ausgebaut 
ich wollte die Statemachine zum finden des Fehlers nur so klein wie 
möglich halten damit das ganze übersichtlicher ist und es selbst so 
schon nicht funktioniert. Der Motor braucht länger als 50ms zum 
Ausfahren bzw bis er den Hallsensor erreicht, was kein Problem 
darstellen sollte da die Statemachine solange immer wieder in den 
Ausgefahren case springen sollte und prüfen sollte ob der Motor am 
Hallsensor angekommen ist. Der Motor ist auch nicht schnell genug um dem 
Hallsensor in 50ms überfahren zu können. Hier nochmal etwas mehr code 
ich hoffe das macht das ganze verständlicher. resetSerCommand() am ende 
setzt das Serielle Komando zurück was verhindert das befehle doppelt 
ausgeführt werden.
1
enum States
2
{
3
  Warten = 0,
4
  Ausfahren,
5
  Ausgefahren,
6
  Einfahren,
7
  Eingefahren,
8
};
9
10
volatile int State = 0;
11
12
int main(void)
13
{
14
  init_all();
15
  FILE str_uart = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
16
  stdout = &str_uart;
17
    while (1) 
18
    {
19
    if(getTimeSliceFlag() == 1)
20
    {
21
      resetTimeSliceFlag();
22
      switch(getSerCommand())
23
      {
24
        case 1:        //ausfahren
25
          State = Ausfahren;
26
        break;
27
        
28
        case 2:        //einfahren
29
          motor_down();
30
        break;
31
        
32
        case 3:        //Motor halt
33
          motor_stop();
34
        break;
35
        
36
        case 4:        //Hallschalter auslesen
37
          if(getHall_up() == 1)
38
          {
39
            printf("oberer Hallschalter\r\n");
40
          }
41
          
42
          if(getHall_down() == 1)
43
          {
44
            printf("unterer Hallschalter\r\n");
45
          }
46
        break;
47
        
48
      }
49
      
50
      switch(State)
51
      {
52
        case Ausfahren:
53
          motor_up();
54
          State = Ausgefahren;
55
        break;
56
        
57
        case Ausgefahren:
58
          if(getHall_up())
59
          {
60
            motor_stop();
61
            State = Warten;
62
          }
63
        break;
64
      }
65
      resetSerCommand();
66
    }
67
    }
68
}

von Christian H. (netzwanze) Benutzerseite


Lesenswert?

Da fehlt noch einiges vom Code.
Der angegebene alleine, kann nicht compiliert werde.

von Dirk B. (dirkb2)


Lesenswert?

Die ISR spielt doch auch an State rum.
Die solltest du also auch zeigen.

von Teo D. (teoderix)


Lesenswert?

Lukas W. schrieb:
> if(getTimeSliceFlag() == 1)

Sicher dass das Flag auch dort wieder gelöscht wird?!

Lukas W. schrieb:
> resetSerCommand() am ende
> setzt das Serielle Komando zurück was verhindert das befehle doppelt
> ausgeführt werden.

Das ist Mist (die wahrscheinliche Fehlerquelle). Bau das in die 
Statemachine ein! (Setzt das doch sicher eh nur auf "case 4:"!?


So wie ich das sehe, sollte das zweite Switch außerhalb der If Bedingung 
liegen. Also ständig ausgeführt werden....
Bzw. ich würde die Main, also die Switch Blöcke ungebremst laufen lassen 
und nur die Sensor/etc.-Abfrage, in den 50ms Task-Slot (IF-Block 
außerhalb der Switches) legen.

von Lukas W. (lukas_we)


Lesenswert?

Dirk B. schrieb:
> Die ISR spielt doch auch an State rum.
> Die solltest du also auch zeigen.
1
void resetTimeSliceFlag(void)
2
{
3
  TimeSliceFlag = 0;
4
}
5
6
int getTimeSliceFlag(void)
7
{
8
  return TimeSliceFlag;
9
}
10
11
ISR(TIMER0_COMPA_vect)
12
{
13
  static int counter = 0;
14
  if(counter == 50)
15
  {
16
    TimeSliceFlag = 1;
17
    counter = 0;
18
  }
19
  else
20
  {
21
    counter++;
22
  }
23
}

von Lukas W. (lukas_we)


Lesenswert?

Teo D. schrieb:
> Lukas W. schrieb:
>> if(getTimeSliceFlag() == 1)
>
> Sicher dass das Flag auch dort wieder gelöscht wird?!

Zwei zeilen darunter wird es gelöscht. Das Timing habe ich mit einem 
Oszi gemessen indem ich einen Ausgang toggeln lassen habe.

> Das ist Mist (die wahrscheinliche Fehlerquelle). Bau das in die
> Statemachine ein! (Setzt das doch sicher eh nur auf "case 4:"!?
Was soll case 4: mit meinem Problem zutun haben case 4: wird nur 
aufgerufen wenn ich befehl 4 per seriellen Komando ausführen will

von leo (Gast)


Lesenswert?

Lukas W. schrieb:
> TimeSliceFlag = 0;

ob das "volatile" ist, zeigst du nicht. So wird das nix.

leo

von Teo D. (teoderix)


Lesenswert?

Lukas W. schrieb:
>> Das ist Mist (die wahrscheinliche Fehlerquelle). Bau das in die
>> Statemachine ein! (Setzt das doch sicher eh nur auf "case 4:"!?
> Was soll case 4: mit meinem Problem zutun haben case 4: wird nur
> aufgerufen wenn ich befehl 4 per seriellen Komando ausführen will

Lukas W. schrieb:
> resetSerCommand() am ende
> setzt das Serielle Komando zurück was verhindert das befehle doppelt
> ausgeführt werden.

Wie funst das den dann, bzw. was wird den anstelle ausgefürt?

von Lukas W. (lukas_we)


Lesenswert?

Ich schicke den Befehl 3 (case 3: des ersten Switch) per serielle 
Schnittstelle was soweit funktionert. In dem case 3 des ersten Switch 
wird der State der Statemachine gleich Ausfahren gesetzt was den Motor 
Startet und den State auf Ausgefahren setzt. Der Motor dreht sich aber 
die Statemachine kommt beim nächsten mal ausführen der Statemachine 
nicht in den  Ausgefahren State wo es den Motor stoppen sollte wenn er 
den Hallschalter erreicht sondern springt in den Warten state was 
eigentlich nicht passieren darf solange der Motor nicht am Hallsensor 
angekommen ist.

von leo (Gast)


Lesenswert?

Lukas W. schrieb:
> Ich schicke den Befehl 3

Zu viel Blabla, zu wenig Code. Mach ein minimales und vollstaendiges 
Beispiel, das den Fehler wiedergibt.

leo

von Lukas W. (lukas_we)


Lesenswert?

Also nach Stundenlangen Kopfzerbrechen habe ich den Fehler jetzt 
gefunden. Der Fehler lag nicht in der Software sondern in der Hardware. 
Das Labornetzteil das ich verwende konnte den hohen Anlaufstrom des 
Motors nicht bewältigen was zur folge hatte das die Versorgungsspannung 
etwas auf der Mikrocontroller Seite zusammengebrochen ist und dieser das 
Programm dann anscheinend von neuem gestartet hat oder irgend einen 
anderen mist gemacht hat. Vielen dank an alle die versucht haben mir bei 
meinem Problem zu helfen.

Mit freundlichen Grüßen.
Lukas W.

von Stefan F. (Gast)


Lesenswert?

Lukas W. schrieb:
> Das Labornetzteil das ich verwende konnte den hohen Anlaufstrom des
> Motors nicht bewältigen

Hat es das nicht deutlich angezeigt?

von c-hater (Gast)


Lesenswert?

Lukas W. schrieb:

> Also nach Stundenlangen Kopfzerbrechen habe ich den Fehler jetzt
> gefunden. Der Fehler lag nicht in der Software sondern in der Hardware.
> Das Labornetzteil das ich verwende konnte den hohen Anlaufstrom des
> Motors nicht bewältigen was zur folge hatte das die Versorgungsspannung
> etwas auf der Mikrocontroller Seite zusammengebrochen ist und dieser das
> Programm dann anscheinend von neuem gestartet hat

Selbst wenn das wirklich so gewesen wäre, hätte ein gutes Programm 
auch dann (also nach einem unerwarteten Reset mit kompletten 
Informationsverlust) noch keinen völligen Mist gemacht...

Für sowas sieht man natürlich einen initialen "Unknown"-Status vor, wenn 
es keine beschaffbare Information über eine bestehende Aktivität des 
Aktors gibt. Den braucht man sowieso, denn mindestens beim allerersten 
Start des Programms kann diese Situation eintreten, wenn der Aktor 
(warum auch immer) schon was tut.
Der Status endet, wenn entweder einer der Endlagensensoren was meldet 
oder eine Maximalzeit vergangen ist (die für den vollen Weg nötige + 
etliche % Sicherheit). Dann wechselt man entweder nach in einen der 
beiden Endlagen-Stati (wenn halt der entsprechende Sensor sich gemeldet 
hat) oder nach "kaputt". Denn das kann nur bedeuten, dass der Aktor 
einen erteilten Befehl nicht bis zum Ende ausführen konnte oder halt en 
Sensor defekt ist. In jedem Fall: kaputt.

Sprich: die hast noch viel zu lernen...

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.