Skip to main content
SWenn.1
Senior III
December 30, 2022
Solved

STM32L476 Timer: on button push, load Compare Register with debounce value, then generate IRQ?

  • December 30, 2022
  • 14 replies
  • 3893 views

Hello....

I have TIM1 clocking at 1ms. It rolls over at 1000 counts (ie 1 second). Within the pushbutton IRQ I add 50 to the timer register and disable the GPIO IRQ. I would also like to enable a compare IRQ here as well, such that after the counter counts 50ms it generates a compare IRQ at which point I will re-enable the GPIO IRQ and set a flag for main.

 

I used LL code to set the timer and disable the GPIO IRQ but don't know exactly what registers to touch to enable the compare IRQ....can someone please tell me how to do this?

 

I do not want to have a timer that gets enabled in the GPIO IRQ and counts for 50 then generates an interrupt as I want to make use of a continuous timer that is running at 1ms and rolls every second as this is available for other purposes.

 

Thanks

 

This topic has been closed for replies.
Best answer by Piranha
htim1.Instance->CCR1 |= (htim1.Instance->CNT + 50);

You are ORing the numbers and ignoring overflows.

htim1.Instance->SR &= ~TIM_SR_CC1IF;

Another unnecessary and broken RMW (read-modify-write) operation. And clearing of that flag should already be done in the HAL internally.

Also take a note that RMW operations and probably that code in general are not interrupt safe. Such code requires all of the related interrupts to be at the same priority level and similar code cannot be used from non-interrupt context outside of a critical section.

14 replies

S.Ma
Principal
December 30, 2022

Debounce is a slow human speed filtering, hence no need to consume hw resources for it. For example, set a compare every 50 msec (timer running value + 50) with interrupt, read the button(s) pin level, and if pushed, increass a simple ram byte value until it reaches 3, reset the byte value otherwise. Scaling to 6 keys won't need more timers. If using LPTIM, combined with EXTI wakeup, later low power schemes remains achievable when needed.

And if multiple keys, analog keyboard scheme works using same principle with adc. (Your LCD monitor buttons usually have 5 or 6 buttons going to max 2 mcu pins.....

SWenn.1
SWenn.1Author
Senior III
December 30, 2022

that doesn't answer my question...I have a running timer.....I do load it with cnt + 50 after a button is pressed....The question is what ISR should I be using? See code below....it is not working always....is the stm32 smart enough to load cnt + 50 on a rolling timer....for instance if I have 975 on my timer and add 50 the CCR hopefully would sit at 25 and after 50 ticks generate an interrupt

SWenn.1
SWenn.1Author
Senior III
December 30, 2022

Here is my code.....I am monitoring the switch going low and the EN_12V IO line but the HAL_TIM_OC_DelayElapsedCallback isn't going off 50ms after the switch goes low....instead it goes off about 1 or 2 us later as witnessed with scope on EN_12V line. In CubeMX I have Channel 1 output set to Output Compare No Output and the Mode is set to Frozen with the Pulse set to 1????

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
 /* Prevent unused argument(s) compilation warning */
 UNUSED(GPIO_Pin);
 if (GPIO_Pin == PUSHBUTTON_Pin)
 {
	 //Disable pushbutton until debounce over
	 EXTI->IMR1 &= ~(PUSHBUTTON_Pin);
	 htim1.Instance->CCR1 |= (htim1.Instance->CNT + 50);
	 htim1.Instance->DIER |= TIM_DIER_CC1IE;
 }
}
 
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim == &htim1)	//Switch debounce timer
	{
		htim1.Instance->DIER &= ~TIM_DIER_CC1IE;
		htim1.Instance->SR &= ~TIM_SR_CC1IF;
		HAL_GPIO_WritePin(EN_12V_GPIO_Port, EN_12V_Pin, GPIO_PIN_SET);
 
		if (HAL_GPIO_ReadPin(PUSHBUTTON_GPIO_Port, PUSHBUTTON_Pin) == GPIO_PIN_RESET)
			ISR.cleanCycle = T;
	}
}

Piranha
PiranhaBest answer
Principal III
December 30, 2022
htim1.Instance->CCR1 |= (htim1.Instance->CNT + 50);

You are ORing the numbers and ignoring overflows.

htim1.Instance->SR &= ~TIM_SR_CC1IF;

Another unnecessary and broken RMW (read-modify-write) operation. And clearing of that flag should already be done in the HAL internally.

Also take a note that RMW operations and probably that code in general are not interrupt safe. Such code requires all of the related interrupts to be at the same priority level and similar code cannot be used from non-interrupt context outside of a critical section.

SWenn.1
SWenn.1Author
Senior III
December 30, 2022

Thank you!

That |= was a problem. I was not sure where and when the flags get cleared. The documentation on the hw of a 32 bit processor is overwhelming (I come from MSP430 land and am used to register level settings). The HAL stuff on STM32 can be quite bloated. I would like to hear / understand more on why you think it not safe?

