Search

Industrial incremental encoder and arduino

Please let us in the comment zone any suggestions that you think will improve the article!

If you like the article click the follow button  to stay in touch with us!

 

 

First of all what is an incremental encoder???

An incremental encoder is a device which can be mounted on a machine to measure the linear or angular position and convert them in to binary outputs.

When the encoder rotate(for angular position) they produce binary signals on two outputs. Depending the model the outputs can be(usually notated)

-channel A

-channel B

-channel A*(inverted)

-channel B*(inverted)

-channel Z(give one pulse for each rotation)

The model used in this post is  BHK 03.05A1000. It has four channels without the Z chhanel and for each revolution generate 1000 pulses.

An image of this encoder is:

industrial encoder

The channels A and B are ciclycals and  shifted by 90 degrees-quadrature. An image with those two signals is:

industrial encoder signals for arduino

So for each revolution our encoder generate 1000 pulses, but we can know the rotating direction also from those two signals. When signal A is in front of signal B the rotating direction is counterclockwise and when B is in front of A is clockwise.

Another thing about incremental encoders is that we can modify their resolution, that means for this encoder we can reach at 4000 pulses per revolution(ppr).

encoder resolution

Ok, for first example we will use only channel A and counting every time this signal rise. The next program read pins from registers so, for more detailes read this post and because we use external interrupts on pin 2 or 3(channel A will be connected to one of them(for our examples we choose  pin 2)) we have in the program the attachInterrupt() also about this function you can read here.

As we say the next program counts every time the channel A rise:

const byte ledPin = 13;
const byte Pin2 = 2;// we use pin 2 beacause pins 2 and 3 are the only pins on arduino uno
//with external interrupts
volatile byte state = LOW;
volatile int x=0;

void setup() {
DDRB |= (1<<DDB5);//pin 13 is in output mode
DDRD&=~(1<<DDD2);//pin 2 is in input mode
PORTD|=(1<<DDD2);// activate the pull-up resistor 14.2.1 atmega 328 datasheet
attachInterrupt(digitalPinToInterrupt(Pin2),encoder , RISING);//call encoder function when pin 2 became high
Serial.begin(250000);
}

void loop() {
digitalWrite(ledPin, state);//change the led state from pin 13 at every rising of pin 2
Serial.println(x);
}

void encoder() {
x=x+1;
state = !state;
}

When the state of pin 2 is from LOW to HIGH the attachInterrupt work and call the encoder function where x is indexed and led from pin 13 change is state. Also in this case we have 1000ppr.

Now for the  second example we just change the parameter RISISING in the attachInterrupt( ) with the CHANGE parameter.

The program is:
const byte ledPin = 13;
const byte Pin2 = 2;// we use pin 2 beacause pins 2 and 3 are the only pins on arduino uno
//with external interrupts
volatile byte state = LOW;
volatile int x=0;

void setup() {
DDRB |= (1<<DDB5);//pin 13 is in output mode
DDRD&=~(1<<DDD2);//pin 2 is in input mode
PORTD|=(1<<DDD2);// activate the pull-up resistor 14.2.1 atmega 328 datasheet
attachInterrupt(digitalPinToInterrupt(Pin2),encoder , CHANGE );//call encoder function when pin 2 toggle
Serial.begin(250000);
}

void loop() {
digitalWrite(ledPin, state);//change the led state from pin 13 at every rising of pin 2
Serial.println(x);
}

void encoder() {
x=x+1;
state = !state;
}

With this program for a revolution we have 2000 pulses.

But what if we want to know the rotate direction???

For that we use the both channels-channel A and channel B.

When the encoder rotates counterclockwise, channel A is in front of channel B with 90 degrees. If channel B is in front of channel A with 90 degrees the rotate direction is clockwise.

So we need a program to check what channel is in front.

The next program does that:

const byte ledPin = 13;
const byte leftPin = 2;
const byte rightPin = 3;
volatile byte state = LOW;
volatile int x=0;
boolean OK;
void setup() {
DDRB |= (1<<DDB5);//pin 13 is in output mode
DDRD&=~(1<<DDD2);//pin 2 is in input mode
PORTD|=(1<<DDD2);// activate the pull-up resistor 14.2.1 atmega 328 datasheet
DDRD&=~(1<<DDD3);//pin 3 is in input mode
PORTD|=(1<<DDD3);// activate the pull-up resistor 14.2.1 atmega 328 datasheet

attachInterrupt(digitalPinToInterrupt(leftPin), left, RISING);//call left function when pin 2 became high
attachInterrupt(digitalPinToInterrupt(rightPin), right, RISING);// call right function when pin 3 became high
Serial.begin(250000);
}

void loop() {
digitalWrite(ledPin, state);
// Serial.println(x);
Serial.println(OK);
}

void left() { // function to check if the rotate direction is counterclockwise
if((PIND&B00000100)==4 && (PIND&B00001000)==0){// check to see if pin 2 is high and pin 3 low
//channel A before channel B
OK=false; // OK false if the direction is counterclockwise
x=x+1; //increment a variable
}
state = !state; //blink LED from pin 13
}
void right(){ //function to check if the rotate direction is clockwise
if((PIND&B00001000)==8 && (PIND&B00000100)==0){// check to see if pin 3 is high and pin 2 low
//channel A before channel B
OK=true; //OK true if the direction is clockwise
x=x-1; //decrement a variable
}
state = !state; //blink LED from pin 13
}

