LED Blinks "Too" Fast @ NUCLEO-H755ZI-Q

469 Views Asked by At

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();
}
0

There are 0 best solutions below