Encoder Konfiguration

Hallo zusammen,

Ich will einen Encoder für mein aktuelles Projekt verwenden. Der Encoder soll hoch- und runterzählen. Hier ist mein Code für den Encoder:

int clkPin = 12;
int dtPin = 13;
int switchPin = 10;
int count = 0;
int clkPinLast = LOW;
int clkPinCurrent = LOW;
int previousCount = 0;
int switchState = HIGH; // Anfangszustand des Schalters (nicht gedrückt)
bool switchPressed = false;  // Variable, um zu überprüfen, ob der Schalter bereits gedrückt wurde

void setup() {
  pinMode(clkPin, INPUT);
  pinMode(dtPin, INPUT);
  pinMode(switchPin, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  int newSwitchState = digitalRead(switchPin);

  // Überprüfen Sie, ob der Schalter gedrückt wurde (von HIGH auf LOW geändert hat) und ob er noch nicht gedrückt wurde
  if (newSwitchState == LOW && switchState == HIGH && !switchPressed) {
    Serial.println("E1_push");
    switchPressed = true;  // Markieren Sie den Schalter als gedrückt
  }

  // Überprüfen Sie, ob der Schalter losgelassen wurde (von LOW auf HIGH geändert hat)
  if (newSwitchState == HIGH) {
    switchPressed = false;  // Markieren Sie den Schalter als nicht mehr gedrückt
  }

  // Aktualisieren Sie den Schalterzustand
  switchState = newSwitchState;

  if (switchState == LOW) {
    count = 0;
  }

  clkPinCurrent = digitalRead(clkPin);

  if ((clkPinLast == LOW) && (clkPinCurrent == HIGH)) {
    if (digitalRead(dtPin) == HIGH) {
      count--;
    } else {
      count++;
    }

    // Nur wenn sich der Wert des Encoders ändert, sende das Signal
    if (count != previousCount) {
      Serial.println(count);
      previousCount = count;
    }
  }

  clkPinLast = clkPinCurrent;
}

Das Problem besteht darin, dass mein Encoder nur hochzählt, aber nicht herunterzählt, wenn ich ihn in die andere Richtung drehe. Ansonsten funktioniert alles einwandfrei (der Stick Push wird erkannt und der Encoder zählt ordentlich hoch).
Weiß jemand, wo das Problem liegt?

LG

Ich mache Encoder-Impulse auswerten nie "zu Fuß" sondern nehme eine library oder einen Timer-Interrupt dafür

interrupt-basierte library demo

#include "Arduino.h"
#include "NewEncoder.h"

const byte EncChA_Pin = 2;
const byte EncChB_Pin = 3;
const int minVal = -20;
const int maxVal =  20;
const int startVal = 0;

// Pins 2 and 3 should work for many processors, including Uno. See README for meaning of constructor arguments.
// Use FULL_PULSE for encoders that produce one complete quadrature pulse per detnet, such as: https://www.adafruit.com/product/377
// Use HALF_PULSE for endoders that produce one complete quadrature pulse for every two detents, such as: https://www.mouser.com/ProductDetail/alps/ec11e15244g1/?qs=YMSFtX0bdJDiV4LBO61anw==&countrycode=US&currencycode=USD

NewEncoder myEncoderObject(EncChA_Pin, EncChB_Pin, minVal, maxVal, startVal, FULL_PULSE);

int16_t currentValue;
int16_t prevEncoderValue;

void setup() {
  // myEncState is a variable of type EncoderState
  // EncoderState is a structured variable that has two "simple" variables
  // .currentValue which is type int16_t
  // (16 bit signed integer valuerange -36767 to 36767)
  // currentValue counts up / down with each pulse created through rotating the encoder
  // and
  // .currentClick which is of type "EncoderClick"
  // the variable type "EncoderClick" can have just 3 values
  // NoClick, DownClick, UpClick where "click" means a "pulse" created through rotating the encoder
  NewEncoder::EncoderState myEncState;

  Serial.begin(115200);
  delay(2000);
  Serial.println("Starting");

  if (!myEncoderObject.begin()) {
    Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
    while (1) {
      yield();
    }
  } else {
    // store values of currentValue and EncoderClick into variable myEncState
    myEncoderObject.getState(myEncState);
    Serial.print("Encoder Successfully Started at value = ");
    prevEncoderValue = myEncState.currentValue;
    Serial.println(prevEncoderValue);
  }
}

void loop() {
  NewEncoder::EncoderState myCurrentEncoderState;

  // store actual values into variable myCurrentEncoderState
  if (myEncoderObject.getState(myCurrentEncoderState)) {
    Serial.print("Encoder: ");
    currentValue = myCurrentEncoderState.currentValue;

    // if currentValue has REALLY changed print new currentValue
    if (currentValue != prevEncoderValue) {
      Serial.println(currentValue);
      prevEncoderValue = currentValue;


      // if currentValue stayed the same because the number is at upper/lower limit
      // check if encoder was rotated by using the UpClick / DownClick-values
    } else
      switch (myCurrentEncoderState.currentClick) {
        case NewEncoder::UpClick:
          Serial.println("at upper limit.");
          break;

        case NewEncoder::DownClick:
          Serial.println("at lower limit.");
          break;

        default:
          break;
      }
  }
}

TimerInterrupt basierte library

/* Demo-Code that uses the Rotary-library from GitHub-User https://github.com/buxtronix
 * using his library https://github.com/buxtronix/arduino/tree/master/libraries/Rotary  
 * in combination with a timer-interrupt executed 10000 times per second
 * Copyright 2023 StefanL38. Licenced under the GNU GPL Version 3.
 * A T T E N T I O N ! 
 * this demo-code uses Timer1 which is used by other libraries too. 
 * This means using this code can interfere with other libraries
 * causing malfunction of both
 * 
 * This demo-code uses the TimerInterrupt.h-library from here
 * https://github.com/khoih-prog/TimerInterrupt/tree/master
 * 
 * The examples in the GiPo show how to use different timers
 * The demo-code simply prints the value of variable myCounter 
 * each time the value of the variable changes
 */

#define USE_TIMER_1     true

#if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__)  || \
        defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) ||    defined(ARDUINO_AVR_ETHERNET) || \
        defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT)   || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO)      || \
        defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \
        defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \
        defined(ARDUINO_AVR_PROTRINKET3FTDI) )
