STM32cubeMX配置Systick的bug

STM32cubeMX版本:6.11.0

现象

STM32cubeMX配置Systick的时钟,不管选择不分频 还是8分频。

生成的代码都是一样的,代码都是不分频。

即不管选择不分频还是8分频,Systick都是使用的系统时钟

函数调用

HAL_Init() →

HAL_InitTick(TICK_INT_PRIORITY) →

HAL_SYSTICK_Config(SystemCoreClock / (1000UL / (uint32_t)uwTickFreq)) →

SysTick_Config(TicksNumb)

各个函数的源码如下所示:

c 复制代码
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Check uwTickFreq for MisraC 2012 (even if uwTickFreq is a enum type that don't take the value zero)*/
  if((uint32_t)uwTickFreq == 0UL)
  {
    return HAL_ERROR;
  }

    /* Configure the SysTick to have interrupt in 1ms time basis*/
    if (HAL_SYSTICK_Config(SystemCoreClock / (1000UL / (uint32_t)uwTickFreq)) > 0U)
    {
      return HAL_ERROR;
    }

  /* Configure the SysTick IRQ priority */
  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
  {
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
    uwTickPrio = TickPriority;
  }
  else
  {
    return HAL_ERROR;
  }

  /* Return function status */
  return HAL_OK;
}

uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
   return SysTick_Config(TicksNumb);
}

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}

可以看到

HAL_SYSTICK_Config(SystemCoreClock / (1000UL / (uint32_t)uwTickFreq))

括号里的(SystemCoreClock / (1000UL / (uint32_t)uwTickFreq)) 会被设置为SysTick的LOAD寄存器,即重装载值。

SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */

Cortex M7内核手册有对Systick 的控制和状态寄存器的介绍。

bit2 CLKSOURCE 表明了Systick的时钟源。

为1 时,时钟源为处理器时钟,即系统时钟,也就是不分频或者说1分频。

为0 时,时钟源为外部时钟,也就是系统时钟的8分频。

而Systick的控制与状态寄存器的时钟源选择位设为1。

SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk,即使用系统时钟。

不管选择不分频还是8分频,生成的代码都是这样的,也就是SysTick一直使用的是系统时钟。

配置时钟源HAL_SYSTICK_CLKSourceConfig

其实HAL是有选择时钟源的函数的,就是HAL_SYSTICK_CLKSourceConfig。

源码如下:

c 复制代码
/**
  * @brief  Configures the SysTick clock source.
  * @param  CLKSource specifies the SysTick clock source.
  *         This parameter can be one of the following values:
  *             @arg SYSTICK_CLKSOURCE_HCLK_DIV8: AHB clock divided by 8 selected as SysTick clock source.
  *             @arg SYSTICK_CLKSOURCE_HCLK: AHB clock selected as SysTick clock source.
  * @retval None
  */
void HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource)
{
  /* Check the parameters */
  assert_param(IS_SYSTICK_CLK_SOURCE(CLKSource));
  if (CLKSource == SYSTICK_CLKSOURCE_HCLK)
  {
    SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK;
  }
  else
  {
    SysTick->CTRL &= ~SYSTICK_CLKSOURCE_HCLK;
  }
}

我们可以看到 ,配置时钟源也就是设置状态与控制寄存器SysTick->CTRL。

但是这个函数从未被调用。

继续文件搜索这个函数

在stm32h7xx_hal_cortex.c文件中 ,发现了这么一段注释:

You can change the SysTick Clock source to be HCLK_Div8 by calling the macro

HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8) just after the

HAL_SYSTICK_Config() function call. The HAL_SYSTICK_CLKSourceConfig() macro is defined

inside the stm32h7xx_hal_cortex.h file.

你可以通过在这个函数HAL_SYSTICK_Config()之后调用HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8)来更改SysTick的时钟源为HCLK_Div8 。

验证

我们来实验一下

main中的大循环里只有一个点灯程序,每1s切换一次。

修改HAL_SYSTICK_Config函数,在SysTick_Config之后调用HAL_SYSTICK_CLKSourceConfig。

c 复制代码
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
    //return SysTick_Config(TicksNumb);

    uint32_t ret = 0;
    ret = SysTick_Config(TicksNumb);
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);
    return ret;
}

现象:led灯由原来的1s切换一次,变为8s切换一次。

这是因为SysTick的频率变为原来的1/8。

但是重装载值依然为原来1ms时的装载值(SystemCoreClock / (1000UL / (uint32_t)uwTickFreq)) 。

也就是重装载值没变,但是速度变慢了8倍。

将(SystemCoreClock / (1000UL / (uint32_t)uwTickFreq))修改为

(SystemCoreClock/ 8 / (1000UL / (uint32_t)uwTickFreq))。

现象:led灯1s切换一次。

相关推荐
LCG元25 分钟前
STM32F103 硬件 I2C 通信全流程调试实录:从寄存器配置到总线死锁恢复
stm32·单片机·嵌入式硬件
LCG元43 分钟前
STM32L4 ADC+DMA 多通道采集实战:从 CubeMX 配置到 VREFINT 电压校准
stm32·单片机·嵌入式硬件
单片机仿真设计1 小时前
【proteus仿真】基于 STM32 快递运输避障小车设计(仿真图+程序)
stm32·单片机·嵌入式硬件·proteus·毕设
lhbweilai1 小时前
工业互联网(二)——USB 设备编程(一)
单片机·嵌入式硬件
Capricorn19882 小时前
Bug排障实录:Software 3.0 遭遇文献幻觉?知芽 Notebook Skill 底层架构解析
人工智能·笔记·架构·bug·论文笔记
国科安芯2 小时前
低轨卫星姿轨控系统中抗辐射MCU的选型依据与工程实践
单片机·嵌入式硬件·算法·架构·系统安全·低轨卫星·抗辐射
国科安芯2 小时前
卫星电源管理系统中高可靠MCU的功耗特性与电源监控功能分析
人工智能·单片机·嵌入式硬件·mcu·安全·电源管理系统·抗辐射
qq_402995753 小时前
诊断协议栈配置Agent
arm开发·人工智能·stm32·单片机·mcu
西城微科方案开发3 小时前
制氧机方案——方案开发商西城微科
单片机·嵌入式硬件
LCG元3 小时前
STM32L4 STOP2 模式下 LPUART 串口唤醒:三种唤醒方式实测对比与避坑指南
stm32·单片机·嵌入式硬件