目标 :掌握 OpenHarmony 轻量系统的延时 API,理解
osDelay和osDelayUntil的区别前置条件:已完成 Day 4 的定时器教程
一、工程结构
app/
├── BUILD.gn
└── 03_delay/ # 模块目录
├── BUILD.gn
└── demo.c # 延时测试代码
1.1 app/BUILD.gn
gn
import("//build/lite/config/component/lite_component.gni")
lite_component("app") {
features = [
"03_delay:delay_demo", # 引用 03_delay 模块
]
}
1.2 03_delay/BUILD.gn
gn
static_library("delay_demo") {
sources = [
"demo.c"
]
include_dirs = [
"//utils/native/lite/include",
"//kernel/liteos_m/components/cmsis/2.0",
]
}
二、完整代码详解
2.1 头文件
c
#include <stdio.h> // 标准输入输出
#include <unistd.h> // UNIX 标准函数
#include "ohos_init.h" // OpenHarmony 系统初始化
#include "cmsis_os2.h" // CMSIS-RTOS2 接口
2.2 宏定义
c
#define STACK_SIZE (1024) // 线程栈大小
#define DELAY_TICKS_100 (100) // 100 个 tick 延时
2.3 延时测试主函数
c
void rtosv2_delay_main(void)
{
// 1. 获取当前系统 tick 计数
printf("[Delay Test] Current system tick: %d.\r\n", osKernelGetTickCount());
// 2. 相对延时:从当前时刻起延时 100 tick
osStatus_t status = osDelay(DELAY_TICKS_100);
printf("[Delay Test] osDelay, status: %d.\r\n", status);
// 3. 再次获取 tick,验证延时效果
printf("[Delay Test] Current system tick: %d.\r\n", osKernelGetTickCount());
// 4. 计算目标 tick:当前 tick + 100
uint32_t tick = osKernelGetTickCount();
tick += DELAY_TICKS_100;
// 5. 绝对延时:延时到指定的 tick 值
status = osDelayUntil(tick);
printf("[Delay Test] osDelayUntil, status: %d.\r\n", status);
// 6. 最终 tick 值
printf("[Delay Test] Current system tick: %d.\r\n", osKernelGetTickCount());
}
2.4 系统入口
c
static void DelayTestTask(void)
{
osThreadAttr_t attr = {
.name = "rtosv2_delay_main",
.attr_bits = 0U,
.cb_mem = NULL,
.cb_size = 0U,
.stack_mem = NULL,
.stack_size = STACK_SIZE,
.priority = osPriorityNormal,
};
if (osThreadNew((osThreadFunc_t)rtosv2_delay_main, NULL, &attr) == NULL) {
printf("[DelayTestTask] Failed to create rtosv2_delay_main!\n");
}
}
APP_FEATURE_INIT(DelayTestTask);
三、核心 API 详解
3.1 osKernelGetTickCount --- 获取系统 Tick 计数
c
uint32_t osKernelGetTickCount(void);
| 说明 | 内容 |
|---|---|
| 功能 | 获取系统启动以来的 tick 计数 |
| 返回值 | 32 位无符号整数,从 0 开始递增 |
| 溢出 | 约 49.7 天后溢出(1ms tick 时),CMSIS-RTOS2 已处理溢出兼容 |
什么是 Tick?
Tick 是 RTOS 的心跳节拍,由硬件定时器中断产生:
-
默认配置:1 tick = 1 毫秒
-
系统每 1ms 产生一次 tick 中断
-
在 tick 中断中:更新计数器、检查线程调度、触发软件定时器
时间轴: 0ms 1ms 2ms 3ms ... 100ms 101ms
│ │ │ │ │ │
Tick: 0 1 2 3 ... 100 101
↑↑↑↑↑_↑
硬件定时器中断(1ms 周期)
3.2 osDelay --- 相对延时
c
osStatus_t osDelay(uint32_t ticks);
| 参数 | 说明 |
|---|---|
ticks |
延时的 tick 数 |
特点:
- 相对延时 :从调用时刻开始计算
- 线程进入 Blocked(阻塞) 状态
- 延时期间不占用 CPU,其他线程可运行
执行流程:
时刻 T: 调用 osDelay(100)
↓
线程阻塞,等待 100 tick
↓
时刻 T+100: 线程唤醒,继续执行
⚠️ 注意 :
osDelay的实际延时 ≥ 100 tick,因为线程唤醒后需要等待调度器分配 CPU。
3.3 osDelayUntil --- 绝对延时
c
osStatus_t osDelayUntil(uint32_t ticks);
| 参数 | 说明 |
|---|---|
ticks |
目标 tick 值(绝对时间点) |
特点:
- 绝对延时 :延时到指定的 tick 值
- 用于周期性任务,保证执行间隔固定
典型用法 --- 固定周期任务:
c
void periodic_task(void)
{
uint32_t next_tick = osKernelGetTickCount(); // 记录起始 tick
while (1) {
next_tick += 100; // 下一次执行时间点:+100 tick
// 执行业务代码
do_work();
osDelayUntil(next_tick); // 绝对延时到目标 tick
}
}
osDelay vs osDelayUntil 对比:
| 特性 | osDelay(100) |
osDelayUntil(T+100) |
|---|---|---|
| 基准点 | 调用时刻 | 指定的绝对 tick 值 |
| 适用场景 | 单次延时、不严格要求周期 | 周期性任务、固定频率 |
| 误差累积 | 有(每次调用都有偏差) | 无(始终对准目标 tick) |
| 图示 | ` | ---业务--- |
误差累积示例:
假设 do_work() 执行耗时 5ms:
使用 osDelay(100):
0ms: 开始执行
5ms: 执行完毕
5ms: osDelay(100) → 105ms 唤醒
105ms: 开始执行
110ms: 执行完毕
110ms: osDelay(100) → 210ms 唤醒 ← 实际周期 = 105ms,累积偏差!
使用 osDelayUntil:
0ms: next = 0, 执行
5ms: 执行完毕
5ms: osDelayUntil(100) → 100ms 唤醒
100ms: next = 100, 执行 ← 周期严格 = 100ms,无累积偏差!
105ms: 执行完毕
105ms: osDelayUntil(200) → 200ms 唤醒
四、底层实现:LiteOS 原生延时
CMSIS-RTOS2 的延时 API 在 LiteOS-M 中的映射:
| CMSIS-RTOS2 | LiteOS-M 原生 | 说明 |
|---|---|---|
osKernelGetTickCount |
LOS_TickCountGet |
获取 tick 计数 |
osDelay |
LOS_TaskDelay |
相对延时 |
osDelayUntil |
LOS_TaskDelayUntil |
绝对延时 |
LiteOS-M 的延时实现:
- 将当前线程从 Ready(就绪) 队列移除
- 计算唤醒 tick 值,加入 Delay(延时) 队列
- 触发线程调度,切换至其他就绪线程
- 在每次 tick 中断中检查:若唤醒 tick 到达,将线程移回 Ready 队列
五、编译与验证
5.1 编译烧录
VSCode 点击 Build → Upload ,串口波特率 115200。
5.2 预期输出
[Delay Test] Current system tick: 1234.
[Delay Test] osDelay, status: 0.
[Delay Test] Current system tick: 1334. ← 增加了约 100 tick
[Delay Test] osDelayUntil, status: 0.
[Delay Test] Current system tick: 1434. ← 再次增加约 100 tick
实际 tick 值取决于系统启动后的运行时间,两次差值应接近 100。
六、总结
| 要点 | 内容 |
|---|---|
| Tick 概念 | 系统心跳,默认 1ms,驱动调度 |
osDelay |
相对延时,从调用时刻起算 |
osDelayUntil |
绝对延时,到指定 tick 值 |
| 周期任务 | 用 osDelayUntil 避免误差累积 |
| 状态变化 | 调用延时 → 线程进入 Blocked → tick 到达 → Ready → Running |
七、下一步
Day 6 预告:互斥锁(Mutex) ------ 多线程共享资源保护。