当前位置: 首页 > news >正文

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

目标:掌握 OpenHarmony 轻量系统的延时 API,理解osDelayosDelayUntil的区别
前置条件:已完成 Day 4 的定时器教程


一、工程结构

app/ ├── BUILD.gn └── 03_delay/ # 模块目录 ├── BUILD.gn └── demo.c # 延时测试代码

1.1app/BUILD.gn

import("//build/lite/config/component/lite_component.gni") lite_component("app") { features = [ "03_delay:delay_demo", # 引用 03_delay 模块 ] }

1.203_delay/BUILD.gn

static_library("delay_demo") { sources = [ "demo.c" ] include_dirs = [ "//utils/native/lite/include", "//kernel/liteos_m/components/cmsis/2.0", ] }

二、完整代码详解

2.1 头文件

#include<stdio.h>// 标准输入输出#include<unistd.h>// UNIX 标准函数#include"ohos_init.h"// OpenHarmony 系统初始化#include"cmsis_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_tstatus=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 + 100uint32_ttick=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 系统入口

staticvoidDelayTestTask(void){osThreadAttr_tattr={.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.1osKernelGetTickCount— 获取系统 Tick 计数

uint32_tosKernelGetTickCount(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.2osDelay— 相对延时

osStatus_tosDelay(uint32_tticks);
参数说明
ticks延时的 tick 数

特点

  • 相对延时:从调用时刻开始计算
  • 线程进入Blocked(阻塞)状态
  • 延时期间不占用 CPU,其他线程可运行

执行流程

时刻 T: 调用 osDelay(100) ↓ 线程阻塞,等待 100 tick ↓ 时刻 T+100: 线程唤醒,继续执行

⚠️注意osDelay的实际延时 ≥ 100 tick,因为线程唤醒后需要等待调度器分配 CPU。

3.3osDelayUntil— 绝对延时

osStatus_tosDelayUntil(uint32_tticks);
参数说明
ticks目标 tick 值(绝对时间点)

特点

  • 绝对延时:延时到指定的 tick 值
  • 用于周期性任务,保证执行间隔固定

典型用法 — 固定周期任务

voidperiodic_task(void){uint32_tnext_tick=osKernelGetTickCount();// 记录起始 tickwhile(1){next_tick+=100;// 下一次执行时间点:+100 tick// 执行业务代码do_work();osDelayUntil(next_tick);// 绝对延时到目标 tick}}

osDelayvsosDelayUntil对比

特性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-RTOS2LiteOS-M 原生说明
osKernelGetTickCountLOS_TickCountGet获取 tick 计数
osDelayLOS_TaskDelay相对延时
osDelayUntilLOS_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)—— 多线程共享资源保护。

http://www.jsqmd.com/news/944784/

相关文章:

  • 强化学习里的‘隐世高手’:拆解Robbins-Monro算法如何悄悄搞定Q-learning和策略梯度
  • 告别手机性能限制:Winlator让Android设备流畅运行Windows应用和游戏
  • 从普刊到 SCI 一键分层创作:Paperxie 期刊论文智能写作,打破不同级别刊物撰稿壁垒
  • 基于Arduino与MAX7219的智能LED时钟:从硬件选型到外壳制作全解析
  • 别光会apt install了!手把手教你拆解deb包,读懂control文件里的“说明书”
  • 在线溶解氧仪十大品牌排行榜 - 水质仪表品牌排行榜
  • 如何从安卓手机完整导出微信聊天记录?wechat-dump帮你轻松搞定
  • 为什么83%的AI评价项目半年内失败?资深架构师拆解4层技术-管理协同断层
  • 2026重庆导游推荐官方解析|纯玩小团TOP榜、联系方式与避坑指南 - 随峰国旅
  • 突破投稿撰稿瓶颈:Paperxie 期刊论文分层创作方案,一站式适配普刊 / 北核 / SCI 全层级撰稿需求
  • 2026北京16区通用!家里发现天牛虫千万别忽视,木质家具根治方法 - 苏易修缮
  • Qwopus3.6-27B-v1-preview-GGUF未来路线图:更大规模训练与功能升级展望
  • 终极图像标注指南:5分钟掌握LabelImg创建AI训练数据
  • 基于T-S模糊模型的强流电子枪建模与控制算法改进【附仿真】
  • FLUX.1-dev量化推理实践:w8a16与w8a8_dynamic方案对比
  • 2026年螺杆式制冷压缩机公司推荐榜单:高效节能、稳定耐用的工业冷源实力品牌深度解析 - 品牌企业推荐师(官方)
  • 基于宽动态视觉传感的GMAW机器人焊接偏差实时识别及电弧监测解析方案【附数据】
  • OneMore插件终极指南:让OneNote笔记体验提升10倍的秘密武器
  • 终极指南:如何在Windows Vista SP2和Server 2008 SP2上安装Python 3.8-3.14
  • 旧Mac重生指南:使用OpenCore Legacy Patcher实现macOS系统升级
  • 2026年北方变频热氟融霜工厂推荐榜单:高效节能、稳定耐寒的冷库制冷优选品牌解析 - 品牌企业推荐师(官方)
  • 2026年 北京冷库供应商推荐榜:嘉德实创深度解析,专业冷库定制与服务口碑之选 - 品牌企业推荐师(官方)
  • 鸿蒙南向开发教程 Day 6:事件标志组(Event Flags)
  • 显微操作系统怎么选?液压、电动、手动三大方案全面对比! - 实了个验
  • 基于贝叶斯网络和多源信息构建可靠性分析模型方法解析【附数据】
  • 2026北京除天牛攻略|木质家具被天牛蛀损?高效处理方案 - 苏易修缮
  • imFile下载管理器:5个颠覆性功能与3大实战技巧
  • Windows系统优化终极指南:Chris Titus Tech WinUtil一键管理工具完全教程
  • 连续使用三个月向量 API 中转站,它真的适配向量落地场景吗?
  • NJU OS 进程的地址空间