QEI_hw interface implementation notes

 

The QEI_hw library that is found on my Libraries page requires hardware modification of the mbed module. The usual disclaimer applies : modifying the mbed will void the warranty and, if you make a mistake, you could end up with permanent damage. So proceed with caution!

The rotary encoder must be connected to nodes p1.20 and p1.23. These nodes are also used to connect LED2 and LED4, respectively, on the mbed module.

The necessary hookup for a simple mechanical panel encoder is shown in the following schematic diagram:

 

From mbed

In deciding how to hook up the encoder, I took care to minimize the chance of overloading the mbed should the user inadvertently use LED2 or LED4 when the encoder is connected. So the encoder does not directly switch p1.20 or p1.23 to either ground or Vout. Had that been done, the port would see a short circuit when trying to turn the port pins on or off, respectively.

The encoder pulls the port pins up through 330 ohm series resistors to +3.3 volts. When the encoder breaks contact, the port pins are pulled down passively by 1K ohm resistors to ground. This scheme works well for panel encoders turned by humans. For very high-speed encoders, as might be found on motor shafts, the passive pull down may be too slow; in that case an active interface would be preferred.

While not for the novice, the mods are feasible for anyone who has access to a few modern tools.

Tools required:

  • A low-wattage, temperature controlled soldering iron with a fine tip (suitable for working with surface-mount devices). A Weller WES51 is one example.
  • Very fine insulated wire. 30 AWG (0.05 mm2 ) wire-wrap wire should work.
  • Needle nose pliers, diagonal cutters, wire-stripper, solder, magnifying glass, etc.

Modification steps:

  • Place the mbed module top down on the work surface. An anti-static mat is suggested.
  • Locate the two gold-plated holes shown in the following photo:

 

From mbed

 

  • Solder one wire to each hole.
  • Connect the 1K ohm pull-down resistors, and the 330 ohm series resistors to the end of each wire, as shown in the schematic.
  • Connect the other end of each series resistor to one of the encoder phase terminals. It really doesn't matter which one, as you can adjust the sense of rotation with the direction invert parameter of the initialization method.
  • Connect the common terminal of the encoder to +3.3 volts.

Here is a picture after the wires have been soldered to the mbed:

 

From mbed

You are now good to go.


12 comments

21 Jan 2011

Great work!

This is just what I was looking for for use with high speed motor encoders - Thanks

21 Jan 2011

Yes great work.  I took the 2 and 4 LED off my board with hopes to solder directly to the pads.  I dont want the dual functionality so hopefully wont need all the pullup junk and an just connect my encoder as I do on the interrupt based QEI example.

 

21 Jan 2011 . Edited: 21 Jan 2011

 

user Matt Clement wrote:

Yes great work.  I took the 2 and 4 LED off my board with hopes to solder directly to the pads.  I dont want the dual functionality so hopefully wont need all the pullup junk and an just connect my encoder as I do on the interrupt based QEI example.

 

The PhA and PhB inputs are configured for "No Resistor", so they do not pull up or pull down on their own. I think the interrupt-based QEI example has default pull-down on its pins. So you might need to add a pull down resistor to your encoder when you switch to the hardware driver.

 

Or, just wire it up as it shows above (leaving out the two 330 ohm resistors) and don't worry about it :-)

21 Jan 2011

Is there a way with the hardware inputs to specify internal pull up/downs?  I would like to not have external components if possible.

Matt

 

21 Jan 2011

user Matt Clement wrote:

Is there a way with the hardware inputs to specify internal pull up/downs?  I would like to not have external components if possible.

Call the mode function on the DigitalIn object (works also for interrupt pins).

21 Jan 2011
user Matt Clement wrote:

Is there a way with the hardware inputs to specify internal pull up/downs?  I would like to not have external components if possible.

Matt

 

The qei_hw driver sets the pin modes in the initialization module:

