2024-09-19 12:42 AM - last edited on 2024-09-19 01:05 AM by SofLit
Hello,
I‘m creating a counter that increments/decrements according to cw/ccw direction of an encoder.
An interrupt is triggered by the rising and falling edge on each channel.
Due to mechanical jitter, the interrupts are triggered (sometimes) simultaneously and will increment by values greater than 1. I was thinking of using a delay but how can I make my interrupt handlers more robust to prevent this occurring?
int main(){
FLASH_Init();
RCC_Init();
GPIO_Init();
USART2_Init();
EXTI0_Init();
EXTI1_Init();
NVIC_Init();
while(1){
}
}
void EXTI0_IRQHandler(void){
if (EXTI -> PR & (0x01U)){
if (((GPIOA -> IDR & (0x1U)) && (GPIOA -> IDR & (0x2U))) || ((!(GPIOA -> IDR & (0x1U))) && (!(GPIOA -> IDR & (0x2U))))){
STATE++;
printf("Encoder counts = %d \n\r",STATE);
//GPIOA -> ODR ^= (0x01U << 5);
}
if (((GPIOA -> IDR & (0x1U)) && (!(GPIOA -> IDR & (0x2U)))) || ((!(GPIOA -> IDR & (0x1U))) && (GPIOA -> IDR & (0x2U)))) {
STATE --;
printf("Encoder counts = %d \n\r",STATE);
}
}
EXTI -> PR = (0xFFFFFU);
// NVIC -> ICER[0] |= (0x1U << 6);
// NVIC -> ISER[0] |= (0x1U << 7);
}
void EXTI1_IRQHandler(void){
if (EXTI -> PR & (0x01U<<1)){
if (((!(GPIOA -> IDR & (0x2U))) && (GPIOA -> IDR & (0x1U))) || ((GPIOA -> IDR & (0x2U)) && (!(GPIOA -> IDR & (0x1U))))) {
STATE ++;
printf("Encoder counts = %d \n\r",STATE);
//GPIOA -> ODR ^= (0x01U << 5);
}
if (((GPIOA -> IDR & (0x2U)) && (GPIOA -> IDR & (0x1U))) || ((!(GPIOA -> IDR & (0x2U))) && (!(GPIOA -> IDR & (0x1U))))){
STATE --;
printf("Encoder counts = %d \n\r",STATE);
}
}
EXTI -> PR = (0xFFFFFU);
// NVIC -> ICER[0] |= (0x1U << 7);
// NVIC -> ISER[0] |= (0x1U << 6);
}
2024-09-19 01:17 AM
Hello @XPChi and welcome to the community,
First of all, remove all these printfs from the interrupt handlers. This blocks the execution for a considerable time.
See this link: https://developer.arm.com/documentation/ka002394/latest/
Second, I'm wondering why you are clearing all the EXTI flags for a specific EXTI interrupt. Also you need to clear the interrupt as soon as you get into the interrupt handler.
See for example how The EXTI interrupt handler is implemented in HAL:
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
2024-09-19 05:58 AM
> EXTI -> PR = (0xFFFFFU);
Don't blindly clear all the flags. You may be clearing ones which haven't been processed yet. Only clear the flag you have handled.
2024-09-19 07:09 AM
Hi @SofLit , thanks for the welcome. I'm avoiding using HAL as I would rather have full control of all the registers that I'm using and also for debugging purposes.
I'm aware of the interrupt clear, this due to the fact that only EXTI0 and EXTI1 are being used. I clear them after an interrupt has been triggered to prevent a jitter enabling an interrupt incorrectly.
I've cleared the interrupt in the beginning as you suggested, however, this doesn't seem to ignore any mechanical noise.
Thanks
XPChi
2024-09-19 07:17 AM
@XPChi wrote:
I'm aware of the interrupt clear, this due to the fact that only EXTI0 and EXTI1 are being used.
Better to clear them separately.
@XPChi wrote:
I'm avoiding using HAL as I would rather have full control of all the registers that I'm using and also for debugging purposes.
I'm not suggesting you to use HAL but inspiring from it.
Did you also remove all the printfs from the IRQ handlers?
2024-09-19 01:26 PM
Why are you using GPIO interrupts to read the encoder? You're like recreating the wheel.
Instead, use a Timer in encoder mode which does all the hard work for you.
Just create a new project in STM32CubeIDE in Timer Encoder mode and extract the parts you need from the HAL driver.
2024-09-20 08:32 AM
@Karl Yamashita wrote:
Why are you using GPIO interrupts to read the encoder? You're like recreating the wheel.
Indeed!