systemtick使用详解章
下面我将详细解释如何配置 SysTick 定时器为 1ms 中断,并提供完整的实现代码。这里以 ARM Cortex-M 系列处理器为例,但概念适用于大多数微控制器。
1. 计算重装载值
系统时钟为 106MHz,要产生 1ms 中断:
重装载值 = (系统时钟频率 / 1000) - 1= (106,000,000 / 1000) - 1= 106,000 - 1 = 105,999
- 初始化 SysTick 定时器
#include <stdint.h>// 系统时钟频率定义
#define SYS_CLOCK_HZ 106000000u// 定义 SysTick 寄存器
#define SYSTICK_BASE 0xE000E010
typedef struct {volatile uint32_t CTRL; // 控制状态寄存器volatile uint32_t LOAD; // 重装载值寄存器volatile uint32_t VAL; // 当前值寄存器volatile uint32_t CALIB; // 校准值寄存器
} SysTick_Type;#define SysTick ((SysTick_Type *)SYSTICK_BASE)// 初始化 SysTick
void SysTick_Init(void) {// 计算1ms中断的重装载值uint32_t reload_value = (SYS_CLOCK_HZ / 1000) - 1;// 禁用SysTickSysTick->CTRL = 0;// 设置重装载值SysTick->LOAD = reload_value;// 清除当前值计数器SysTick->VAL = 0;// 启用SysTick,使用处理器时钟,启用中断SysTick->CTRL = (1 << 2) | // CLKSOURCE = 处理器时钟(1 << 1) | // TICKINT = 启用中断(1 << 0); // ENABLE = 启用SysTick
}
- 实现 SysTick 中断处理函数
// 系统tick计数器(volatile确保编译器不优化)
volatile uint32_t system_tick = 0;// SysTick中断处理函数
void SysTick_Handler(void) {// 每次中断增加tick计数(增加每毫秒的时钟周期数)system_tick += (SYS_CLOCK_HZ / 1000);
}
- 实现获取当前tick的函数
// 获取当前系统tick
uint32_t Get_Current_Tick(void) {return system_tick;
}
- 在启动文件中配置中断向量
; 中断向量表
g_pfnVectors:.word _estack.word Reset_Handler....word SysTick_Handler ; SysTick中断...
- 启用全局中断
// 启用全局中断的函数(具体实现取决于MCU)
void Enable_Global_IRQ(void) {__asm volatile ("cpsie i"); // 对于ARM Cortex-M
}
完整示例代码
#include <stdint.h>// 系统时钟频率定义
#define SYS_CLOCK_HZ 106000000u// 定义 SysTick 寄存器
#define SYSTICK_BASE 0xE000E010
typedef struct {volatile uint32_t CTRL;volatile uint32_t LOAD;volatile uint32_t VAL;volatile uint32_t CALIB;
} SysTick_Type;#define SysTick ((SysTick_Type *)SYSTICK_BASE)// 系统tick计数器
volatile uint32_t system_tick = 0;// 初始化 SysTick
void SysTick_Init(void) {// 计算1ms中断的重装载值uint32_t reload_value = (SYS_CLOCK_HZ / 1000) - 1;// 禁用SysTickSysTick->CTRL = 0;// 设置重装载值SysTick->LOAD = reload_value;// 清除当前值计数器SysTick->VAL = 0;// 启用SysTick,使用处理器时钟,启用中断SysTick->CTRL = (1 << 2) | (1 << 1) | (1 << 0);
}// SysTick中断处理函数
void SysTick_Handler(void) {// 增加每毫秒的时钟周期数system_tick += (SYS_CLOCK_HZ / 1000);
}// 获取当前系统tick
uint32_t Get_Current_Tick(void) {return system_tick;
}// 启用全局中断
void Enable_Global_IRQ(void) {__asm volatile ("cpsie i");
}// 主程序
int main(void) {// 初始化SysTick定时器SysTick_Init();// 启用全局中断Enable_Global_IRQ();// 其他初始化代码...while(1) {// 主循环代码...}
}
关键注意事项
中断优先级:SysTick 通常具有可配置的中断优先级,确保它不会被其他中断长时间阻塞
32位计数器溢出:
106MHz 下,system_tick 每 1ms 增加 106,000
32位计数器溢出周期 = 2³² / 106,000 ≈ 40秒
在时间比较中,使用 (current_time - last_feed_time) >= interval 可以正确处理溢出
中断延迟:
实际中断间隔可能有微小变化(通常 < 1%)
对于喂狗操作,这种精度通常足够
初始化顺序:
先初始化 SysTick,再启用全局中断
确保在调用 Get_Current_Tick() 前已初始化
功耗考虑:
SysTick 中断会定期唤醒 CPU
在低功耗应用中,可能需要调整策略