STM32CubeMX滴答定时器Systick的相关问题分析(含HAL_Delay函数分析与重写、微秒延时函数delay_us实现原理)

 参考资料:1. 正点原子

2.http://t.csdnimg.cn/dhPeN

摘要:本篇文章首先讨论了Systick的配置过程、HAL_Delay()函数的实现原理并分析了中断服务函数不能使用延时函数的原因,其次讨论了正点原子的delay_us()函数实现原理以及在使用delay_us()函数时必须重写HAL_Delay()的原因。

作者水平有限,非常欢迎各路大仙批阅指正文章中可能会分析的不恰当之处!非常感谢!!

一、Systick初始化过程

1.1 Systick 寄存器简介

        Systick系统滴答定时器是Cortex-M3架构的资源,参阅《Cortex-M3开发手册》,Systick有校准寄存器、重装载寄存器、当前计数值寄存器以及状态及控制寄存器,程序中只对其重装载寄存器SysTick -> LOAD; 当前计数值寄存器Systick -> VAL; 以及控制及状态寄存器Systick -> CTRL进行了配置。我们先来看一下这些寄存器的定义:

这里重点强调一下CTRL控制及状态寄存器的bit2,在STM32中,当配置Systick -> CTRL的bit2 = 0时,选择外部时钟源STCLK,而这里的STCLK即为CUBEMX时钟树上“To Cortex System timer(MHz)”,虽然CubeMX中可以选择是否配置八分频,但是实际上只要CTRL的bit2为0,STCLK的时钟就一定是HCLK的八分频,不管你配没配置八分频...(可能是bug吧)

如果配置Systick -> CTRL的bit2 = 1,则对应时钟树为FCLK:

综上,Systick的时钟源要么是HCLK(CTRL的bit2 = 1),要么是HCLK的八分频(CTRL的bit2 = 0)

注意!无论是在CUBEMX还是STM32手册里都明确说明了HSI固定为8M,但是在HAL_Init()程序的注释中表明HSI为16M!!!且后续Systick的配置均以16M为基准进行的(后续有分析)。因此我大胆猜测是STM32后续更新了HSI为16M但是没有更新CUBEMX和手册?

1.2 上电启动后至系统时钟初始化之前的阶段

在这个阶段,系统时钟由HSI提供。在CubeMX生成的代码中,会自动生成一个HAL_Init()函数

它的作用是初始化HAL库,而且必须在main中第一个被执行。其中一个很重要的功能是初始化Systick定时器(使用HSI时钟源(16M),并产生1ms的中断,中断分组为4)

HAL_StatusTypeDef HAL_Init(void)
{
  /* Configure Flash prefetch */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
    defined(STM32F102x6) || defined(STM32F102xB) || \
    defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
    defined(STM32F105xC) || defined(STM32F107xC)

  /* Prefetch buffer is not available on value line devices */
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */

  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
  HAL_InitTick(TICK_INT_PRIORITY);

  /* Init the low level hardware */
  HAL_MspInit();

  /* Return function status */
  return HAL_OK;
}

其中SysTick的配置通过HAL_InitTick(TICK_INT_PRIORITY)配置,TICK_INT_PRIPORITY通过宏定义设置为15(在中断分组为4的情形下,为系统最低优先级

我们来具体看一下这个函数:

简而言之这个函数就干了俩事:1. 配置SysTick产生1ms中断 2. 配置SYsTick优先级(抢占优先级为15)

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Configure the SysTick to have interrupt in 1ms time basis*/
  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / 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;
}

优先级配置我们在这里不做讨论,重点讨论SysTick的配置函数HAL_SYSTICK_Config()

其中传递的参数: 

