Can the encoder.h library be used with a single-channel rotary incremental encoder?

Status
Not open for further replies.

johannbor

Member
My question is in the title. I am using a Teensy 4.0 to count the pulses from a single-channel (i.e., no quadrature encoding) optical incremental encoder. I am using the example sketch "Basic.pde" provided with the Encoder.h library (see sketch copied below). My encoder is attached to Pin 5 but nothing is attached to Pin 6.

When I run the example code and rotate the encoder slowly by hand, I see numbers 0, 1, 2, or 3 in the Serial monitor, but the count doesn't increase beyond those numbers. I suppose that is because there is no encoder Channel B attached to Pin 6. Hence my question: can I use Encoder.h without the second channel?

Alternatively, if you could point me to an example of an interrupt-driven encoder pulse counter without using the Encoder.h library, then that would be just as well.

Many thanks,

Johann


/* Encoder Library - Basic Example
* http://www.pjrc.com/teensy/td_libs_Encoder.html
*
* This example code is in the public domain.
*/

#include <Encoder.h>

// Change these two numbers to the pins connected to your encoder.
// Best Performance: both pins have interrupt capability
// Good Performance: only the first pin has interrupt capability
// Low Performance: neither pin has interrupt capability
Encoder myEnc(5, 6);
// avoid using pins with LEDs attached

void setup() {
Serial.begin(9600);
Serial.println("Basic Encoder Test:");
}

long oldPosition = -999;

void loop() {
long newPosition = myEnc.read();
if (newPosition != oldPosition) {
oldPosition = newPosition;
Serial.println(newPosition);
}
}
 
As you've discovered you can't use a quadrature encoder library to count pulses on a single pin, as
quadrature requires both signals to change to go round the state transition diagram. If only one
signal changes all it will do is oscillate by one count. And you can't connect the pins together either
as for quadrature both signals changing simultaneously is an error state.
 
If you only need to count the number of pulses, just use attachInterrupt() and in your interrupt function increment a volatile variable.
 
Thank you for your answer!

As you've discovered you can't use a quadrature encoder library to count pulses on a single pin, as
quadrature requires both signals to change to go round the state transition diagram. If only one
signal changes all it will do is oscillate by one count. And you can't connect the pins together either
as for quadrature both signals changing simultaneously is an error state.

I spent all day today trying to create a fake second channel that would appear to my Teensy as though it was Channel B of a quadrature encoder. I used a 555 timer to create that second pulse train that was delayed from the original encoder pulse train by 500 us or so. Sadly, that didn't work, I wasn't able to create a delay with the 555 timer. An alternative that I haven't tested yet is to use a 74HC595 shift register to create a delayed second pulse train. Anyway, thanks for having pointed out to me that a second pulse train was necessary.
 
If you only need to count the number of pulses, just use attachInterrupt() and in your interrupt function increment a volatile variable.

Hi Paul, thank you for your reply. I haven't been able to generate an interrupt in response to an external trigger on my Teensy 4.0. I have tried attachInterrupt() and spent many hour on that but without success. My sketch is complex and comprises multiple tabs in the Arduino IDE. That's why I don't want to bother you or myself with that much code, so I won't upload it. If you could point me to an example sketch that uses attachInterrupt() with a Teensy 4.0, then that would be hugely helpful. I couldn't find a useful example.

The solution that IS working for me is based on an example (of yours, I believe) of an interrupt-generating Timer1 that is posted somewhere on your site. I use the Timer1-generated interrupt to poll the encoder pin every 200 us. That is not quite as elegant as it would be if the encoder pulse triggered an interrupt instantly, but it is sufficient for my needs. Thank you for your wonderful website and the even more wonderful Teensy 4.0. I am a huge fan of it now, after having run repeatedly into the memory and speed limitations of the original Arduinos.
 
Try this, with a wire connecting pins 3 and 4.

Code:
volatile unsigned int count = 0;

void setup() {
  analogWriteFrequency(3, 123.5); // pulses on pin 3
  analogWrite(3, 128);
  attachInterrupt(4, isr, RISING); // short pins 3 & 4
}

void isr() {
  count = count + 1;
}

void loop() {
  Serial.println(count);
  delay(100);
}
 
