Zähler eines Interrups umkehren ohne diesen zu stoppen

Hallo,
Ich will einen Motor über einen Kraftsensor (Wägezelle) ansteuern, um diesen zu positionieren.
Der Motor hat also 2 Laufrichtungen. Am Motor sind Hall-Sensoren verbaut, von denen einer für die Positionsbestimmung ausgelesen wird.
Je nach Krafteintrag und Position soll der Motor seine Laufrichtung ändern und eine neue Position anfahren. Wenn ich den externen Interrupt nach einer Addition stoppe und bei Positionsumkehr neu starte um zu Subtrahieren entsteht zu viel Slippage. Die Idee ist, je nach Drehrichtung den Wert zu errechnen und dann abzuziehen. Der letzte Teil des Sketch ist das Problem.

//Waage
#include "HX711.h"
HX711 scale;
uint8_t dataPin = 6;
uint8_t clockPin = 7;
//Motor
const int SensorPin = 2;  //Interrupt Pin (nur 2 oder 3 @ Arduino Uno)
const byte Brake = 8;//Pinausgang Bremse, HIGH = Vollbremsung
const byte PWM = 9;// Pinausgang für Drehzahl
const byte power = 50;// Motor-Drehzahl-Normalbetrieb
const byte Drehrichtung = 10;// Pin Drehrichtungswechsel LOW/HIGH Drehrichtung
unsigned long wzMillis;
unsigned int Counter;
bool setupStep = LOW;
bool Normalbetrieb = LOW;
String Programmposition;//dient nur der Kontrolle

void motor(bool Stop, bool Richtung, int pwmSignal) {
  digitalWrite (Brake, Stop);
  digitalWrite (Drehrichtung, Richtung);
  analogWrite (PWM, pwmSignal);
}
void M_Stop() {
  motor(HIGH, LOW, 0);
}
void M_Hoch() {
  motor(LOW, HIGH, power);
}
void M_Runter() {
  motor(LOW, LOW, power);
}
void count() {
  Counter++;
}
void setup() {
  Serial.begin(9600);
  //Waage
  scale.begin(dataPin, clockPin);
  scale.set_scale(127.15);
  //  scale.tare();//muss unterdrückt bleiben um die Interruptfuktion zu gewährleisten
  pinMode(dataPin, INPUT);
  pinMode(clockPin , OUTPUT);

  //Motor
  pinMode(SensorPin, INPUT); //definiertes Potenzial (HIGH/LOW) von einem Hall-Sensor
  pinMode(Brake , OUTPUT);
  pinMode(PWM , OUTPUT);
  pinMode(Drehrichtung , OUTPUT);//Drehrichung
}
void loop() {
  motorSetup();
  motorMatik();
  //  Serial.print("\t Programmposition: ");
  //  Serial.print(Programmposition);
  Serial.print("\t Counts: ");
  Serial.println(Counter);
}
void motorSetup() {
  if (!setupStep) {  //Variable um den Setupvorgang vom Normalbetrieb zu trennen
    Counter = 0;
    attachInterrupt(digitalPinToInterrupt(SensorPin), count, CHANGE);
    delay(500);//kann delay sein, weil motorSetup nur einmal und ohne Wägezelle durchlaufen wird
    detachInterrupt(digitalPinToInterrupt(SensorPin));//Stop zur Ermittlung der Drehzahl über 500ms
    motor(LOW, LOW, 30); //Kriechgang (30) zum Anfahren der Endposition (Anschlag)
  }
  static  unsigned long SetupMillis = 0;
  if ((Counter < 10) && ((millis() - SetupMillis >= 1000))) {// 1s um den Anlaufprozess zu überbrücken
    M_Stop (); // Drehzahl sinkt am Anschlag unter (10) , der Motor stopt
    setupStep = HIGH;
    Counter = 1000;// Counter wir auf 100 gesetzt um ein "Überfahren" in den negativen Bereich zu verhindern
  }
  // Der Zähler wird auf Positionsmods umgestellt, absolute Werte werden gespeichert
  if (setupStep && !Normalbetrieb) {
    attachInterrupt(digitalPinToInterrupt(SensorPin), count, CHANGE);
    motor (LOW, HIGH, 120); //Richtungswechsel nach Anschlag
    if (Counter >= 1010) {//Fahrt auf Ausgangsposition (z.B X + 10)
      M_Stop ();//Setup-Phase ist beendet, Motor steht in Ausgangsposition
      Normalbetrieb = HIGH;//Umschalten auf Normalbetrieb
      detachInterrupt(digitalPinToInterrupt(SensorPin));
    }
  }
}
void motorMatik () {
  if (Normalbetrieb) {
    long wz = scale.get_units();
    long druck[] = {2000, 3000, 4000, 6000};
    long positionM[] = {1040, 1100, 1150, 1200, 1250};
    long druckPosition = map (wz, 260, 4000, 1000, 1250);
    attachInterrupt(digitalPinToInterrupt(SensorPin), count, CHANGE);
    if (druckPosition >= Counter && Counter < positionM[4]) {
      M_Runter();//Druck läßt nach, wenn der Motor runter schaltet, Counterposition wird größer
    }
    else if ( druckPosition <= Counter && Counter > positionM[0] ) {
      M_Hoch();//Druck steigt, wenn der Motor hoch schaltet, Counterposition wird kleiner
    }
    else {
      M_Stop();
    }
    Serial.print("\t wz: ");
    Serial.print(wz);
    Serial.print("\t druckPosition: ");
    Serial.print(druckPosition);
    Serial.print("\t countFlag_1: ");
    Serial.print(countFlag_1);
    Serial.print("\t countFlag_2: ");
    Serial.print(countFlag_2);
  }
}

