1、实验简介

参考网址:https://gitee.com/Lockzhiner-Electronics/lz3863/tree/master/apps/a1_kernel_task

1.1、实验目的

本实验旨在帮助学习者掌握 OpenHarmony 轻量系统中 任务(线程) 的基本概念与编程方法。通过本实验,你将学会:

  1. 理解 OpenHarmony 多任务调度机制;
  2. 使用 CMSIS-RTOS v2 标准接口创建和管理线程;
  3. 通过 osDelay() 实现任务的周期性执行;
  4. 完成案例代码的编译、烧录与串口调试。

1.2、实验内容

本案例在 LZ3863-星闪开发板上创建 两个独立任务

任务名称 优先级 执行周期 输出内容
task_helloworld 普通(Normal) 约 1 秒 Hello World
task_openharmony 低于普通(BelowNormal) 约 2 秒 Hello OpenHarmony

两个任务并发运行,由内核调度器按优先级和时间片进行调度,通过串口周期性打印日志,直观展示多任务并发效果。

1.3、实验环境

项目 说明
硬件 LZ3863-星闪开发板、USB 数据线
软件 OpenHarmony v5.1.0 源码、hb 编译工具
调试工具 串口助手(波特率 115200)
案例路径 applications/sample/wifi-iot/app/a1_kernel_task/

2、基础知识

2.1、什么是任务(线程)

从操作系统的角度看,任务是竞争系统资源的最小运行单元。每个任务可以:

  • 独立使用或等待 CPU;
  • 拥有独立的栈空间;
  • 与其他任务并发执行,互不阻塞(除非主动等待)。

在 OpenHarmony 轻量系统中,一个任务对应一个线程(Thread)。本实验使用的 CMSIS-RTOS v2 是 ARM 定义的标准 RTOS 接口,OpenHarmony LiteOS-M 对其提供了完整支持。

2.2、任务调度机制

OpenHarmony 采用 抢占式调度,同时支持 时间片轮转

  • 抢占式调度:高优先级任务可以打断正在运行的低优先级任务;
  • 时间片轮转:同优先级任务按时间片轮流获得 CPU;
  • 阻塞等待:任务调用 osDelay() 等阻塞接口时主动让出 CPU,其他任务得以运行。

本实验中,task_helloworld 优先级高于 task_openharmony,因此在两者同时就绪时,前者优先获得调度。

2.3、核心 API 介绍

2.3.1、头文件
#include "ohos_init.h"
#include "cmsis_os2.h"
#include <stdio.h>
2.3.2、常用线程 API
API 名称 功能说明
osThreadNew 创建线程并加入就绪队列
osThreadGetName 获取线程名称
osThreadGetId 获取当前线程 ID
osThreadSetPriority 设置线程优先级
osThreadGetPriority 获取线程优先级
osThreadSuspend 挂起指定线程
osThreadResume 恢复被挂起的线程
osThreadTerminate 终止指定线程
osDelay 当前线程延时(让出 CPU)
2.3.3、osThreadNew — 创建线程
osThreadId_t osThreadNew(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);
参数 说明
func 线程入口函数
argument 传递给线程函数的参数指针
attr 线程属性(名称、栈大小、优先级等)

注意:不能在中断服务程序(ISR)中调用 osThreadNew

2.3.4、osThreadAttr_t — 线程属性结构体
osThreadAttr_t attr = {0};
attr.name       = "task_name";      // 线程名称
attr.stack_size = 2048;             // 栈大小(字节)
attr.priority   = osPriorityNormal; // 线程优先级
2.3.5、osDelay — 线程延时
osStatus_t osDelay(uint32_t ticks);

参数 ticks 为内核时钟节拍数。本系统默认 1 tick = 10 ms,因此:

  • osDelay(100) ≈ 延时 1 秒;
  • osDelay(200) ≈ 延时 2 秒。
2.3.6、APP_FEATURE_INIT — 应用入口注册
APP_FEATURE_INIT(HelloWorldLauncher);

该宏将 HelloWorldLauncher 注册为应用特性初始化函数,系统启动完成后自动调用,无需手动修改 main() 函数。


3、程序设计

3.1、程序架构

本案例文件目录结构

a1_kernel_task/
├── kernel_task_example.c   # 主程序源码
├── BUILD.gn                # GN 编译配置
├── README_zh.md            # 案例简要说明
└── 实验手册.md              # 本实验手册

本案例采用 三层任务结构

系统启动
    │
    ▼
HelloWorldLauncher()          ← APP_FEATURE_INIT 注册,系统启动时自动执行
    │
    ▼
hello_main()                  ← 主任务,负责创建子任务
    ├── task_helloworld()     ← 子任务 1,每 1 秒打印 "Hello World"
    └── task_openharmony()    ← 子任务 2,每 2 秒打印 "Hello OpenHarmony"

3.2、源文件说明

文件 说明
kernel_task_example.c 主程序源码
BUILD.gn GN 编译配置文件,定义静态库 task_example

3.3、关键代码分析

(1)应用启动入口 — HelloWorldLauncher

系统启动后,通过 APP_FEATURE_INIT 宏自动调用此函数,创建主任务 hello_main

static void HelloWorldLauncher(void)
{
    osThreadAttr_t attr = {0};
    attr.name       = "hello_main";
    attr.stack_size = 2048;
    attr.priority   = osPriorityNormal;

    if (osThreadNew(hello_main, NULL, &attr) == NULL) {
        printf("[HelloWorldLauncher] Failed to create hello_main!\n");
        return;
    }
    printf("[HelloWorldLauncher] create hello_main successful!\n");
}

