cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407 discovery board. Basic Timer TIM6 and Interrupt to toggle LED without HAL driver

AAnth.1
Senior

Hi

I am trying to better understand the registers and register manipulation to set up the following scenario:

Toggle LED on PD12 every 1 second via TIM6 and Interrupt Handler.

Below is my code. I enter the interrupt service routine (verified by setting a breakpoint in the ISR) and I toggle the LED (via step by step debugging within the ISR) . However, when I remove the breakpoint and run the code, the LED toggles at a much higher frequency. Could you please double check my code because I believe I have something wrong in my TIM6 setup. Thank you,

#include "stm32f4xx.h"
 
void funcIntInit();
void funcTimerInit();
void funcGPIOInit();
 
int main(void)
{
	funcIntInit();
	funcTimerInit();
	funcGPIOInit();
	for(;;);
}
 
void funcIntInit()
{
	// Initialize the interrupt
	__NVIC_EnableIRQ(TIM6_DAC_IRQn);
 
	EXTI->IMR |= (1<<0);
	EXTI->EMR |= (1<<0);
	EXTI->FTSR &= ~(1<<0);
	EXTI->RTSR |= (1<<0);
 
	// SysCfg not needed for basic timer TIM6???
}
 
void funcTimerInit()
{
	// Peripheral clock
	RCC->APB1ENR |= (1<<4);	// Enables APB1 bus to use Timer 6
	// 2. Init timer
	TIM6->CR1 |= (1<<0);	// CEN: Enables counter
	TIM6->CR1 &= ~(1<<1);	// UDIS: Update event enabled
	TIM6->CR1 |= (1<<2);	// URS: Update request source enabled for overflow/underflow only
	TIM6->CR1 &= ~(1<<3);	// OPM: One Pulse Mode. Counter continues counting.
	TIM6->CR1 &= ~(1<<7);	// Auto reload preload enabled
	TIM6->DIER |= (1<<0);	// UIE: Update interrupt enabled
	TIM6->EGR |= (1<<0);	// UG: Update generation. Re-initializes the counter and updates registers
	//TIM6->CNT = 0x00FA;		// Counter goes up to 250 to have 1s timer ???
	TIM6->PSC = 0xFA00;		// Sets prescaler to 64000. Timer clock is now 16MHz/64000=250Hz
	TIM6->ARR = 0x00FA;		// Counter goes up to 250 to have 1s timer
 
 
}
 
void funcGPIOInit()
{
	// Peripheral clock
	RCC->AHB1ENR |= (1<<3);	// Enables AHB1 to use GPIOD peripheral
	// 2. Init GPIO
	GPIOD->MODER |= (1<<24);	// Sets GPIOD as Output
	GPIOD->MODER &= ~(1<<25);	// Sets GPIOD as Output (redundand because bit 25 is already 0 after reset)
	GPIOD->OTYPER &= ~(1<<12);	// Set PD12 as Push Pull
	GPIOD->OSPEEDR &= ~(1<<24); // Sets speed on PD12 to low
	GPIOD->OSPEEDR &= ~(1<<25); // Sets speed on PD12 to low
}
 
 
void TIM6_DAC_IRQHandler()	// Overrides the weak implementation of the IRQ in the startup file
{
	// Toggle LED
	static uint8_t check = 1;
	if(check)
	{
		GPIOD->ODR |= (1<<12);		// Sets PD12
		check = 0;
	}
	else
	{
		GPIOD->ODR &= ~(1<<12);		// Clears PD12
		check = 1;
	}
	__NVIC_ClearPendingIRQ(TIM6_DAC_IRQn);
}

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

You need to clear the timer's update flag, otherwise the interrupt will keep firing. Here's the relevant portion from the default HAL IRQHandler:

/* TIM Update event */
  if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
  {
    if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
      htim->PeriodElapsedCallback(htim);
#else
      HAL_TIM_PeriodElapsedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
    }
  }

 So do:

TIMx->SR = ~TIM_SR_UIF;

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

5 REPLIES 5
TDK
Guru

You need to clear the timer's update flag, otherwise the interrupt will keep firing. Here's the relevant portion from the default HAL IRQHandler:

/* TIM Update event */
  if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
  {
    if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
      htim->PeriodElapsedCallback(htim);
#else
      HAL_TIM_PeriodElapsedCallback(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
    }
  }

 So do:

TIMx->SR = ~TIM_SR_UIF;

If you feel a post has answered your question, please click "Accept as Solution".

Boom! Thank you - that was exactly what I missed.

Thank you very much for your help - much appreciated.

BR,

turboscrew
Senior III

BTW, why are you playing with EXTI0 in the funcIntInit?

PSC and ARR should both be 1 less than the count (period).

Hi turboscrew,

because I thought it was a smart idea to play with the EXTI0, although not entirely knowing what I was doing. I went through the ref manual one more time and agree that EXTI0 is not needed for my basic timer, but only for GPIO.

And thank you very much for the hint about PSC and ARR. The code is updated with the correct values.

I have one more question:

What exactly is the register TIMx_CNT used for? The reference manual says Counter value, which I understand as the current counter value that increases with every clock cycle (after prescaler etc). If I am correct with that, then what is the reason that the register is both read and WRITE?

Thank you,

0693W000000WboRQAS.png

TDK
Guru

> What exactly is the register TIMx_CNT used for? The reference manual says Counter value, which I understand as the current counter value that increases with every clock cycle (after prescaler etc). If I am correct with that, then what is the reason that the register is both read and WRITE?

Your understanding is correct. It's read/write because sometimes you want to change the value, simple as that. Your program doesn't need to, but sometimes it makes sense to start the counter from ARR-1, or from 0, or from the middle. Or if you want to reset the counter to avoid an update event, or whatever.

If you feel a post has answered your question, please click "Accept as Solution".