|
|
View previous topic :: View next topic |
Author |
Message |
mdemuth
Joined: 16 Apr 2007 Posts: 71 Location: Stuttgart, Germany
|
Encoder problem (one more) - solved |
Posted: Wed Jun 12, 2013 3:39 am |
|
|
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
|
|
Posted: Wed Jun 12, 2013 5:19 am |
|
|
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
|
|
Posted: Wed Jun 12, 2013 5:31 am |
|
|
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
|
|
Posted: Wed Jun 12, 2013 5:36 am |
|
|
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
|
Still another open problem |
Posted: Thu Jun 13, 2013 6:53 am |
|
|
I found out that most of these copal enocoders have 4 state changes going from each detent position from the next.
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.
No solution for this problem yet...anybody an idea? |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Thu Jun 13, 2013 7:20 am |
|
|
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
|
|
|
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
Re: Still another open problem |
Posted: Thu Jun 13, 2013 7:56 am |
|
|
mdemuth wrote: | I found out that most of these copal enocoders have 4 state changes going from each detent position from the next.
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.
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
|
|
Posted: Thu Jun 13, 2013 8:07 am |
|
|
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
|
|
Posted: Thu Jun 13, 2013 8:35 am |
|
|
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
|
|
Posted: Thu Jun 13, 2013 8:40 am |
|
|
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
|
|
Posted: Thu Jun 13, 2013 8:59 am |
|
|
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
|
|
Posted: Thu Jun 13, 2013 9:00 am |
|
|
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
|
|
Posted: Thu Jun 13, 2013 9:14 am |
|
|
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
|
|
Posted: Thu Jun 13, 2013 9:28 am |
|
|
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 |
|
|
|
|
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
|