鸿蒙南向开发教程 Day 5:延时与系统节拍

目标 :掌握 OpenHarmony 轻量系统的延时 API,理解 osDelayosDelayUntil 的区别

前置条件:已完成 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 的延时实现:

  1. 将当前线程从 Ready(就绪) 队列移除
  2. 计算唤醒 tick 值,加入 Delay(延时) 队列
  3. 触发线程调度,切换至其他就绪线程
  4. 在每次 tick 中断中检查:若唤醒 tick 到达,将线程移回 Ready 队列

五、编译与验证

5.1 编译烧录

VSCode 点击 BuildUpload ,串口波特率 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) ------ 多线程共享资源保护。

相关推荐
2501_919749031 小时前
鸿蒙 Flutter 实战:saver_gallery 5.1.0 适配 3.27-ohos 全流程
flutter·华为·harmonyos
无限码力2 小时前
华为非AI方向笔试真题 - 任意矩形图案解锁路径验证
华为·华为非ai方向笔试真题·华为笔试真题·华为最新笔试真题
co_wait2 小时前
【华为】OSPF协议Stub和NSSA区域
华为
前端不太难2 小时前
鸿蒙游戏需要 GameEngine 吗?
游戏·状态模式·harmonyos
●VON2 小时前
鸿蒙Flutter实战:异步回调mounted检查安全实践
flutter·华为·harmonyos·鸿蒙
特立独行的猫a2 小时前
鸿蒙 PC 移植记:将微软的 `edit` 轻量级终端编辑器带到 OpenHarmony
microsoft·rust·编辑器·harmonyos·鸿蒙pc·edit
●VON2 小时前
鸿蒙Flutter实战:MethodChannel桥接获取OHOS文件目录
flutter·华为·harmonyos·鸿蒙
HwJack203 小时前
HarmonyOS APP开发中Native 层崩溃信号全集与生存指南
华为·harmonyos
搜移IT科技3 小时前
华为发布 “韬定律”,半导体换道超车引市场重估
华为