I am a HW guy who has had to learn FW. My coding style is ALWAYS keep ISR short and tight, and set a flag for main to resolve all. All code is driven by ISR. So far I have not had to use any RTOS. Sure the button push could occur at the same instant another ISR but I would expect HW to enter / exit all ISRs and I need to recognize that one could go off before another and handle it appropriately.

In this instance I wanted a background timer that continuously runs without entering waking up the processor to enter an interrupt. I then want to, as needed, load CCR to do timing work on a multitude of things (debounce being one of them). I do understand that the resource needs to have a mutex associated with it to indicate that it is free/busy.

S.Ma
Principal
December 30, 2022

Button debounce can create multiple pulses....

gbm
Super User
December 30, 2022

Doing what you want to achieve in the proper way, using periodic timer interrupt only, takes 4 lines of code in timer ISR. What you unsuccessfully attempt enages at least two interrupts and 12 or more lines of code. The choice is clear, at least for me.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
SWenn.1
SWenn.1Author
Senior III
December 30, 2022

Please share as I am not sure how that is possible??....I wanted the code to have a background timer ALWAYS running for other reasons. As necessary then I will create a mutex for the shared resource and when free load CCR with values for doing delay work for hw pieces.

I could have used lines like (see below) but I can tell you they are FAR from 4 lines of code. HAL is bloated and creates long ISRs....Even in LL I don't see how to get in and out in 4 lines (part of it is it is unclear to me (due to the volume of literature) on the interrupt flags that automatically get cleared and the ones that software has to clear)

HAL_TIM_Base_Start_IT
 
HAL_TIM_Base_Stop_IT

Karl Yamashita
Principal
December 31, 2022

I have a YouTube video that shows the uses of a custom Timer Callback that can debounce a button. It uses the Systick to keep track of time in ms. This is none blocking so you can do other tasks as you're waiting for the button to debounce and register as a good button press.

This Timer Callback was really meant as a simple callback to blink an LED and periodically send telemetry data. But then i realized by cascading a several callbacks this could debounce a button and count button presses.

Not only can it debounce a button, but you can

  • Use it to count button presses
  • Callback a function at a certain amount of time and repeat continuously or it can be a one shot
  • The callback can repeat with a timeout time
  • The callback can repeat and stop after x amount of times
  • Is has a 2nd callback after the 1st callback is done with it's timeout or repetition count.

https://youtu.be/o0qhmXR5LD0

If a reply has proven helpful, click on Accept as Solution so that it'll show at top of the post.CAN Jammer an open source CAN bus hacking toolCANableV3 Open Source
gbm
Super User
December 31, 2022

In almost every piece of microcontroller software we use some periodic timer. In almost all Cortex-M projects we have SysTick programmed for 1kHz interrupts. In almost every program we also need to do something with smaller frequency, lets say 50 Hz. So, simply put the following code in SysTick_Handler (or any other periodic timer interrupt service):

static uint8_t div_20ms;
if (++div_20ms == 20)
{
 div_20ms = 0;
 // here comes the famous code for button handling - 5 lines
 static bool was_pressed;
 bool is_pressed = ~BTN_GPIO->IDR & BTN_Msk;
 if (is_pressed & !was_pressed)
 handle_button_pressed();
 was_pressed = is_pressed;
}

Well, it's 5 lines, not 4 - my mistake.

Now, dear Mr Karl, please compare your code using EXTI and timer interrupt with the code above which you kindly referred to as "bs" and explain to me and other participants of this forum in which aspects the solution with 2 interrupts is better: programmer's effort (length of code source text), memory footprint, clarity, number of MCU peripheral modules needed?

As far as I can see, your "non-bs" solution requires EXTI and a dedicated timer channel. What you call "bs" uses only an already-used timer timebase interrupt.

As I wrote a few times before, there is an exception to my "rule #1". It's when you use deeper sleep modes of the MCU.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
SWenn.1
SWenn.1Author
Senior III
December 31, 2022

Thank you all so much for the support! I am new to the STM32 world and it is nice to see support on these parts. As a HW person who has had to learn FW (my focus has always been MSP430) I am sure my coding is not up to par and trying to wrap my head around the HAL stuff is somewhat challenging.

gbm
Super User
December 31, 2022

You are welcome. Save your time and ditch the HAL. (Hope nobody from ST will see it.) ;) My code doesn't use HAL and isn't particularly STM32-specific. I work with many different micros, few years ago I also used MSP430 in some projects, that was before the Cortex-M 32-bit era. :)

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
Karl Yamashita
Principal
December 31, 2022

@gbm​ I'm just saying it's bs telling people to never use EXTI when in fact you can. Telling people to never using EXTI is not the solution but instead we should try to give a solution. I''ve no quarrels over your code and it's small footprint. And it's nice that you've shared it so others can gain new knowledge.

I'm also sharing my code which can be used for any microcontroller as well. Is it a small footprint? By means no way. However it does make it easier to do a lot more tasks than just debouncing a button which was the point of me showing the different examples. You've might secretly use some of the ideas yourself :face_savoring_food:

If a reply has proven helpful, click on Accept as Solution so that it'll show at top of the post.CAN Jammer an open source CAN bus hacking toolCANableV3 Open Source