HarmonyOS第一个程序 – Hello World 精华

诚迈科技王亚军
发布于 2021-9-30 15:01
浏览
3收藏

HarmonyOS第一个程序 – Hello World

搭建完环境之后,就可以开始编写代码啦。

这里从代码编写到运行,一步一步的过程总结到这儿。

一、编写代码

这个官方文档有示例,有基础的同学可以直接跟着实操。

下面以新增业务(my_first_app)为例,向开发者介绍如何进行源码修改。

参考:https://device.harmonyos.com/cn/docs/start/introduce/quickstart-lite-steps-hi3861-running-0000001105048942

1. 确定目录结构

开发者编写业务时,务必先在./applications/sample/wifi-iot/app路径下新建一个目录(或一套目录结构),用于存放业务源码文件。

例如:在app下新增业务my_first_app,其中hello_world.c为业务代码,BUILD.gn为编译脚本,具体规划目录结构如下:

.
└── applications
    └── sample
        └── wifi-iot
            └── app
                │── my_first_app
                │  │── hello_world.c
                │  └── BUILD.gn
                └── BUILD.gn

2. 编写业务代码

新建./applications/sample/wifi-iot/app/my_first_app下的hello_world.c文件,在hello_world.c中新建业务入口函数HelloWorld,并实现业务逻辑。并在代码最下方,使用HarmonyOS启动恢复模块接口SYS_RUN()启动业务。(SYS_RUN定义在ohos_init.h文件中)

#include <stdio.h>
#include "ohos_init.h"

void Hello_World(void)
{
    printf("[Demo] Hello World!\n");
}
SYS_RUN(Hello_World);

3. 编写用于将业务构建成静态库的BUILD.gn文件

新建./applications/sample/wifi-iot/app/my_first_app下的BUILD.gn文件,并完成如下配置。

如步骤1所述,BUILD.gn文件由三部分内容(目标、源文件、头文件路径)构成,需由开发者完成填写。

static_library("myapp") {
    sources = [
        "hello_world.c"
    ]
    include_dirs = [
        "//utils/native/lite/include"
    ]
}
  • static_library中指定业务模块的编译结果,为静态库文件libmyapp.a,开发者根据实际情况完成填写。
  • sources中指定静态库.a所依赖的.c文件及其路径,若路径中包含"//“则表示绝对路径(此处为代码根路径),若不包含”//"则表示相对路径。
  • include_dirs中指定source所需要依赖的.h文件路径。

4. 编写模块BUILD.gn文件,指定需参与构建的特性模块

配置./applications/sample/wifi-iot/app/BUILD.gn文件,在features字段中增加索引,使目标模块参与编译。features字段指定业务模块的路径和目标,以my_first_app举例,features字段配置如下。

import("//build/lite/config/component/lite_component.gni")

lite_component("app") {
    features = [
        "my_first_app:myapp",
    ]
}
  • my_first_app是相对路径,指向./applications/sample/wifi-iot/app/my_first_app/BUILD.gn。

  • myapp是目标,指向./applications/sample/wifi-iot/app/my_first_app/BUILD.gn中的static_library(“myapp”)。

二、构建代码

1. 代码编译

hb 是 HarmonyOS 编译构建命令行工具,常用的命令有 hb clean 清除上一次的编译产物、hb set 设置编译类型,hb build 执行编译。

在源代码根目录下执行

root@141da74fa142:/home/openharmony/code-2.0-canary# hb set
[OHOS INFO] Input code path: .
OHOS Which product do you need?  wifiiot_hispark_pegasus
root@141da74fa142:/home/openharmony/code-2.0-canary# hb build -b release
[OHOS INFO] [1/2] ACTION //device/hisilicon/hispark_pegasus/sdk_liteos:run_wifiiot_scons(//build/lite/toolchain:riscv32-unknown-elf)
[OHOS INFO] /home/openharmony/code-2.0-canary/vendor/hisilicon/hispark_pegasus/fs.yml not found, 
stop packing fs. If the product does not need to be packaged, ignore it.
[OHOS INFO] wifiiot_hispark_pegasus build success
root@141da74fa142:/home/openharmony/code-2.0-canary# 

以上为编译Hi3861的过程,出现build success表示编译成功

输出的过程文件和最终bin文件,在以下路径:

out\hispark_pegasus\wifiiot_hispark_pegasus

目录中有多个bin文件,bin文件说明:

HarmonyOS第一个程序 – Hello World-鸿蒙开发者社区

2. 编译工具简介

在Unix/Linux下通常使用Make/Makefile来控制代码的编译,但是Makefile对于比较大的项目有时候会比较慢,Ninja是Google的一名程序员推出的注重速度的构建工具,目标是替代makefile之类的构建器,通过将编译任务并行组织,大大提高了构建速度。

Ninja的目标是成为汇编程序,这样才能从指令层面来优化编译性能。

Gn 是 Generate ninja 的缩写,用于产生 ninja文件。HarmonyOS 的编译构建子系统就是基于 gn 和 ninja,编译构建子系统支持OpenHarmony 组件化开发的编译框架,主要提供以下功能:构建已有产品,独立构建芯片厂商源码,独立构建单个组件。

3. 平台配置文件

vendor\hisilicon\hispark_pegasus\config.json

该文件定义的子系统中需要编译的组件。

