|
|
View previous topic :: View next topic |
Author |
Message |
scottc
Joined: 16 Aug 2010 Posts: 95
|
Mechanical rotary encoder routine woes :) |
Posted: Fri Sep 10, 2010 3:19 am |
|
|
I have been working on a routine to handle decoding of a 2 bit mechanical
rotary encoder but have run into a count issue, so perhaps someone
can share some insight as to what might be the trouble.
My encoder is currently not running an ISR on port B, but it does respond
very smoothly and increments,decrements ok. Well sort of. The problem
is it is incrementing by 4 and decrementing by 4 each time. But other
than that it works fast and does not appear to miss counts.
The encoder is connected to RB2 and RB3 and is pulled up to vcc via
4k7 resistors.
Code: |
int value; //working vars
int newencoder;
int lastencoder;
void encoder(void)
{
newencoder = (port_b & 0b00001100); // keep 2 bits of port b
if(newencoder != lastencoder) //Check for a change
{
if (bit_test(newencoder,3) == bit_test(lastencoder,2))
value++; else VALUE--;
}
lastencoder = newencoder; // save state
} |
thanks
Scott. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19568
|
|
Posted: Fri Sep 10, 2010 4:51 am |
|
|
Realistically, it sounds as if the encoder is actually generating multiple edges for each transition. - though see comment at the bottom
If you think about it, the code does what it needs to, and will only count, when a transition is seen on one of the signals. If you are getting multiple counts there are two possible causes:
1) 'lastcount' has been corrupted. If this is a subroutine called to do this counting, this _must_ be either a global variable, or a static variable.
2) Multiple edges are arriving.
On mechanical encoders, some degree of signal 'pre-processing', really is needed. Classic would be to feed the signal through a comparator with a lot of hysteresis, or add a re-triggerable mono-stable multivibrator on each signal, with time value selected, just slightly shorter than the shortest pulse expected for the maximum wanted rate.
As a comment, are you actually seeing the count go up in fours, or are you just seeing 4* the count you 'expect'?. You do realise that a quadrature encode returns four edges for every line on the encoder, so a 100 'line' encoder, will return 400 edges for a rotation. If this is what you are seeing , then the code and hardware is working perfectly....
Best Wishes |
|
|
scottc
Joined: 16 Aug 2010 Posts: 95
|
|
Posted: Fri Sep 10, 2010 5:23 am |
|
|
hi Ttelmah,
well, lascount is configured as a global variable so not too sure if the
actual problem is ther or not.
I set the variable "Value" in main equal to 100 so that is the starting point
for the count. when I turn the encoder clockwise 1 click, value increments
by 4 each time
100, 104, 108 etc
the desired increment needless to say is to increment by 1 only per
encoder click. I understand there are for possible states with the encoder
but I had thought the bit fiddling would have reduced that.
Thanks Scott |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Fri Sep 10, 2010 7:07 am |
|
|
Need to see more of the code. This would be an easy problem to reproduce with small working, compilable example!
Code: |
newencoder = (port_b & 0b00001100); // keep 2 bits of port b
|
How is port_b defined ? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19568
|
|
Posted: Fri Sep 10, 2010 8:56 am |
|
|
Are you saying that this is not a normal shaft encoder, but one of the 'rotary switch' style units, with mechanical detents?. If so, you need to check the data sheet. Many of these generate a complete quadrature sequence for each mechanical detent. If so, the behaviour would be as expected. Give the part number or a data sheet reference for the part. For 'single pulse' detection (which these need, not quadrature detection), you would want:
Code: |
void encoder(void) {
newencoder = (port_b & 0b00001100); // keep 2 bits of port b
if(newencoder != lastencoder) {
if (bit_test(newencoder,3)) {
if (!bit_test(lastencoder,3)) {
//Here rising edge on the high pulse train
if (bit_test(newencoder,3) == bit_test(lastencoder,2))
value++; else VALUE--;
}
}
}
lastencoder = newencoder; // save state
}
|
This gives just one count for the complete quadrature sequence, as opposed to counting all four edges....
Best Wishes |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Fri Sep 10, 2010 9:54 am |
|
|
Ttelmah's spot-on.
If it's a quadrature encoder, each click probably *IS* a full sequence.
I posted this for someone else recently who was writing a decoder for a rotary encoder... he went with hardware decoding..
but the PDF is still pretty educational. I used it to write my own routines in verilog.
http://www.benjammin.net/~bkamen/pdf/honeywell-quad-encode-decode.pdf _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
scottc
Joined: 16 Aug 2010 Posts: 95
|
|
Posted: Fri Sep 10, 2010 2:56 pm |
|
|
Hi, Ttelmah, bkamen, Wayne
Thanks for getting back. Ttelmah the rotary encoder is indeed one of
those mechanical types. They are common as dish water these days
and come with the joy of mechanical bounce for free
Anyway, your modified code works like a champ. I now turn the encoder
clockwise and in increments without missing a tick, Turn it anti-clockwise
and it decrements by 1 without missing a beat. Try get it to stick between
indents and it does nothing which is most excellent.
I really appreciate your insight on this as I had been fighting it for awhile.
I can understand now where I went wrong, Thanks much man
Currently, the encoder is hooked to pins RB2,RB3 using 4k7 pullups.
As it stands like that, it works really smooth which is kind of amazing.
I have another circuit that uses some hardware to condition the encoder
before it is connected to the Pic, thats based on a 74hc74. It will be
interesting to see how the routine works with conditioned inputs to the
pins. The intent of the encoder is for a human interface only, similar to
the knob on your stereo or car radio etc.
I think a few weeks back bkamen suggested using a PIC18F2331 part
that has its own QEI decoder built in to hardware. Well I took your suggestion and this circuit is using the PIC18f2331 but I have not attempted to try out the built in qei. I think I will leave that for another
day. I lucked up in that the inputs Microchip used for the qei just happen to be unused on this board so in essence I have the hardware
in place to give the qei a go.
Many Thanks guys
Best Scott. |
|
|
fastlink30
Joined: 14 May 2007 Posts: 33
|
|
Posted: Sun Nov 27, 2011 9:19 am |
|
|
i use this code:
Code: |
void encoder(void) {
if (!ENCODER_STARTSEQ) { // if sequence is started.... (ch_a from high to low)
if (PORTA_B.enc_ch_a == 0) ENCODER_STARTSEQ = 1; // check if ch_a is low (means sequence start)
} else {
if (PORTA_B.enc_ch_a) { // if ch_a is up, means end of sequenxe
if (!PORTA_B.enc_ch_b) { // if ch_b is low means afterward
ENCODER_POSITION++;
}
else {
ENCODER_POSITION--;
}
ENCODER_STARTSEQ=0;
}
};
}
|
channel a/b have pull-up resistor
ENCODER_STARTSEQ is int1
channel b is connected to pb3
channel a is connected to pb4
channel c is connected to pb5 (push button)
port b have interrupt INT_RB enabled
for me this code work quite well, i tryed it on prototype board |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|