While Schleife endet nicht

Hallo,

Ich habe einen Motor mit einem Hall Sensor. Der hall Sensor aktualisiert eine variable mit einem Interrupt.
Nun soll der Motor auf eine bestimmte pos. fahren soll. Da das Programm nichts anderes machen soll, bis die pos. erreicht ist, ist hier eine while schleife eigentlich optimal.

Beispiel Code:

    const int In_Motor_Pos                      =  3;                                   // Eingang Hall Sensor vom Motor
    const int Output                            = A1;                                   // Ausgang Motor hochfahren
    uint64_t  Motor_Pos                         =  0;                                   // Position vom motro (Hall Sensor)

void setup() {
  // Pc ausgabe
    Serial.begin(9600);
    Serial.println("Neustart");

  // IO´s Definieren
    pinMode(In_Motor_Pos, INPUT );
    pinMode(Output      , OUTPUT);

  // Interrupt für Hall Sensor
    attachInterrupt(digitalPinToInterrupt(In_Motor_Pos), Hall_Sensor, RISING );         // Interupt erstellen

  // Motor An
    digitalWrite(Output, HIGH);

  // Auf Pos. Fahren
    while (Motor_Pos <= 100){};

  // Motor Aus
    digitalWrite(Output, LOW);
}

void loop() {}

void Hall_Sensor(){
  Motor_Pos+=1;
  Serial.println((int)Motor_Pos);
}

Leider funktioniert dies nicht. Das Problemm kann behoben werden, indem man ein Delay in die schleife einbaut.

z.B.:

  // Auf Pos. Fahren
    while (Motor_Pos <= 100){
      delay(30);
    };

Dies finde ich aber nicht ideal gelöst, zumal ich auch nicht genau bestimmen kann, wie groß die delay zeit sein muss.

Eventuell wist ihr eine Lösung für mein Problemm.

Die Variable muss Atomar gelesen werden.
Zudem hinter einer "Memoy Barrier" liegen.

#include "util/atomic.h"

constexpr byte In_Motor_Pos  =  3;   // Eingang Hall Sensor vom Motor
constexpr byte Output        = A1;   // Ausgang Motor hochfahren
uint64_t  Motor_Pos          =  0;   // Position vom motro (Hall Sensor)

void setup()
{
  // Pc ausgabe
  Serial.begin(9600);
  Serial.println("Neustart");

  // IO´s Definieren
  pinMode(In_Motor_Pos, INPUT );
  pinMode(Output      , OUTPUT);

  // Interrupt für Hall Sensor
  attachInterrupt(digitalPinToInterrupt(In_Motor_Pos), Hall_Sensor, RISING );         // Interupt erstellen

  // Motor An
  digitalWrite(Output, HIGH);

  // Auf Pos. Fahren
  uint64_t  pos;
  do
  {
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) pos = Motor_Pos;
  }
  while (pos <= 100);

  // Motor Aus
  digitalWrite(Output, LOW);
}

void loop() {}

void Hall_Sensor()
{
  Motor_Pos += 1;
  // Serial.println((int)Motor_Pos); //  benötigt selber ISR, kann dann blockieren
}

Top, es funktioniert. Danke dir.

1 Like

Fein!

OT:
Eigentlich hätte ich erwartet, dass gleich ein Mensch aus den Büschen gehüpft kommt und behauptet, dass da ein volatile fehlt.
Aber da habe ich mich wohl zum Glück geirrt.

Ich vermute mal das das fehlende volatile irgendwie daran liegt (das ist ja eigentlich auch eine). Ich sehe sie aber nicht. Erleuchte mich bitte.
ATOMIC_BLOCK sorgt ja nur für das atomare auslesen.

Gruß Tommy

Hmmm....
Wenn man sich util/atomic.h anschaut, findet man ein paar
__asm__ volatile ("" ::: "memory");
Es setzt also zusätzlich die Memory Barrieren.
Eigentlich sogar zu oft, denn das verwendete cli()/sei() tut das ja auch nochmal.

