Entire Forum This board This topic Members Entire Site
 Pages: [1]
 Author Topic: QEI modules and high resolution encoders.  (Read 2213 times) 0 Members and 1 Guest are viewing this topic.
RaynW
Newbie

Muted
Offline

Posts: 7

Thank You
-Given: 7

 « on: September 06, 2012, 04:34:06 16:34 »

I want to share with you some piece of code I´ve been working on, in hope it will be useful.

The problem arises when using high resolution encoders with 16bit Quadrature Encoder Modules (QEI). They only count to 2^16 wich is pretty low for a 1024 pulses/rev encoder geared through 1:40 transmission. You have 1024*4*25 = 163840 pulses/rev.

The trick is:
1.- Get the velocity first (or position increment/decrement), from a periodic tick interrupt (the same interrupt you would use to sample the axis position; motion controllers I´ve been working with used 250ms). Here you configure the QEI module to 2^16 modulo no matter your axis modulo. This will work provided the encoder module doesn´t count 2^15 or more pulses in the same direction, between two consecutive ticks; or you will lose the wright direction of rotation. You can refer to te following appnote to know how to get the velocity properly. http://ww1.microchip.com/downloads/en/DeviceDoc/93002A.pdf

2.- Then integrate this velocity over a 32bit counter by software.

3.- If you are working with a rotary axis, it has some unwind or modulo value. It is the number of pulses it counts to reach the same position. The 32bit counter must be adjusted by the unwind value before used by the sampling routine.

4.- If working with linear axis, there´s no need of step 3.

Here the code:
Code:
/* #includes here */

/* Used to get the shortest path on a rotary (cyclic) dimension - Refer to appnote for an explanation */
long shortest_path(long path, long unwind)
{
long h_unwind;

h_unwind = unwind / 2; /* Half the axis unwind */

if (path >= 0) {
if (path >= h_unwind)
path -= unwind;
}
else {
if (path < (-h_unwind))
path += unwind;
}

return path;
}

/* Servo tick - aproximately 250 us */
void __attribute__((interrupt, auto_psv)) _T2Interrupt(void)
{
volatile struct T_axis {
unsigned long pos;
unsigned long unwind;
int vel;
} master;

static unsigned raw_pos_m[2];

/* Samples encoder module - POS2CNT */

/* Calculates velocity - or position increment/decrement */
master.vel = (int)shortest_path((long)raw_pos_m[1] - raw_pos_m[0], POSXCNT_MAX);
raw_pos_m[0] = raw_pos_m[1];

/* Integrates velocity and calculates position */
master.pos += master.vel;

/* Unwinds if necessary */
if (master.pos >= master.unwind) {
if (master.vel >= 0)
master.pos -= master.unwind;
else
master.pos += master.unwind;
}

/* master.pos IS ACTUALLY THE POSITION YOU WANT. You can use it from now on. */

IFS0bits.T2IF = 0;
}

int main(void)
{

/* Modules, Interrupts, IO ports config not shown, but would go here */

master.unwind = 40960; /* Used 1024 PPR encoder with 1:10 gear ratio, so 1024*4*10 = 40960 */

/* Main loop */
while (1) {

ClrWdt();
}

return 0;
}

I really hope you find this code useful. Perhaps something is unclear. Please feel free to criticize.
 Logged
Ichan
Hero Member

Offline

Posts: 837

Thank You
-Given: 312

 « Reply #1 on: September 06, 2012, 06:11:58 18:11 »

That's a good one, but I think it will be easier and simpler using QEI interrupt.

Maintain one 16 bit variable as high value together with 16 bit QEI counter as low value to represent a 32 bit counter register. On QEI overflow increment the variable, and decrement it on QEI underflow.

-ichan
 Logged

There is Gray, not only Black or White.
RaynW
Newbie

Muted
Offline

Posts: 7

Thank You
-Given: 7

 « Reply #2 on: September 06, 2012, 06:52:47 18:52 »

Mmm I´m thinking now. Suppose you need to count to 65537.
1.- interrupt on 65535. and increment 16bit high counter.
2.- must change qei module target to 65537-65535 = 2 so next interrupt is on 2.
3.- must change target again to interrupt on 65535. So must change interrupt condition for every interrupt call.

Besides, this application doesn´t need to interrupt on a specified position. When you need that then I think qei counter alone (and its interrupt are the only way). Here we only need to read axis position at a periodic rate to control the motor driver.
 Logged
 Pages: [1]