cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L476 -- How to have a continuous timer that upon a button push can load a Compare Register with a debounce value and then generate an IRQ?

SWenn.1
Senior III

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

1 ACCEPTED SOLUTION

Accepted Solutions
Piranha
Chief II
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.

View solution in original post

14 REPLIES 14
S.Ma
Principal

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
Senior III

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
Chief II
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.

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

S.Ma
Principal

Button debounce can create multiple pulses....

gbm
Lead III

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.

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.

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
Lead II

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 you find my answers useful, click the accept button so that way others can see the solution.