uint32_t SystemCoreClock = 16000000;(印证了HSI实际就是16M

uwTickFreq = 1U;

那么计算传递的参数为:16M/(1000/1)= 16K

uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
   return SysTick_Config(Ticks
<think>好的,我现在需要帮助用户在STM32 HAL库中使用SysTick定时器进行时间管理和延迟处理。首先,我应该回顾一下用户提供的引用内容,看看里面有哪些关键信息。 根据引用[1],SysTick定时器是捆绑在NVIC中的,属于内核级别的定时器,可以用来做延时或作为实时系统的心跳时钟。HAL库已经初始化了SysTick,并且默认配置为1ms中断一次。同时,用户可以通过HAL_SYSTICK_Config()函数来配置时钟频率。还有提到HAL_Delay()函数就是基于SysTick实现的毫秒级延迟。 引用[2]提到SysTick是一个24位倒计时定时器,自动重载初值,并且可以工作在睡眠模式。此外,用户可以通过HAL_SYSTICK_CLKSourceConfig()选择时钟源,比如HCLK或HCLK/8。 引用[3]详细说明了SysTick的控制寄存器、重载寄存器和当前值寄存器,以及如何通过HAL_SYSTICK_Callback()实现自定义中断处理。另外,还提到了如何实现微秒延时,需要手动操作寄存器。 接下来,我需要将这些信息整合成一个清晰的步骤指南,帮助用户实现时间管理和延迟处理。可能需要分几个部分:初始化、使用HAL库的延时函数、自定义微秒延时、中断处理,以及注意事项。 首先,HAL库已经默认初始化了SysTick,所以用户可能不需要额外初始化,但如果需要更改时钟源,可以使用HAL_SYSTICK_CLKSourceConfig()。例如,选择HCLK作为时钟源可以提高定时器精度。 然后,使用HAL_Delay()进行毫秒级延迟是最简单的方法,但要注意不能在中断中调用,且会被中断影响。接着,用户可能需要微秒延时,这里需要手动操作寄存器,比如设置LOAD和VAL寄存器,然后等待计数完成。这部分需要注意时钟频率的计算,比如如果系统时钟是72MHz,那么每微秒的计数值是72。 另外,如果用户需要更精确的时间管理,可以读取SysTick的当前值寄存器VAL,结合重载值来计算经过的时间。例如,通过比较两次读取的VAL值来计算时间差。 在中断处理方面,SysTick的中断服务函数会调用HAL_SYSTICK_IRQHandler(),用户可以通过重写HAL_SYSTICK_Callback()函数来添加自定义的中断处理逻辑,比如维护系统时钟计数器。 注意事项方面,需要注意SysTick的优先级设置,尤其是在实时操作系统中。同时,修改HAL_Delay()的行为需要注意全局影响,比如使用FreeRTOS时的兼容性问题。 用户可能的问题包括如何调整时钟源、如何实现更精确的延时、如何处理中断等。需要确保回答覆盖这些方面,并且示例代码正确,比如微秒延时实现和中断回调的使用。 最后,生成相关问题,帮助用户进一步学习,比如优化延时精度、结合RTOS使用、调试方法等。</think>以下是在STM32 HAL库中使用SysTick进行时间管理/延迟处理的详细步骤: ### 一、SysTick基础配置 1. **系统默认初始化** HAL库在`HAL_Init()`中已自动完成SysTick配置: ```c HAL_SYSTICK_Config(SystemCoreClock / 1000); // 1ms中断周期 ``` 默认使用处理器时钟(HCLK)作为时钟源[^1][^2] 2. **修改时钟源(可选)** 如需更改时钟分频: ```c HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8); // 使用HCLK/8 ``` ### 二、时间管理实现 #### 1. 使用HAL库标准延时 ```c HAL_Delay(100); // 阻塞式延迟100ms ``` 注意: - 不能在中断服务程序中直接调用 - 受SysTick中断优先级影响[^3] #### 2. 实现微秒级延迟 ```c void delay_us(uint32_t us) { uint32_t ticks = us * (SystemCoreClock / 1000000); SysTick->LOAD = ticks; SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); SysTick->CTRL = 0; } ``` 注意:需根据实际时钟频率调整计算[^3] #### 3. 精确时间戳获取 ```c uint32_t get_tick_us(void) { register uint32_t val, load; do { val = SysTick->VAL; load = SysTick->LOAD; } while(val > load); return (load - val) * 1000 / (SystemCoreClock / 1000); } ``` ### 三、中断处理应用 1. **自定义中断回调** ```c void HAL_SYSTICK_Callback(void) { static uint32_t systick_count = 0; systick_count++; // 每1ms执行的任务 } ``` 2. **系统时钟维护** 在`stm32xxxx_hal.c`中: ```c __IO uint32_t uwTick; void SysTick_Handler(void) { HAL_IncTick(); HAL_SYSTICK_IRQHandler(); } ``` ### 四、注意事项 1. 优先级配置:SysTick中断优先级应低于其他硬件中断 2. 低功耗模式:SysTick在Sleep模式下仍可工作,但Stop模式会停止 3. 时间精度:HCLK的稳定性直接影响定时精度,建议使用外部晶振 4. 重载值范围:24位寄存器最大值为0xFFFFFF[^3]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值