Go Down

### Topic: Evil Genius getEncoderTurn() Discarding some aPin and bPin Transitions? (Read 1 time)previous topic - next topic

#### FoxcodeBob

Greetings all,

Regarding getEncoderTurn() first appearing in the listing for Project 11:

int getEncoderTurn()
{
// return -1, 0, or +1
static int oldA = LOW;
static int oldB = LOW;
int result = 0;
int newA = digitalRead(aPin);
int newB = digitalRead(bPin);
if (newA != oldA || newB != oldB)
{
// something has changed
if (oldA == LOW && newA == HIGH)
{
result = -(oldB * 2 - 1);
}
}
oldA = newA;
oldB = newB;
return result;
}

I'm clear once we test to see if a change has occurred [if (newA != oldA || newB != oldB)], but it seems to me that we then only calculate a result for the case when we are transitioning from Phase 2--->Phase 3 or Phase 1--->Phase4 [if (oldA == LOW && newA == HIGH)]. This ignores valid transitions when aPin goes from HIGH to LOW and transitions where aPin stays the same and bPin goes HIGH to LOW or LOW to HIGH, all of which are valid state changes that in this code will result in result being 0, incorrectly signalling no change in the rotary encoder at all.

I think this code drops 75% of the state transition cases, but it's possible I've lost the plot here!

Can anyone shed some light? Many thanks,
Bob

#### olikraus

#1
##### Dec 31, 2012, 03:13 pm
Without a state diagram it is usually difficult to understand such code... Due to this fact, i draw the complete graph some months ago and derived the code from it.

Sorry for not giving you more specific help.

Oliver

#### FoxcodeBob

#2
##### Dec 31, 2012, 04:19 pmLast Edit: Dec 31, 2012, 04:27 pm by FoxcodeBob Reason: 1
Thank you Oliver. Your state diagram is very helpful and shows my point - I've marked (*) the CW and CCW transitions which would be triggered by the IF conditional in the source code; all others would be ignored by the conditional, and result in a "no change" return value of 0.

#### retrolefty

#3
##### Dec 31, 2012, 04:48 pmLast Edit: Dec 31, 2012, 04:50 pm by retrolefty Reason: 1
Encoder manufactures usually specify their encoders by "steps per revolution" where steps means four complete transitions of the A and B in the gray code quadrature.

So you have a choice of how to count 'steps' when using an arduino. If the encoders is rated at say 256 steps per/rev:

Count just one edge condition, say A signal rising and you will measure 256 steps/rev
Count two edge conditions, say either A or B rising and you will measure 512 steps/rev
Count all four edge condition, any change on A or B, and you will measure 1024 steps/rev

So the choice is yours to make and will dictate the decoding algorithm you use.

Lefty

#### dc42

#4
##### Dec 31, 2012, 05:07 pm
You can find the code I use to drive rotary encoders at https://github.com/dc42/arduino. You need to call the poll() function at least every 2ms to avoid missing transitions when the encoder is turned quickly. I normally call it from a tick interrupt or from a task scheduled to run every 2ms. When you want to know how far the encoder has moved, call getChange(). The code allows for the unfortunate tendency of some encoders to be in either of 2 states when in a detent.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

#### MarkT

#5
##### Dec 31, 2012, 06:02 pm

I'm clear once we test to see if a change has occurred [if (newA != oldA || newB != oldB)], but it seems to me that we then only calculate a result for the case when we are transitioning from Phase 2--->Phase 3 or Phase 1--->Phase4 [if (oldA == LOW && newA == HIGH)]. This ignores valid transitions when aPin goes from HIGH to LOW and transitions where aPin stays the same and bPin goes HIGH to LOW or LOW to HIGH, all of which are valid state changes that in this code will result in result being 0, incorrectly signalling no change in the rotary encoder at all.

I think this code drops 75% of the state transition cases, but it's possible I've lost the plot here!

Can anyone shed some light? Many thanks,
Bob

The code will work reliably if the direction never changes, only counting 50% of the transitions.  However when it reverses it miscounts by
1.  If it reverses several times with only a little movement it will repeatedly miscount.

The flaw with this approach is that if you only look at transitions where A changes, you must take account of both 0->1 and 1->0 transitions
of A.

Correct code would be something like
Code: [Select]
`if (newA != oldA)  result = newA ^ newB ? -1 : 1 ;else if (newB != oldB)  result = newA ^ newB ? 1 : -1 ;...`
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up