共计 1846 个字符,预计需要花费 5 分钟才能阅读完成。
痛点分析:传统延时的先天缺陷
在嵌入式开发中,我们经常需要用到延时函数。新手最常用的方式是写一个 for 循环来实现延时,比如:

void delay_us(uint32_t us) {for(uint32_t i=0; i<us*72; i++);
}
这种方法存在几个严重问题:
- 延时精度完全依赖 CPU 时钟频率,不同主频下需要重新计算循环次数
- 容易被中断打断,导致实际延时时间远大于预期
- 编译器优化可能导致循环被完全跳过
- 在低功耗模式下完全失效
硬件定时器方案对比
| 方案 | 精度 | 资源占用 | 中断影响 | 适用场景 |
|---|---|---|---|---|
| SysTick | 1ms | 专用 | 可屏蔽 | 通用延时、RTOS 心跳 |
| 通用定时器 | 1us | 占用 1 个 | 可屏蔽 | 高精度定时 |
| DWT 计数器 | 周期级别 | 无 | 无 | 极短延时测量 |
SysTick 精准延时实现
初始化配置
- 首先计算重装载值(以 72MHz 系统时钟为例):
#define SYSTEM_CLOCK 72000000
#define TICKS_PER_MS (SYSTEM_CLOCK/1000)
void SysTick_Init(void) {
SysTick->LOAD = TICKS_PER_MS - 1; // 1ms 中断
SysTick->VAL = 0; // 清空当前值
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
NVIC_SetPriority(SysTick_IRQn, 0); // 最高优先级
}
带临界区保护的延时函数
volatile uint32_t ticks = 0;
void SysTick_Handler(void) {ticks++;}
void delay_ms(uint32_t ms) {
uint32_t start = ticks;
while((ticks - start) < ms) {__WFI(); // 进入低功耗等待
}
}
// 临界区保护版本
void safe_delay_ms(uint32_t ms) {__disable_irq();
uint32_t start = ticks;
while((ticks - start) < ms);
__enable_irq();}
验证方法与实测技巧
- 准备一个 GPIO 测试引脚
- 在延时前后进行电平翻转
- 用示波器测量高电平持续时间
void test_delay(void) {GPIO_Set();
delay_ms(10); // 测试 10ms 延时
GPIO_Reset();
delay_ms(10);
}
常见问题解决方案
中断嵌套问题
当 SysTick 中断被更高优先级中断频繁打断时,会导致延时变长。解决方案:
- 设置 SysTick 为最高优先级
- 使用 DWT 计数器作为补充
低功耗模式适配
在 STOP 模式下,SysTick 会停止工作。解决方案:
void enter_stop_mode(void) {
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
// 配置低功耗定时器唤醒
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
SystemClock_Config(); // 唤醒后重新初始化时钟
SysTick_Init();}
高阶优化技巧
微秒级延时实现
使用 DWT(Data Watchpoint and Trace) 周期计数器:
#define DWT_CYCCNT ((volatile uint32_t *)0xE0001004)
void delay_us(uint32_t us) {
uint32_t start = *DWT_CYCCNT;
uint32_t cycles = us * (SystemCoreClock/1000000);
while((*DWT_CYCCNT - start) < cycles);
}
RTOS 环境适配建议
在 FreeRTOS 等系统中,需要避免阻塞系统调度:
void rtos_delay_ms(uint32_t ms) {
#ifdef USE_RTOS
vTaskDelay(pdMS_TO_TICKS(ms));
#else
delay_ms(ms);
#endif
}
总结建议
实际项目中,建议根据需求选择合适的延时方案:
- 普通延时:SysTick 即可满足
- 高精度需求:通用定时器 +DWT 组合
- RTOS 环境:使用系统提供的延时 API
- 低功耗场景:配合唤醒定时器使用
通过合理选择硬件资源和编写可靠的延时函数,可以显著提高嵌入式系统的时序稳定性。建议在项目初期就建立完善的延时模块,避免后期出现难以调试的时序问题。
正文完
发表至: 未分类
近一天内
