STM32HAL库滴答定时器(SysTick)实现1ms中断的机制详解

STM32HAL库滴答定时器(SysTick)实现1ms中断的机制详解

1. SysTick定时器基础

  • 功能定位:SysTick是Cortex-M内核自带的24位倒计时定时器,专为操作系统或实时任务设计。
  • 核心寄存器
    • LOAD:设置重载值,决定中断周期。
    • VAL:当前计数值,写入0可复位计数器。
    • CTRL:控制寄存器,配置时钟源、中断使能等。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

2. HAL库的SysTick初始化流程

HAL库通过HAL_Init()函数自动初始化SysTick,但是SystemClock_Config()修改系统时钟时,HAL库会自动更新SysTick,关键步骤如下:

2.1 调用HAL_InitTick()

传入参数为中断优先级,可在cubeMX里的NVIC配置:

// HAL库初始化函数
HAL_StatusTypeDef HAL_Init(void) {
    // ...其他初始化...
    HAL_InitTick(TICK_INT_PRIORITY);  // 配置SysTick
    return HAL_OK;
}

在这里插入图片描述
函数定义如下,主要是设置重装值和中断优先级:

__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;
}

主要调用了HAL_SYSTICK_Config()函数进行配置,参数为自动重装值,该函数定义如下:

/**
  * @brief  Initializes the System Timer and its interrupt, and starts the System Tick Timer.
  *         Counter is in free running mode to generate periodic interrupts.
  * @param  TicksNumb: Specifies the ticks Number of ticks between two interrupts.
  * @retval status:  - 0  Function succeeded.
  *                  - 1  Function failed.
  */
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 */
}

2.2 计算重载值

uint32_t ticks = SystemCoreClock / 1000;  // 根据系统时钟计算1ms所需的计数值
SysTick->LOAD  = (ticks - 1UL);          // 设置重载值(必须减1)
  • 示例:若系统时钟为72MHz,则ticks = 72000,重载值设为71999。

2.3 配置控制寄存器

SysTick->VAL   = 0UL;                     // 清空计数器
SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |  // 选择CPU时钟(HCLK)
                 SysTick_CTRL_TICKINT_Msk   |  // 使能中断
                 SysTick_CTRL_ENABLE_Msk;      // 启动定时器

2.4 关键参数定义

在这里插入图片描述
1、SystemCoreClock:系统时钟频率在这里插入图片描述
初始值是16M,但是SystemClock_Config()修改系统时钟时会自动更新这个值,上面的说明文字说明了这件事,

/** @addtogroup STM32F1xx_System_Private_Variables
  * @{
  */

  /* This variable is updated in three ways:
      1) by calling CMSIS function SystemCoreClockUpdate()
      2) by calling HAL API function HAL_RCC_GetHCLKFreq()
      3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency 
         Note: If you use this function to configure the system clock; then there
               is no need to call the 2 first functions listed above, since SystemCoreClock
               variable is updated automatically.
  */
  /* 此变量通过以下三种方式更新:
      1) 调用CMSIS函数 SystemCoreClockUpdate()
      2) 调用HAL API函数 HAL_RCC_GetHCLKFreq()
      3) 每次调用 HAL_RCC_ClockConfig() 配置系统时钟频率时
         注意:若使用此函数配置系统时钟,则无需调用前两种方式,
               因为 SystemCoreClock 变量会自动更新。
*/
uint32_t SystemCoreClock = 16000000;

具体实现可以在SystemClock_Config()函数最后看到:
在这里插入图片描述
2、uwTickFreq中断频率在这里插入图片描述
在这里插入图片描述
因此 LOAD值 =系统时钟/中断频率 - 1= 72,000,000 / 1000 - 1

3. 时钟配置后的自动调整

当用户调用SystemClock_Config()修改系统时钟时,HAL库会自动更新SysTick,该函数会在时钟配置最后调用:

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

3.1 HAL_RCC_ClockConfig()的内部操作

HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency) {
    // ...配置系统时钟...
    SystemCoreClockUpdate();  // 更新全局时钟变量
    HAL_InitTick();           // 重新初始化SysTick
    return HAL_OK;
}

具体实现:
在这里插入图片描述

