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 CCS Technical Support

Encoder problem (one more) - solved
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
mdemuth



Joined: 16 Apr 2007
Posts: 71
Location: Stuttgart, Germany

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

Encoder problem (one more) - solved
PostPosted: Wed Jun 12, 2013 3:39 am     Reply with quote

Hello,
I am using the following piece of code to read out an mechanical encoder within an IRQ :

Code:
void read_position()
   {
   const signed int table[16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};    // 16  bit-codes that are allowed
   old=old<<2;         // shift old data 2 places to the left
   if (input(SPUR_A)) bit_set(new,0); // look on A
   else bit_clear(new,0);
   if (input(SPUR_B)) bit_set(new,1);       // look on B
   else bit_clear(new,1);
   old |=(new & 0x3);      // or the old with the new bits
   position += table [(old & 0xf)];    // take the action from the table above
   if (position < 7)  // 4 state changes between two clicks
      {
      encoder_value_irq++;
      position=10;
      }   
   if (position > 13) // 4 state changes between two clicks
      {
      encoder_value_irq--;
      position=10;
      }
   }


So far so good. It works fine with all types of encoders.

But now I found the one new type of encoder I am going to use for a human input application has not a stable and defined A and B state in the mechanical detent position.
This is clear because this code (only) works incrementally.
The problem now is: If the encoder is not in a detent position at the start, the "click" (+/-encoder_value_irq) will not refer to the mechanical detent position.

Thoughts and suggestions are welcome!


Last edited by mdemuth on Mon Jun 17, 2013 2:46 am; edited 3 times in total
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Wed Jun 12, 2013 5:19 am     Reply with quote

I agree that the decoder table QEI should work as long as the position doesn't jump more than one increment
back and forth between two interrupts. It can be driven either by input change interrupt or a sufficient fast timer
tic in cases where port B isn't available for QEI.

The initial position must be set by an incremental encoder anyway. If it isn't done in an dedicated initialization
procedure, using a position reference, either a meachnical stop or an index pulse, the position must be
initialized on the first "click".
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Wed Jun 12, 2013 5:31 am     Reply with quote

Based on your comment 'works with all encoders...' you do not have a code issue.

Problem can be the cheap 'new' encoder you've chosen. Without knowng the mfr/mdl/etc. there could be excess grease in the encoder,poor gold plating on the switch contacts,light spring tension on contacts, or ???
Also , what's the MINIMUM current required for the encoder? It's a mfr's spec often overlooked.Encoder might need 5ma..you've setup for .1ma!
Signal conditioning? Not only do you need the proper pullup,but based on distance,speed, wiring may need caps or interface buffers to supply proper logic levels to the PIC.
Hmm...what pins of the PIC are you using? TTL, ST,??

I'm thinking you're using 16 or 24 position mechanical encoders costing 1-2$.If this is going to be a commercial product or you want reliablity, consider optical encoders, 5x the price, 100x better !!

hth
jay
mdemuth



Joined: 16 Apr 2007
Posts: 71
Location: Stuttgart, Germany

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

PostPosted: Wed Jun 12, 2013 5:36 am     Reply with quote

Thanks for the input.
The µC is fast enough - This time I am using int_rb (port change). In many cases it is sufficient to poll within a TMR-IRQ.

The encoder (COPAL REC20D-50-201-1) is OK, it is has optically generated signals, no electrical problems.

Now I have improved my procedure:

