
1. 系统嘀嗒时钟的基本概念系统嘀嗒时钟System Tick Clock是操作系统内核中用于驱动时间管理和任务调度的基础计时机制。它通过硬件定时器产生周期性中断每次中断称为一次“嘀嗒”Tick内核以此作为最小时间单位来推进系统时间、统计运行时长并触发周期性任务。2. 系统嘀嗒时钟的基本应用系统嘀嗒时钟虽然概念简单却是操作系统调度与时间管理的重要基础典型应用包括以下几个方面任务时间片轮转调度器以Tick为粒度统计任务已运行的时间片当前任务耗尽时间片后触发一次重新调度从而实现多任务的公平轮转。系统时间维护内核通过累计Tick数并结合时钟频率换算成实际时间用于更新系统时钟、记录文件时间戳以及提供时间相关系统调用。定时器与超时处理周期性检查内核定时器队列到期后执行回调常用于网络超时判断、资源回收和看门狗等场景。延时与睡眠控制将进程的延时请求转换为若干个Tick进程挂起后由调度器在目标Tick到达时重新唤醒。性能统计与负载计算按Tick采样CPU使用情况统计进程运行时长和系统负载为监控与调优提供基础数据。3. 时钟分析3.1 高速外部时钟在 MCU 时钟系统中外部晶振常作为高速外部时钟HSE输入而负载电容直接决定晶振能否稳定起振以及实际振荡频率。若负载电容选取不合适晶振输出会产生较大频偏进而影响后续 Systick 定时器等依赖此时钟的外设计时精度。负载电容Load CapacitanceCL是晶振数据手册中标定的、晶振正常工作时所要求的外部等效电容。常见匹配电路在晶振两端各接一个电容C1和C2负载电容的近似计算公式为CL (C1 × C2) / (C1 C2) Cstray其中Cstray表示PCB走线、晶振引脚、芯片OSC引脚、焊盘和过孔等引入的寄生电容。工程上很难直接精确测量通常先按经验值选取紧凑布局、短走线时可按2pF3pF估算一般布局按3pF5pF估算走线较长、过孔较多或引脚附近铜皮较大时可取5pF7pF。同时应优先参考晶振数据手册、MCU晶振应用笔记或官方参考设计必要时再用频率计实测校准。实际设计中一般令C1 C2于是公式可简化为C1 C2 2 × (CL - Cstray)举例说明1假设某晶振标称负载电容CL 12pF按照经验取Cstray 7pF则外部匹配电容应取C1 C2 2 × (12pF - 7pF) 10pF因此两个负载电容均可选择10pF左右的电容。实际调试时建议使用频率计观测晶振输出频率若实测频率偏正可适当增大负载电容若偏负则适当减小负载电容最终以实测频率最接近晶振标称频率为准。3.2 高速内部时钟3.3 低速外部时钟3.4 低速内部时钟3.5 PLL倍频锁相环4. 时钟选择通过内核文档可以知道SysTick 定时器有两个时钟源一个是内部时钟FCLK也就是MCU的自由运行时钟另一个是外部时钟输入STCLK在这颗芯片上通常将AHB总线时钟进行8分频后作为STCLK即168MHz / 8 21MHz。5. 代码步骤/** ****************************************************************************** * #文件: delay.c * #简要描述: SysTick定时器应用 * #详细描述: 程序演示了如何使用SysTick定时器实现微秒级延时功能。通过配置系统时钟和SysTick定时器实现了精确的延时控制。 * #作者 游走于孤独的码农 * #时间 2026-10-07 ****************************************************************************** */ // 假设STM32F407ZET6的主频率为168MHz // 168 / 8 21 #define FAC_US 21 // 系统时钟频率换算为微秒的系数 /** * #简要描述微秒级延时函数 * #详细描述该函数通过SysTick定时器实现微秒级延时。输入参数为需要延时的微秒数。 * #参数 nus - 需要延时的微秒数 * #返回值 void */ void delay_us(uint32_t nus) { // SysTick-LOAD最大能写入0xFFFFFF 16777215 // SysTick是递减计数器从LOAD值一直减到0因此16777215 1 // 所以SysTick单次最多能计16777216个时钟周期 // 因为设置了HCLK / 8所以168MHz / 8 21MHz即每微秒需要21个计数 // 得知最大计数次数 / 每微秒需要的计数 最大微秒数 if(nus 0U || nus 798915U) { return; } SysTick-LOAD (nus * FAC_US) - 1U; // 设置计数器的重装值 SysTick-VAL 0U; // 清空计数器的当前值 SysTick-CTRL SysTick_CTRL_ENABLE_Msk; // 使能SysTick计数器 while((SysTick-CTRL SysTick_CTRL_COUNTFLAG_Msk) 0) { // 等待计数完成 } SysTick-CTRL 0U; // 禁用SysTick计数器 } // 作业实现毫秒级延时、秒级延时的接口5.1 这段代码可以用于 HAL 库吗可以但需要满足一个前提HAL库的SysTick中断必须保持可用。因为HAL库内部大量依赖SysTick来维护HAL_GetTick()的毫秒计数例如HAL_Delay()、超时判断等都会用到它。上面这段代码在delay_us()中会临时关闭SysTick中断SysTick-CTRL 0U如果此时HAL库恰好需要读取Tick计数就会导致时间基准短暂停滞。因此建议避免与HAL_Delay混用在同一个工程中要么统一使用HAL的HAL_Delay()要么统一使用自写的delay_ms()不要交叉调用否则可能造成时间基准错乱。保留SysTick中断如果必须使用自写延时建议在delay_us()中只关闭计数器而不关闭中断或者改用其他定时器如 TIM实现微秒延时避免影响HAL的Tick计数。初始化顺序在调用HAL_Init()之后再使用自写延时函数确保SysTick已正确配置。5.2 对读者的建议这段代码结构清晰、注释完整适合初学者理解SysTick的工作原理。但在实际项目中建议注意以下几点优先使用HAL自带延时在 HAL 库工程中HAL_Delay()已经封装好了毫秒延时且与系统 Tick 保持一致直接使用更安全。微秒延时用定时器更稳妥如果项目对微秒级延时精度要求较高建议使用通用定时器如 TIM2/TIM3实现避免占用SysTick影响系统时间基准。注意延时上限delay_us()中限制了最大延时为798915微秒约0.8秒超过该值会直接返回使用时需注意。秒级延时实现delay_s()通过两次500ms延时组合实现逻辑正确但也可以直接调用delay_ms(1000U)更简洁。总的来说这段代码作为学习SysTick的示例非常合适但在实际工程中建议根据项目需求选择合适的延时方案。