Trying to trigger one-shot timer on TIM1 from TIM2 interrupt not working
stm32 - Timer1 on stm32f4 - Electrical Engineering Stack Exchange
microcontroller - Unable to initialize STM32 timer in one pulse mode with ST HAL - Electrical Engineering Stack Exchange
stm32 - STM32F103 simple countdown timer (with no interrupts) - Electrical Engineering Stack Exchange
Can I use FreeRTOS software timers inside an interrupt (ISR)?
osTimerStart() or osTimerStop() from an ISR. These APIs are not ISR-safe. Instead, trigger a task using a semaphore or event flag, and control the timer from that task.How many software timers can I create in FreeRTOS?
configTIMER_QUEUE_LENGTH).Is it safe to use printf inside a timer callback?
printf can be slow and may delay other timer callbacks.Videos
I have setup a system where TIM2 is measuring a frequency, and each time a new frequency measurement is ready I want to start a One Pulse timer on TIM1 with the same frequency and a specific duty cycle. My frequency measurements are working perfectly (tested with mains power and calculated 59.94 Hz)
I have debugged my code and I am setting the ARR and CCRx registers correctly, and I am writing a 1 to the CEN bit to start the counter, but from what I can tell the counter is not starting and I am getting no output pulse on the pin. If anyone can share a tip for what to check or an example of what I'm trying to accomplish that would be appreciated.
I don't really like the HAL, so here is how to do what you want by just accessing the timer peripheral directly:
// SETUP STUFF:
// Enable the timer clock. I use the HAL for this
// as it adds the required startup delay. The code
// is pretty simple though.
__HAL_RCC_TIM1_CLK_ENABLE();
// Reset the control register. This gives us the
// default operation which is counting up with no
// divider.
TIM1->CR1 = 0;
// Set prescaler
TIM1->PSC = 16799;
// Will generate interrupt when this value is reached
TIM1->ARR = 4999;
// The PSC and ARR values are currently in the preload
// registers. To load them into the active registers we
// need an update event. We can do this manually as
// follows (or we could wait for the timer to expire).
TIM1->EGR |= TIM_EGR_UG;
// Timer is now ready to use.
// POLLING OPERATION:
// Next we setup the interrupts. We should first clear
// the update interrupt flag in case it has already been
// set.
TIM1->SR = ~TIM_SR_UIF;
// Then we can enable the update interrupt source
TIM1->DIER |= TIM_DIER_UIE;
// Note: we also need to setup the interrupt channel on
// the NVIC. Once that is done the isr will fire
// when the timer reaches 5000.
// We can now start the timer running...
TIM1->CR1 |= TIM_CR_CEN;
while ((GPIOA->IDR & GPIO_PIN_3) == 0x08)
{
Clk_h
DWT_Delay(200);
Clk_l
DWT_Delay(200);
}
// ...and stop the timer when we're done
TIM1->CR1 &= ~TIM_CR_CEN;
// Note if we want to repeat the polling loop again we should
// issue another TIM1->EGR |= TIM_EGR_UG event as this
// resets the timer to zero.
My solution would be almost identical to @Jon's, but I'd start the timer in one-shot mode, to avoid getting a second interrupt when processing the first takes too long. This way, no timer stop function is needed.
void TIM1_Init() {
// edit: added clock and interrupt enable
__HAL_RCC_TIM1_CLK_ENABLE();
NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0);
NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
TIM1->PSC = 16799; // prescaler
TIM1->EGR = TIM_EGR_UG; // generate an update event to load the prescaler
TIM1->ARR = 4999; // counter limit
TIM1->SR = 0; // clear interrupt status after the update event
TIM1->DIER = TIM_DIER_UIE; // enable interrupt on update (overflow) event
}
void TIM1_Stop() {
TIM1->CR1 = 0; // stop timer by clearing CEN (and everything else) in CR1
TIM1->CNT = 0; // reset counter, so it will start from 0 at restart
}
void TIM1_Start() {
TIM1->CR1 = TIM_CR1_CEN // start the timer
| TIM_CR1_OPM; // in one-pulse-mode
}
void TIM1_UP_TIM10_IRQHandler(void) {
TIM1->SR = 0;
strcpy(str123,"<<FAIL>>"); // sprintf should not be used in an interrupt handler
CDC_Transmit_FS((uint8_t*)str123,strlen(str123));
}
I have finally figured out, what was missing in my code.
Apart from HAL_TIM_OnePulse_Start_IT(), also HAL_TIM_Base_Start() has to be called in order to set up the timer operation in one pulse mode.
Now my main function looks like below and the interrupt is fired when after the set time.
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM10_Init();
HAL_TIM_Base_Start(&htim10);
HAL_TIM_OnePulse_Start_IT(&htim10, TIM_CHANNEL_1);
while (1)
{
}
}
You haven't shown us all the code. For example, where is the Timer and GPIO peripherals enabled? And where is the Timer interrupt enabled?
Typically with STM32Cube HAL applications, the functions like HAL_TIM_Base_Init() call another function like HAL_TIM_Base_MspInit(), which you may have to provide an implementation for. The xxx_MspInit() functions are typically implemented in a file called stm32xxxx_hal_msp.c. ST provides a template for this file with the HAL but I believe the expectation is that you will copy the template to your project file and customize it. (I'm not familiar with the STM32CubeMX tool, which might do some of this automatically.)
Typically within HAL_TIM_Base_MspInit() in stm32xxxx_hal_msp.c is where you will enable the timer and the timer interrupt. That code will look something like this:
// Enable the peripheral clock.
__HAL_RCC_TIM10_CLK_ENABLE();
// NVIC configuration for the TIM10 interrupt.
HAL_NVIC_SetPriority(TIM10_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(TIM10_IRQn);
Then you also need to provide an implementation for the timer interrupt handler. Typically this is done in a file called stm32xxxx_it.c. This is another file provided as a template with the HAL but you're expected to customize it in your project directory. (And again, STM32CubeMX may do some of this automatically.)
/**
* @brief This function handles TIM10_IRQHandler interrupt request.
* @param None
* @retval None
*/
void TIM10_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim10);
}
It's HAL_TIM_IRQHandler() which then calls your HAL_TIM_PeriodElapsedCallback() function.
So make sure you've enabled the timer and the timer interrupt in HAL_TIM_Base_MspInit(). Then make sure you've provided an implementation for TIM10_IRQHandler() which calls HAL_TIM_IRQHandler(). Then use the debugger to step through the code. Set breakpoints in HAL_TIM_Base_MspInit() and TIM10_IRQHandler() and HAL_TIM_PeriodElapsedCallback() to figure out what is working and what is not working.
After some investigation, it seems like I can't have a down counter which stops at zero without using interrupts (as far as I can discover). It also seems that down counters are not possible unless I'm running in centre-aligned or encoder mode.
As a result, I've gone for the following implementation, which uses an up-counter. It seems to work fine, although I'm slightly nervous that the counter has the slight possibility to automatically reload before my while loop has seen that it has gone over the threshold - but as long as my check value is well below the automatic reload value I should be fine.
There may be better ways to do this.
/* Set timer prescaler (0 = no divide - clk = 8MHz) */
TIM2->PSC = 0;
/* Set reload register well above the longest time that we're interested in */
TIM2->ARR = 0xFFFF;
/* Enable the timer */
TIM2->CR1 |= TIM_CR1_CEN;
/* Wait for 1ms */
TIM2->CNT = 0u;
while (TIM2->CNT < 8000u) {}
It sounds like the SysTick is ideal for your purposes.
SysTick is a timer which is loaded with a customizable value. Every instruction, it gets decremented by one. When the value reaches 0, it gets reset to it's load value and started again. Also, a status flag is set. You can poll this status flag in software.