【STM32】Systick滴答定时器-延时函数详细解析

SysTick 定时器详解(适用于 Cortex-M3/M4)

SysTick 定时器是 Cortex-M3/M4 系列内核的一个非常重要的 内置定时器资源,在 裸机延时RTOS 系统(如 uC/OS、FreeRTOS)调度心跳 等场景中广泛使用。

什么是 SysTick?

SysTick(System Tick Timer) 是 ARM Cortex-M 内核提供的一个 专用 24位递减定时器,用于产生周期性中断。

内置于 ARM Cortex-M3/M4 内核中(不是外设)
24位递减计数器,最大值为 0xFFFFFF(约 16M)
可以配置为 定时中断,用作系统心跳(Tick)
与外设定时器相比,不占用外设资源

Systick定时器,是一个简单的定时器,对于CM3,CM4内核芯片,都有Systick定时器。Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用SystickUCOS心跳时钟。

Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。

SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。

Systick中断的优先级也可以设置。

SysTick 相关寄存器和用途

用途说明
延时函数在裸机程序中用于微秒、毫秒级延迟
系统心跳在 RTOS 中用于任务调度(如 uC/OS、FreeRTOS)
系统时间戳提供毫秒级系统时基
定时轮询周期性执行某些任务(如 LED 闪烁、按键扫描)

4个Systick寄存器

寄存器名称地址偏移功能
CTRL0x00控制与状态寄存器
LOAD0x04计数初始值(定时周期)
VAL0x08当前计数值(递减)
CALIB0x0C校准值(可选,不常用)

SysTick 时钟源:AHB/8(默认) / AHB(需配置)

SysTick 控制和状态寄存器- CTRL

在这里插入图片描述

对于STM32,外部时钟源是 HCLK(AHB总线时钟)的1/8
内核时钟是 HCLK时钟
配置函数:SysTick_CLKSourceConfig();

SysTick 重装载数值寄存器- LOAD

在这里插入图片描述

SysTick 当前值寄存器- VAL

在这里插入图片描述


Systick库函数

这部分内容涵盖了 SysTick 定时器在 STM32 标准固件库中的使用方法,包括:

  • Systick 的配置函数(SysTick_Config)
  • 时钟源选择函数(SysTick_CLKSourceConfig)
  • 中断实现的非阻塞 delay
  • SysTick_Handler() 中断处理函数
  • 结合 main() 主函数的使用示例说明,便于工程实践
固件库中的Systick相关函数:

SysTick_CLKSourceConfig() //Systick时钟源选择 —> 存在于 misc.c文件中 —> 功能:一步配置 SysTick:加载值、启动、开启中断
SysTick_Config(uint32_t ticks) //初始化 systick,时钟为HCLK,并开启中断
-------------------------------------------------------- // 存在于 core_cm3.h/core_cm4.h 文件中
-------------------------------------------------------- // 功能:手动设置 SysTick 使用的时钟源(HCLK or HCLK/8)

SysTick_Config 的核心逻辑解析
SysTick_Config(SystemCoreClock / 1000);

假设 SystemCoreClock = 72MHz,则:

  • 取值为 72000,意味着 每 1ms 触发一次中断
  • 设置了:
    • LOAD = 72000 - 1
    • 清空 VAL
    • 启动 SysTick 和中断
    • 设置中断优先级为最低
Systick中断服务函数:

void SysTick_Handler(void);


SysTick_CLKSourceConfig 的作用

默认 SysTick 使用的是 HCLK / 8 时钟(即 72MHz / 8 = 9MHz),你可以通过切换为 HCLK(全速时钟) 提高精度。

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); // 设置为 HCLK
SysTick_CLKSourceConfig 函数:
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
  /* Check the parameters */
  assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));

  if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
  {
    SysTick->CTRL |= SysTick_CLKSource_HCLK;
  }
  else
  {
    SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
  }
}
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);         /* Reload value impossible */
   
 /* set reload register */                                                            
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; 
/* set Priority for Cortex-M0 System Interrupts */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); 
  SysTick->VAL   = 0;                                        /* 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 (0);                                                  /* Function successful */
}

基于 SysTick 延时的实现原理(裸机)
  • 使用一个全局变量 TimingDelay 来计数
  • 每次中断(1ms)时 TimingDelay--
  • Delay(n) 调用后进入循环,直到 TimingDelay == 0

在这里插入图片描述
例子:生成 1ms 的中断 ↓

假设:系统主频 = 72MHz,SysTick 使用 AHB/8,则计数频率:

72 MHz / 8 = 9 MHz

要得到 1ms 的中断周期:

LOAD = 9,000 - 1 = 8999

代码示例:

void SysTick_Init(void)
{
    SysTick->LOAD = 8999;                 // 1ms
    SysTick->VAL = 0;                     // 清零计数器
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |   // 使用 AHB(或 AHB/8)
                    SysTick_CTRL_TICKINT_Msk |     // 使能中断
                    SysTick_CTRL_ENABLE_Msk;       // 启动定时器
}

中断服务函数:

void SysTick_Handler(void)
{
    // 每1ms触发一次
    system_ticks++;   // 自增系统时基
}

SysTick 在 RTOS 中的作用(如 uC/OS、FreeRTOS):

RTOS的“心跳” --->	SysTick 提供周期性中断,每次中断触发调度器
时间片轮转    --->	控制任务切换的时间片
延时函数      --->	实现 OSTimeDly() 或 vTaskDelay() 等函数

uC/OS 使用 SysTick 的配置逻辑:

void OS_CPU_SysTickInit(void)
{
    uint32_t ticks = SystemCoreClock / OS_TICKS_PER_SEC;

    SysTick->LOAD  = ticks - 1;
    SysTick->VAL   = 0;
    SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                     SysTick_CTRL_TICKINT_Msk   |
                     SysTick_CTRL_ENABLE_Msk;
}

