目录

1.  SysTick 简介

2.  SysTick 工作原理

3.  中断

4.  工程配置

5.  代码编写


1.  SysTick 简介

        SysTick— 系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。系统定时器从硬件特性来看:

  • 它是一个24 位向下递减计数器,最大计数值为 2²⁴‑1;
  • 计数时钟通常来源于系统时钟 SYSCLK,常见配置为 72MHz;
  • 每计数一次所消耗的时间为 1/SYSCLK,计时精度由系统主频决定。

        由于属于内核级外设,只要是 CM3 内核的单片机,无论厂家、型号差异,其 SysTick 的寄存器定义、工作机制完全一致。

2.  SysTick 工作原理

        SysTick 的核心工作机制非常简洁,以向下计数 + 中断触发为核心:

计数逻辑:SysTick 从程序设定的重装载值开始,每来一个时钟周期,计数值自动减 1。当计数值递减到 0 时,会自动触发一次 SysTick 中断,并在中断后自动重新加载计数值,继续循环计数。

中断触发条件:当重装载数值寄存器中的值递减到 0 时,计数器溢出,硬件自动置位中断标志,并向 NVIC 提交中断请求。如果开启了 SysTick 中断,CPU 就会进入对应的中断服务函数。

循环工作机制:计数器归零后,会自动从重装载寄存器中取出预设值,重新开始新一轮递减,从而实现周期性定时、周期性中断,形成稳定的系统时基。

3.  中断

        在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行:

        在此时的断点部分,若是此时主程序还在进行例如加减乘除运算,但是突然有个更加紧急的的事情需要处理,可以在此时进行中断,优先处理更加紧急的事情,等事情处理完后,在返回主程序继续处理。

        在中断这里,我们在断点处离开,编译器会自动帮我们保护现场(当前程序),等我们回来(中断完成后),继续工作。

详细的解释可以参考:

STM32F1中断NVIC-CSDN博客

4.  工程配置

        这里不再一步一步演示步骤,详细可以参考:

STM32学习·HAL库速通篇(三)·如何在STM32CubeMX新建工程_stm32cubemx 配置cmsis库-CSDN博客

        唯一区别就是多声明了两个引脚:

        生成代码,找到 main 函数中的 HAL_init() 函数跳转(注意先编译一下):

        可以发现这样一行代码:

        起作用就是使用系统定时器(SysTick)作为时基源,并配置1毫秒的时钟节拍(复位后的默认时钟为高速内部时钟HSI),我们再次跳转看一下:

/**
  * @brief  该函数配置时基的来源。
  *         时基源被配置为生成1毫秒的时间基准,并设置专属的Tick中断优先级。
  * @note   该函数会在程序复位后由HAL_Init()自动调用,或在任何通过HAL_RCC_ClockConfig()
  *         重新配置时钟时被调用。
  * @note   在默认实现中,SysTick定时器是时基的来源。它用于按固定时间间隔生成中断。
  *         若在外设中断服务函数(ISR)中调用HAL_Delay(),需特别注意:
  *         SysTick中断的优先级必须高于(数值上更小)该外设中断。否则,调用该ISR的进程会被阻塞。
  *         该函数被声明为__weak(弱定义),以便用户文件中可以重写此实现。
  * @param  TickPriority: Tick中断的优先级。
  * @retval HAL状态(成功/失败)。
  */
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* 配置SysTick,使其每隔1毫秒触发一次中断 */
  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
  {
    return HAL_ERROR;  // 配置失败,返回错误状态
  }

  /* 配置SysTick中断的优先级 */
  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
  {
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);  // 设置SysTick中断优先级
    uwTickPrio = TickPriority;  // 保存配置的优先级值
  }
  else
  {
    return HAL_ERROR;  // 优先级值非法,返回错误状态
  }

  /* 返回函数执行状态 */
  return HAL_OK;  // 配置成功,返回正常状态
}

        其中对于 SystemCoreClock 的值,我们跳转可以发现其值为:

        那么这个 8000000 是怎么来的呢?由于我们这里使用的是高速内部晶振(HSI),其对应的复位后默认的核心时钟频率为 8MHz 也就是 8000000。

5.  代码编写

        我们要如何使用呢?首先找到 stm32f1xx_it.c 文件,这里存放的是一些中断服务函数,找到 SysTick_Handler() 函数:

        我们在 SysTick 中断服务函数编写代码,累加实现每隔500ms电平翻转一次:

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
  // 静态变量:累计SysTick中断次数(1ms/次),仅初始化一次
  static uint16_t count = 0;

  // 计数自增:每1ms加1
  count++;

  // 累计500ms(500次中断),翻转LED电平
  if(count >= 500)
  {
    count = 0; // 清零,重新计数
    // 翻转PA0/PA1/PA2电平:亮→灭、灭→亮
    HAL_GPIO_TogglePin(GPIOB, LED_R_Pin | LED_G_Pin | LED_B_Pin);
  }

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

完整工程:

基于STM32实现系统定时器点灯实验(HAL版本)资源-CSDN下载

HAL库学习笔记_时光の尘的博客-CSDN博客

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