Hi3861 OpenHarmony鸿蒙开发(嵌入式方向) (一)
文章目录
- Hi3861 OpenHarmony
- 1. 学习内容
- 2. Hi3861 芯片概述
- 3. OpenHarmony 概述
- 4. 开发环境概述
- 4. OpenHarmony 第一行代码
- 4.1 代码实现
- 4.2 编译烧录过程
- 6. 多线程操作【重点】
- 6.1 OHOS 多线程操作
- 6.2 多个线程创建和调度
- 6.3 Mutex 互斥锁机制
- 6.4 Semaphore 信号量机制
Hi3861 OpenHarmony
1. 学习内容
- GPIO 输入输出,高低电平
- 系统编程多线程控制,锁机制
- 单总线数据传感器,设备控制。
- 联网操作 + MQTT + 云平台初步使用
2. Hi3861 芯片概述
官方海思提供的手册内容
3. OpenHarmony 概述
OpenHarmony 是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的一款面向全场景的开源分布式操作系统。以下为你从其诞生背景、特点、应用场景、技术架构
主要用于智能家居,工业物联网,穿戴系统使用,是一个【完整的实时操作系统】。可以提供系统支持,并且兼容多平台硬件。
4. 开发环境概述
基于 Ubuntu 22.04 搭建的 OpenHarmony 3.0 开发环境,利用 Ubuntu 环境进行程序编程,通过**【交叉编译工具】**将程序编译为 hi3861 支持的可执行系统文件,烧录到开发版中,进行后续的程序开发。
代码根据开发要求,对应的路径为
~/Desktop/OpenHarmony/code-v3.0-LTS/OpenHarmony/applications/sample/wifi-iot/app
4. OpenHarmony 第一行代码
4.1 代码实现
OpenHarmony 代码都是给予【多线程模式实现】,完成的每一个模块都是一个【线程模块】
【程序模块】
/*
C 语言标准库三剑客
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>/*
以下是需要导入的 OpenHarmony 相关头文件ohos_init.h OpenHarmony OS 系统初始化相关头文件cmsis_os2.h 实时操作系统头文件hi_timer.h 海思提供的 timer 定时器头文件
*/
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_timer.h"/*
【第三步】在 OHOS 每一个组件都是一个线程模块,需要提供对应的线程任务代码线程任务代码要求1. 返回值为 void2. 参数为 void *
*/
/*** @brief 测试使用 Hello 线程任务函数* * @param arg 用户提供的 void * 参数。*/
void hello_thread(void * arg);/*
【第一步】注册组件函数,函数要求 static 修饰,同时返回值和参数都是 void函数名称根据当前模块名称决定。*/
static void helloTask(void)
{/*osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);osThreadId_t OHOS 中对应线程号类型。 真实类型是 void *。details Thread ID identifies the thread.typedef void *osThreadId_t;osThreadFunc_t 函数指针变量名,要求提供的函数必须是返回值 为 void参数为 void * 类型。typedef void (*osThreadFunc_t) (void *argument);void *argument 当前线程任务函数所需参数const osThreadAttr_t *attr OHOS 提供的线程属性结构体类型。其中【重点】内容1. 线程名称2. 线程占用的【stack】 栈区内存大小3. 线程优先级当前程序创建线程任务,需要提供1. 线程目标任务函数2. 线程任务函数所需参数3. 线程任务相关属性结构体返回结果是当前 osThreadId_t,对应线程【地址】*//*【第五步】创建线程 */// 5.1 创建 osThreadAttr_t 线程属性结构体变量osThreadAttr_t thread_attr;memset(&thread_attr, 0, sizeof(osThreadAttr_t));thread_attr.name = "hello_DD"; // 线程名称thread_attr.attr_bits = 1024; // 当前线程占用的栈区大小,1024 ==> 1KBthread_attr.priority = osPriorityNormal; // 默认优先级// 5.2 利用 osThreadNew 创建线程,线程完毕,立即执行。osThreadId_t thread_id = osThreadNew(hello_thread,HI_NULL, // HI_NULL <==> NULL 海思公司自行定义的 NULL 类型。&thread_attr);if (HI_NULL == thread_id){perror("[osThreadNew] create thread [hello_thread] failed!");exit(EXIT_FAILURE);}
}/*
【第二步】对当前组件进行注册,利用 OHOS 提供的有参数宏进行注册操作APP_FEATURE_INIT 是 OHOS 提供的用于注册组件的有参数宏,要求提供的函数返回值必须是 void 类型,参数也是 void 类型。利用函数指针对当前组件功能进行注册操作。
*/
APP_FEATURE_INIT(helloTask);/*
【第四步】线程任务函数实现
*/
void hello_thread(void * arg)
{while (1){printf("Hello OHOS Thread\n");/*osStatus_t osDelay (uint32_t ticks);ticks 嘀嗒,当前系统执行一次任务的时间。Hi3861 基于 OHOS tick 时间为 【10 ms】,可以认为当前开发板一秒钟执行 100 次任务。100 ticks == 100 * 10ms == 1 s*/osDelay(100);}
}
BUILD.gn 文件内容
- 【注意】BUILD.gn 文件名称必须按照规范,不得拼写错误,不得小写
当前组件 demo01_hello 中的 BUILD.gn 文件内容
static_library("demo01_hello") {sources = ["demo_hello.c"]include_dirs = ["//utils/native/lite/include", "//kernel/liteos_m/kal/cmsis"]
}
APP 目录下的 BUILD.gn 文件内容
import("//build/lite/config/component/lite_component.gni")lite_component("app") {features = ["demo01_hello",]
}
4.2 编译烧录过程
安装 CH340 驱动。
编译程序
切换路径到 OpenHarmony 原码根目录下
qf@qf:~/Desktop/OpenHarmony/code-v3.0-LTS/OpenHarmony
使用编译命令
hb build
对项目进行编译操作
使用 HiBurn 烧录程序
端口确定
需要烧录到开发版中的程序在 Linux 虚拟机中,利用共享文件夹通过 IP 地址找到对应文件,文件路径如下, 需要根据自行的 IP 地址进行修改,目标文件为
Hi3861_wifiiot_app_allinone.bin
\\192.168.25.129\qf\Desktop\OpenHarmony\code-v3.0-LTS\OpenHarmony\out\hispark_pegasus\wifiiot_hispark_pegasus
进行烧录操作
Connect 之后,开发板按下 RESET 键,进入烧录过程
烧录完成
HiBurn 断开连接,XCOM 连接当前开发板,打开串口,开发板按键 RESET ,启动程序运行
6. 多线程操作【重点】
6.1 OHOS 多线程操作
学习 OHOS 多线程操作是为后续的 RTT 准备
- 多线程操作
- 锁机制【Mutex Sem】
- 消息队列【MessageQueue】
6.2 多个线程创建和调度
多线程案例
/*
C 语言标准库三剑客
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>/*
以下是需要导入的 OpenHarmony 相关头文件ohos_init.h OpenHarmony OS 系统初始化相关头文件cmsis_os2.h 实时操作系统头文件hi_timer.h 海思提供的 timer 定时器头文件
*/
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_timer.h"/*
多个线程任务执行和调度
*/
void test_thread1(void *arg);
void test_thread2(void *arg);
void test_thread3(void *arg);static void threadTestTask(void)
{osThreadAttr_t thread_attr;memset(&thread_attr, 0, sizeof(osThreadAttr_t));thread_attr.name = "thread1";thread_attr.stack_size = 1024;thread_attr.priority = osPriorityNormal;osThreadId_t thread_id1 = osThreadNew(test_thread1, HI_NULL, &thread_attr);if (HI_NULL == thread_id1){perror("[osThreadNew] create thread [thread_id1] failed!");exit(EXIT_FAILURE);}thread_attr.name = "thread2";osThreadId_t thread_id2 = osThreadNew(test_thread2, HI_NULL, &thread_attr);if (HI_NULL == thread_id2){perror("[osThreadNew] create thread [thread_id2] failed!");exit(EXIT_FAILURE);}thread_attr.name = "thread3";osThreadId_t thread_id3 = osThreadNew(test_thread3, HI_NULL, &thread_attr);if (HI_NULL == thread_id3){perror("[osThreadNew] create thread [thread_id3] failed!");exit(EXIT_FAILURE);}}APP_FEATURE_INIT(threadTestTask);void test_thread1(void *arg)
{while (1){printf("Thread Test1 Running!\n");osDelay(200);}
}
void test_thread2(void *arg)
{while (1){printf("Thread Test2 Running!\n");osDelay(500);}
}
void test_thread3(void *arg)
{while (1){printf("Thread Test3 Running!\n");osDelay(1000);}
}
组件内部 BUILD.gn
static_library("demo02_thread") {sources = ["demo_thread.c"]include_dirs = ["//utils/native/lite/include", "//kernel/liteos_m/kal/cmsis"]
}
App 目录下 BUILD.gn
import("//build/lite/config/component/lite_component.gni")lite_component("app") {features = [# "demo01_hello","demo02_thread",]
}
6.3 Mutex 互斥锁机制
Linux 系统编程中互斥锁使用方式
- 定义 Mutex 互斥锁变量
- 对 Mutex 进行 init 初始化操作
- 利用 lock 和 unlock 限制同步资源内容
- 使用完毕利用 destroy 对锁变量进行销毁
OpenHarmony 中的操作流程和 Linux 系统编程一致,只是函数方式略有不同。
互斥锁案例代码
/*
C 语言标准库三剑客
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>/*
以下是需要导入的 OpenHarmony 相关头文件ohos_init.h OpenHarmony OS 系统初始化相关头文件cmsis_os2.h 实时操作系统头文件hi_timer.h 海思提供的 timer 定时器头文件
*/
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_timer.h"/*** @brief 自定义字符串数据处理函数** @param str 所需参数是字符串数据*/
void print_string(const char *str);/*
线程任务函数
*/
void test_thread1(void *arg);
void test_thread2(void *arg);/// \details Mutex ID identifies the mutex.
// typedef void *osMutexId_t; 互斥锁类型
osMutexId_t mutex;static void mutexTestTask(void)
{/*osMutexId_t osMutexNew (const osMutexAttr_t *attr);互斥锁初始化操作,所需参数是 osMutexAttr_t 类型,互斥锁属性类型。/// Create and Initialize a Mutex object./// \param[in] attr mutex attributes; NULL: default values./// \return mutex ID for reference by other functions or NULL in case of error.osMutexId_t osMutexNew (const osMutexAttr_t *attr);osMutexAttr_t 互斥属性结构体,默认情况下 osMutexNew 所需参数为 NULL*/mutex = osMutexNew(HI_NULL);if (HI_NULL == mutex){perror("[osMutexNew] create and Initialize Mutex object Failed!");exit(EXIT_FAILURE);}osThreadAttr_t thread_attr;memset(&thread_attr, 0, sizeof(osThreadAttr_t));thread_attr.name = "thread1";thread_attr.stack_size = 1024;thread_attr.priority = osPriorityNormal;osThreadId_t tid1 = osThreadNew(test_thread1, "Hello_XX", &thread_attr);if (HI_NULL == tid1){perror("[osThreadNew] create thread [thread1] failed!");exit(EXIT_FAILURE);}thread_attr.name = "thread2";osThreadId_t tid2 = osThreadNew(test_thread2, "Hello_ZZ", &thread_attr);if (HI_NULL == tid2){perror("[osThreadNew] create thread [thread2] failed!");exit(EXIT_FAILURE);}
}APP_FEATURE_INIT(mutexTestTask);void print_string(const char *str)
{while (*str){printf("%c", *str);str++;osDelay(100);}
}void test_thread1(void *arg)
{/*osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout);去指针化操作 osMutexId_t mutex_id 实际类型是一个 void *获取当前 Mutex 互斥锁执行权限,osMutexId_t mutex_id mutex 互斥锁 IDuint32_t timeout 指定获取的时间。返回值 osStatus_t OHOS 中错误类型,枚举类型typedef enum {osOK = 0, ///< Operation completed successfully.osError = -1, ///< Unspecified RTOS error: run-time error but no other error message fits.osErrorTimeout = -2, ///< Operation not completed within the timeout period.osErrorResource = -3, ///< Resource not available.osErrorParameter = -4, ///< Parameter error.osErrorNoMemory = -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.osErrorISR = -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.osStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.} osStatus_t;*/osStatus_t status = osMutexAcquire(mutex, osWaitForever);if (status){perror("[osMutexAcquire] Acquire a Mutex Failed!\n");exit(EXIT_FAILURE);}print_string((const char *)arg);status = osMutexRelease(mutex);if (status){perror("[osMutexRelease] Release a Mutex Failed!\n");exit(EXIT_FAILURE);}}
void test_thread2(void *arg)
{osStatus_t status = osMutexAcquire(mutex, osWaitForever);if (status){perror("[osMutexAcquire] Acquire a Mutex Failed!\n");exit(EXIT_FAILURE);}print_string((const char *)arg);status = osMutexRelease(mutex);if (status){perror("[osMutexRelease] Release a Mutex Failed!\n");exit(EXIT_FAILURE);}
}
组件内部 BUILD.gn
static_library("demo03_mutex") {sources = ["demo_mutex.c"]include_dirs = ["//utils/native/lite/include", "//kernel/liteos_m/kal/cmsis"]
}
App 目录下 BUILD.gn
import("//build/lite/config/component/lite_component.gni")lite_component("app") {features = [# "demo01_hello",# "demo02_thread","demo03_mutex",]
}
6.4 Semaphore 信号量机制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "ohos_init.h" // HarmonyOS 初始化头文件
#include "cmsis_os2.h" // CMSIS-RTOS2 API 头文件
#include "hi_timer.h" // 定时器头文件// 线程函数声明
void hello_thread1(void *arg);
void hello_thread2(void *arg);
void hello_thread3(void *arg);osSemaphoreId_t sem; // 信号量句柄static void helloTask(void)
{// 创建一个信号量// 参数说明:// 2 - 信号量的最大计数值(关键修改:从3改为2)// 2 - 信号量的初始计数值(确保一开始就有两个可用)// HI_NULL - 无额外属性sem = osSemaphoreNew(2, 2, HI_NULL); // 关键修改:最大计数值改为2if(HI_NULL == sem){perror("osSemaphoreNew failed!\n");exit(EXIT_FAILURE);}// 定义线程属性结构体osThreadAttr_t thread_attr;memset(&thread_attr, 0, sizeof(osThreadAttr_t));// 设置线程1属性thread_attr.name = "hello_D1"; // 线程名称thread_attr.attr_bits = 1024; // 栈大小(字节)thread_attr.priority = osPriorityNormal; // 线程优先级// 创建线程1osThreadId_t thread_id1 = osThreadNew(hello_thread1, HI_NULL, &thread_attr);if (thread_id1 == HI_NULL){perror("osThreadNew failed!");exit(EXIT_FAILURE);}// 设置线程2属性thread_attr.name = "hello_D2";osThreadId_t thread_id2 = osThreadNew(hello_thread2, HI_NULL, &thread_attr);if (thread_id2 == HI_NULL){perror("osThreadNew failed!");exit(EXIT_FAILURE);}// 设置线程3属性thread_attr.name = "hello_D3";osThreadId_t thread_id3 = osThreadNew(hello_thread3, HI_NULL, &thread_attr);if (thread_id3 == HI_NULL){perror("osThreadNew failed!");exit(EXIT_FAILURE);}
}// HarmonyOS 应用特性初始化宏,指定 helloTask 为入口函数
APP_FEATURE_INIT(helloTask);// 线程1的执行函数
void hello_thread1(void *arg)
{int count = 0;while (1){// 获取信号量(等待直到获取成功)osSemaphoreAcquire(sem, osWaitForever);printf("[T1] hello ohos thread1 - count: %d\n", ++count);osDelay(1000); // 延迟1000毫秒(1秒)// 释放信号量osSemaphoreRelease(sem);osDelay(10); // 小延迟,避免立即重新获取信号量}
}// 线程2的执行函数
void hello_thread2(void *arg)
{int count = 0;while (1){// 获取信号量(等待直到获取成功)osSemaphoreAcquire(sem, osWaitForever);printf("[T2] hello ohos thread2 - count: %d\n", ++count);osDelay(1000); // 延迟1000毫秒(1秒)// 释放信号量osSemaphoreRelease(sem);osDelay(10); // 小延迟,避免立即重新获取信号量}
}// 线程3的执行函数
void hello_thread3(void *arg)
{int count = 0;while (1){// 获取信号量(等待直到获取成功)osSemaphoreAcquire(sem, osWaitForever);printf("[T3] hello ohos thread3 - count: %d\n", ++count);osDelay(1000); // 关键修改:统一延迟时间为1000毫秒// 释放信号量osSemaphoreRelease(sem);osDelay(10); // 小延迟,避免立即重新获取信号量}
}
运行结果为
[T1] hello ohos thread1 - count: 1
[T2] hello ohos thread2 - count: 1
(等待约1秒)
[T3] hello ohos thread3 - count: 1
[T1] hello ohos thread2 - count: 2
(等待约1秒)
[T2] hello ohos thread2 - count: 2
[T3] hello ohos thread3 - count: 2