HarmonyOS第一个程序 – Hello World-鸿蒙开发者社区

  • 模块:applications
    作用:这个路径下存放了hi3681编写的应用程序代码,例如 hello world 代码就放在这个路径下。

  • 模块:iot_hardware
    作用:存放了 hi3681 芯片相关的驱动、例如spi、gpio、uart等。

  • 模块:vendor
    作用:存放了 hi3681 相关的厂商SDK之类的文件。其中,app_io_init.c 是hi3681内核启动后的io口相关设置,用户需根据应用场景,合理选择各外设的IO复用配置。app_main.c 是内核启动进入的应用程序入口。

三、启动流程

kernel初始化默认在OS main函数中已经执行,无需外部再进行初始化:
系统初始化函数调用流程为:main -> app_main -> HOS_SystemInit。其中main函数中执行了Hi3861硬件和内存相关配置/初始化、LOS_KernelInit、APP初始化(即创建app_main任务)、LOS_Start。

1. app_main()

device\hisilicon\hispark_pegasus\sdk_liteos\app\wifiiot_app\src\app_main.c

在app_main()中,打印了SDK的版本

const hi_char* sdk_ver = hi_get_sdk_version();
printf("sdk ver:%s\r\n", sdk_ver);

然后进行外设初始化、内存初始化、文件系统初始化、WIFI初始化等等一些操作,

最后在OHOS_Main()中执行 HOS_SystemInit() 进行鸿蒙系统的初始化。

2. HOS_SystemInit()

void OHOS_SystemInit(void)
{
    MODULE_INIT(bsp);
    MODULE_INIT(device);
    MODULE_INIT(core);
    SYS_INIT(service);
    SYS_INIT(feature);
    MODULE_INIT(run);
    SAMGR_Bootstrap();
}

在 HOS_SystemInit()中,主要是初始化了一些相关模块、系统,包括有 bsp、device(设备)。其中最终的是 MODULE_INIT(run),它负责调用了所有 run 段的代码,那么 run 段的代码是哪些呢?事实上就是我们前面 application 中 hello_world.c 使用 SYS_RUN() 宏设置的函数名。

3. 链接方式

看了唐老师的一个文档受益匪浅,有兴趣的小伙伴也可以看下 https://harmonyos.51cto.com/posts/2017

我在这里做下总结:

在业务代码中通过SYS_RUN把参数定义成函数指针__zinitcall_run_hello_world通过强制编译的方式进入 .zinitcall.run2.init 段中。在ohos_init.h中可以看到定义:

HarmonyOS第一个程序 – Hello World-鸿蒙开发者社区

再看MODULE_INIT怎么调用函数的,在链接脚本中定义的两个符号 __zinitcall_run_start (理解为数组名)和 __zinitcall_run_end 分别指向 __zinitcall_run_hello_world 所在数据段的起始位置和结束位置。

而MODULE_INIT作用就是遍历这个“数组”,是这样定义的:

#define MODULE_BEGIN(name, step)                          \
    ({        extern InitCall __zinitcall_##name##_start;       \
        InitCall *initCall = &__zinitcall_##name##_start; \
        (initCall);                                       \
    })
#define MODULE_END(name, step)                          \
    ({        extern InitCall __zinitcall_##name##_end;       \
        InitCall *initCall = &__zinitcall_##name##_end; \
        (initCall);                                     \
    })
#define MODULE_CALL(name, step)                                      \
    do {                                                             \
        InitCall *initcall = (InitCall *)(MODULE_BEGIN(name, step)); \
        InitCall *initend = (InitCall *)(MODULE_END(name, step));    \
        for (; initcall < initend; initcall++) {                     \
            (*initcall)();                                           \
        }                                                            \
    } while (0)
#define MODULE_INIT(name)     \
    do {                      \
        MODULE_CALL(name, 0); \
    } while (0)

在MODULE_CALL里面定义了一个起始地址和一个结束地址,然后使用一个for循环遍历起始和结束之间的内存空间,从而运行用户定义的任务。

四、烧录文件

上面介绍了如何编写文件、编译文件以及启动的一个流程,那么在这里再烧录运行一遍。

我在这里演示使用Hi3861 开发板,烧录直接使用海思工具HiBurn.exe,软件可以通过在VS Code安装中devicetool-windows-tool插件获得,也可以在购买开发板套件时给卖家索要,获取方式有很多。

打开软件需要设置相关的一下配置,我的烧录配置截图放到下面:

HarmonyOS第一个程序 – Hello World-鸿蒙开发者社区

  1. 端口设置开发板使用数据线连接PC,在PC设备管理中可以看到CH340的一个COM口,然后在软件中找到这个COM口;
  2. Setting->Com setting波特率设置为2000000,决定烧录速度,不建议设置过高,太高了容易失败;
  3. 选择编译生成的Bin文件Hi3861_wifiiot_app_allinone.bin,文件位置以及说明上面有;
  4. 勾选Auto burn,自动烧写,不勾选可能有误;
  5. 点击Connect,然后复位单板,看到successful字样就是烧录成功了;

烧录完之后再按下复位,打开串口助手就可以看到 ”[Demo] Hello World!“ 啦

总结

为什么烧录的时候点击Connect还需要手动复位呢,这个其实和单板boot启动流程有关,有兴趣的同学也可以找下资料了解下;

其实官方给的文档还是比较多的,在学习鸿蒙系统的时候还是要多看多练的。

我也是刚接触鸿蒙,还有很长的路要走,以后会在这里给大家分享一下文档总结,欢迎大家指导。

7
收藏 3
回复
举报
回复
    相关推荐