2024-10-22 05:24 AM - last edited on 2024-10-22 01:17 PM by SofLit
Hi,
I have three buttons - START, STOP and CH_SELECT which are serviced using EXTI interrupts. I have also a rotary encoder (A and B pins). Every button line is serviced on a separate IRQ line. Debouncing of the switch and rotary encoder pins is handled in HAL_GPIO_EXTI Callback function.
The code is actually working but sometimes, actually very often, code snippet of wrong button is executed. When I pressing for example button START sometimes I get “STOP button pressed” or “CH_SELECT_button pressed” randomly. Rotary encoder works very well. Am I missing something, every help is really appreciated.
Microcontrooller is STM32L431 with 80MHz master clock. I am using STM32CubeIDE 1.16.1
Those are my EXTI handlers (in stm32l4xx_it.c file):
void EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(STOP_BUTTON_Pin);
}
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(START_BUTTON_Pin);
}
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(A_Pin);
HAL_GPIO_EXTI_IRQHandler(B_Pin);
HAL_GPIO_EXTI_IRQHandler(CH_SELECT_Pin);
}
This is my callback function (in main.c file):
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
uint32_t currentTime = HAL_GetTick();
if ((currentTime - lastDebounceTime) < DEBOUNCE_DELAY_ENCODER) {
return;
}
lastDebounceTime = currentTime;
if (GPIO_Pin == A_Pin) {
if (HAL_GPIO_ReadPin(B_GPIO_Port, B_Pin) == GPIO_PIN_SET) {
encoderValue+=20;
if(encoderValue>2666) encoderValue=0;
} else {
encoderValue-=20;
if(encoderValue<0) encoderValue=2666;
}
}
if (GPIO_Pin == B_Pin) {
if (HAL_GPIO_ReadPin(A_GPIO_Port, A_Pin) == GPIO_PIN_SET) {
encoderValue-=20;
if(encoderValue<0) encoderValue=2666;
} else {
encoderValue+=20;
if(encoderValue>2666) encoderValue=0;
}
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, encoderValue);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, encoderValue);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_3, encoderValue);
}
//START button
if (GPIO_Pin == START_BUTTON_Pin) {
if ((currentTime - last_start_button_tick) > DEBOUNCE_DELAY_BUTTON) {
last_start_button_tick = currentTime;
treatment_time=0;
start_treatment=1;
TIM1->CR1 |= TIM_CR1_CEN; //start timer1
//printf("start button pressed\n\r");
flag=1;
}
}
//STOP button
if (GPIO_Pin == STOP_BUTTON_Pin) {
if ((currentTime - last_stop_button_tick) > DEBOUNCE_DELAY_BUTTON) {
last_stop_button_tick = currentTime;
start_treatment=0;
//printf("stop button pressed\n\r");
flag=2;
}
}
//CH_SELECT button
if (GPIO_Pin == CH_SELECT_Pin) {
if ((currentTime - last_ch_select_tick) > DEBOUNCE_DELAY_BUTTON) {
last_ch_select_tick = currentTime;
//printf("CH_SELECT button pressed\n\r");
flag=3;
}
}
}
I am printing debug messages in the main loop:
while(1) {
if(flag==1) { printf("START button pressed\n\r"); flag=0;}
if(flag==2) { printf("STOP button pressed\n\r"); flag=0;}
if(flag==3) { printf("CH_SELECT button pressed\n\r"); flag=0;}
//…
//…
}
AND my debounce values are:
const uint32_t DEBOUNCE_DELAY_ENCODER = 50;
const uint32_t DEBOUNCE_DELAY_BUTTON = 50;
Thank you very much, I really appreciated any advice...
2024-10-22 05:33 AM - edited 2024-10-22 05:35 AM
Declare flag as volatile. Rewrite the handler code to use "else if" instead of just "if" - its not possible to enter more than one if anyway.
Even better: don't use EXTI at all. It makes no sense. Do it all in timer interrupt with 20 ms period.
2024-10-22 09:36 AM
Hello gbm,
Thank you very much for your reply,
yes flag is declared as volatile and all the tick variables also, I tried also to do a printf from interrupt (only for debugging purpose) but the problem persists...
Uh, not to use EXTI is also an interesting idea for think about it but now I wonder why this is not working properly...
2024-10-22 09:45 AM
In your code, if any of A, B or Select is pressed, you call the handlers for all three of them instead of checking which pin caused the interrupt and invoking only the handler for that pin. That's the problem.
2024-10-22 10:29 AM
Uhhhh!!! You are right! But unfortunately still the same problem... :(
My handlers are now:
void EXTI4_IRQHandler(void)
{
if (__HAL_GPIO_EXTI_GET_IT(STOP_BUTTON_Pin) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(STOP_BUTTON_Pin);
HAL_GPIO_EXTI_Callback(STOP_BUTTON_Pin);
}
void EXTI9_5_IRQHandler(void)
{
if (__HAL_GPIO_EXTI_GET_IT(START_BUTTON_Pin) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(START_BUTTON_Pin);
HAL_GPIO_EXTI_Callback(START_BUTTON_Pin);
}
}
void EXTI15_10_IRQHandler(void)
{
if (__HAL_GPIO_EXTI_GET_IT(A_Pin) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(A_Pin);
HAL_GPIO_EXTI_Callback(A_Pin);
}
else if (__HAL_GPIO_EXTI_GET_IT(B_Pin) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(B_Pin);
HAL_GPIO_EXTI_Callback(B_Pin);
}
else if (__HAL_GPIO_EXTI_GET_IT(CH_SELECT_Pin) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(CH_SELECT_Pin);
HAL_GPIO_EXTI_Callback(CH_SELECT_Pin);
}
}
2024-10-22 12:16 PM
You use single debounce variable for all inputs - maybe it's the reason - it masks some encoder events.
All of this would be much easier without pin interrupts.
2024-10-22 12:57 PM
Consider to use the timer encoder hardware for the encoder task. Using interrupt for encoder task is error prone.
2024-10-22 01:16 PM
Uh I have different ticks variables (last_stop_button_tick, last_start_button_tick and last_ch_select_tick). lastDebounceTime is actually only for decoder debouncing - maybe decoder should be written in the same way as buttons but I tried to completely remove decoder and the situation is the same...
Unfortunately I can't implement timer interrupt for polling the buttons because in the main I have a very time demanding task - I need to generate very precise pulses of 1 to 15us width and with repetition rate of 10Hz to 500Hz.
And during that pulses my buttons should be active (enabled) so interrupt of some 50ms for buttons polling will ruine
my pulses timing.
2024-10-22 01:22 PM
Hello Uwe Bonnes,
Thank you very much for your reply,
uh I have a finished PCB so I can't do that unfortunately.
Actually my encoder is working very well... My buttons are acting strange...
2024-10-22 04:13 PM
@tompa wrote:Uh I have different ticks variables (last_stop_button_tick, last_start_button_tick and last_ch_select_tick). lastDebounceTime is actually only for decoder debouncing - maybe decoder should be written in the same way as buttons but I tried to completely remove decoder and the situation is the same...
Unfortunately I can't implement timer interrupt for polling the buttons because in the main I have a very time demanding task - I need to generate very precise pulses of 1 to 15us width and with repetition rate of 10Hz to 500Hz.
And during that pulses my buttons should be active (enabled) so interrupt of some 50ms for buttons polling will ruine
my pulses timing.
Instead of doing your pulses in main. why don't you use a timer in pulse mode?
As for button debouncing, I don't see any code where you check a timer after the button is pressed for at lease n amount of ms?
See this video to use a timer callback for debouncing a switch and many other uses.
https://www.youtube.com/watch?v=o0qhmXR5LD0