Code viel zu langsam (Positionsbestimmung Motor durch Hall-Sensoren)

Hallo,
Ich habe eine Frage zu meinem Code(Board: Arduino Mega 2560).
Dieser rechnet die Position eines Motors durch Hall-Sensoren (schon original am Motor verbaut) relativ zum Endlagensensor aus. Jedoch hab ich das Problem dass der Arduino wenn der Motor schnell fährt die Position nicht mehr richtig ausrechnet und Werte verliert(?) und die Position somit nicht mehr stimmt. Liegt das daran dass der Code (evtl durch die Interrupts) zu langsam ist?

Hier der Code:

volatile int Hall1_X=0,Hall2_X=0,Hall3_X=0,Hall1_Z=0, Hall2_Z=0,Hall3_Z=0;
int Richtung_X, Richtung_Z, Position_X, Position_Z, Anschlag_X=0, Anschlag_Z=0, Ausgabespannung_X, Ausgabespannung_Z;


void setup() {
  // put your setup code here, to run once:
TCCR2B = (TCCR2B & 0b11111000) | 0x01 ;

pinMode(9,OUTPUT);   //Spannungsausgabe an SPS X-Achse (Positionierung)
pinMode(10,OUTPUT);   //Spannungsausgabe an SPS Z-Achse (Positionierung)
pinMode(32,INPUT);    //Endlagensensor X-Achse  
pinMode(33,INPUT);    //Endlagensensor Z-Achse
pinMode(2,INPUT_PULLUP);    //Hall_1 Sensor Z-Achse
pinMode(18,INPUT_PULLUP);   //Hall_2 Sensor Z-Achse
pinMode(19,INPUT_PULLUP);   //Hall_3 Sensor Z-Achse
pinMode(3,INPUT_PULLUP);    //Hall_1 Sensor X-Achse
pinMode(20,INPUT_PULLUP);   //Hall_2 Sensor X-Achse
pinMode(21,INPUT_PULLUP);   //Hall_3 Sensor X-Achse

attachInterrupt(digitalPinToInterrupt(3),ISR_HALL_1_X_C,CHANGE);   //Interrupt für Hall_1 Sensor X-Achse
attachInterrupt(digitalPinToInterrupt(2),ISR_HALL_1_Z_C,CHANGE);   //Interrupt für Hall_1 Sensor Z-Achse
}

void loop() {
  // put your main code here, to run repeatedly:
Hall1_Z=digitalRead(2);
Hall2_Z=digitalRead(18);     //Einlesen der Hall-Sensoren 2&3 der Z_Achse
Hall3_Z=digitalRead(19);
Hall1_X=digitalRead(3);
Hall2_X=digitalRead(20);     //Einlesen der Hall-Sensoren 2&3 der X_Achse
Hall3_X=digitalRead(21);
Anschlag_X=digitalRead(32);  //Einlesen der Endlagensensoren X & Z-Achse
Anschlag_Z=digitalRead(33);
if (Anschlag_X==HIGH)
{
 Position_X=0; 
}
if (Anschlag_Z==HIGH)
{
 Position_Z=0; 
}

Ausgabespannung_X=(25+Position_X);
analogWrite(9,Ausgabespannung_X);

Ausgabespannung_Z=(25+(Position_Z*2));
analogWrite(10,Ausgabespannung_Z);

}
//----------------------------------------------
void ISR_HALL_1_X_C ()
{
 if (Hall1_X==1)
 {
  if (Hall2_X==1)
  {
    Richtung_X=1;
    Position_X=Position_X+1;
  }
  else if (Hall3_X==1)  
  {
    Richtung_X=2;
    Position_X=Position_X-1;
  }
 }
}
//----------------------------------------------

void ISR_HALL_1_Z_C ()
{
 if (Hall1_Z==1)
 {
  if (Hall2_Z==1)
  {
    Richtung_Z=1;
    Position_Z=Position_Z+1;
  }
  else if (Hall3_Z==1)
  {
    Richtung_Z=2;
    Position_Z=Position_Z-1;
  }
 }
}

Danke schon mal im Voraus

Martin

Grundsätzlich:
Variablen/Datensätze, welche in ISR und im Hauptprogramm genutzt werden wollen, müssen als volatile deklariert werden.
Und im Hauptprogramm atomar ausgelesen werden.

Wird das nicht eingehalten, gibts keine gültigen Werte, sondern nur Zahlensalat.

Ok vielen Dank dann probier ich das mal aus...
aber was heißt 'atomar ausgelesen'?
Und hast du eine Ahnung warum das Programm die oben genannten Fehler hat?