00028 QEIHW::QEIHW(uint32_t _dirinv, uint32_t _sigmode, uint32_t _capmode, uint32_t _invinx)
00029 {
. . .
00041     // MCI0 (PhA)
00042     LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & PINSEL3_MCI0_MASK) | PINSEL3_MCI0 ;
00043     LPC_PINCON->PINMODE3 = (LPC_PINCON->PINMODE3 & PINMODE3_MCI0_MASK) | PINMODE3_MCI0;
00044
00045     // MCI1 (PhB)
00046     LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & PINSEL3_MCI1_MASK) | PINSEL3_MCI1 ;
00047     LPC_PINCON->PINMODE3 = (LPC_PINCON->PINMODE3 & PINMODE3_MCI1_MASK) | PINMODE3_MCI1;
00048
00049     // MCI2 (Index)
00050     LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & PINSEL3_MCI2_MASK) | PINSEL3_MCI2 ;
00051     LPC_PINCON->PINMODE3 = (LPC_PINCON->PINMODE3 & PINMODE3_MCI2_MASK) | PINMODE3_MCI2;

The pin modes are defined in qeihw.h

00599 /*********************************************************************//**
00600  * Macro defines for PINSEL3 register QEI-related bits
00601  **********************************************************************/
00602 #define PINSEL3_MCI0                ((uint32_t)(1<<8))     /**< MCIO (PhA) pin select */
00603 #define PINSEL3_MCI0_MASK          ~((uint32_t)(3<<8))     /**< MCIO (PhA) pin mask */
00604 #define PINSEL3_MCI1                ((uint32_t)(1<<14))    /**< MCI1 (PhB) pin select */
00605 #define PINSEL3_MCI1_MASK          ~((uint32_t)(3<<14))    /**< MCI2 (PhB) pin mask */
00606 #define PINSEL3_MCI2                ((uint32_t)(1<<16))    /**< MCI2 (Index) pin select */
00607 #define PINSEL3_MCI2_MASK          ~((uint32_t)(3<<16))    /**< MCI2 (Index) pin mask */
00608 
00609 /*********************************************************************//**
00610  * Macro defines for PINMODE3 register QEI-related bits
00611  **********************************************************************/
00612 #define PIN_PULL_UP                     0UL
00613 #define PIN_REPEATER                    1UL
00614 #define PIN_NORESISTOR                  2UL
00615 #define PIN_PULL_DOWN                   3UL     
00616 
00617 #define PINMODE3_MCI0                ((uint32_t)(PIN_NORESISTOR<<8))     /**< MCIO (PhA) resistor selection */
00618 #define PINMODE3_GPIO1p20            ((uint32_t)(PIN_PULL_DOWN<<8))      /**< GPIO 1.20) resistor selection */
00619 #define PINMODE3_MCI0_MASK          ~((uint32_t)(3<<8))                  /**< MCIO (PhA) resistor mask */
00620 
00621 #define PINMODE3_MCI1                ((uint32_t)(PIN_NORESISTOR<<14))    /**< MCI1 (PhB) resistor selection */
00622 #define PINMODE3_GPIO1p23            ((uint32_t)(PIN_PULL_DOWN<<14))      /**< GPIO 1.23) resistor selection */
00623 #define PINMODE3_MCI1_MASK          ~((uint32_t)(3<<14))                 /**< MCI1 (PhB) resistor mask */
00624 
00625 #define PINMODE3_MCI2                ((uint32_t)(PIN_PULL_UP<<16))       /**< MCI2 (Index) resistor selection */
00626 #define PINMODE3_GPIO1p24            ((uint32_t)(PIN_PULL_DOWN<<16))      /**< GPIO 1.24) resistor selection */
00627 #define PINMODE3_MCI2_MASK          ~((uint32_t)(3<<16))                 /**< MCI2 (Index) resistor mask */

You could add to your own program some statements to override the pin mode. Be sure to do it after the initialization, of course. Something like this should work:

 

// Start by initializing the driver

QEIHW qei(QEI_DIRINV_CMPL, QEI_SIGNALMODE_QUAD, QEI_CAPMODE_2X, QEI_INVINX_NONE );

// Then redefine the pin modes as you like.

#define MY_PIN_MODE_PUP 00 // Pull up

