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

鸿蒙南向开发教程 Day 5:延时与系统节拍 目标掌握 OpenHarmony 轻量系统的延时 API理解osDelay和osDelayUntil的区别前置条件已完成 Day 4 的定时器教程一、工程结构app/ ├── BUILD.gn └── 03_delay/ # 模块目录 ├── BUILD.gn └── demo.c # 延时测试代码1.1app/BUILD.gnimport(//build/lite/config/component/lite_component.gni) lite_component(app) { features [ 03_delay:delay_demo, # 引用 03_delay 模块 ] }1.203_delay/BUILD.gnstatic_library(delay_demo) { sources [ demo.c ] include_dirs [ //utils/native/lite/include, //kernel/liteos_m/components/cmsis/2.0, ] }二、完整代码详解2.1 头文件#includestdio.h// 标准输入输出#includeunistd.h// UNIX 标准函数#includeohos_init.h// OpenHarmony 系统初始化#includecmsis_os2.h// CMSIS-RTOS2 接口2.2 宏定义#defineSTACK_SIZE(1024)// 线程栈大小#defineDELAY_TICKS_100(100)// 100 个 tick 延时2.3 延时测试主函数voidrtosv2_delay_main(void){// 1. 获取当前系统 tick 计数printf([Delay Test] Current system tick: %d.\r\n,osKernelGetTickCount());// 2. 相对延时从当前时刻起延时 100 tickosStatus_tstatusosDelay(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 100uint32_ttickosKernelGetTickCount();tickDELAY_TICKS_100;// 5. 绝对延时延时到指定的 tick 值statusosDelayUntil(tick);printf([Delay Test] osDelayUntil, status: %d.\r\n,status);// 6. 最终 tick 值printf([Delay Test] Current system tick: %d.\r\n,osKernelGetTickCount());}2.4 系统入口staticvoidDelayTestTask(void){osThreadAttr_tattr{.namertosv2_delay_main,.attr_bits0U,.cb_memNULL,.cb_size0U,.stack_memNULL,.stack_sizeSTACK_SIZE,.priorityosPriorityNormal,};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.1osKernelGetTickCount— 获取系统 Tick 计数uint32_tosKernelGetTickCount(void);说明内容功能获取系统启动以来的 tick 计数返回值32 位无符号整数从 0 开始递增溢出约 49.7 天后溢出1ms tick 时CMSIS-RTOS2 已处理溢出兼容什么是 TickTick 是 RTOS 的心跳节拍由硬件定时器中断产生默认配置1 tick 1 毫秒系统每 1ms 产生一次 tick 中断在 tick 中断中更新计数器、检查线程调度、触发软件定时器时间轴 0ms 1ms 2ms 3ms ... 100ms 101ms │ │ │ │ │ │ Tick: 0 1 2 3 ... 100 101 ↑______↑______↑______↑____________↑_______↑ 硬件定时器中断1ms 周期3.2osDelay— 相对延时osStatus_tosDelay(uint32_tticks);参数说明ticks延时的 tick 数特点相对延时从调用时刻开始计算线程进入Blocked阻塞状态延时期间不占用 CPU其他线程可运行执行流程时刻 T: 调用 osDelay(100) ↓ 线程阻塞等待 100 tick ↓ 时刻 T100: 线程唤醒继续执行⚠️注意osDelay的实际延时 ≥ 100 tick因为线程唤醒后需要等待调度器分配 CPU。3.3osDelayUntil— 绝对延时osStatus_tosDelayUntil(uint32_tticks);参数说明ticks目标 tick 值绝对时间点特点绝对延时延时到指定的 tick 值用于周期性任务保证执行间隔固定典型用法 — 固定周期任务voidperiodic_task(void){uint32_tnext_tickosKernelGetTickCount();// 记录起始 tickwhile(1){next_tick100;// 下一次执行时间点100 tick// 执行业务代码do_work();osDelayUntil(next_tick);// 绝对延时到目标 tick}}osDelayvsosDelayUntil对比特性osDelay(100)osDelayUntil(T100)基准点调用时刻指定的绝对 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-RTOS2LiteOS-M 原生说明osKernelGetTickCountLOS_TickCountGet获取 tick 计数osDelayLOS_TaskDelay相对延时osDelayUntilLOS_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—— 多线程共享资源保护。