⚠️ 注意:SysTick 中断优先级应设置为最低,否则会影响实时性。

✅ 优势:

  1. 非阻塞式,无需使用外设定时器
  2. 延时精度可达 1ms,可再优化为微秒级

🧱 SysTick 延时的局限性与改进思路

问题改进思路
阻塞型延时(while 等待)可改为非阻塞状态机结构
精度限制为 1ms使用 DWT 寄存器实现微秒延时(Cortex-M3/M4)
只能做一个 delay改为多个定时器变量,拓展为软件定时器

用中断的方式实现delay延时
static __IO uint32_t TimingDelay;
void Delay(__IO uint32_t nTime)
{ 
   TimingDelay = nTime;
   while(TimingDelay != 0);
}
void SysTick_Handler(void)
{
    if (TimingDelay != 0x00) 
     { 
       TimingDelay--;
     }
}
 int main(void)
 {if (SysTick_Config(SystemCoreClock / 1000)) //systick时钟为HCLK,中断时间间隔1ms
     {
     while (1);
     }
    while(1)
     { Delay(200);//2ms}
}

完整使用模板(适合工程移植)
#include "stm32f10x.h"

static __IO uint32_t TimingDelay;

void Delay(__IO uint32_t nTime)
{
    TimingDelay = nTime;
    while(TimingDelay != 0);
}

void SysTick_Handler(void)
{
    if (TimingDelay != 0)
        TimingDelay--;
}

int main(void)
{
    SystemInit();  // 初始化系统时钟
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);  // 可选,设置 SysTick 时钟源为 HCLK
    SysTick_Config(SystemCoreClock / 1000);  // 配置为 1ms 中断周期

    while (1)
    {
        // 示例延时
        Delay(1000); // 延时 1000ms
        // 执行某个任务
    }
}

使用时钟过程中的常见问题与注意事项:

中断不触发 		--->		没有设置 TICKINT 或未使能 NVIC
延时不准 		--->		主频设置错误,或 LOAD 值计算错误
优先级导致系统卡死 --->		SysTick 优先级过高,打断关键任务
占用系统资源 		--->		在裸机中频率太高会增加中断负载

SysTick 与外设定时器相比仍存在诸多不足之处:

对比项SysTick外设定时器(TIMx)
来源内核自带外设模块
通道数1 个多通道(支持 PWM、捕获)
中断用途系统 Tick、延时精确时间控制、PWM、输入捕获
配置复杂度简单较复杂
精度中等高(支持预分频器)

总而言之,SysTick 是一个高效、轻量级的定时器,适合用于系统心跳、RTOS调度和短延时控制,是 Cortex-M3/M4 单片机中不可忽视的核心资源。

SysTick_Config() 是 STM32 标准库中用于快速配置 SysTick 的核心函数,结合中断回调函数 SysTick_Handler() 和 全局计数变量可简洁实现 1ms 精度的延时控制与系统时间基准。

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!

### STM32 SysTick滴答定时器使用教程 #### 介绍 SysTick 是 ARM Cortex-M 系列处理器中的一个简单易用的倒计数定时器,广泛应用于嵌入式系统中作为时间基准。该定时器包含四个主要寄存器用于配置和监控其行为[^1]。 #### 主要特性 - **24位递减计数器**:当计数值达到零时触发中断并重新加载预设值。 - **灵活的时间间隔设置**:支持从几微秒到几百毫秒不等的各种延迟需求。 - **低开销实现精准延时功能**:相比其他硬件定时器更加高效地利用 CPU 资源[^3]。 #### 初始化过程 为了初始化 SysTick 定时器,在程序启动阶段通常会调用 `delay_init()` 函数来计算不同单位时间内所需的滴答次数,并关闭外部时钟源以便于后续调整[^4]: ```c void delay_init(){ SysTick->CTRL &= ~(1<<2); // 关闭外设时钟 fac_s = SystemCoreClock / 8; // 计算每秒钟对应的 Systick 数量 fac_ms = fac_s / 1000; // 每毫秒对应多少次 Systick 中断 fac_us = fac_ms / 1000; // 每微秒对应多少次 Systick 中断 } ``` 上述代码片段展示了如何基于当前系统的主频 (`SystemCoreClock`) 来确定每一秒、每一毫秒以及每一微秒所经历的具体滴答数目。这里假设每次溢出产生的事件频率大约为主频除以八的结果。 #### 延迟函数设计 通过设定合适的重装载值 (RELOAD),可以精确控制等待多久才会发生下一次中断请求 IRQ 或者单纯依靠轮询方式查询是否已经到达目标时刻。下面给出了一种常见的微秒级延迟算法实现方法: ```c // 微秒级别延时函数定义 void delay_us(u32 nus){ u32 temp; SysTick->LOAD=nus*fac_us; // 设置比较匹配值 SysTick->VAL=0x00; // 清空当前计数值 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; // 开启计数 do{ temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); // 判断是否已过期 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;// 关闭计数 SysTick->VAL=0X00; // 清空计数器 } ``` 此部分实现了对指定时间段内的阻塞型等待逻辑,其中涉及到几个重要的操作步骤: - 配置 LOAD 寄存器为期望经过的时间长度; - 将 VAL 设定成初始状态(通常是全清零); - 启动控制器开始向下计数直至归零; - 不断循环检测直到满足条件为止; - 最终停止计数并将内部变量复原至默认态。 #### 应用场景举例 除了基本的软件延时之外,SysTick 还经常被用来充当 RTOS 的调度依据或是驱动某些周期性的任务执行,如 LED 流水灯效果展示、传感器数据采集等场合均可见其身影。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫猫的小茶馆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值