APP_FEATURE_INIT(HelloWorldLauncher);
(2)主任务 — hello_main

hello_main 负责创建两个子任务,并设置各自的名称、栈大小和优先级:

void hello_main(void *arg)
{
    (void)arg;

    // 创建 task_helloworld,优先级:Normal
    osThreadAttr_t attr1 = {0};
    attr1.name       = "task_helloworld";
    attr1.stack_size = 2048;
    attr1.priority   = osPriorityNormal;
    if (osThreadNew(task_helloworld, NULL, &attr1) == NULL) {
        printf("[HelloMain] Failed to create task_helloworld!\n");
        return;
    }

    // 创建 task_openharmony,优先级:BelowNormal
    osThreadAttr_t attr2 = {0};
    attr2.name       = "task_openharmony";
    attr2.stack_size = 2048;
    attr2.priority   = osPriorityBelowNormal;
    if (osThreadNew(task_openharmony, NULL, &attr2) == NULL) {
        printf("[HelloMain] Failed to create task_openharmony!\n");
        return;
    }

    printf("[HelloMain] Both tasks created successfully.\n");
}
(3)子任务 — task_helloworld

每 1 秒打印一次 "Hello World",然后调用 osDelay(100) 主动让出 CPU:

void task_helloworld(void *arg)
{
    (void)arg;
    for (;;) {
        printf("Hello World\n");
        osDelay(100);   // 延时约 1 秒
    }
}
(4)子任务 — task_openharmony

每 2 秒打印一次 "Hello OpenHarmony"

void task_openharmony(void *arg)
{
    (void)arg;
    for (;;) {
        printf("Hello OpenHarmony\n");
        osDelay(200);   // 延时约 2 秒
    }
}

3.4、程序执行流程

task_openharmony task_helloworld hello_main HelloWorldLauncher 系统启动 task_openharmony task_helloworld hello_main HelloWorldLauncher 系统启动 loop [每 1 秒] loop [每 2 秒] APP_FEATURE_INIT 触发 osThreadNew(hello_main) osThreadNew(task_helloworld) osThreadNew(task_openharmony) printf("Hello World") + osDelay(100) printf("Hello OpenHarmony") + osDelay(200)

4、编译步骤

4.1、确认案例目录

确认案例已位于 OpenHarmony 源码目录下:

applications/sample/wifi-iot/app/a1_kernel_task/
├── kernel_task_example.c
├── BUILD.gn
└── 实验手册.md

若从外部复制,请将 a1_kernel_task 目录放到上述 app/ 路径下。

4.2、修改 BUILD.gn(注册编译组件)

编辑 applications/sample/wifi-iot/app/BUILD.gn,在 features 列表中添加本案例:

lite_component("app") {
  features = [
    "startup",
    "a1_kernel_task:task_example",   // 添加此行
  ]
}

4.3、修改 SDK 配置文件

步骤 1:编辑 device/soc/hisilicon/ws63v100/sdk/build/config/target_config/ws63/config.py

找到 'ws63-liteos-app' 配置段,在其 'ram_component' 列表中添加:

"task_example"

步骤 2:编辑 device/soc/hisilicon/ws63v100/sdk/libs_url/ws63/cmake/ohos.cmake

找到 "ws63-liteos-app" 对应的 set(COMPONENT_LIST 部分,添加:

"task_example"

4.4、执行编译

在 OpenHarmony 源码根目录下执行:

rm -rf out
hb set -root .
# 通过上下方向键选择 ws63 对应的编译分支(如 ws63-liteos-app)
hb build -f

编译成功后,固件输出路径通常在 out/ws63/ 目录下。

4.5、烧录固件

使用开发板配套的烧录工具,将编译生成的固件烧写到 LZ3863-星闪开发板。具体烧录步骤请参考开发板用户手册。


5、运行结果

5.1、串口配置

参数
波特率 115200
数据位 8
停止位 1
校验位

5.2、预期输出

烧录并复位开发板后,串口助手应显示类似以下日志:

[HelloWorldLauncher] create hello_main successful!
[HelloMain] Both tasks created successfully.
Hello World
Hello World
Hello OpenHarmony
Hello World
Hello World
Hello OpenHarmony
Hello World
Hello World
...

5.3、结果分析

现象 说明
启动日志 HelloWorldLauncherhello_main 依次打印,表明任务创建成功
Hello World 约每 1 秒出现 1 次 task_helloworldosDelay(100) 生效
Hello OpenHarmony 约每 2 秒出现 1 次 task_openharmonyosDelay(200) 生效
两任务输出交替出现 多任务并发调度正常工作

5.4、常见问题排查

问题 可能原因 解决方法
无任何串口输出 串口参数错误或线序接错 确认波特率 115200,检查 TX/RX 接线
编译报错找不到 task_example BUILD.gn 或 config.py 未正确修改 逐步核对 4.2、4.3 节的三处配置
只打印启动日志,无循环输出 任务创建失败 检查串口是否有 Failed to create 报错信息
输出频率不对 内核 tick 配置不同 确认 tick 为 10 ms,或相应调整 osDelay 参数

6、实验扩展

完成基本实验后,可尝试以下扩展练习:

  1. 修改优先级:将 task_openharmony 的优先级改为 osPriorityAboveNormal,观察输出顺序变化;
  2. 增加第三个任务:新建 task_custom,每 500 ms 打印自定义字符串;
  3. 动态挂起/恢复:在 hello_main 中延时一段时间后调用 osThreadSuspend / osThreadResume 控制任务运行;
  4. 调整栈大小:将 stack_size 改为 512,观察是否出现栈溢出异常。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