Aus Deiner Beschreibung werde ich nicht so richtig schlau. Üblicherweise kann man aus 2 Sensoren auch die Drehrichtung ermitteln, siehe die vielen Beispiele zu Rotary Encoder.

Ja, das ist sicher der falsche Ansatz.
Und deine aktuelle Ausführung, jedesmal attachInterrupt aufzurufen, ist auch Mist.
Generell sind Interrupts der Regel keine Lösung, sondern Teil des Problems.

Variable, die sowohl in ISR-Routinen wie in loop() verwendet werden, müssen als volatile deklariert werden und in loop() atomic gelesen werden.
Was spricht dagegen, es einfach ohne Interruptroutine zu machen?

Und, wenn wir bei Interrupt bleiben, warum definierst du nicht einfach eine Richtungsvariable

volatile bool dir; // true: incr ; false: decr`
void  count() {
  Counter += dir? +1:-1;
}

oder ein paar Buchstaben ausführlicher

void  count() {
  if (dir) Counter++; else Counter--;
}

... und setzt dir in Motormatik nach Bedarf?

1 Like

ja, die Drehrichtung ist jedoch durch das Programm schon gegeben, die muss nicht ermittelt werden, es geht darum durch das Zählen der Signale die Position möglichst exakt zu ermitteln, und das funktioniert durch ein und Ausschalten des Interrups bei Drehrichtungsumkehr nicht wirklich gut

ja, das hatte ich versucht, bin aber daran gescheitert und habe deshalb hier nach Hilfe gesucht, ich versuche das jetzt mal einzubauen, schon mal Danke in Voraus

Der Motor schaltet die Richtung nicht sofort um, die Achse kann auch zittern. Der Encoder liefert immer die richtige Position.

Aber wenn Du unbedingt die gesetzte Drehrichtung nehmen möchtest, dann setze eine globale Variable auf +1 oder -1, je nach Drehrichtung, und addiere sie in der ISR zur Position.

Hallo,
Wenn Du den Motor bei laufendem Betrieb in der Richtung ändern willst wird das nichts. Der Motor muss ja erst mal abbremsen und dann wieder loslaufen. Dabei wird Dir was verloren gehen. Du kannst eventuell erst ausschalten, dann etwas warten , und dann die andere Richtung einschalten. Aber das wird immer zu Fehlern führen.
Nimm einen Encoder mit zwei Spuren A u B , eine nimmst Du zum Zählen und die zweite für die Richtung.

