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

Mechanical rotary encoder routine woes :)

 
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

Mechanical rotary encoder routine woes :)
PostPosted: Fri Sep 10, 2010 3:19 am     Reply with quote

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: 19350

View user's profile Send private message

PostPosted: Fri Sep 10, 2010 4:51 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Sep 10, 2010 5:23 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Sep 10, 2010 7:07 am     Reply with quote

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: 19350

View user's profile Send private message

PostPosted: Fri Sep 10, 2010 8:56 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Sep 10, 2010 9:54 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Sep 10, 2010 2:56 pm     Reply with quote

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 Smile

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 Smile

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

View user's profile Send private message

PostPosted: Sun Nov 27, 2011 9:19 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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