I’m migrating to a new board design that replaces the USBizi processor with the G120 module. I’m monitoring a pendant encoder using simple RLP code that will detect the direction of rotation and update a counter value. The same code is used on both designs (besides pin number modifications). I’m seeing an issue with the G120 board, where when the encoder is rotated quickly, it doesn’t seem to be picking up all the edges. I’m using P0.4 and P0.5 to detect the edges on Chan A and B of the encoder.
Are there any differences between the G120 and USBizi that I need to consider in this scenario? The code is working with the G120, as I am getting count changes and the outputs are correctly toggling with direction change, but high speed edges aren’t being counted.
My RLP code is shown below. Let me know if more details are needed. Thanks!
#include "RLP.h"
#define CHAN_A 5 // pins used for pendant encoder
#define CHAN_B 4
#define OUT_13 70
#define OUT_14 73
int counter;
// channel A ISR quadrature decoding
//
// a rising edge when channel B is high means one count in positive direction
// when channel B is low it means a count in the negative direction.
//
// etc
//
void chan_A_ISR(unsigned int Pin, unsigned int PinState, void* Param)
{
if(RLPext->GPIO.ReadPin(CHAN_B))
{
RLPext->GPIO.WritePin(OUT_13, RLP_FALSE);
RLPext->GPIO.WritePin(OUT_14, RLP_TRUE);
counter++;
return;
}
else
{
RLPext->GPIO.WritePin(OUT_13, RLP_TRUE);
RLPext->GPIO.WritePin(OUT_14, RLP_FALSE);
counter--;
return;
}
}
void chan_B_ISR(unsigned int Pin, unsigned int PinState, void* Param)
{
if(RLPext->GPIO.ReadPin(CHAN_A))
{
RLPext->GPIO.WritePin(OUT_13, RLP_TRUE);
RLPext->GPIO.WritePin(OUT_14, RLP_FALSE);
counter--;
return;
}
else
{
RLPext->GPIO.WritePin(OUT_13, RLP_FALSE);
RLPext->GPIO.WritePin(OUT_14, RLP_TRUE);
counter++;
return;
}
}
int initEncoder(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
RLP_InterruptInputPinArgs InputPinArgs;
counter = 0;
InputPinArgs.GlitchFilterEnable = RLP_TRUE;
InputPinArgs.IntEdge = RLP_GPIO_INT_EDGE_HIGH;
InputPinArgs.ResistorState = RLP_GPIO_RESISTOR_PULLUP;
if(!RLPext->GPIO.EnableInterruptInputMode(CHAN_A,&InputPinArgs,chan_A_ISR,(void*) 0)) return 0; // ERROR
if(!RLPext->GPIO.EnableInterruptInputMode(CHAN_B,&InputPinArgs,chan_B_ISR,(void*) 0)) return 0; // ERROR
RLPext->GPIO.EnableOutputMode(OUT_13, RLP_FALSE); // up count diagnostic LED
RLPext->GPIO.EnableOutputMode(OUT_14, RLP_FALSE); // down count diagnostic LED
return 1; // OK
}
int readCounter(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
return counter;
}
Correct. When I do slow ticks on the encoder the count increments as expected, but as you increase speed, the count rate increase to a certain point and drops to a consistently slow rate.
If this was managed code, I would have an explanation. This would involve interrupt queueing.
There is a delay between the hardware interrupt and when the interrupt handler is called. Reading the opposite edge state via a direct read would result in a missing pulse if an interrupt for the opposite edge was queued. I am not sure about interrupt queueing with RLP.
I suggest that you maintain a variable for the A and B states. When an interrupt occurs, update the state of the edge using the passed PinState, and use the stored variable for the other edge state. This will avoid possible problems with pending interrupt race conditions.
Thanks for the suggestion Mike. I tried to implement your recommendation but the result still wasn’t correct. If I went slow things were ok, but if rotated quickly the increments weren’t consistent. Sometimes it would add and sometimes it would subtract even when the rotation direction was the same. My modified RLP code is shown below.
#include "RLP.h"
#define CHAN_A 5 // pins used for pendant encoder
#define CHAN_B 4
#define OUT_13 70
#define OUT_14 73
int counter;
unsigned int a_state = 0;
unsigned int b_state = 0;
// channel A ISR quadrature decoding
//
// a rising edge when channel B is high means one count in positive direction
// when channel B is low it means a count in the negative direction.
//
// etc
//
void chan_A_ISR(unsigned int Pin, unsigned int PinState, void* Param)
{
a_state = PinState;
if (a_state == 1)
{
if(RLPext->GPIO.ReadPin(CHAN_B))
{
RLPext->GPIO.WritePin(OUT_13, RLP_FALSE);
RLPext->GPIO.WritePin(OUT_14, RLP_TRUE);
counter++;
return;
}
else
{
RLPext->GPIO.WritePin(OUT_13, RLP_TRUE);
RLPext->GPIO.WritePin(OUT_14, RLP_FALSE);
counter--;
return;
}
}
}
void chan_B_ISR(unsigned int Pin, unsigned int PinState, void* Param)
{
b_state = PinState;
if (b_state == 1)
{
if(RLPext->GPIO.ReadPin(CHAN_A))
{
RLPext->GPIO.WritePin(OUT_13, RLP_TRUE);
RLPext->GPIO.WritePin(OUT_14, RLP_FALSE);
counter--;
return;
}
else
{
RLPext->GPIO.WritePin(OUT_13, RLP_FALSE);
RLPext->GPIO.WritePin(OUT_14, RLP_TRUE);
counter++;
return;
}
}
}
int initEncoder(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
RLP_InterruptInputPinArgs InputPinArgs;
counter = 0;
InputPinArgs.GlitchFilterEnable = RLP_TRUE;
InputPinArgs.IntEdge = RLP_GPIO_INT_EDGE_BOTH;
InputPinArgs.ResistorState = RLP_GPIO_RESISTOR_PULLUP;
if(!RLPext->GPIO.EnableInterruptInputMode(CHAN_A,&InputPinArgs,chan_A_ISR,(void*) 0)) return 0; // ERROR
if(!RLPext->GPIO.EnableInterruptInputMode(CHAN_B,&InputPinArgs,chan_B_ISR,(void*) 0)) return 0; // ERROR
RLPext->GPIO.EnableOutputMode(OUT_13, RLP_FALSE); // up count diagnostic LED
RLPext->GPIO.EnableOutputMode(OUT_14, RLP_FALSE); // down count diagnostic LED
return 1; // OK
}
int readCounter(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
return counter;
}
My apologies. I tried a lot of different things before responding to the post, and I didn’t grab the right version of the code. This is the code that matches your suggestion. The result that I described in the previous post is still valid for this code.
#include "RLP.h"
#define CHAN_A 5 // pins used for pendant encoder
#define CHAN_B 4
#define OUT_13 70
#define OUT_14 73
int counter;
unsigned int a_state = 0;
unsigned int b_state = 0;
// channel A ISR quadrature decoding
//
// a rising edge when channel B is high means one count in positive direction
// when channel B is low it means a count in the negative direction.
//
// etc
//
void chan_A_ISR(unsigned int Pin, unsigned int PinState, void* Param)
{
a_state = PinState;
if (a_state == 1)
{
if (b_state == 1)
{
RLPext->GPIO.WritePin(OUT_13, RLP_FALSE);
RLPext->GPIO.WritePin(OUT_14, RLP_TRUE);
counter++;
return;
}
else
{
RLPext->GPIO.WritePin(OUT_13, RLP_TRUE);
RLPext->GPIO.WritePin(OUT_14, RLP_FALSE);
counter--;
return;
}
}
}
void chan_B_ISR(unsigned int Pin, unsigned int PinState, void* Param)
{
b_state = PinState;
if (b_state == 1)
{
if (a_state == 1)
{
RLPext->GPIO.WritePin(OUT_13, RLP_TRUE);
RLPext->GPIO.WritePin(OUT_14, RLP_FALSE);
counter--;
return;
}
else
{
RLPext->GPIO.WritePin(OUT_13, RLP_FALSE);
RLPext->GPIO.WritePin(OUT_14, RLP_TRUE);
counter++;
return;
}
}
}
int initEncoder(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
RLP_InterruptInputPinArgs InputPinArgs;
counter = 0;
InputPinArgs.GlitchFilterEnable = RLP_TRUE;
InputPinArgs.IntEdge = RLP_GPIO_INT_EDGE_BOTH;
InputPinArgs.ResistorState = RLP_GPIO_RESISTOR_PULLUP;
if(!RLPext->GPIO.EnableInterruptInputMode(CHAN_A,&InputPinArgs,chan_A_ISR,(void*) 0)) return 0; // ERROR
if(!RLPext->GPIO.EnableInterruptInputMode(CHAN_B,&InputPinArgs,chan_B_ISR,(void*) 0)) return 0; // ERROR
RLPext->GPIO.EnableOutputMode(OUT_13, RLP_FALSE); // up count diagnostic LED
RLPext->GPIO.EnableOutputMode(OUT_14, RLP_FALSE); // down count diagnostic LED
return 1; // OK
}
int readCounter(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
return counter;
}
The LED’s are only for a quick visual reference that the code is running. I’m printing out the count value from my managed code that calls the “readCounter” procedure, so I’m able to see the rate at which the count is being updated as the encoder rotates.