Encoder to Arduino

dogman

Member
I have an Arduino Pro Mini 5V & would like to correctly connect an Incremental encoder to it, part No E6B2-CWZ1X.
It has a Line Driver output. Please see datasheet attached.
I want to connect Channel A & Channel B only to digital Pins 2 & 3 & use Interrupts to read the encoder disregarding the other wires from the encoder.
I was told that I can just connect it directly to the digital pins using 10K pull down resistors.
I have tried this & the encoder does work but it appears to falsely trigger at times with incorrect number of pulses.
How do I do this correctly?
 

Attachments

  • e6b2-c_incremental_rotary_encoder.pdf
    925.2 KB · Views: 265
I'd suggest also adding 10K or 1K pulldowns to the unused signal wires, to damp them.

How long is the cable from the encoder? If it's more than a couple of metres I'd suggest using proper RS422 line receivers, such as a 26C32 with eg. 150 Ohm termination.

How are you trying to count the encoder steps? Interrupts generated by pin level changes, or from a timer so you read at fixed intervals?
 
There are probably hundreds of encoder examples for Arduino out there, why not try using google.

Usually they are connected with pull-ups rather than pull-downs? - but your issue sounds like you're not debouncing the inputs?.
 
The cable is only about 50mm long, inside a device.
Yes, Interrupts generated by pin level changes.
The unused wires are just floating at this stage, obviously they can't be grounded but I didn't think of pulling them down with resistors.
 
There are probably hundreds of encoder examples for Arduino out there, why not try using google.

Usually they are connected with pull-ups rather than pull-downs? - but your issue sounds like you're not debouncing the inputs?.
Not many for this particular encoder with a line driver output & when you do find them they all have the same issues as what I am having.
I did not realise that an optical encoder needs to be debounced?
I thought it best to ask people who actually can help rather than use google, you don't know what you get!
Do I really need to debounce this?
 
I don't know, but trying it won't do any harm.

But there's so much Arduino stuff out there, it's always worth having a look and seeing how other people are doing it.
 
I don't know, but trying it won't do any harm.

But there's so much Arduino stuff out there, it's always worth having a look and seeing how other people are doing it.
The main reason I didn't worry about debouncing is that it takes time & missed pulses are sure to follow.
I am reading 3600 pulses per revolution, only at hand speed for a resolution of 0.1 degrees.
I will try it however & see what happens.
 
From the data, that presumably means you are using an 1800 line one?

For that, I'd only interrupt on one signal, both edges, and when the interrupt occurs read the other signal to get the step direction.

If necessary, connect one signal to two pins with interrupts on opposite levels, then in each interrupt read the B level. The combination of A rising or falling plus B level give the 3600 count and direction info.
 
Yes, that's correct 1800 line one.
That is a very interesting idea in your suggestions, I will try everything that has been suggested & get back with the results.
Good Thinking, thank you!
 
I have used line driver quadrature encoder style outputs for decades and I have always used a RS485 line receiver to match the line driver style output of the encoder, never had any problems using very long distance runs.

 
Last edited:
I was unsure if I needed a line receiver for the very short length of cable that I am using, about 50mm long.
I am trying the previous suggestions at the moment to see if it corrects the issues I am having.
In the datasheet it mentions that this encoder is RS-422A compatible, if needed will the RS485 line receiver be suitable?
The link you supplied will be very helpful.
 
Here is a copy of some code that may help you. I found is some years ago when I was planning on building a quadrature decoder using one of the ATtiny range. I did not take much notice of it at the time as I work mainly with assembler. I seems to use assembler embeded in C code so it should be quicker than pure C code.

Les.
 

Attachments

  • 205 _ NoMi DESIGN).pdf
    322.9 KB · Views: 282
I'll have a look at that Les, thank you.
 
Just an update on my progress with this.
I have been seriously delayed due to getting many many upload errors trying to upload sketches to the Pro Mini, very frustrating AARRGGHH!

Firstly I tried connecting resistors to the unused encoder signal wires to ground, the problem still persisted.
I tried using both Pullup & Pulldown resistors on both A & B channel connections, no difference & the problem still exists, I decided to use software Pullup resistors.
I then insulated the ends of the unused signal wires & twisted them & shielded them with Copper tape & presto it worked & no noise on the main signal wires.
With that being fixed I discovered that my program was missing some pulses, reading to slow or maybe the Oled display refresh rate was hindering progress.

My repeatability test setup is a small lever arm of about 200mm or 8" long & it is connected to the shaft of the encoder, the device the encoder is housed in is fixed solid to my bench & I rotate the encoder either clockwise or counter clockwise with the lever using the bench as a solid stop on either side.
So I settle the lever on the bench stop, Zero the display & rotate in whatever direction to the stop on the other side.
Just say I am testing for a number of pulses & I have zero on one side & rotate the lever to the other side & have say 2,250 pulses.
I should be able to rotate back & forth & have Zero & 2,250 but it is not.
I also position the lever up vertically & flick it back & forth between my fingers & return to either stop which should still read Zero or 2,250 if repeatability is good.
Not so, the count differs by a couple of pulses with the program I have, it is missing pulses.

