View previous topic :: View next topic |
Author |
Message |
scottc
Joined: 16 Aug 2010 Posts: 95
|
Interfacing Mechanical Rotary encoder to the pic |
Posted: Mon Aug 16, 2010 6:58 pm |
|
|
Hello, I am in the process of designing a LCD based UI for use with a
mechanical rotary encoder as the element to select menus etc.
The encoder type is here.
http://www.bitechnologies.com/pdfs/en11.pdf
The processor is a pic16f887. I would like to be able to use the encoder
to simply increment and decrement a variable based on if the encoder direction is clockwise or anti clockwise. I ran into some issues
in that sometimes if I turn the encoder fairly fast by hand it misses a count. I am using pins RC6,RC7 as the input pins, they are tied to 5vDc via a 47k pullup. Currently I am not running a ISR for the encoder but just a standard function instead that gets called from main as needed.
In looking at the hardware I am thinking that it might be best to use something like a LS7183 or decrete logic to decode the encoder prior
to interfacing it to the pic. I was wondering if this would be the best solution in the long run as I would like the flexibility of using any input
pins on the processor.
The basic idea is to use the encoder instead of two buttons to increment,
decrement a INT 16 Value.
Thanks In advance, Scott |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Mon Aug 16, 2010 11:11 pm |
|
|
Well, this is essentially a quadrature encoder and an IRQ is really the best (almost essential) way to do it.
You can do it one of 2 ways:
If you use an INT line (like RB0) where you can set the polarity, an easy cheat (if I remember right) is to just IRQ on positive transitions and then in your routine, look at the encoder's "B" input. If high, increment your counter. If low, decrement your counter.
The other way is to use the RB interrupt (usually on RB4-RB7 -- but only using B4 and B5 since B6 and B7 are used for ICSP if you are doing that)
And essentially you're doing the same thing except you have to track the transitions of A and B with an non-vol variable. Every time RB changes, you'll get an IRQ. So A will cause IRQ's with every transition and so will B. You'll get 4x the number of interrupts.
Does this help?
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
scottc
Joined: 16 Aug 2010 Posts: 95
|
|
Posted: Tue Aug 17, 2010 12:01 am |
|
|
Hi Ben, I tried the ISR using RB0 for the cheat, it does work but even
running the pic at 8mhz I miss counts if I twist the encoder fairly fast.
I think part of the problem is that the encoder has jitter when its turned
so sometimes the pulse might not be optimal or it can get in a transition
state between indents thats neither on or off.
I was looking at that LSI LS7183 part and it appears to be designed just for the sole function of interfacing to a mechanical encoder. It would be
interesting to know if anyone on here used it with success.
Thanks Scott |
|
|
jfarkas
Joined: 07 Dec 2006 Posts: 4 Location: Croatia
|
|
Posted: Tue Aug 17, 2010 2:02 am |
|
|
Try to change pullup from 47k to 4,7k.. |
|
|
jbmiller
Joined: 07 Oct 2006 Posts: 73 Location: Greensville,Ontario
|
|
Posted: Tue Aug 17, 2010 5:15 am |
|
|
I've used a lot of the US Digital products in the past for precise servos.Their shaft encoder design is far superior to that of HP. I've used the quadrature encoder/counter chips to 'offload' the PICs,allowing the PIC to do more-faster.Also you can preset the counter to flag you when nearing the limit of your servo(saves time in checking...am I there yet ?, am I there yet? )
hth
jay |
|
|
andrewg
Joined: 17 Aug 2005 Posts: 316 Location: Perth, Western Australia
|
|
Posted: Tue Aug 17, 2010 7:31 am |
|
|
I've not used it, but the 18F4331/4431 has a quadrature encoder interface built in, and has a similar pinout to the 16F887. _________________ Andrew |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Tue Aug 17, 2010 8:34 am |
|
|
andrewg wrote: | I've not used it, but the 18F4331/4431 has a quadrature encoder interface built in, and has a similar pinout to the 16F887. |
Yes it does.. I haven't used it either yet, but it looks nice.
I don't know why most new designs aren't using the 18F's. They're quite nice. _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Tue Aug 17, 2010 9:16 am |
|
|
Why send the encoder outputs to Port C where you need pullup resistors, as opposed to Port B where you don't?
What people have recommended in the past is to read the encoder in an interrupt controlled by a timer. Then combine the encoder's present and previous states to make up a 4-bit number, and use that in a lookup table to decide what to do.
Code: |
// This happens in a timer-based interrupt
encoder = ((encoder << 2) + (portb & 3)) & 0xF;
position += lookup[encoder];
|
The lookup[] array would need 16 elements, which have the value +1, -1 or 0 depending on which direction the encoder moved, with 0 if new and old states are the same, obviously. |
|
|
scottc
Joined: 16 Aug 2010 Posts: 95
|
|
Posted: Tue Aug 17, 2010 9:38 am |
|
|
Hi , John and others that have responded. Well I tried reducing the 47k pull-up to 4K7, but that did not make much of a difference.
John on that code snip, can you expand on it to include a complete listing.
I think it might be worth giving it a shot before I go the road of a off board decoder chip.
Port B does have pullups but I am using port B for other things like driving a LCD display in 4-bit mode.
I used port C for the encoder pins because I had free pins there.
Earlier this-morning I hooked up the encoder to a scope to look at
the waveform while turning the shaft. This thing even with pullups is
very glitchy, In looking at the waveform I think the real issue is the
actual encoder can give a bad pulse to the pins on the pic.
I certainly think the cpu is plenty fast, but I am guessing its only going
to decode correctly if the input to its pins are presenting the right info.
Or a clean signal. I will try decouple the pins with a cap to see if that cleans things up a bit.
Thanks for the advice guys
Scott |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Tue Aug 17, 2010 11:00 am |
|
|
scottc wrote: |
I certainly think the cpu is plenty fast, but I am guessing its only going
to decode correctly if the input to its pins are presenting the right info.
Or a clean signal. I will try decouple the pins with a cap to see if that cleans things up a bit.
|
A decouple cap might be an easy cheat... If you are getting bouncy transitions, that will definitely be a problem.
If you have a timer running already, you could use the timer value inside the ISR handling the shaft to either take a value or dump out if sufficient time hasn't elapsed. (DON'T USE ANY of the DELAY_?S() routines in the ISR!)
Cheers,
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
scottc
Joined: 16 Aug 2010 Posts: 95
|
|
Posted: Tue Aug 17, 2010 11:26 am |
|
|
Ben, a 10nf cap seems to help with the transitons.
I am not running a timer or any ISR at the moment. I guess I am stuck
on why I even have to use an interrupt for this mechanical encoder.
The krux of this is I believe is really presenting either a 1 or a zero to
the port pins on the pic. If I interface the encoder directly and use
pull-ups then the only transition will be logic high to low on either pin.
From a hardware perspective both pins wll be logic 1 right out the gate.
I dont think this is good because I have to determine in code if either
pin went High to low and then calculate what to do.
Im looking at another aproach using a 74HC74 FlipFlop to decode the
encoder before the pic as I dont have the lsi chip at hand. I think this
may work good because it will take care of the direction problem so
that does not have to be done in code.
In essence the logic transition to the port pins will be Low to high, but
both pins will not go low to high at the same time. I can then just create
code for the encoder if it was a simple button connected to a port pin.
It will be interesting to see how this aproach turns out.
Thanks Scott |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Tue Aug 17, 2010 12:41 pm |
|
|
scottc wrote: | Ben, a 10nf cap seems to help with the transitons.
I am not running a timer or any ISR at the moment. I guess I am stuck
on why I even have to use an interrupt for this mechanical encoder.
|
It's the difference between polling vs. interrupt driven routines.
You can either poll for that transition as fast as the pic can go (doing nothing else BUT) or you can have lots of time to do other things and let the ISR service the event (the transition) when it happens.
ALso, with RB_INT, you get a little more definition of what an event IS. 0->1 or 1->0 which is nice.
Otherwise, as typical with software polling, if the CPU is doing something else, it might miss the event. And that's bad.
You have to determine how busy this PIC might be doing those other things while NOT polling the shaft encoder and whether it might miss an event.
Quote: |
The krux of this is I believe is really presenting either a 1 or a zero to
the port pins on the pic. If I interface the encoder directly and use
pull-ups then the only transition will be logic high to low on either pin.
|
They won't alwys transition from 0 to 1. Eventually as you turn them, they go back to 0 and then back to 1...
Quote: |
From a hardware perspective both pins wll be logic 1 right out the gate.
I dont think this is good because I have to determine in code if either
pin went High to low and then calculate what to do.
Im looking at another aproach using a 74HC74 FlipFlop to decode the
encoder before the pic as I dont have the lsi chip at hand. I think this
may work good because it will take care of the direction problem so
that does not have to be done in code.
|
They shouldn't. Normally a quadrature encoder starts up with signals that reflect it's position. Is your device not doing this?
And yes, that's right. You keep a variable around (maybe called last_reading) that allows you to track the last capture from the inputs.
I wrote a shaft decoder in verilog once (that was fun) and the same thing needs to be done in hardware (so to speak).
Here's a great appnote from HoneyWell about quad decoders...
http://www.benjammin.net/~bkamen/pdf/honeywell-quad-encode-decode.pdf
It has flowcharts and some circuitry you might be interested in.
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
scottc
Joined: 16 Aug 2010 Posts: 95
|
|
Posted: Tue Aug 17, 2010 1:10 pm |
|
|
Hi John, thanks for the link. Looking at the code its interesting how
it was designed to compare the actual encoder states to a memory map.
Kind of cool, Kudos to the guy that developed that. I also found here on the CCS board, BTW This board is way cool a port of that code.
I think the code can do its job, but and the "But will allways" get ya, I bet
that it will have difficulty in keeping up if the encoder is turned very fast.
I'm also betting that if the encoder has slippage then that routine will be
out to lunch and miss a count if the encoder gets in a in-between state.
Its possible to do a Software routine in CCS using the rb0 int on change function for example we could connect our encoder to port b pins 0 and
3.
When the Interrupt hits we look at pin 3 and increment or decrement a
value.
This works, but it is prone to encoder slippage. Also the ISR is not running
at the full cpu speed, from memory I think its divided by 4
Code: |
#byte port_b = 0x06
#BIT ENCA = PORT_B.0 // Encoder Phase A
#BIT ENCB = PORT_B.3 // Encoder Phase B
int16 Value;
int16 Tmp;
---------------------------------------------------------------------
#INT_EXT
void encoder(void) // Change on falling edge of RB
{
if(Tmp == TRUE)
{
if( ENCB == 1 )
++Value;
}
else
{
--Value;
}
}
void Enable_ISR(void){
enable_interrupts(GLOBAL);
enable_interrupts(INT_EXT);
ext_int_edge(L_TO_H);
}
Void Disable_ISR(void){
disable_interrupts(GLOBAL);
disable_interrupts(INT_EXT);
}
void main(Void){
Disable_ISR();
Enable_ISR();
while (true){
encoder();
}
}
|
I had used this code in another design, and found that updating the LCD
display with the changing "int 16 value" was somewhat slow, what I mean is, the user interaction felt clunky if looking at the display while turning the encoder by hand. The cpu was running at 4Mhz back then.
Even running the code above, it was possible to get slippage if the encoder
was spun fairly fast. There is also a couple of hardware limitations to think about as well, mainly the actual encoder transition states not to mention
an external ESD zap at the encoder wheel.
So for this design, I think the best solution is to get that encoder logic
squared away first so the states of the encoder output are something
that the CPU will like.
Over lunch I hit up the Microchip site and found a good app note titled
AN718
There is a schematic of a encoder interface using 2 external chips, a 74hc14 and a 74hc74 flip flop. Looks like they pre filter the encoder with
the 74hc14, kind of clever really as that will get rid of encoder transition spikes etc and then pump that logic to the flip/flop.
I am breadbording this to see how it works, I bet this will be the big
kahuna to resolve pic / mechanical encoder interface issues going forward.
Thanks Scott |
|
|
scottc
Joined: 16 Aug 2010 Posts: 95
|
|
Posted: Tue Aug 17, 2010 4:46 pm |
|
|
Ben, thanks for the insight and link to the PDF,
I will check it out
Best Scott |
|
|
|