I have the following code:
#include "stm32f407xx.h"
#define TIM2_IRQNO 28
void TIM2_IRQHandler(void) {
printf("tick\n");
}
int main() {
TIM_PeriClockControl(TIM2, ENABLE); //enable system clock on timer
TIM2->PSC = 168; //set prescaler
TIM2->ARR = 1000; //set frequency to 1kHz
TIM2->CR1 |= (1 << TIM_CR1_URS); //update enable (after overflow)
TIM2->EGR |= (1 << 0); //update generation
TIM2->DIER |= (1 << 0); //update interrupt enabled
TIM2->CR1 |= 1 << TIM_CR1_CEN; //enable timer
uint32_t *pISER1 = (uint32_t *)0xE000E100; //NVIC_ISER base addr (or ..104?)
*pISER1 |= ( 1 << (TIM2_IRQNO % 32) ); //enable NVIC for TIM2
for(;;); //main loop
}I have an STM32F407VG microcontroller. It features a 168MHz clock. Whith the prescaler I bring down it's speed to 1MHz on the peripherals and I further decrease the timers speed to 1kHz with the help of the ARR Auto Reload register.
Not sure about EGR and DIER registers, but have seen others use them in their implementations.
After enabling the timer I try to enable the interrupt on the timer using the base address of the ISER register of the NVIC. It should probably end in 104, not 100, but with 104 I managed to print nothing, while using 100 I could print the "tick" text, but uncontrollably fast.
What I would expect form this code is to call the TIM2_IRQHandler after every millisecond (1 tick of the timer with the given PSC and ARR configuration). What am I doing wrong?
OK... I have an STM32L452 MCU running at 80MHz
I have a UART4 RX interrupt that is working. That ISR sets a pin high, and kick off a timer3 interrupt after 500 us.
The HAL generated code for setting up TIM3 is
static void MX_TIM3_Init(void)
{
/* USER CODE BEGIN TIM3_Init 0 */
/* USER CODE END TIM3_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 79;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 499;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
}When the UART4 interrupt is triggered, I call
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance==UART4){
newMasterMessage = 1;
HAL_GPIO_WritePin(UART4_DE_GPIO_Port, UART4_DE_Pin, GPIO_PIN_SET);
HAL_TIM_Base_Start_IT(&htim3);
}
}...the ISR for the timer3 interrupt just sets that pin low.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// Check which version of the timer triggered this callback and toggle LED
if (htim == &htim3)
{
HAL_GPIO_WritePin(UART4_DE_GPIO_Port, UART4_DE_Pin, GPIO_PIN_RESET);
}
}My goal is to have the timer fire an interrupt after 500us. With an 80 MHz clock, I use a pre-scalar of 79, and counter of 499, which should do the trick.
However, the interrupt always fires after just ~5us, no matter what parameters I use. I have also tried using tim16, but it does the same thing. I feel like I'm missing something fundamental, but I don't know what.
Can anyone help?
I do not know much about coding.. but I cannot see the place where you are enabling the interrupts..
The interrupt for timer TIM3 can be enabled by adding the following line to the timer setup function:
NVIC_EnableIRQ(TIM3_IRQn);
I was using CubeMX to generate the code, For some reason the line NVIC_EnableIRQ(TIM3_IRQn); was missing in the timers setup function, adding this solved my issue.
Well it is a bug in the generated code. During timer initialization the call TIM_Base_SetConfig is called. This call has as a side effect that update interrupt bit is set in the SR. This means that when interrupt are enable in the HAL_TIM_Base_Start_IT immediately an interrupt is generated. This is what you are experiencing. See also https://www.youtube.com/watch?v=3yvY7pLMHAg 5:40. There also a HAL compatible fix is explained.
I would recommend the following implementation:
- Setup EXTI pin for rising and falling edges
- Check pin state in interrupt handler(instead of is_pressed variable)
EXTI handler can be implemented like below:
#define BUTTON_GPIO GPIOA
#define BUTTON_PIN GPIO_PIN_0
#define BUTTON_PRESSED_STATE GPIO_PIN_RESET
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
if (HAL_GPIO_ReadPin(BUTTON_GPIO, BUTTON_PIN) == BUTTON_PRESSED_STATE ) {
HAL_TIM_Base_Start_IT(&htim7);
__HAL_TIM_SET_COUNTER(&htim7, 0);
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET);
} else {
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET);
HAL_TIM_Base_Stop_IT(&htim7);
}
}
I ran into this with an STM32F105. The STM32F1xx Standard Peripheral Library functions are a bit different than what you are using, but the idea should be the same.
Issuing the TIM_TimeBaseInit() function caused the TIM_SR_UIF flag to become set. I haven't gone back yet to figure out why. Once this bit is set, the interrupt will trigger as soon as it is enabled.
To fix it, after calling TIM_TimeBaseInit(), I immediately called TIM_ClearITPendingBit(). Then I would enable the interrupt with TIM_ITConfig(). This fixed the problem.
My complete initialization routine looks like this:
// Enable the peripheral clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
// Configure the timebase
TIM_TimeBaseInitStructure.TIM_Prescaler = 1;
TIM_TimeBaseInitStructure.TIM_Period = 35999;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStructure);
// That last function caused the UIF flag to get set. Clear it.
TIM_ClearITPendingBit(TIM5, TIM_IT_Update);
// Configure so that the interrupt flag is only set upon overflow
TIM_UpdateRequestConfig(TIM5, TIM_UpdateSource_Regular);
// Enable the TIM5 Update Interrupt type
TIM_ITConfig(TIM5, TIM_IT_Update, ENABLE);
Based on the solution from programmersought.com
Clear TIM_SR_UIF flag before every HAL_TIM_Base_Start_IT() call
#define FIX_TIMER_TRIGGER(handle_ptr) (__HAL_TIM_CLEAR_FLAG(handle_ptr, TIM_SR_UIF))
...
void myfunc(){
FIX_TIMER_TRIGGER(&htim7);
HAL_TIM_Base_Start_IT(&htim7);
}
Tested on STM32F407 Discovery board.