I then tried using a very simple pulse count program just using the Serial monitor readout & everything works perfectly no matter how fast you can flick or rotate the lever. Repeatability is perfect.

So it appears I have to use more brain cells & try to configure or use another program that can read fast enough for the application, at first I thought it may be just the Pro Mini but I am sure it is just actually how good the program is & to suit the Pro Mini.
I'll keep trying.
 
Do you have to use that Arduino?

A PIC with a configurable logic cell module such as a 16F18323 or 16F18446 can pretty much read an encoder in hardware.

Or some of the more powerful DSPIC or PIC24 MC version devices have dedicated quadrature input modules.
eg.

That has a quadrature interface with 32 bit position register; see section 17 of the data sheet.

The DSPIC or PIC24 are also far better for driving graphics displays - that one has 256K flash and 32K RAM, with a 16 bit CPU.

Or, if you want to stick with the Arduino IDE, I've used an ESP32 devkit C with this library in one gadget:

That uses some of the hardware peripherals; I only used it for a digital pot style encoder so no idea how well it works.

For the 16F18323 or other PICs that have the CLC module, it needs a few lines to configure it, then just the count increment in one interrupt routine and the decrement in the other.
 
Yes, I get what you are saying.
I actually have just finished sorting this out for the Pro Mini & it is working very accurately with excellent repeatability with a resolution of 0.05 degrees which is better than initially expected.

I am no code genius but I have put together something that is reading very fast. I'll post it here.
Feel free to tell me I don't know what I am doing.
I am still trying to get my head around your suggestion of reading one channel & just direction from the other channel.
Any suggestions on this would would help me out.

Code Here:

Code:
/*
Dogman 15/12/2023.
Quadrature Encoder - Line Driver Output.
7200 Pulses Per Revolution - Resolution of 0.05 Degrees.
Oled 128 x 32 I2C Display.
Debounce of Button to Zero the display is not required for this application.
This Sketch has tested to be Repeatable & Accurate using a Pro Mini 5V version.
It is recommended to delete or comment out all "Serial Functions" upon installation.
Maximum rpm is unknown at this stage as this is to be used at hand rotation speeds.
*/

#include <Wire.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 32  // OLED display height, in pixels
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define PULSES_PER_REV 7200  // Adjust this to match the encoder pulses.

int encPinA = 2;  // Interrupt Pins, 2 & 3.
int encPinB = 3;
int ZeroPin = 4;  // Button to Zero Display.

volatile int lastEnc = 0;
volatile long encValue = 0;

long lastencValue = 0;

int lastMSB = 0;
int lastLSB = 0;

void setup() {
  display.begin();
  Serial.begin(115200); // Delete on installation.

  pinMode(encPinA, INPUT_PULLUP);  // Enable onboard pullup resistors.
  pinMode(encPinB, INPUT_PULLUP);

  pinMode(ZeroPin, INPUT_PULLUP);

  attachInterrupt(0, updateEnc, CHANGE);  // Call updateEncoder() when any pin level change occurs.
  attachInterrupt(1, updateEnc, CHANGE);
}
void updateEnc() {                 // Keep this out of the loop.
  int MSB = digitalRead(encPinA);  // MSB = most significant bit.
  int LSB = digitalRead(encPinB);  // LSB = least significant bit.

  int enc = (MSB << 1) | LSB;      // Converting the 2 pin value to single number.
  int sum = (lastEnc << 2) | enc;  // Adding it to the previous encoded value.

  if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encValue++;
  if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encValue--;

  lastEnc = enc;  // Storage value for the next time
}
void loop() {

  if (digitalRead(ZeroPin) == LOW) {  // Set Display to Zero
    encValue = 0;
  }

  display.clearDisplay();
  display.setTextSize(3);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print((encValue % 7200 * 360.0 / PULSES_PER_REV), 2);  // Convert pulses to degrees - 0.05 degree resolution.
  display.display();

  Serial.println((encValue % 7200 * 360.0 / PULSES_PER_REV), 2); // Delete on installation.
  Serial.println(encValue);  // Delete on installation.
  delay(5); 
}
 
Look good!

I can see a couple of possible optimisation tweaks for the encoder routine:

Only do the IF comparisons if lastenc != enc

Once the equal states are omitted, the long comparison lines can actually be removed and increment or decrement come down to XORing the middle two bits of the sum!
If they are different (XOR result = 1), increment. Otherwise, decrement.

Something like:

if ((sum ^ (sum << 1)) & 0x04) encValue++;
else encValue--;

That skips the invalid states, where the count changes by two - but if that ever occurs, it's lost sync anyway & a faster routine reduces the chance of that happening.
 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…