Code:
void read_position()
{
const signed int table[16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; // 16 bit-codes that are allowed
old=old<<2; // shift old data 2 places to the left
if (input(SPUR_A)) bit_set(new,0); // look on A
else bit_clear(new,0);
if (input(SPUR_B)) bit_set(new,1); // look on B
else bit_clear(new,1);
// NEW:
if (!encoder_startseq) // Index setzen
      {
      if (new == 0b00000000 && encoder_offset==0) encoder_startseq=1;
      if (new == 0b00000001 && encoder_offset==1) encoder_startseq=1;
      if (new == 0b00000010 && encoder_offset==2) encoder_startseq=1;
      if (new == 0b00000011 && encoder_offset==3) encoder_startseq=1;
      }
 else
     {
     old |=(new & 0x3); // or the old with the new bits
     position += table [(old & 0xf)]; // take the action from the table above
     if (position < 7) // 4 state changes between two clicks
         {
        encoder_value_irq++;
        position=10;
        }
    if (position > 13) // 4 state changes between two clicks
       {
       encoder_value_irq--;
       position=10;
       }
    }
}

I also added some lines to determine the encoder_offset.
When the unit is put into operation the encoder must the in the detent position:

Code:
if (read_EEPROM (0x10)==0xff) // determine encoder offset
  {
  if (!input(spur_A) && !input(spur_B)) write_eeprom(0x10,0x00);
  if ( input(spur_A) && !input(spur_B)) write_eeprom(0x10,0x01);
  if (!input(spur_A) &&  input(spur_B)) write_eeprom(0x10,0x02);
  if ( input(spur_A) &&  input(spur_B)) write_eeprom(0x10,0x03);
  }
delay_us(100);
encoder_offset= read_eeprom(0x10); // read back encoder offset

If call the function read_position() before activating the IRQ and the encoder is in the detent-position which is for 99% of the case true, the position reading works great.

Asuming the device is in detent position at startup, you can make it a little easier:
If the detent position is not A=0 and B=0 the function must be called within the startup process and then set position=10.
mdemuth



Joined: 16 Apr 2007
Posts: 71
Location: Stuttgart, Germany

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

Still another open problem
PostPosted: Thu Jun 13, 2013 6:53 am     Reply with quote

I found out that most of these copal enocoders have 4 state changes going from each detent position from the next. Very Happy
I also found out that some of the copal encoders have sometimes 3 and sometimes 5 state changes from each detent position from the next. Crying or Very sad

No solution for this problem yet...anybody an idea?
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Jun 13, 2013 7:20 am     Reply with quote

please post a link to the make/model on the encoder so we can grab a
datasheet
mdemuth



Joined: 16 Apr 2007
Posts: 71
Location: Stuttgart, Germany

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

PostPosted: Thu Jun 13, 2013 7:22 am     Reply with quote

http://copal-electronics.info/en/CopalElectronics/encoders_en/rec&res_en.pdf
oxo



Joined: 13 Nov 2012
Posts: 219
Location: France

View user's profile Send private message

Re: Still another open problem
PostPosted: Thu Jun 13, 2013 7:56 am     Reply with quote

mdemuth wrote:
I found out that most of these copal enocoders have 4 state changes going from each detent position from the next. Very Happy
I also found out that some of the copal encoders have sometimes 3 and sometimes 5 state changes from each detent position from the next. Crying or Very sad

No solution for this problem yet...anybody an idea?


There is no solution. It's how the encoders are built.
mdemuth



Joined: 16 Apr 2007
Posts: 71
Location: Stuttgart, Germany

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

PostPosted: Thu Jun 13, 2013 8:07 am     Reply with quote

I cannot quite believe this, the encoder is a Japanese standard product sold over millions of times.
The main problem is that the detent position is not clearly defined.
If I pick one controller where the detent position is right on a state-change position the state changes from one to the next position is sometimes 3 and sometimes 5.
I increment or decrement after 4 state changes (position = 14 or position = 6). This is here a problem.
I think increment (or decrement) enocder_value at (position = 13 or position = 7). But still set back position to 10 at 14 or 6.
Could this solve the problem? And if so how could the code look like?
(all my attempts failed so far...)
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Thu Jun 13, 2013 8:35 am     Reply with quote

OK.
First thing, what are you using as the interface to the PIC input?. These units require a 5KR pull-up resistor. The output amplifier can oscillate if this is not present. I prefer the units without the built in amplifier, then use a comparator, with a lot of hysteresis. You'll get much cleaner results....

There is a fundamental problem with your code. Since it is dependant on being called at least as often as the encoder changes. If the encoder is reversed momentarily, you can get a group of pulses much closer together than the standard waveform, and the logic then fails, if the reader is not called often enough.
Use an interrupt instead. INT_RB.
Code:

#byte portb=getenv("SFR:PORTB")
signed int16 position=0;

#int_rb
void quad(void)
{
   //quadrature decoder on RB4/RB5
   static int old;
   static int new;
   static int value;
   //Here I have an edge on one of the quadrature inputs
   new=portb;
   //Now I have to decode the quadrature changes. There are four possibilities:
   //I can have a rising or falling edge, on each of the two inputs. I have to
   //look at the state of the other bit, and increment/decrement according to
   //this.
   value=new^old;
   if ((value & 0x30)==0) return; //ignore B6 & B7
   //'value', now has the bit set, which has changed
   if (value & 0x10) {
      //Here the low bit has changed
      if (new & 0x10) {
         //Here a rising edge on A
         if (new & 0x20) --position;
         else ++position;
      }
      else {
         //Here a falling edge on A
         if (new & 0x20) ++position;
         else --position;
      }
   }
   else {
      //Here the high bit (B) must have changed
      if (new & 0x20) {
         //Here a rising edge on B
         if (new & 0x10) ++position;
         else --position;
      }
      else {
         //Here a falling edge on B
         if (new & 0x10) --position;
         else ++position;
      }
   }
   old=new;
}


If the detent, is not at the zero count, then just store 'position' in the EEPROM, and retrieve this before starting the interrupt.
Since this directly accesses the input bits, you need to have set TRIS to 0bxx11xxxx before using this.

It actually takes significantly _longer_ to access a const table, than to just do the tests like this. Also there is extra time involved in casting the table values 'up' to the larger 'position' type, and performing addition, rather than just incrementing/decrementing.

Best Wishes


Last edited by Ttelmah on Thu Jun 13, 2013 8:54 am; edited 1 time in total
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Thu Jun 13, 2013 8:40 am     Reply with quote

w/o using CCS delay functions - admittedly crude...
note: in my case -this assumes that B0 - B1 have encoder data
Code:

// this is UGLY but ....
// inside the B change int handler
//
unsigned int8 x,t=255;
byte bv,bx;
bv=input_b()&0b00000011; // first reference read of encoder
while (t){
      x=255;

      while (x--){}; // short processor delay see .LST file to calulate
      bx=input_b()&0b00000011;  // re read
      if (bx==bv) t=0; // did the read change or not during X delay
      else --t;
      bv=bx; // bump the ref byte to new value
} // end while t
// BX[0:1]  now has a 'more' stable encoder read


re the COpal unit:
IF it is being turned by human hands and is NOT a motor driven rotating shaft encoder - i would add a .1uf capacitor between the white/green output terminals and GROUND to create an appx 200usec external glitch reduction helper , if the software - re-read method above is inadequate by itself
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Thu Jun 13, 2013 8:59 am     Reply with quote

Worth remembering that the actual 'read' in the handler, will be about 30 instructions _after_ the interrupt actually triggered. This should be enough delay on it's own, without adding more. By returning if there is no change on the 'used' bits, from the last decoded state; momentary glitches are ignored.

Also, 90% of PIC's only support 'B changed' on RB4 to RB7. We haven't been told what processor is involved, hence I have assumed B4 and b5 are used.

Best Wishes
mdemuth



Joined: 16 Apr 2007
Posts: 71
Location: Stuttgart, Germany

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

PostPosted: Thu Jun 13, 2013 9:00 am     Reply with quote

Thanks for the input.
PIC18F6390 running with 32MHZ.
The outputs do have the pull-up and are connected to RB6 and RB7.
The signals are clear and have no jitter (on fast or slow movements).
The function is triggered on the #int_rb.
There are no missing pulses looking at the raw data "position" even at fast spins. I am not loosing any pulse.
Code:

void read_position()
{
const signed int table[16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; // 16 bit-codes that are allowed
old=old<<2; // shift old data 2 places to the left
if (input(SPUR_A)) bit_set(new,0); // look on A
else bit_clear(new,0);
if (input(SPUR_B)) bit_set(new,1); // look on B
else bit_clear(new,1);
old |=(new & 0x3); // or the old with the new bits
position += table [(old & 0xf)]; // take the action from the table above
}


The problem is to go from X1 (position) to X4 (encoder_value_irq) having not always 4 changes in state (sometimes 3, sometimes 5, just 4 in the average).
The electrical state at the detent position over one revolution are not always the same.
The value displayed (encoder_value_irq) should change when going from one into the next detent position.
This following section is the cruel part:
Code:

if (position > 13)
     {
     position=10;
     encoder_value_irq--;
     }


   if (position < 7)
     {
     position=10;
     encoder_value_irq++;
     }


@ I will test the suggested code for advantages...Thanks for that!
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Thu Jun 13, 2013 9:14 am     Reply with quote

Are you using any other interrupts?.
I'd be strongly suspicious, that if so, you are taking too long in the handler, and as a result one or more states is getting missed. These encoders if properly loaded, are normally 100% reliable on their state changes.

Best Wishes
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Jun 13, 2013 9:28 am     Reply with quote

There are several versions of that encoder, so which part number are you using as the interface is not the same for all of them.

The 'click spot' is also different depending on the model.

The good news is they are very low count encoders so an easy solution can be found, once we have better info.

hth
jay
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