Wenn Du eine Position anfahren willst wirst Du auch nach dem Einschalten mal Referenzieren müssen, damit Du eine definierte Position hast.
Heinz

Hallo Heinz,
schön Dich mal wieder zu "lesen", ein Decoder hat in der Anwendung leider keinen Platz, ich hatte schon mit Reed-Kontakten und fest positionierten Hall Sensoren experimentiert, bin dann aber bei den schon verbauten Sensoren am Motor geblieben. Das Problem ist, dass der Stellmotor ein Getriebe antreibt und die Position des Getrieberades bei der Montage nicht definiert ist. Der Motor fährt zuerst einen Endanschlag hinter dem Getriebe an, welcher jedoch nicht genau bekannt ist und richtet sich dann nach dieser Position. Da auch die spätere Stellung nicht ganz genau stimmen muss ist die Variante mit den Hall Sensoren von Vorteil. Es funktioniert eben nur nicht, wenn sich das Getriebe immer weiter "verstellt".

Wie soll das passieren?

Das passiert, weil durch das Aus- und Einschalten des Interrups (also wie ich es zuvor versucht hatte) einige Signale nicht erfasst werden und der zwar Zähler auf der gewollten Position steht, aber das Getriebe weiter gefahren oder eben zurück geblieben ist. Das Übersetzungsverhältnis wird dann nicht richtig abgebildet. Die Anwendung genau zu erklären, würde den Rahmen hier sprengen, nur soviel, es handelt sich um ein stufenloses Rollreibgetriebe, das durch den Stellmotor eingestellt wird.

Wenn Du mit Deinen Versuchen ständig auf die Schnauze fliegst, wäre es dann nicht langsam an der Zeit, die Sensoren endlich richtig anzuschließen und zu programmieren?

Hallo Michael,
es funktioniert perfekt!
Danke

Ich muss nun nur noch eine Lösung finden, eine Hysterese einzubauen.
Das habe ich mit "druckPosition + 70" versucht, aber dann fährt er natürlich auch nur bis dahin zurück.

//Waage
#include "HX711.h"
HX711 scale;
uint8_t dataPin = 6;
uint8_t clockPin = 7;
//Motor
const int SensorPin = 2;  //Interrupt Pin (nur 2 oder 3 @ Arduino Uno)
const byte Brake = 8;//Pinausgang Bremse, HIGH = Vollbremsung
const byte PWM = 9;// Pinausgang für Drehzahl
const byte power = 50;// Motor-Drehzahl-Normalbetrieb
const byte Drehrichtung = 10;// Pin Drehrichtungswechsel LOW/HIGH Drehrichtung
volatile long Counter;
volatile bool dir; // true: incr ; false: decr
bool setupStep = LOW;
bool Normalbetrieb = LOW;
String Programmposition;//dient nur der Kontrolle

