Hi!
According to the STM32H755ZI's manual RM0399, §9.5.6 (pg. 377), the default clock should be 64 MHz (HSI) after reset. Since the same code worked perfectly on other devices like e.g. a NUCLEO-F303K8, I expected the (red) LED to blink with 1 Hz. But, it does not. I also tried to play around with the prescaler value (TIM2->PSC
), but that does not seem to take any effect... By the way, the LED actually blinks, but with ~7 Hz instead of the expected 1 Hz.
Seems the ISR is triggered by something else, but program execution does not reach the ISR, if I simply disable TIM2
.
/*!*****************************************************************************
* \file main.cpp
* \brief
* This program implements a very basic application that makes a LED blink with
* 1 Hz.
*/
#include <stdlib.h>
#include <stm32h755xx.h>
#define SYSTEM_CLOCK 64000000
/*!
* \brief Configures pin PB14 ("LD3/Red LED") as digital output.
*/
static void Config_Red_Led(void) {
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN;
GPIOB->MODER &= ~(0x3u << (2 * 14));
GPIOB->MODER |= (0x1u << (2 * 14));
GPIOB->OTYPER &= ~(0x1u << 14);
GPIOB->ODR = ~(0x1u << 14);
}
/*!
* \brief Configures TIM2 overflow with 10 us period.
*/
static void Config_10us_Blink_Timer(void) {
//Enable the TIM2 clock.
RCC->APB1LENR |= RCC_APB1LENR_TIM2EN;
//Enable TIM2 interrupts.
NVIC_EnableIRQ(TIM2_IRQn);
//Make sure the timer's "counter" is off.
TIM2->CR1 &= ~TIM_CR1_CEN;
//Reset the peripheral.
RCC->APB1LRSTR |= (RCC_APB1LRSTR_TIM2RST);
RCC->APB1LRSTR &= ~(RCC_APB1LRSTR_TIM2RST);
//Set the timer prescaler/autoreload timing registers.
TIM2->PSC = SYSTEM_CLOCK / 1000000;
TIM2->ARR = 10 - 1;
//Send an update event to reset the timer and apply settings.
TIM2->EGR |= TIM_EGR_UG;
}
/*!
* \brief Enables the "Blink Timer", which will now fire interrupts that trigger
* execution of the "App Loop" (--> \c TIM2_IRQHandler()).
*/
static void Run_App(void) {
//Clear TIM2_IRQn update interrupt,
TIM2->SR &= ~TIM_SR_UIF;
//Enable the hardware interrupt.
TIM2->DIER |= TIM_DIER_UIE;
//Enable the timer.
TIM2->CR1 |= TIM_CR1_CEN;
}
/*!
* \brief Initializes any peripheral being used.
*/
static void Init(void) {
//Disable CPU to respond to any configurable interrupt.
__disable_irq();
Config_Red_Led();
Config_10us_Blink_Timer();
//Enable CPU to respond to any configurable interrupt.
__enable_irq();
}
/*!
* \brief Initializes the system and runs the application.
*/
int main(void) {
Init();
Run_App();
while(true);
return EXIT_SUCCESS;
}
/*!
* \brief This IRQ handler will be triggered every 10 us by the "Blink Timer".
* This "time base" is used to blink a LED with a defined pattern.
*/
extern "C" void TIM2_IRQHandler(void) {
//Blink the LED with a PWM signal of 1 Hz period and 50% DS.
{
static auto s = 0;
if(++s >= 50000) {
//Toggle PB14, which is the LED ("LD3/Red LED").
GPIOB->ODR ^= (1 << 14);
s = 0;
}
}
//Clear TIM2 update interrupt flag.
TIM2->SR &= ~TIM_SR_UIF;
//Power-down until next "tick"/interrupt.
//__WFE();
}
If I use TIM6 instead, the frequency is ~2 Hz, and now modifying the prescaler value does matter. I.e. if I assume 128 MHz instead of the 64 MHz I get the 1 Hz LED Blinker:
/*!*****************************************************************************
* \file main.cpp
* \brief
* This program implements a very basic application that makes a LED blink with
* 1 Hz.
*/
#include <stdlib.h>
#include <stm32h755xx.h>
/*!
* \brief Configures pin PB14 ("LD3/Red LED") as digital output.
*/
static void Config_Red_Led(void) {
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN;
GPIOB->MODER &= ~(0x3u << (2 * 14));
GPIOB->MODER |= (0x1u << (2 * 14));
GPIOB->OTYPER &= ~(0x1u << 14);
GPIOB->ODR = ~(0x1u << 14);
}
/*!
* \brief Configures TIM6 overflow with 10 us period.
*/
static void Config_10us_Blink_Timer(void) {
//Enable the TIM6 clock.
RCC->APB1LENR |= RCC_APB1LENR_TIM6EN;
//Enable TIM6 interrupts.
NVIC_EnableIRQ(TIM6_DAC_IRQn);
//Make sure the timer's "counter" is off.
TIM6->CR1 &= ~TIM_CR1_CEN;
//Reset the peripheral.
RCC->APB1LRSTR |= (RCC_APB1LRSTR_TIM6RST);
RCC->APB1LRSTR &= ~(RCC_APB1LRSTR_TIM6RST);
//Set the timer prescaler/autoreload timing registers.
TIM6->PSC = 128000000 / 1000000;
TIM6->ARR = 10 - 1;
//Send an update event to reset the timer and apply settings.
TIM6->EGR |= TIM_EGR_UG;
}
/*!
* \brief Enables the "Blink Timer", which will now fire interrupts that trigger
* execution of the "App Loop" (--> \c TIM6_DAC_IRQHandler()).
*/
static void Run_App(void) {
//Clear TIM6_IRQn update interrupt,
TIM6->SR &= ~TIM_SR_UIF;
//Enable the hardware interrupt.
TIM6->DIER |= TIM_DIER_UIE;
//Enable the timer.
TIM6->CR1 |= TIM_CR1_CEN;
}
/*!
* \brief Initializes any peripheral being used.
*/
static void Init(void) {
//Disable CPU to respond to any configurable interrupt.
__disable_irq();
Config_Red_Led();
Config_10us_Blink_Timer();
//Enable CPU to respond to any configurable interrupt.
__enable_irq();
}
/*!
* \brief Initializes the system and runs the application.
*/
int main(void) {
Init();
Run_App();
while(true);
return EXIT_SUCCESS;
}
/*!
* \brief This IRQ handler will be triggered every 10 us by the "Blink Timer".
* This "time base" is used to blink a LED with a defined pattern.
*/
extern "C" void TIM6_DAC_IRQHandler(void) {
//Blink the LED with a PWM signal of 1 Hz period and 50% DS.
{
static auto s = 0;
if(++s >= 50000) {
//Toggle PB14, which is the LED ("LD3/Red LED").
GPIOB->ODR ^= (1 << 14);
s = 0;
}
}
//Clear TIM6 update interrupt flag.
TIM6->SR &= ~TIM_SR_UIF;
//Power-down until next "tick"/interrupt.
//__WFE();
}