#endif

// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "TimerInterrupt.h"

#define TIMER1_INTERVAL_MS    1
 
#include <Rotary.h>

// Rotary encoder is wired with the common to ground and the two
// outputs to pins 5 and 6.
const byte channel_A_Pin = 5;
const byte channel_B_Pin = 6;
Rotary rotary = Rotary(channel_A_Pin, channel_B_Pin);

unsigned long myISR_TimerFrequency = 10000;
// myCounter that will be incremented or decremented by rotation.
// as this variable is changed in an interrupt-service-routine
// this variable MUST !! be declared volatile to make sure 
// that it works properly !
volatile int8_t myCounter = 0;
int8_t last_myCounter = 0;


void TimerHandler1() {
  unsigned char result = rotary.process();

  // depending on having detected rotation
  if (result == DIR_CW) {
    myCounter++;
  }
  else if (result == DIR_CCW) {
    myCounter--;
  }
}


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

unsigned long MyTestTimer = 0;  // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();

  Serial.print(F("\nStarting Argument_None on "));
  Serial.println(BOARD_TYPE);
  Serial.println(TIMER_INTERRUPT_VERSION);
  Serial.print(F("CPU Frequency = ")); 
  Serial.print(F_CPU / 1000000); 
  Serial.println(F(" MHz"));

  // Timer0 is used for micros(), millis(), delay(), etc and can't be used
  // Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4
  // Timer 2 is 8-bit timer, only for higher frequency
  // Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency

  ITimer1.init();

  // Using ATmega328 used in UNO => 16MHz CPU clock ,
  // For 16-bit timer 1, 3, 4 and 5, set frequency from 0.2385 to some KHz
  // For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz

  if (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS, TimerHandler1)) {
    Serial.print(F("Starting  ITimer1 OK, millis() = ")); Serial.println(millis());
  }
  else {
    Serial.println(F("Can't set ITimer1. Select another freq. or timer"));
  }
}


void loop() {

  BlinkHeartBeatLED(OnBoard_LED, 250);

  // check if value has changed
  if (last_myCounter != myCounter) {
    // only if value REALLY HAS changed
    byte difference = abs(last_myCounter - myCounter);
    if (difference != 1) {
      Serial.println("difference != 1 counter jumped !!");
    }
    last_myCounter = myCounter; // update last_myCounter
    Serial.print("myCounter=");
    Serial.println(myCounter);
  }
}

vgs

Was ist das für ein Encoder ?
Tausch mal die beiden Anschlüsse clkPin und dtPin. Manchmal beißt sich bei den billigen Dingern das mechanische Raster mit dem elektrischen Kontakt.

Zaehlen muss aber unabhängig vom Raster funktionieren.

Ja da hast Du Recht. Dennoch ich sehe keinen Fehler im Skech an der Stelle. Die Flankenauswertung ist ok und die Abfrage des 2. Kanals bei dem Flankenwechsel ist auch ok denke ich. Dann muss es aber an der Phasenverschiebung liegen das der else Zweig nicht zum Tragen kommt.
Damit macht edr Versuch "tauschen" doch Sinn.

Guß Heinz

Hallo kawkas