Now let’s explain a little this program. PIND&B00000100 means an AND operation between the register which read the state of pins and an ordnary 8 bits. If the bit corresponding to 2^2 is one, after the AND operation the result for that position will be also one. In decimal that is equivalent with 4-which means that the digital pin 2 is HIGH.

The same principle is for digital pin 3 (PIND&B00001000) but because the equivalent bit for pin 3 is position 2^3 the result in decimal will be 8.

The position of digital pins is specified in atmega328 datasheet.

For the final example we use the both channels but  the intterupt function will work for every change in the pin(2 and 3 ) state.

So in this example we will reach for this encoder at 4000 pulses per revolution.But this situation create another problem. At a high speeds arduino could miss steps. To eliminate this, in the program we will memorize the previous state of pins and compare with the new one in a register with 8 bits like 0000xxyy. Now the xx represent the new state of pins(2 and 3) and the yy pair represent the previous state of pins.

To make the if function the xxyy format is transformed in the decimal format.

For the incremental encoder we know the corresponding state of pins(rising and falling for pin 2 and pin 3) for each rotate direction. This data are below:

clockwise
Channel A Channel B Decimal for xxyy format
0 0 4
0 1 13
1 1 11
1 0 2
counterclockwise
Channel A Channel B Decimal for xxyy format
1 0 14
1 1 7
0 1 1
0 0 8

The next program do that:

const byte ledPin = 13;
const byte leftPin = 2;
const byte rightPin = 3;
volatile byte state = LOW;
volatile byte x;
volatile byte a;
volatile byte b;
volatile byte c;
boolean OK=false;
boolean OK1=true;
boolean OK2=false;
void setup() {
DDRB |= (1<<DDB5);//pin 13 is in output mode
DDRD&=~(1<<DDD2);//pin 2 is in input mode
PORTD|=(1<<DDD2);// activate the pull-up resistor 14.2.1 atmega 328 datasheet
PORTD&=~(1<<DDD3);//pin 3 is in input mode
PORTD|=(1<<DDD3);// activate the pull-up resistor 14.2.1 atmega 328 datasheet
attachInterrupt(digitalPinToInterrupt(leftPin), encoder, CHANGE);//call left function when pin 2 is changing state
attachInterrupt(digitalPinToInterrupt(rightPin), encoder1, CHANGE);// call right function when pin 3 is changing state
Serial.begin(9600);
}

void loop() {
if(OK2==false){//just for one time
x=PIND&B00001100;// when the sistem start x takes the pins state
a=PIND&B00001100;// when the sistem start a takes the pins state
OK2=true;
}
digitalWrite(ledPin, state);
//Serial.println(x);
Serial.println(a);
// Serial.println(OK);
if (OK1==false){
Serial.println(“error”);// to appear more time on the serial monitor
}

}

void encoder() {// function to check the rotate direction when pin 2 is changing state
cli();// stop interrupts which could appear
a=PIND&B00001100;// a takes the new state of pins beacuse encoder function is call when pin 2 is changing state
b=a>>2;// move bits for pin 2 and 3 in the places coressponding to 2^0 and 2^1
c=b | x;// OR function between b and x
if(c==2 || c==4 || c==11 || c==13)// values in pairs of bits in the format xxyy
{
OK=false;// right rotate direction
}
if(c==1 || c==7 || c==8 || c==14)// values in pairs of bits in the format xxyy
{
OK=true;// left rotate direction
}
else if(OK==true){// if OK is true and c has other values the arduino miss steps in the ccw direction
OK1=false;
//Serial.println(“error”);
}
x=PIND&B00001100;// x takes the new state of pins
state = !state;// blink LED from pin 13
sei();// enable interrupts
}
void encoder1(){// function to check the rotate direction when pin 2 is changing state
cli();// stop interrupts which could appear
a=PIND&B00001100;// a takes the new state of pins beacuse encoder1 function is call when pin 2 is changing state
b=a>>2;// move bits for pin 2 and 3 in the places coressponding to 2^0 and 2^1
c=b | x;
if(c==1 || c==7 || c==8 || c==14)// values in pairs of bits in the format xxyy
{
OK=true;// left rotate direction
}
if(c==2 || c==4 || c==11 || c==13)// values in pairs of bits in the format xxyy
{
OK=false;// right rotate direction
}
else if(OK==false){// if OK is false and c has other values the arduino miss steps in the cw direction
OK1=false;
//Serial.println(“error”);
}
x=PIND&B00001100;// x takes the new state of pins
state = !state;// blink LED from pin 13
sei();// enable interrupts
}

In this program arduino counts every time pins 2 and 3 change their states reaching at 4000ppr, also from the pairs of bits read if the direction is clockwise or counterclockwise and if arduino can’t read all the pulses and they are not like in the table above an error message will bee emited.

Facebooktwitterpinterest

Related posts

Leave a Comment

Show Buttons
Hide Buttons