avr/interrupt.h (Auszug)

# define sei()  __asm__ __volatile__ ("sei" ::: "memory")
# define cli()  __asm__ __volatile__ ("cli" ::: "memory")

Aber die überflüssigen fressen kein Brot, weil damit nur das Verhalten des Compilers gesteuert wird.

Allgemein:
volatile ist nur für Hardware Zugriffe wirklich nötig, denn die führen bei aggressiver Optimierung manchmal in ein Fehlverhalten.

volatile wird hier nicht gebraucht, da keine Optimierung hart unterbunden werden muss.

Man/Ich kann schon sagen, dass volatile viel zu häufig eingesetzt wird, auch wohl viel zu häufig hier empfohlen wird.

Nachtrag:
volatile ist eine einfache Lösung für ein etwas komplizierteres Problem.
Das macht wohl den Reiz aus.....

1 Like

Ok, danke.
Wenn das volatile implizit in den Macros (mehrfach) mit gesetzt wird, dann wäre es ja wohl auch nicht schädlich, wenn man es bei der Variablendeklaration explizit mit angibt.
Es würde in meinen Augen aber klar im Code Anzeigen, dass diese Variable gesondert behandelt wird. Sozusagen als "Denkhilfe".

Gruß Tommy

Wird es nicht!

volatile und memory barriers sind zwei verschiedene paar Schuhe.

volatile unterbindet ALLE Optimierungen auf der Variablen (auch in der ISR)
Eine memory barrier sorgt nur dafür dass die Variablen neu eingelesen werden.

Das setzt also kein volatile? Dann verstehe ich Deinen Beitrag nicht.

Gruß Tommy

Nein, dass ersetzt es nicht.
Volatile bedeutet in dem Statement:

Das Schlüsselwort volatile teilt dem Compiler mit, dass dieser Assembly-Block nicht verschoben werden darf.

Bemerke:
Das volatile im inline ASM ist ein ganz anderes volatile als das im C++/C.

Mein Beitrag drückt aus, dass im Beispiel des TO kein volatile nötig ist, incl. Begründung.
Es ist sogar ein Klötzchen am Bein.

Ich glaube, jetzt habe ich es?
Das inline ASM volatile wirkt hier nur innerhalb des ATOMIC_BLOCK (bzw. wenn man mit cli/sei arbeitet auch dort) aber nicht in der ISR.

Das volatile im Code bei der Anlage der Variablen wirkt überall.

Ok so?

Gruß Tommy

Halb!

Eigentlich darf der Compiler alle Statements wild durcheinander würfeln, zwecks Optimierung, solange das richtige dabei raus kommt.
Das macht er auch!

Aber!
z.B. bei cli() und sei() ist die Position, an denen das stattfindet Überlebens wichtig.
Darum wird unterbunden, dass er sie verschieben darf.
Alle anderen Optimierungen finden unbehindert statt.

Ja!
Ein kapitaler Stock in den Speichen des Optimizers.

Ok, danke nochmal für Deine Erklärungen.
Ich habe volatile bisher zu eng ausschließlich auf Variablen bezogen gesehen.

Gruß Tommy

Gerne doch!

Da die Variable Motor_Pos mit 0 initialisiert wurde, kann der Compiler hier ein Programm-Stop erzeugen.
Falls in {} eine Funktion aufgerufen wird, (z.B. delay), könnte es sein, dass diese Motor_Pos verändert.
volatile wäre natürlich auch halb richtig gewesen.
Dass eine 64bit Variable generell atomic gelesen werden muss (was hier while <100 praktisch egal ist), stimmt natürlich auch.

Gerade zu "volatile" gefunden:
https://www.mikrocontroller.net/topic/552696

1 Like

Danke.

Gruß Tommy

Interessant!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.