文章目录
- [一、SysTick 核心寄存器](#一、SysTick 核心寄存器)
-
- [ SysTick 寄存器地址固定在 0xE000E010,核心 4 个寄存器:](# SysTick 寄存器地址固定在 0xE000E010,核心 4 个寄存器:)
- [ 核心规则:](# 核心规则:)
- [二、SysTick 寄存器编程](#二、SysTick 寄存器编程)
-
- [ 1、精准 us/ms 延时函数(非中断方式)](# 1、精准 us/ms 延时函数(非中断方式))
- [ 2、SysTick 周期性中断(1ms 中断)](# 2、SysTick 周期性中断(1ms 中断))
- 三、小结
SysTick(系统滴答定时器)是 Cortex-M 内核自带的外设,无需依赖 STM32 片上外设,常用来实现精准延时、系统心跳(如 RTOS 时钟节拍)。
一、SysTick 核心寄存器
SysTick 寄存器地址固定在 0xE000E010,核心 4 个寄存器:

核心规则:
SysTick 时钟源默认是系统时钟(72MHz)(STM32F103);
递减计数:从 LOAD 值减到 0,触发 COUNTFLAG(或中断),然后自动重载;
延时计算公式:LOAD = (时钟频率 × 延时时间) - 1(减 1 是因为从 0 开始计数)。
二、SysTick 寄存器编程
1、精准 us/ms 延时函数(非中断方式)
实现微秒 / 毫秒级阻塞延时,无中断、占用 CPU 但简单高效。
c
#include "stm32f10x.h"
// 全局变量:SysTick时钟频率(72MHz)
#define SYSTICK_CLK 72000000
/**
* @brief SysTick初始化(关闭中断,仅用于延时)
*/
void SysTick_Init(void)
{
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 先关闭SysTick
SysTick->VAL = 0; // 清空当前计数值
}
/**
* @brief SysTick微秒延时(寄存器版)
* @param us:延时微秒数(最大支持 999999us,超过需拆分)
*/
void SysTick_Delay_us(uint32_t us)
{
uint32_t load_val;
// 1. 计算重装载值:72MHz → 1us需要计数72次
load_val = (SYSTICK_CLK / 1000000) * us - 1;
// 防止重载值超过SysTick_LOAD_RELOAD_Msk(24位最大值)
if(load_val > SysTick_LOAD_RELOAD_Msk)
{
load_val = SysTick_LOAD_RELOAD_Msk;
}
// 2. 设置重装载值
SysTick->LOAD = load_val;
// 3. 清空当前计数值
SysTick->VAL = 0;
// 4. 开启SysTick(关闭中断,时钟源=系统时钟)
SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
// 5. 等待计数完成(COUNTFLAG位为1)
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
// 6. 关闭SysTick
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
/**
* @brief SysTick毫秒延时(寄存器版)
* @param ms:延时毫秒数
*/
void SysTick_Delay_ms(uint32_t ms)
{
// 拆分长延时,避免LOAD值溢出(24位最大值约186ms@72MHz)
while(ms--)
{
SysTick_Delay_us(1000);
}
}
/**
* @brief 测试:LED闪烁(PC13)
*/
void GPIO_Init_LED(void)
{
// 开启GPIOC时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
// 配置PC13为推挽输出
GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13);
GPIOC->CRH |= GPIO_CRH_MODE13_0;
GPIOC->ODR |= GPIO_ODR_ODR13; // 初始熄灭
}
// 主函数测试
int main(void)
{
SysTick_Init(); // 初始化SysTick
GPIO_Init_LED(); // 初始化LED
while(1)
{
GPIOC->ODR ^= GPIO_ODR_ODR13; // 翻转LED
SysTick_Delay_ms(500); // 延时500ms
}
}
2、SysTick 周期性中断(1ms 中断)
用于系统节拍(如 RTOS 时钟、定时任务),中断方式不阻塞 CPU。
c
#include "stm32f10x.h"
// 全局变量:1ms中断计数
uint32_t SysTick_Count = 0;
/**
* @brief SysTick初始化(1ms中断)
*/
void SysTick_IRQ_Init(void)
{
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭SysTick
SysTick->VAL = 0; // 清空计数值
// 1. 设置1ms重装载值:72MHz → 1ms需要72000次计数
SysTick->LOAD = (SYSTICK_CLK / 1000) - 1;
// 2. 配置NVIC(SysTick中断优先级)
// SysTick是内核中断,优先级由SCB->SHPR3的bit24-31配置
SCB->SHPR3 |= (0x20 << 24); // 抢占优先级2(高4位有效)
// 3. 开启SysTick中断 + 开启SysTick + 时钟源=系统时钟
SysTick->CTRL = SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
}
/**
* @brief SysTick中断服务函数(内核中断,名称固定)
*/
void SysTick_Handler(void)
{
// 清除中断标志(读COUNTFLAG自动清,或写VAL=0)
SysTick->VAL = 0;
// 业务逻辑:1ms计数+1
SysTick_Count++;
}
/**
* @brief 基于SysTick计数的ms延时(非阻塞版)
* @param ms:延时毫秒数
*/
void SysTick_Delay_ms_IRQ(uint32_t ms)
{
uint32_t target = SysTick_Count + ms;
while(SysTick_Count < target);
}
// 主函数测试
int main(void)
{
SysTick_IRQ_Init(); // 初始化SysTick中断(1ms)
GPIO_Init_LED(); // 复用上面的GPIO_Init_LED函数
while(1)
{
GPIOC->ODR ^= GPIO_ODR_ODR13; // 翻转LED
SysTick_Delay_ms_IRQ(500); // 基于中断的500ms延时
}
}
三、小结
SysTick 寄存器编程核心:配置LOAD(重装载值)→ 清空VAL → 控制CTRL(开启 / 中断);
非中断版延时简单高效,适合短延时;中断版不阻塞 CPU,适合系统节拍;
注意 24 位 LOAD 寄存器的溢出限制,长延时需拆分处理,且时钟源需与计算值匹配。