#define MY_PIN_MODE_RPT 01 // Repeater mode

#define MY_PIN_MODE_NOR 02 // Neither pull up nor pull down

#define MY_PIN_MODE_PDN 03 // Pull down

// Configure PhA for pull up

LPC_PINCON->PINMODE3 = (LPC_PINCON->PINMODE3 & ~((uint32_t)(3<<8)) | ((uint32_t)( MY_PIN_MODE_PUP <<8));

// Configure PhB for pull up

LPC_PINCON->PINMODE3 = (LPC_PINCON->PINMODE3 & ~((uint32_t)(3<<14)) | ((uint32_t)( MY_PIN_MODE_PUP <<14));

// Configure Index for pull up

LPC_PINCON->PINMODE3 = (LPC_PINCON->PINMODE3 & ~((uint32_t)(3<<16)) | ((uint32_t)( MY_PIN_MODE_PUP <<16));

// Rest of program follows

- - -

I haven’t compiled this, so you may need to tweak it a bit. But it should illustrate the concept.

Good luck.

04 Oct 2011 . Edited: 04 Oct 2011

 

Hello,

thanks for that great work, it saved me a lot of time.

I managed to hook up also the index pin to the mbed

GetIndex() is counting each time the index hit's.

Now I tried to add an ISR using AppendISR.

mbed jumps into the ISR but stalls there.

Is ther something to special care about or anything I'm missing?

here the code I use.

 

// Display changes in encoder position and direction
#include "mbed.h"
#include "qeihw.h"

DigitalOut led1(LED1);
DigitalOut led3(LED3);
QEIHW qei(QEI_DIRINV_NONE, QEI_SIGNALMODE_QUAD, QEI_CAPMODE_2X, QEI_INVINX_NONE );


void toggleIndexLed(void) {
led3 = !led3;
}

int main() {
int32_t temp, position = 0;
qei.SetDigiFilter(480UL);
qei.SetMaxPosition(1024);
qei.AppendISR(QEI_INTFLAG_INX_Int, &toggleIndexLed);

qei.IntCmd(QEI_INTFLAG_INX_Int, ENABLE);

while (1) {
while (position == (temp = qei.GetPosition()) );
position = temp;
printf("New position = %d. idx : %d\r\n", temp, qei.GetIndex());
led1 = qei.Direction() == SET ? 1 : 0;
//         led3 = !led1;
wait(0.1);
}
}

 

 

06 Oct 2011 . Edited: 06 Oct 2011

 

Now I figured out what happens.

qei.IntCmd(QEI_INTFLAG_INX_Int, ENABLE);

makes the system sensitive for the Index interrupt and jumps correctly in the ISR assigned by AppendISR().

but on occuring IRQ there are more bit's set in  LPC_QEI->QEIINTSTAT.

In my case the 3 PositionCompare Flags are set too, because compare register is 0.

So, the PositionCompare IRQ's has no ISR and bits are not cleared, => system stalls.

So, my workaround is to clear all that bits in LPC_QEI->QEIINTSTAT, and it works.

I do that in hexley's  QEIHW::Qeiisr(void)

by adding

IntClear( ((uint32_t)(1<<i)));

after calling the ISR.

I don't know if this is the right way, but it works for me.

Ju

 

 

 

 

 

 

 

thank you very much  for your post. It sloved lot of problems we faced and saved money.

We were planing to buy a QEI counter IC becaue QEI library didnt enough for our work.

we used 5000ppr incremental encoder with QEI and it was reliable only up to 60 rpm as mbed get 20000pps.

There are two encoders for two motors which are  running at same time

But using QEIHW we just check up to 700 rpm and it was more enough for the work.

Great Work !!!

04 Nov 2017
tested it works correctly thx
04 Nov 2017 . Edited: 05 Nov 2017
1000000 cycles witout loose pulses thx
15 Mar 2018
I'm looking to use this for motion control. Could you elaborate on what you mean by "active interface"? What changes would I need to make to what you've described to allow for a high speed/resolution encoder? Thanks

You need to log in to post a comment