https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html

Und hast du eine Ahnung warum das Programm die oben genannten Fehler hat?

Höchst vermutlich, weil der Programmierer weder die Variablen als volatile deklariert hat, noch sie atomar ausliest.
Auch werden in der ISR Werte verwendet, welche in loop() gelesen werden (die Hallxxx Dinger). Die können zu dem Zeitpunkt schon lange veraltet sein.

Deine ISR ist durch den Umweg über AttachInterrupt echt lahmarschig.
Die dutzende von push und pop sind unnötig, wenn man es geschickter anstellt.

Atomic ist nur für volatile Variablen nötig, die mehr als ein Byte lang sind.

Du kannst also die Komplexität deines Programms deutlich verkleinern,
wenn du für die Halls und die Richtungen Bytes verwendest.

Dass alle geteilten Daten volatile sein müssen, hast du ja hoffentlich inzwischen verinnerlicht.

Du hast ein anderes zusätzliches grundlegendes Problem mit deinem Kode.

Deine CHANGE Interrupts benutzen die letzten vom Hauptkode gelesen Werte,
das werden ziemlich regelmäßig die ungeänderten sein,
hast du das berücksichtigt?

Warum liest du die relevaten nicht in der ISR aus?

Vielleicht so (ungetestet):

volatile byte Richtung_X, Richtung_Z;
volatile int Position_X, Position_Z;

void setup() {
  //sets Arduino Mega's pin 10 and 9 to frequency 31250
  TCCR2B = (TCCR2B & 0b11111000) | 0x01;

  pinMode(9, OUTPUT);  //Spannungsausgabe an SPS X-Achse (Positionierung)
  pinMode(10, OUTPUT);  //Spannungsausgabe an SPS Z-Achse (Positionierung)
  pinMode(32, INPUT);   //Endlagensensor X-Achse
  pinMode(33, INPUT);   //Endlagensensor Z-Achse
  pinMode(2, INPUT_PULLUP);   //Hall_1 Sensor Z-Achse
  pinMode(18, INPUT_PULLUP);  //Hall_2 Sensor Z-Achse
  pinMode(19, INPUT_PULLUP);  //Hall_3 Sensor Z-Achse
  pinMode(3, INPUT_PULLUP);   //Hall_1 Sensor X-Achse
  pinMode(20, INPUT_PULLUP);  //Hall_2 Sensor X-Achse
  pinMode(21, INPUT_PULLUP);  //Hall_3 Sensor X-Achse

  attachInterrupt(digitalPinToInterrupt(3), ISR_HALL_1_X_C, CHANGE);
  attachInterrupt(digitalPinToInterrupt(2), ISR_HALL_1_Z_C, CHANGE);
}

void loop() {
  byte Anschlag_X = digitalRead(32);
  byte Anschlag_Z = digitalRead(33);
  if (Anschlag_X == HIGH) {
    noInterrupts();
    Position_X = 0;
    interrupts();
  }
  if (Anschlag_Z == HIGH) {
    noInterrupts();
    Position_Z = 0;
    interrupts();
  }
  noInterrupts();
  int Ausgabespannung_X = (25 + Position_X);
  int Ausgabespannung_Z = (25 + (Position_Z * 2));
  interrupts();
  analogWrite(9, Ausgabespannung_X);
  analogWrite(10, Ausgabespannung_Z);
}

void ISR_HALL_1_X_C () {
  byte Hall1_X = digitalRead(3);
  if (Hall1_X == HIGH) {
    byte Hall2_X = digitalRead(20);
    if (Hall2_X == HIGH) {
      Richtung_X = 1;
      Position_X = Position_X + 1;
    } else {
      byte Hall3_X = digitalRead(21);
      if (Hall3_X == HIGH) {
        Richtung_X = 2;
        Position_X = Position_X - 1;
      }
    }
  }
}

void ISR_HALL_1_Z_C () {
  byte Hall1_Z = digitalRead(2);
  if (Hall1_Z == HIGH) {
    byte Hall2_Z = digitalRead(18);
    if (Hall2_Z == HIGH) {
      Richtung_Z = 1;
      Position_Z = Position_Z + 1;
    } else {
      byte Hall3_Z = digitalRead(19);
      if (Hall3_Z == HIGH) {
        Richtung_Z = 2;
        Position_Z = Position_Z - 1;
      }
    }
  }
}

