从本质上学会基于HarmonyOS开发Hi3861(主要讲授方法)

引言:花半秒钟就看透事物本质的人,和花一辈子都看不透事物本质的人,注定是截然不同的命运

做开发也一样,如果您能看透开发的整个过程,就不会出现“学会了某个RTOS的开发,同样的RTOS开发换一块开发板又不会了”,“跟着教程学会了某块开发板的某个Demo开发,自己开发另一个Demo又不会了”等等问题,只要能看透就能做到触类旁通,游刃有余!一定要活学活用,不能学死了,多想想为什么,不要死记过程。

在基于HarmonyOS开发Hi3861之前,需要对整个开发环境及过程有一个全局上的了解,首先还是从这一张最经典的框架图给大家讲起:

目前我们对Hi3861的开发主要涉及上图中的内核抽象层、系统能力子系统、DXF子系统、公共基础库子系统(提供KV存储、文件操作、定时器、IoT外设控制等能力供OpenHarmony各业务子系统及上层应用使用)、系统服务框架子系统(用于提供面向服务编程和对外提供能力用于分布式任务调度)

1、构建系统

该构建系统由python脚本配合gn、ninja组成,若是为了开发Demo或者应用,不必细究编译构建系统的具体实现细节,只需要做到会使用即可。

当我们输入python build.py wifiiot指令,python脚本开始读取build目录中与wifiiot设备相关的各项参数信息并构造编译指令如下:

gn工具所在目录/gn gen 源码所在目录/out/wifiiot --root=. --dotfile=build/lite/.gn --args='product = "wifiiot" ohos_build_type = "release"' 这条指令用于生成一些xxx.ninja文件,这些文件将在下一阶段指导ninja编译源码生成烧录文件

ninja工具所在目录/ninja -w dupbuild=warn -C 源码所在目录/out/wifiiot 这条指令用于根据前面生成的xxx.ninja文件调用工具链编译源码最终生成烧录文件

gn用于根据每个目录下的BUID,gn文件搜寻编译生成烧录文件所需的依赖文件,所以我们只要学会如何写BUILD.gn文件即可,关于具体实现本章就暂且略过,后期会给大家补上。

这里以led_example.c程序为例,给大家分析BUILD.gn文件,希望大家能举一反三:

在code-1.0\applications\sample\wifi-iot\app目录中有一个BUILD.gn文件,大家可以将该文件理解为一个管理者,它管理app目录中的每个子目录,通过这个BUILD.gn文件中的features字段可以决定哪一个子目录中的BUILD.gn中指定的源文件会被编译到烧录文件中,如下图所示:

假设我们要将app/iothardware目录中的led_example.c文件编译到烧录程序中,需要打开app/iothardware/BUILD.gn文件查看该文件中的源代码被为静态库的名称,可以看到名称为led_example,如下图所示:


这时我们将app/BUILD.gn文件中的startup修改为iothardware:led_example就大功告成啦!如下图所示:

.gn文件的feature字段格式为:模块源文件所在路径:模块名称

请注意:.gn文件中的空白都是空格,不是Tab键(制表符),如果您输入了制表符,在生成ninja文件时就会产生如下图所示错误:

我出一个问题考考大家,如果我们在app/iothardware目录中添加一个hello_world.c文件,主要用于打印hello_world,假设源代码已经写好了,如下图所示,您应该如何将其添加进编译列表中与其他程序一起进行编译呢?

您应该修改app/iothardware/BUILD.gn文件,将hello_world.c文件添加到sources字段中,如下图所示:

若这时我们在app/iothardware目录下新建一个head的目录,并在其中新建一个名为hello_world.h的头文件,内容如下图所示:

并修改hello_world.c的内容如下图所示:

这时如果直接进行编译,则会产生找不到头文件错误,如下图所示:

我们应该继续对app/iothardware/BUILD.gn文件进行修改,在include_dirs中添加hello_world.h头文件所在路径,如下图所示:

上面的路径中以 //开头的路径为绝对路径,//表示root参数指定的路径,也就是code-1,0,而test路径则为相对路径,以当前BUILD.gn文件所在目录作为参照。

这样一个简单的Demo就开发好了,不知道读者有没有这样的疑问:为什么我知道启动一个任务的宏是SYSY_RUN(),IIC、SPI等等外设操作的函数是什么?一系列类似的问题,那您继续往下看就能找到答案。

2、目录结构

注意:Hi3861模组只用到了部分组件

希望大家能跟随我对目录的介绍,自己打开对应本地SDK的目录来看一看

├── applications   存放例程

│   └── sample

├── base

│   ├── global  全球化子系统

│   ├── hiviewdfx  DXF子系统

│   ├── iot_hardware  iot设备的公共基础库子系统,提供外设操作,IIC、SPI等等

│   ├── security         安全子系统

│   └── startup          启动恢复相关

├── build                   构建系统相关,存放各类芯片的编译构建参数等等

│   └── lite

├── build.py -> build/lite/build.py  与构建系统相配合的python脚本(用于启动构建)

├── docs   文档

├── domains 集成各个厂商的SDK

│   └── iot

├── drivers 驱动相关,HDF驱动框架

│   ├── hdf

│   └── liteos

├── foundation

│   ├── aafwk    提供一个Want名称的数据类型用于加速应用的启动

│   ├── ace        JS应用开发框架

│   ├── appexecfwk  用于程序框架子系统

│   ├── communication  分布式通信子系统(软总线)

│   ├── distributedschedule  系统服务框架子系统(面向服务编程,提供服务、使用服务等)、分布式任务调度子系统

│   ├── graphic  图形子系统

│   └── multimedia 媒体子系统

