SparkFun Forums 

Where electronics enthusiasts find answers.

Your source for all things Atmel.
By rst26508
#93489
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);
}
By Liencouer
#93540
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.
By rst26508
#93700
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);
}
By monstrum
#93704
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.
By Liencouer
#93833
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
By rst26508
#94121
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