1,定时器和延时使用场景对比
| 场景分类 | 具体子场景 | 推荐方式 | 原因/备注 |
|---|---|---|---|
| 硬件初始化 | 时钟稳定等待(如 PLL 起振) | 硬件循环 delay | 调度器未启动,不能用 OS 延时 |
| 外设复位释放(如 LCD 复位脉冲 100ms) | 硬件循环 delay | 同上,或者用硬件定时器(如 SysTick 裸机轮询) | |
| 软件初始化 | 数据结构、变量赋值 | 无需延时 | - |
| 等待其他模块就绪标志(如传感器上电初始化完成) | 阻塞 delay + 检查标志 | 简单实现,但会占用任务;推荐用事件/队列 | |
| 任务内操作 | 周期性读取传感器(允许抖动) | vTaskDelay |
阻塞任务,适合低优先级周期任务 |
| 高精度周期控制(PWM、电机控制) | 硬件定时器 | OS 软件定时器精度不足(受 tick 限制) | |
| 一次性延时后修改 GPIO | 软件定时器 | 不占用任务堆栈,不影响其他任务实时性 | |
| 与多个异步事件同步(如等待超时或收到消息) | 队列/事件组 + 超时 | vTaskDelay 无法同时等待事件 |
|
| 中断上下文 | 中断中需要短延时(去抖动、总线保持) | 绝对禁止 delay | 必须用硬件定时器或直接退出中断 |
| 中断后需要延时处理(如按键长按检测) | 启动软件定时器 | 中断中启动定时器,回调中处理长按逻辑 | |
| 低功耗 | CPU 进入睡眠模式,需要定时唤醒 | 硬件定时器(RTC/WDT) | OS 软件定时器依赖系统 tick,睡眠时可能不工作 |
| 空闲任务自动休眠(Tickless) | OS 内置机制 | FreeRTOS/LiteOS 支持 tickless,使用硬件定时器作为唤醒源 | |
| 通信 | UART/I2C 接收超时检测 | 软件定时器或带超时的队列接收 | 队列接收时直接使用 xQueueReceive(..., timeout) 更好,内部阻塞不占CPU |
| 模拟 I2C 的 SCL 时钟延时 | 硬件循环 delay | 微秒级延时,OS 无法满足 | |
| 用户交互 | 按键消抖(20ms) | 软件定时器 | 避免阻塞主任务,消抖期间系统仍可响应其他操作 |
| LED 闪烁(1Hz) | 软件定时器或任务 + delay | 定时器方式更节省任务资源;任务方式更直观 | |
| 调试与监测 | 串口打印心跳 | vTaskDelay |
简单,无需额外创建定时器 |
| 看门狗喂狗 | 周期软件定时器 | 喂狗必须可靠且不依赖某个任务可能被饿死 | |
| 系统行为 | 系统启动后先进行 500ms 预热 | 起始任务中 vTaskDelay |
预热期间无需做其他事,阻塞当前任务即可 |
| 多个外设需要按顺序延时上电(如先开 3.3V,20ms 后再开 1.8V) | 软件定时器链或任务顺序 delay | 简单用任务 delay,复杂用定时器链(首个定时器回调启动第二个) |
系统启动后(复位后),某个 GPIO 引脚立即输出高电平 ,经过 10ms 后自动恢复为低电平
2,定时器实现
#include "los_swtmr.h"
#include "gpio.h"
// 定时器回调函数
void TimerCallback(UINT32 arg) {
GPIO_SetLow(PIN_LED); // 恢复低电平
}
void main(void) {
// 硬件初始化
GPIO_Init(PIN_LED, OUTPUT);
GPIO_SetHigh(PIN_LED);
// 创建单次软件定时器
UINT32 timerId;
SWTMR_CTRL_S stCtrl = {0};
stCtrl.uwMode = LOS_SWTMR_MODE_ONCE; // 单次模式
stCtrl.uwInterval = 10; // 10ms(注意:LiteOS 定时器单位是 tick,默认 1ms/tick)
stCtrl.pfnHandler = (SWTMR_PROC_FUNC)TimerCallback;
LOS_SwtmrCreate(&stCtrl, &timerId);
LOS_SwtmrStart(timerId);
// 启动 LiteOS 调度器
LOS_Start();
}
任务内使用 LOS_TaskDelay(阻塞)
#include "los_task.h"
#include "gpio.h"
UINT32 vPulseTask(UINT32 arg) {
GPIO_Init(PIN_LED, OUTPUT);
GPIO_SetHigh(PIN_LED);
LOS_TaskDelay(10); // 阻塞当前任务 10ms
GPIO_SetLow(PIN_LED);
return LOS_OK;
}
void main(void) {
UINT32 taskId;
TSK_INIT_PARAM_S taskParam = {0};
taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)vPulseTask;
taskParam.usTaskPrio = 1;
taskParam.uwStackSize = 0x800;
taskParam.pcName = "PulseTask";
LOS_TaskCreate(&taskId, &taskParam);
LOS_Start();
}
| 特性 | 软件定时器(单次/周期) | delay(阻塞延时) |
|---|---|---|
| 是否阻塞 | 不阻塞任何任务(回调在定时器任务/中断中执行) | 阻塞调用该函数的任务 |
| 上下文要求 | 回调中不能调用阻塞 API(如队列接收、delay) | 只能在任务中调用 |
| CPU 占用 | 极低,仅系统时钟中断 + 定时器任务 | 阻塞期间让出 CPU,唤醒后继续 |
| 精度 | 取决于系统时钟 tick,通常 1ms ~ 10ms | 相同 |
| 典型用途 | 超时处理、非阻塞的延时动作、周期性维护 | 简单的顺序延时、初始化等待 |
补充: freertos的实现
#include "FreeRTOS.h"
#include "timers.h"
#include "gpio.h" // 假设的 GPIO 操作函数
// 定时器回调函数:将引脚设为低电平
void vTimerCallback(TimerHandle_t xTimer) {
GPIO_SetLow(PIN_LED); // 恢复低电平
}
void main(void) {
// 硬件初始化
GPIO_Init(PIN_LED, OUTPUT);
GPIO_SetHigh(PIN_LED); // 立即输出高电平
// 创建单次定时器,周期 10ms(单位:Tick,假设 configTICK_RATE_HZ = 1000)
TimerHandle_t xTimer = xTimerCreate("OneShot", pdMS_TO_TICKS(10), pdFALSE,
NULL, vTimerCallback);
if (xTimer != NULL) {
xTimerStart(xTimer, 0); // 启动定时器,非阻塞
}
// 启动调度器(之后定时器会在系统时钟中断/软件定时器任务中执行)
vTaskStartScheduler();
// 不会执行到这里
for(;;);
}
-
xTimerStart立即返回,不阻塞当前上下文。 -
10ms 后系统自动调用回调,在软件定时器任务(或中断)中完成电平切换。
-
不影响其他任务运行,适合在多任务环境中让高电平脉冲独立完成。
延时函数实现
#include "FreeRTOS.h"
#include "task.h"
#include "gpio.h"
void vPulseTask(void *pvParameters) {
GPIO_Init(PIN_LED, OUTPUT);
GPIO_SetHigh(PIN_LED); // 高电平
vTaskDelay(pdMS_TO_TICKS(10)); // 阻塞当前任务 10ms
GPIO_SetLow(PIN_LED); // 低电平
vTaskDelete(NULL); // 任务结束,可选
}
void main(void) {
xTaskCreate(vPulseTask, "Pulse", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
vTaskStartScheduler();
}
-
任务进入阻塞状态,让出 CPU 给其他就绪任务。
-
实现简单,但占用一个任务堆栈,且该任务在 10ms 内无法做其他事。