3.2 动态调整示例

  • 原始时钟:HSI 8MHz → SysTick重载值为7999(1ms中断)。
  • 切换后时钟:HSE+PLL 72MHz → 重载值更新为71999,维持1ms中断周期。

4. 中断服务函数与时间管理

4.1 默认中断处理

HAL库通过这个中断实现ms级延时:

// 在stm32f1xx_it.c中定义
void SysTick_Handler(void) {
    HAL_IncTick();  // 递增全局计数器HAL_Tick
}
__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;//1ms计数加1
}
__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}
  • uwTick:32位变量,每1ms加1,用于HAL_Delay()和超时判断。

4.2 延时函数实现

记录起始计数值,获取当前计数值,当差值大于延时时间时说明延迟时间到了,退出死循环。

__weak void HAL_Delay(uint32_t Delay)
{
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = Delay;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)
  {
    wait += (uint32_t)(uwTickFreq);
  }

  while ((HAL_GetTick() - tickstart) < wait)
  {
  }
}

5. 关键配置参数详解

参数说明典型值
SystemCoreClock系统时钟频率(HAL库通过SystemCoreClockUpdate()更新)72,000,000 Hz (72MHz)
ticks每毫秒所需的时钟周期数(= SystemCoreClock / 1000)72,000
LOAD寄存器实际写入的重载值(ticks - 1)71,999
CTRL寄存器配置0x7:启用中断、选择CPU时钟、启动定时器0x00000007

6. 验证与调试方法

6.1 GPIO翻转测试

void SysTick_Handler(void) {
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);  // PA5每1ms翻转一次
    HAL_IncTick();
}
  • 示波器测量:PA5输出方波,周期应为2ms(每个中断翻转一次)。

6.2 代码调试

  • 检查SystemCoreClock

    printf("SystemCoreClock = %lu Hz\n", SystemCoreClock);  // 确认与预期时钟一致
    
  • 查看HAL_Tick递增
    在调试器中观察HAL_Tick变量是否每1ms增加1。

7. 常见问题与解决

问题现象可能原因解决方案
延时函数不准确系统时钟配置错误检查SystemClock_Config()是否正确配置时钟
SysTick中断未触发全局中断未使能main()中调用__enable_irq()
HAL_Tick不更新SysTick中断服务函数未执行检查是否重写了SysTick_Handler()未调用HAL_IncTick()

8. 自定义中断周期

若要修改SysTick中断周期(如改为2ms):

// 在main()中重新配置
uint32_t new_ticks = SystemCoreClock / 500;  // 500Hz → 2ms
SysTick->LOAD = new_ticks - 1;
SysTick->VAL = 0;
// 注意:需同步修改HAL_Delay()逻辑或避免使用HAL库延时函数

9. 总结

  • 自动化管理:HAL库通过HAL_Init()HAL_RCC_ClockConfig()自动完成SysTick的初始化和动态调整,确保1ms中断周期的准确性。
  • 依赖系统时钟:SysTick的配置直接关联SystemCoreClock,时钟配置错误将导致定时异常。
  • 调试建议:通过GPIO翻转和调试器监控,可快速定位时钟或中断配置问题。
HAL中的滴答定时器是指SysTick定时器,它是CM4内核中的一个外设,内嵌在NVIC中。SysTick定时器是一个24bit的向下递减的计数器,计数器每计数一次的时间为1/SYSCLK,一般我们设置系统时钟SYSCLK等于180M。当重装载数值寄存器的值递减到0的时候,系统定时器就产生一次中断,以此循环往复。SysTick定时器常用来做延时,或者实时系统的心跳时钟,以节省MCU资源,不用浪费一个定时器。在HAL中,可以通过HAL_InitTick()函数来配置滴答定时器的重装载值和中断优先级。该函数会根据系统时钟频率SystemCoreClock来重新配置滴答定时器的重装载值,以决定产生每个中断的时间。同时,该函数还会设置定时器中断优先级。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [STM32F407HAL-10.滴答定时器](https://blog.youkuaiyun.com/qq_41422043/article/details/100106891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【STM32HAL-系统滴答定时器SysTick](https://blog.youkuaiyun.com/qq_45607873/article/details/124654107)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值