├── kernel 内核以及kal层

│   ├── liteos_a 面向Hi3516 3518等资源较丰富设备的内核

│   └── liteos_m 面向资源受限设备的内核

├── out  编译输出文件

│   ├── ipcamera_hi3516dv300

│   └── wifiiot

├── prebuilts 提供一些库文件

│   └── lite

├── test 测试子系统

│   ├── developertest

│   ├── xdevice

│   └── xts

├── third_party  第三方库,例如cmsis、cJSON、Fatfs等等

├── utils  公共基础系统,提供文件操作统一接口、KV存储、文件操作、定时器

│   └── native

└── vendor 各个厂商提供的SDK

    ├── hisi

    └── huawei

base以及foundation中的各个组件中都有两个名字相同的文件夹frameworks和interfaces,其中frameworks中存放该组件的具体实现,interfaces中存放对外提供的调用接口,这里以base/iot_hardware为例,其中hal文件夹中存放hi3861的SDK中提供的KV存储、文件操作、定时器和IoT外设控制的函数接口(函数接口指的是函数声明,我们只要知道函数声明,就不用关心函数的实现细节就能调用该函数完成相应操作),frameworks是对hal中的函数声明进行一定的封装,从而实现统一的接口,封装后的函数声明位于interfaces文件夹中,换句话说,我们想使用某个组件只需要查看interfaces文件夹中的声明,这样做的好处是:更换硬件或软件实现的情况下无需改动上层应用(例如目前我使用hi3861开发板实现了一些功能,这时甲方爸爸叫我用hi3516来实现同样的功能,我只需要将相应功能的底层支持函数的调用接口修改为interfaces文件夹中的形式,即可完成新的需求),大大的提高了开发效率。


3、如何创建一个任务?

SYS_RUN宏定义的正确用法为:

首先定义一个“初始化函数”,例如下面的“LedExampleEntry()”,所谓初始化是指初始化即将启动的任务需要的各类资源(例如:GPIO外设初始化),在这个“初始化”函数中初始化好了各类资源后调用osThreadNew函数(该创建线程的函数中调用了LOS_TASK_Create函数,也就是上面liblitekernel_flash.a库中提供的函数)创建线程即可。最后将“初始化”函数传入SYS_RUN宏中,在系统启动阶段时,系统会为其创建一个进程,优先级默认为2。SYS_RUN宏会在链接时将进程入口函数统一链接到某个段中,等待系统启动去这个段中将这些进程加载并运行(之前LiteOS的思想),这样做的优势是:可以让系统在合适的时候自动加载这些进程,无需用户考虑什么时候加载进程比较合适。

其中体现了一个进程和线程的思想,首先通过SYS_RUN宏创建了一个进程,该进程下面可以有多个线程。

//来源于code-1.0\utils\native\lite\include\ohos_init.h/**

* @brief Identifies the entry for initializing and starting a system running phase by the

* priority 2.

*

//来源于code-1.0\utils\native\lite\include\ohos_init.h

/**

* @brief Identifies the entry for initializing and starting a system running phase by the

* priority 2.

*

* This macro is used to identify the entry called at the priority 2 in the system startup

* phase of the startup process. \n

*

* @param func Indicates the entry function for initializing and starting a system running phase.

* The type is void (*)(void).

*/

#define SYS_RUN(func) LAYER_INITCALL_DEF(func, run, "run")


  //来源于code-1.0\applications\sample\wifi-iot\app\iothardware\led_example.c

  static void LedExampleEntry(void)

{

    osThreadAttr_t attr;

  //第一步初始化该进程需要用到的资源

    GpioInit();

    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_9, WIFI_IOT_IO_FUNC_GPIO_9_GPIO);

    GpioSetDir(WIFI_IOT_IO_NAME_GPIO_9, WIFI_IOT_GPIO_DIR_OUT);

    attr.name = "LedTask";

    attr.attr_bits = 0U;

    attr.cb_mem = NULL;

    attr.cb_size = 0U;

    attr.stack_mem = NULL;

    attr.stack_size = LED_TASK_STACK_SIZE;

    attr.priority = LED_TASK_PRIO;

  //第二步:为该进程创建线程

    if (osThreadNew((osThreadFunc_t)LedTask, NULL, &attr) == NULL) {

        printf("[LedExample] Falied to create LedTask!\n");

    }

}

SYS_RUN(LedExampleEntry);

4、如何找到您想使用函数API?

您首先需要对开头的框架图以及第2点的目录结构有一个大概的了解,并且根据您需要的API进行分析该API可能位于哪里。

例如:我需要找一个创建线程的函数,通过框架图我能得知,线程创建函数在KAL层或者内核层中,Hi3861设备遵循cmsis接口标准,首先我打开kernel\liteos_m\components目录,即可在其中寻找,最终在cmsis文件中找到该函数。

我需要寻找一个iic操作的函数,根据目录结构,我能得知该函数在base/iot_hardware/interfaces目录中,最终找到wifiiot_i2c.h。

搭建iot世界的积木已经交给您啦,最后能搭建出什么样子就全看您啦!

作者:HMFanXinhao

想了解更多内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

扫码关注公众号,【免费课程+一手资讯+福利掉落】,速来!


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • 1.系统要求 运行10.12.6或更高版本的64位Mac。Xcode 10.0+。JDK的当前版本(Closure...
    Mr_Watson阅读 5,115评论 10 2
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,729评论 0 10
  • 刚进到新的项目组,就接到WebRTC相关的任务。我们的项目是需要基于WebRTC上做二次开发。而最新版本的WebR...
    kim4apple阅读 8,519评论 18 17
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,515评论 16 22
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,559评论 0 11