Thank you so much for your help. I tried your code and it worked with the Teensy-generated oscillator as given in your code. Then I substituted my rotary optical encoder output as the input and that didn't work at all: I found that the interrupt-driven counter vastly overcounted ticks, on average, 20 pulses for every actual pulse. I verified that against my already working polling-based encoder tick counter. But, knowing that your ISR works ok, I concluded that my two-dollar optical encoder was producing the equivalent of bounce, like a push button, even though I am not seeing that bounce on an oscilloscope. My solution, which may be more of a hack since I have only little electronics training, was to put a 6 nF capacitor between the encoder output and ground, and then route the signal through two Schmitt Trigger inverters (LS7414). With that fix in place, I am seeing a perfect match between the interrupt-driven count and the polled encoder tick count.
 
You need to use CHANGE, not RISING or FALLING on an ISR used for decoding a quadrature encoder, otherwise
oscillating values will invariably be miscounted like this(*). Its very common to have bounce on a mechanical
encoder and a correct quadrature decoder will cope fine with this (although you might want to reduce
the number of interrupts with a low pass filter, its not necessary).

Note that if you have too long a time-constant on the encoder inputs it will malfunction when twisted
rapidly.

(*) Due to bounces or even vibration one of the two inputs may switch high-low-high-low repeatedly -
if you only respond to rising edges the count will go on one direction only rather than back and forth
between consecutive
counts which would be correct.
 
Thank you, MarkT, for your good advise. I found your comments about low-pass filtering especially useful. Some of your well-explained points don't necessarily apply to my case, as in my application I am using a single channel, non-quadrature encoder. All pulses from that encoder are counting upward and vibration around a trigger point will only result in more up-ticks. I understand your point that in a quadrature encoder, vibration will cause equal numbers of up-ticks and down-ticks. My application is a Persistence of Vision (PoV) clock that twirls a LED-studded rotor around at 10 revs per sec. For that clock I made a 60-slot optical encoder disk. Each pulse from the encoder activates and, a millisecond later, deactivates certain LEDs, with the effect of showing a clock face with seconds, minutes, and hour hands. In seeing how much trouble I had with bounce from the cheap optical photo-interrupter I used, I will, for future projects, only use off-the shelf quadrature encoders, and for that your points about quadrature encoders will be very useful.
 
You can use the hardware quad encoder to read this really easy: https://github.com/mjs513/Teensy-4.x-Quad-Encoder-Library
Here’s an example I modified about a week ago:
Code:
 /* Teensy 4 H/S Encoder Library - TwoKnobs Example
 * http://www.pjrc.com/teensy/td_libs_Encoder.html
 *
 * This example code is in the public domain.
 */

#include "QuadEncoder.h"

// Change these pin numbers to the pins connected to your encoder.
// Allowable encoder pins:
// 0, 1, 2, 3, 4, 5, 7, 30, 31 and 33
// Encoder on channel 1 of 4 available
// Phase A (pin0), PhaseB(pin1), 
QuadEncoder knobLeft(1, 0, 1);

void setup() {
  Serial.begin(9600);
  Serial.println("Tape Encoder Test:");
  pinMode(LED_BUILTIN, OUTPUT);
  /* Initialize Encoder/knobLeft. */
  knobLeft.setInitConfig();
  knobLeft.EncConfig.decoderWorkMode = true;
  knobLeft.init();
}

long positionLeft  = -999;
long positionRight = -999;

elapsedMillis secondTimer = 0;

void loop() {
  long newLeft; '
  if(secondTimer >= 1000){
    secondTimer -= 1000;
    newLeft = knobLeft.read();
    if (newLeft != positionLeft) {
      digitalToggle(LED_BUILTIN);
      Serial.print("Left = ");
      Serial.print(newLeft);
      Serial.println();
      positionLeft = newLeft;
    }
    knobLeft.write(0);
  }
  // if a character is sent from the serial monitor,
  // reset both back to zero.
  if (Serial.available()) {
    Serial.read();
    Serial.println("Reset both knobs to zero");
    knobLeft.write(0);;
  }
}
Connect your encoder to pin 0 and tie pin 1 high or low to set the desired direction, this example counts the number of pulses per second.
 
Thank you, vjmuzik, for taking the time to offer advise. At this time I have a working solution, two solutions in fact, so I won't test your suggestion on my current project. However, I have taken note of your post and will come back to it when I start my next Teensy project.
 
Status
Not open for further replies.
Back
Top