Nimm mal diesen Link als Sprungbrett zum Schlaumachen :grinning:

So einfach ist das nicht...

@kawkas Hier siehst Du die Zusammenhänge der Pins und deren Stati:

Ich empfehle einfach mit so einer simplen lib zu arbeiten. Die kann dann auch die unterschiedlichen Encodertypen abbilden....

So einen :slight_smile:
Ich probiere das mal auf einen anderen Pin zu löten. Wenn es nicht klappt dann versuche ich es mal mit so einer Anordnung hier:

Danke für den Tip ich versuche es mal umzusetzen!

Empfehlung: Nimm die lib - das Example ist selbsterklärend.

Es ist wirklich einfach - hat hier immer funktioniert...

Ich habe jetzt einen Encoder der diesem auf deinem Bild


Sehr ähnlich sieht

Auf einem Arduino Uno clon mit deinem Sketch aus Post #1 getestet.
Zählt rauf und runter

17:38:50.660 -> 1
17:38:50.848 -> 0
17:38:50.895 -> 1
17:38:51.176 -> 2
17:38:51.316 -> 1
17:38:51.363 -> 2
17:38:51.785 -> 3
17:38:52.113 -> 4
17:38:53.660 -> 5
17:38:54.129 -> 6
17:38:54.410 -> 7
17:38:54.598 -> 8
17:38:54.692 -> 9
17:38:54.738 -> 8
17:38:54.738 -> 7
17:38:54.785 -> 8
17:38:54.832 -> 9
17:38:54.973 -> 10
17:38:55.207 -> 11
17:38:55.863 -> 10
17:38:56.004 -> 9
17:38:57.598 -> 8
17:38:57.738 -> 7
17:38:58.113 -> 6
17:38:58.394 -> 5
17:38:58.582 -> 4
17:38:58.723 -> 3
17:38:58.863 -> 2
17:38:59.004 -> 3
17:38:59.004 -> 4
17:38:59.004 -> 3
17:38:59.192 -> 2
17:38:59.192 -> 1
17:38:59.426 -> 2
17:38:59.520 -> 1
17:39:00.035 -> 0
17:39:01.066 -> -1
17:39:01.254 -> -2
17:39:01.395 -> -3
17:39:02.145 -> -4
17:39:02.332 -> -5
17:39:02.567 -> -6
17:39:02.660 -> -7
17:39:03.082 -> -6
17:39:03.129 -> -7
17:39:03.176 -> -6
17:39:03.363 -> -5
17:39:03.719 -> -4
17:39:04.282 -> -3
17:39:05.406 -> -4
17:39:05.500 -> -3
17:39:05.734 -> -2
17:39:05.875 -> -1
17:39:05.969 -> 0
17:39:06.063 -> 1
17:39:06.203 -> 2
17:39:06.391 -> 1

Hast du denn Plus und Minus auch angeschlossen?
Das kann man auf deinem Bild nicht sehen.

vgs

Zum Testen ob denn die Signalpegel überhaupt wechseln kannste ma diesen Sketch probieren

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298


#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

int clkPin = 12;
int dtPin  = 13;
int switchPin = 10;


void setup() {
  Serial.begin(9600);
  Serial.println("Setup-Start");
  pinMode(clkPin, INPUT);
  pinMode(dtPin, INPUT);
  pinMode(switchPin, INPUT_PULLUP);  
}


void loop() {
  //dbgc("12",digitalRead(clkPin) );
  dbgc("13",digitalRead(dtPin) );
  //dbgc("10",digitalRead(switchPin) );
}

vgs

Hallo,
hab ich auch probiert, es scheint aber schon mal zu prellen. Dann zählts schon mal 2 rauf oder auch runter obwohl es rauf gehen sollte. Habe in den loop noch ein delay(5) eingebaut, damit war es dann zuverlässiger.

Heinz

Und was passiert, wenn Du den mal schneller drehst? Dann verlierst counts.
Darum wertet man auch alle Zustände aus.

Ich versteh nicht, warum man sich mit halben Sachen abgeben muss, wenn es doch sowas in ein paar Zeilen fertig gibt.
Das kann notfalls sogar in den eigenen Sketch reinkopiert werden.
Schliesslich wird für eine zuverlässige Funktion der selbe Aufwand getrieben werden müssen.

Meiner Erfahrung nach, funktioniert die Lib von Matthias Hertel (mathertel) sehr gut. Die gibt es auch über die Bibliotheksverwaltung.

In Verbindung mit dem gezeigten Encoder ist der Latchmode FOUR3 gut.

RotaryEncoder encoder{PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::FOUR3};

Hey Danke für deine Mühe :slight_smile:
Hab auch den Plus und Minus angeschlossen. Ich befürchte mein Encoder ist irgendwie Kaputt?

hoffe es liegt daran

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