combie:
avr-libc: <util/atomic.h> Atomically and Non-Atomically Executed Code Blocks
Deine ISR ist durch den Umweg über AttachInterrupt echt lahmarschig.
Die dutzende von push und pop sind unnötig, wenn man es geschickter anstellt.

Was wäre denn eine andere möglichkeit? bin leider in c/c++ sehr neu und hab diese Funktion als einzige funktionierende Funktion für meine Anwendung gefunden...

Whandall:
Du kannst also die Komplexität deines Programms deutlich verkleinern,
wenn du für die Halls und die Richtungen Bytes verwendest.

Kannst du mir vielleicht ein Beispiel dafür geben? :smiley:

Vielen Dank für die Hilfe

Wäre nicht auch RISING die besser Wahl?

Ja RISING hatte ich auch zuerst aber irgendwie ist er dann manchmal nicht in die interrupt-funktion gesprungen... werds aber nochmal probieren

Und vielen Dank für den Code, werd es gleich mal ausprobieren :smiley:

Möglich dass du die Logik des Hall 1 jetzt umkehren musst,
bisher hast du ja fast immer die alten Werte betrachtet (in einem CHANGE Interrupt).

P.S. keine Namen für Pins zu vergeben finde ich gruslig und zwingt dich zu Kommentaren.
Ich würde mir die ganzen Bedeutungen nicht merken wollen.

martinputz:
Ja RISING hatte ich auch zuerst aber irgendwie ist er dann manchmal nicht in die interrupt-funktion gesprungen...

Logisch, deine ISR tut ja nichts wenn der Hall LOW war (vor dem CHANGE),
hingesprungen ist die CPU schon... :wink:

Wenn man etwas umbenennt und nur an einer Stelle benutzte Variablen durch ihre Initialisierung ersetzt,
erhält man etwas das ich für leichter zu lesen halte, aber schau selbst.

const byte pin_X_down = 21;
const byte pin_X_up = 20;
const byte pin_X_stop = 32;
const byte pin_X_irq = 3;
const byte pin_X_pwm = 9;
const byte pin_Z_down = 19;
const byte pin_Z_up = 18;
const byte pin_Z_stop = 33;
const byte pin_Z_irq = 2;
const byte pin_Z_pwm = 10;

enum {
  up = 1,
  down,
};

volatile byte Richtung_X, Richtung_Z;
volatile int Position_X, Position_Z;

void setup() {
  //sets Arduino Mega's pin 10 and 9 to frequency 31250
  TCCR2B = (TCCR2B & 0b11111000) | 0x01;

  pinMode(pin_X_pwm, OUTPUT);
  pinMode(pin_Z_pwm, OUTPUT);
  pinMode(pin_X_stop, INPUT);
  pinMode(pin_Z_stop, INPUT);
  pinMode(pin_Z_irq, INPUT_PULLUP);
  pinMode(pin_Z_up, INPUT_PULLUP);
  pinMode(pin_Z_down, INPUT_PULLUP);
  pinMode(pin_X_irq, INPUT_PULLUP);
  pinMode(pin_X_up, INPUT_PULLUP);
  pinMode(pin_X_down, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(pin_X_irq), ISR_HALL_1_X_C, CHANGE);
  attachInterrupt(digitalPinToInterrupt(pin_Z_irq), ISR_HALL_1_Z_C, CHANGE);
}

void loop() {
  if (digitalRead(pin_X_stop)) {
    noInterrupts();
    Position_X = 0;
    interrupts();
  }
  if (digitalRead(pin_Z_stop)) {
    noInterrupts();
    Position_Z = 0;
    interrupts();
  }
  noInterrupts();
  int Ausgabespannung_X = (25 + Position_X);
  int Ausgabespannung_Z = (25 + (Position_Z * 2));
  interrupts();
  analogWrite(pin_X_pwm, Ausgabespannung_X);
  analogWrite(pin_Z_pwm, Ausgabespannung_Z);
}

void ISR_HALL_1_X_C () {
  if (digitalRead(pin_X_irq)) {
    if (digitalRead(pin_X_up)) {
      Richtung_X = up;
      Position_X = Position_X + 1;
    } else if (digitalRead(pin_X_down)) {
      Richtung_X = down;
      Position_X = Position_X - 1;
    }
  }
}

void ISR_HALL_1_Z_C () {
  if (digitalRead(pin_Z_irq)) {
    if (digitalRead(pin_Z_up)) {
      Richtung_Z = up;
      Position_Z = Position_Z + 1;
    } else if (digitalRead(pin_Z_down)) {
      Richtung_Z = down;
      Position_Z = Position_Z - 1;
    }
  }
}