void  count() {
  Counter += dir ? +1 : -1;
}
void motor(bool Stop, bool Richtung, int pwmSignal) {
  digitalWrite (Brake, Stop);
  digitalWrite (Drehrichtung, Richtung);
  analogWrite (PWM, pwmSignal);
}
void M_Stop() {
  motor(HIGH, LOW, 0);
}
void M_Hoch() {
  motor(LOW, HIGH, power);
}
void M_Runter() {
  motor(LOW, LOW, power);
}
//**********************************************
void setup() {
  Serial.begin(9600);
  //Waage
  scale.begin(dataPin, clockPin);
  scale.set_scale(127.15);
  pinMode(dataPin, INPUT);
  pinMode(clockPin , OUTPUT);
  //Motor
  pinMode(SensorPin, INPUT); //definiertes Potenzial (HIGH/LOW) von einem Hall-Sensor
  pinMode(Brake , OUTPUT);
  pinMode(PWM , OUTPUT);
  pinMode(Drehrichtung , OUTPUT);//Drehrichung
}
//**********************************************
void loop() {
  motorSetup();
  motorMatik();
   /*
  Serial.print("\t Counts: ");
  Serial.println(Counter);
  // */
}
void motorSetup() {
  if (!setupStep) {  //Variable um den Setupvorgang vom Normalbetrieb zu trennen
    Counter = 0;
    dir = 1;
    attachInterrupt(digitalPinToInterrupt(SensorPin), count, CHANGE);
    delay(500);//kann delay sein, weil motorSetup nur einmal und ohne Wägezelle durchlaufen wird
    detachInterrupt(digitalPinToInterrupt(SensorPin));//Stop zur Ermittlung der Drehzahl über 500ms
    motor(LOW, LOW, 30); //Kriechgang (30) zum Anfahren der Endposition (Anschlag)
  }
  static unsigned long SetupMillis;
  if ((Counter < 10) && ((millis() - SetupMillis >= 1000))) {// 1s um den Anlaufprozess zu überbrücken
    M_Stop (); // Drehzahl sinkt am Anschlag unter (10) , der Motor stopt
    setupStep = HIGH;
    Counter = 1000;// Counter wir auf 100 gesetzt um ein "Überfahren" in den negativen Bereich zu verhindern
  }
  // Der Zähler wird auf Positionsmods umgestellt, absolute Werte werden gespeichert
  if (setupStep && !Normalbetrieb) {
    attachInterrupt(digitalPinToInterrupt(SensorPin), count, CHANGE);
    motor (LOW, HIGH, 120); //Richtungswechsel nach Anschlag
    if (Counter >= 1010) {//Fahrt auf Ausgangsposition (z.B X + 10)
      M_Stop ();//Setup-Phase ist beendet, Motor steht in Ausgangsposition
      Normalbetrieb = HIGH;//Umschalten auf Normalbetrieb
    }
  }
}
void motorMatik () {
  if (Normalbetrieb) {
    static unsigned long m_Millis;
    long wz = scale.get_units();
    long positionM[] = {1040, 1600};
    long druckPosition = map (wz, 260, 4000, 1000, 1600);

    if (druckPosition >= Counter && Counter < positionM[1]) {
      dir = 1;
      if (millis() - m_Millis >= 100) {//Pause, um M_Hoch auslaufen zu lassen
        M_Runter();//Druck läßt nach, wenn //Pause, um M_Runter auslaufen zu lassender Motor runter schaltet, Counterposition wird größer
      }
    }
    else if ((druckPosition + 70) <= Counter && Counter > positionM[0] ) {
      dir = 0;
      if (millis() - m_Millis >= 2000) {//Pause, um M_Runter auslaufen zu lassen
        M_Hoch();//Druck steigt, wenn der Motor hoch schaltet, Counterposition wird kleiner
      }
    }
    else {
      M_Stop();
      m_Millis = millis();
    }
     /*
    Serial.print("\t druckPosition: ");
    Serial.print(druckPosition);
    Serial.print("\t millis: ");
    Serial.print(millis());
    Serial.print("\t m_Millis: ");
    Serial.print(m_Millis);
    // */
  }
}

Hallo,
Du hast eigentlich alle wichtigen Infos bekommen . Du musst sie halt umsetzen. Leider habe ich nich verstanden wie deine Mechanik nun aussieht.
Setze das in deinen Sketch mal um was dir geschrieben wurde
Auf ab zählen
Interrupts richtig verarbeiten und erst sicher anhalten bevor es in die andere Richtung geht. Mal sehen was dann raus kommt
Heinz

Nein, alles gut, ich habe den Lösungsvorschlag von Michael umgesetzt und es funktioniert,
aber Danke auch Dir, für den Rat.

Hallo Heinz,
habe ich gemacht, es funktioniert, ich bin heute nur nicht sofort dazu gekommen.

Ich habe es mir noch nicht verdient, mich mit Deinem Alias schmücken zu können. :face_with_hand_over_mouth: