Hi3861 OpenHarmony鸿蒙开发(嵌入式方向) (一)

文章目录

    • [Hi3861 OpenHarmony](#Hi3861 OpenHarmony)
        • [1. 学习内容](#1. 学习内容)
        • [2. Hi3861 芯片概述](#2. Hi3861 芯片概述)
        • [3. OpenHarmony 概述](#3. OpenHarmony 概述)
        • [4. 开发环境概述](#4. 开发环境概述)
        • [4. OpenHarmony 第一行代码](#4. OpenHarmony 第一行代码)
          • [4.1 代码实现](#4.1 代码实现)
          • [4.2 编译烧录过程](#4.2 编译烧录过程)
        • [6. 多线程操作【重点】](#6. 多线程操作【重点】)
          • [6.1 OHOS 多线程操作](#6.1 OHOS 多线程操作)
          • [6.2 多个线程创建和调度](#6.2 多个线程创建和调度)
          • [6.3 Mutex 互斥锁机制](#6.3 Mutex 互斥锁机制)
          • [6.4 Semaphore 信号量机制](#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 复制代码
/*
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. 返回值为 void
        2. 参数为 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 ==> 1KB
    thread_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 文件内容
python 复制代码
static_library("demo01_hello") {
    sources = [
        "demo_hello.c"
    ]

    include_dirs = [
        "//utils/native/lite/include", 
        "//kernel/liteos_m/kal/cmsis"
    ]
}

APP 目录下的 BUILD.gn 文件内容

python 复制代码
import("//build/lite/config/component/lite_component.gni")

lite_component("app") {
    features = [
      "demo01_hello",
    ]
}
4.2 编译烧录过程

安装 CH340 驱动。

编译程序

  • 切换路径到 OpenHarmony 原码根目录下

    sh 复制代码
    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 复制代码
/*
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

python 复制代码
static_library("demo02_thread") {
    sources = [
        "demo_thread.c"
    ]

    include_dirs = [
        "//utils/native/lite/include", 
        "//kernel/liteos_m/kal/cmsis"
    ]
}

App 目录下 BUILD.gn

python 复制代码
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 复制代码
/*
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 互斥锁 ID
        uint32_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

python 复制代码
static_library("demo03_mutex") {
    sources = [
        "demo_mutex.c"
    ]

    include_dirs = [
        "//utils/native/lite/include", 
        "//kernel/liteos_m/kal/cmsis"
    ]
}

App 目录下 BUILD.gn

python 复制代码
import("//build/lite/config/component/lite_component.gni")

lite_component("app") {
    features = [
      # "demo01_hello",
      # "demo02_thread",
      "demo03_mutex",
    ]
}
6.4 Semaphore 信号量机制
c 复制代码
#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);  // 关键修改:最大计数值改为2
    if(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; // 线程优先级

    // 创建线程1
    osThreadId_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
相关推荐
代码79725 小时前
【无标题】使用 Playwright 实现跨 Chromium、Firefox、WebKit 浏览器自动化操作
运维·前端·深度学习·华为·自动化
yenggd5 小时前
华为bgp路由的各种控制和团体属性及orf使用案例
网络·华为
COWORKSHOP5 小时前
华为芯片泄密案警示:用Curtain e-locker阻断内部数据泄露
运维·服务器·前端·数据库·安全·华为
晚风(●•σ )16 小时前
【华为 ICT & HCIA & eNSP 习题汇总】——题目集24
计算机网络·华为·ensp
云雾J视界16 小时前
数字化转型的核心引擎:解读华为“业务重构”三层设计模型
华为·业务重构·数字化设计·y模型·三层数字化·流程化组织·t型结构
江湖有缘16 小时前
基于华为openEuler部署webmin服务器管理工具
运维·服务器·华为
无风听海18 小时前
HarmonyOS之 @Provide 装饰器实现跨组件双向状态同步
华为·harmonyos
安卓开发者18 小时前
鸿蒙NEXT传统蓝牙开发指南:从基础到实战的完整解决方案
harmonyos
代码797220 小时前
使用会话存储时,处理存储信息加密问题
深度学习·算法·自动化·散列表·harmonyos