Reading 10K positions from a 2.5K rotary encoder

Your source for all things Atmel.

Moderator: phalanx

Post Reply
rst26508
Posts: 23
Joined: Tue Sep 01, 2009 2:06 am

Reading 10K positions from a 2.5K rotary encoder

Post by rst26508 » Sun Feb 14, 2010 3:00 pm

I been playing with a US Digital Optical Encoders (S2-2500-250-I-N-D - superceeded bvy http://www.usdigital.com/assets/general ... heet_3.pdf

I've done some testing with a 5V mini-pro and am currently using a Mega.
The encoder has an index line which helps get an absolute position.

By sensing a change of state each of the A and B lines I can get 10k positions out of the encoder. At a fast hand turn speed the accuracy drops off (some missed steps) but for slow fine control it's great.

/*
* Basic Rotary Encoder reader.
*/

const int Channel_a = 2;
const int Channel_b = 3;
const int IndexPin = 21;

int StepCount = 0;
int Ch_a_state;
int Ch_b_state;
int Index_state;
int LastCount = 0;

void StateChange0() {
Ch_a_state = digitalRead(Channel_a);
Ch_b_state = digitalRead(Channel_b);
Index_state = digitalRead(IndexPin);
if ( Ch_a_state == Ch_b_state ) ++StepCount;
else --StepCount;
}

void StateChange1() {
Ch_a_state = digitalRead(Channel_a);
Ch_b_state = digitalRead(Channel_b);
Index_state = digitalRead(IndexPin);
if ( Ch_a_state != Ch_b_state ) ++StepCount;
else --StepCount;
}

void StateChange2() {
Index_state = digitalRead(IndexPin);
// if ( Index_state != LOW )
Serial.println("Index pin state change");
Serial.print("StepCount is ");
Serial.println(StepCount);
StepCount = 0;
}

void setup()
{
pinMode(Channel_a, INPUT);
pinMode(Channel_a, INPUT);
pinMode(IndexPin, INPUT);
attachInterrupt(0, StateChange0, CHANGE);
attachInterrupt(1, StateChange1, CHANGE);
attachInterrupt(2, StateChange2, RISING);
Serial.begin(9600);
}

void loop()
{
if ( StepCount != LastCount )
{
Serial.print("Changing Position to ");
Serial.println(StepCount);
LastCount = StepCount;
}
delay(100);
}
Bob

Liencouer
Posts: 155
Joined: Fri Mar 28, 2008 7:36 am

Post by Liencouer » Mon Feb 15, 2010 6:22 am

if you needed to maintain accurate counts and get a faster shaft speed, you can always use the internal timer/counter modules to do the pulse counting for you. I dont know if the arduino ide supports that - you could probably find some c examples fairly easily enough.

rst26508
Posts: 23
Joined: Tue Sep 01, 2009 2:06 am

Thanks

Post by rst26508 » Wed Feb 17, 2010 4:28 am

Liencouer thanks for that. I'm having a play with direct reading of the ports and some bit manipulation to see if that improves the performance. A few lessons to learn along the way with that but I'm making progress.

The code as it now stands -

/*
* Basic Rotary Encoder reader.
*/

#define Ch_a_state (PINE & (1<<4))>>4
#define Ch_b_state (PINE & (1<<5))>>5
#define Index_state (PIND & (1<<0))
// Channel_a = 2;
// Channel_b = 3;
// IndexPin = 21;

int StepCount = 0;
int LastCount = 0;

void StateChange0() {
if ( Ch_a_state == Ch_b_state ) ++StepCount;
else --StepCount;
}

void StateChange1() {
if ( Ch_a_state != Ch_b_state ) ++StepCount;
else --StepCount;
}

void StateChange2() {
// if ( PIND & (1<<0) )
// Serial.println("Index pin state change");
// Serial.print("StepCount is ");
Serial.println(StepCount);
StepCount = 0;
}

void setup()
{
// Not sure how to do the next two lines's with defines (or const's) to make them more portable.
// Setup for the encoder rotation and index channels
DDRE = DDRE & B11001111;
DDRD = DDRD & B11111110;
attachInterrupt(0, StateChange0, CHANGE);
attachInterrupt(1, StateChange1, CHANGE);
attachInterrupt(2, StateChange2, RISING);

Serial.begin(9600);
}

void loop()
{
if ( StepCount != LastCount )
{
Serial.print("Changing Position to ");
Serial.println(StepCount);
LastCount = StepCount;
}
delay(300);
}
Bob

monstrum
Posts: 162
Joined: Tue Sep 08, 2009 1:42 pm
Location: Gothenburg, Sweden

Post by monstrum » Wed Feb 17, 2010 5:44 am

From what I can see, you are sending serial debug-messages from inside each state-change routine. That is the limiting factor in your case, no matter how you detect the pulses.

So, remove the serial printing, and do that in the main loop instead.

Liencouer
Posts: 155
Joined: Fri Mar 28, 2008 7:36 am

Post by Liencouer » Thu Feb 18, 2010 5:50 pm

Code: Select all

// Not sure how to do the next two lines's with defines (or const's) to make them more portable.
// Setup for the encoder rotation and index channels
DDRE = DDRE & B11001111;
DDRD = DDRD & B11111110; 
can look something like

Code: Select all

 #define BIT0 0
#define BIT4 4
#define BIT5 5
DDRE &= ~((1<<BIT4) | (1<<BIT5)); // will clear bits 4 and 5 of DDRE
DDRD &= ~(1<<BIT0); // will clear bit 0 of DDRD
and you can name BIT0 to something more descriptive, if you feel so inclined

recommended reading: http://www.avrfreaks.net/index.php?name ... ic&t=37871

rst26508
Posts: 23
Joined: Tue Sep 01, 2009 2:06 am

thanks again

Post by rst26508 » Mon Feb 22, 2010 2:21 am

monstrum and Liencouer thank you both for the additional comments. It's much more reliable now. The next step will be to hook up two encoders and see how it goes.

Bob
Bob

Post Reply