CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Interfacing Mechanical Rotary encoder to the pic
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
scottc



Joined: 16 Aug 2010
Posts: 95

View user's profile Send private message

Interfacing Mechanical Rotary encoder to the pic
PostPosted: Mon Aug 16, 2010 6:58 pm     Reply with quote

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: 1611
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Mon Aug 16, 2010 11:11 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 12:01 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 2:02 am     Reply with quote

Try to change pullup from 47k to 4,7k..
jbmiller



Joined: 07 Oct 2006
Posts: 73
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 5:15 am     Reply with quote

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

View user's profile Send private message Visit poster's website

PostPosted: Tue Aug 17, 2010 7:31 am     Reply with quote

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: 1611
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 8:34 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 9:16 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 9:38 am     Reply with quote

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: 1611
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 11:00 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 11:26 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 12:12 pm     Reply with quote

Try this--written for an Arduino, but it should be clear what's going on:

http://www.circuitsathome.com/mcu/programming/reading-rotary-encoder-on-arduino
bkamen



Joined: 07 Jan 2004
Posts: 1611
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 12:41 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 1:10 pm     Reply with quote

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 Smile 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

View user's profile Send private message

PostPosted: Tue Aug 17, 2010 4:46 pm     Reply with quote

Ben, thanks for the insight and link to the PDF,

I will check it out

Best Scott
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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