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系统中,都采用Systick做UCOS心跳时钟。
Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。
Systick中断的优先级也可以设置。
SysTick 相关寄存器和用途
用途 | 说明 |
---|---|
延时函数 | 在裸机程序中用于微秒、毫秒级延迟 |
系统心跳 | 在 RTOS 中用于任务调度(如 uC/OS、FreeRTOS) |
系统时间戳 | 提供毫秒级系统时基 |
定时轮询 | 周期性执行某些任务(如 LED 闪烁、按键扫描) |
4个Systick寄存器
寄存器名称 | 地址偏移 | 功能 |
---|---|---|
CTRL | 0x00 | 控制与状态寄存器 |
LOAD | 0x04 | 计数初始值(定时周期) |
VAL | 0x08 | 当前计数值(递减) |
CALIB | 0x0C | 校准值(可选,不常用) |
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 中断优先级应设置为最低,否则会影响实时性。
✅ 优势:
- 非阻塞式,无需使用外设定时器
- 延时精度可达 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 精度的延时控制与系统时间基准。
以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!