OpenHarmony轻量系统开发【4】编写第一个程序、启动流程分析

摘要:本文简单介绍如何编写第一个hello world程序,以及程序是被执行的

适合群体:适用于Hi3861开发板,启动流程分析

4.1编写第一个程序

编写一个hello world程序比较简单,可以参考官网:

https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-lite-steps-hi3861-application-framework.md

本文在这里做下总结:

(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)编写业务代码。

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

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

void HelloWorld(void)
{
    printf("___________>>>>>>>>>>>>>>>>>>>> [DEMO] Hello world.\n");
}
SYS_RUN(HelloWorld);

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

如步骤1所述,BUILD.gn文件由三部分内容(目标、源文件、头文件路径)构成,需由开发者完成填写。以my_first_app为例,需要创建./applications/sample/wifi-iot/app/my_first_app/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")。

4.2 Hi3861相关代码结构

目前hi3861用的是liteos-m内核,但是目前hi3681的liteos-m被芯片rom化了,固化在芯片内部了。所以在harmonyOS代码是找不到hi3861的内核部分。

但是这样不妨碍我们去理清hi3861的其他代码结构。

hi3861平台配置文件位于:

vendor\hisilicon\hispark_pegasus\config.json

可以看到该配置文件有很多内容,

第一段这里指定了产品名称、版本、使用的内核类型

下面这里都是子系统:

其中我们重点关注这几个模块:

(1)applications:应用子系统

路径:applications/sample/wifi-iot/app

作用:这个路径下存放了hi3681编写的应用程序代码,例如我们刚刚写得hello world 代码就放在这个路径下。

(2)iot_hardware:硬件驱动子系统

头文件路径: base\iot_hardware\peripheral\interfaces\kits

具体代码路径,由device\board\hisilicon\hispark_pegasus\liteos_m\config.gni文件中指定:

config.gni文件内容较多,后续会一一解读

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

(3)xts:xts测试子系统。

这里我们先不要xts子系统,不然每次开机后,系统都要跑xts认证程序,影响我们后面测试,我们先注删除,如下:

4.3 Hi3861启动流程

由于hi3681的liteos-m被芯片rom化了,固化在芯片内部了。所以我们主要看内核启动后的第一个入口函数。

代码路径:

device\soc\hisilicon\hi3861v100\sdk_liteos\app\wifiiot_app\src\app_main.c

hi_void app_main(hi_void)
{
#ifdef CONFIG_FACTORY_TEST_MODE
        printf("factory test mode!\r\n");
#endif

    const hi_char* sdk_ver = hi_get_sdk_version();
    printf("sdk ver:%s\r\n", sdk_ver);
    printf("_____>>>>>>> lza %s %d\r\n", __FILE__, __LINE__);
    hi_flash_partition_table *ptable = HI_NULL;

    peripheral_init();
    peripheral_init_no_sleep();

#ifndef CONFIG_FACTORY_TEST_MODE
    hi_lpc_register_wakeup_entry(peripheral_init);
#endif

    hi_u32 ret = hi_factory_nv_init(HI_FNV_DEFAULT_ADDR, HI_NV_DEFAULT_TOTAL_SIZE, HI_NV_DEFAULT_BLOCK_SIZE);
    if (ret != HI_ERR_SUCCESS) {
        printf("factory nv init fail\r\n");
    }

    /* partion table should init after factory nv init. */
    ret = hi_flash_partition_init();
    if (ret != HI_ERR_SUCCESS) {
        printf("flash partition table init fail:0x%x \r\n", ret);
    }
    ptable = hi_get_partition_table();

    ret = hi_nv_init(ptable->table[HI_FLASH_PARTITON_NORMAL_NV].addr, ptable->table[HI_FLASH_PARTITON_NORMAL_NV].size,
        HI_NV_DEFAULT_BLOCK_SIZE);
    if (ret != HI_ERR_SUCCESS) {
        printf("nv init fail\r\n");
    }

#ifndef CONFIG_FACTORY_TEST_MODE
    hi_upg_init();
#endif

    /* if not use file system, there is no need init it */
    hi_fs_init();

    (hi_void)hi_event_init(APP_INIT_EVENT_NUM, HI_NULL);
    hi_sal_init();
    /* 此处设为TRUE后中断中看门狗复位会显示复位时PC值,但有复位不完全风险,量产版本请务必设为FALSE */
    hi_syserr_watchdog_debug(HI_FALSE);
    /* 默认记录宕机信息到FLASH,根据应用场景,可不记录,避免频繁异常宕机情况损耗FLASH寿命 */
    hi_syserr_record_crash_info(HI_TRUE);

    hi_lpc_init();
    hi_lpc_register_hw_handler(config_before_sleep, config_after_sleep);

#if defined(CONFIG_AT_COMMAND) || defined(CONFIG_FACTORY_TEST_MODE)
    ret = hi_at_init();
    if (ret == HI_ERR_SUCCESS) {
        hi_at_sys_cmd_register();
    }
#endif

    /* 如果不需要使用Histudio查看WIFI驱动运行日志等,无需初始化diag */
    /* if not use histudio for diagnostic, diag initialization is unnecessary */
    /* Shell and Diag use the same uart port, only one of them can be selected */
#ifndef CONFIG_FACTORY_TEST_MODE

#ifndef ENABLE_SHELL_DEBUG
#ifdef CONFIG_DIAG_SUPPORT
    (hi_void)hi_diag_init();
#endif
#else
    (hi_void)hi_shell_init();
#endif

    tcpip_init(NULL, NULL);
#endif

    ret = hi_wifi_init(APP_INIT_VAP_NUM, APP_INIT_USR_NUM);
    if (ret != HISI_OK) {
        printf("wifi init failed!\n");
    } else {
        printf("wifi init success!\n");
    }
    app_demo_task_release_mem(); /* 释放系统栈内存所使用任务 */

#ifndef CONFIG_FACTORY_TEST_MODE
    app_demo_upg_init();
#ifdef CONFIG_HILINK
    ret = hilink_main();
    if (ret != HISI_OK) {
        printf("hilink init failed!\n");
    } else {
        printf("hilink init success!\n");
    }
#endif
#endif
    OHOS_Main();
}

app_main一开始打印了 SDK版本号,中间还会有一些初始化动作,最后一行会调用OHOS_Main();

该函数原型如下:

void OHOS_Main()
{
#if defined(CONFIG_AT_COMMAND) || defined(CONFIG_FACTORY_TEST_MODE)
    hi_u32 ret;
    ret = hi_at_init();
    if (ret == HI_ERR_SUCCESS) {
        hi_u32 ret2 = hi_at_register_cmd(G_OHOS_AT_FUNC_TBL, OHOS_AT_FUNC_NUM);
        if (ret2 != HI_ERR_SUCCESS) {
            printf("Register ohos failed!\n");
        }
    }
#endif
    OHOS_SystemInit();
}

最后,OHOS_SystemInit函数进行鸿蒙系统的初始化。我们进去看下初始化做了哪些动作。

路径:base\startup\bootstrap_lite\services\source\system_init.c

void OHOS_SystemInit(void)
{

    MODULE_INIT(bsp);

    MODULE_INIT(device);

    MODULE_INIT(core);

    SYS_INIT(service);

    SYS_INIT(feature);

    MODULE_INIT(run);

    SAMGR_Bootstrap();

} 

我们可以看到主要是初始化了 一些相关模块、系统,包括有bsp、device(设备)。其中最终的是MODULE_INIT(run);

它负责调用了,所有run段的代码,那么run段的代码是哪些呢?

事实上就是我们前面application中使用SYS_RUN() 宏设置的函数名。

还记得我们前面写的hello world应用程序吗?

#include "ohos_init.h"
#include "ohos_types.h"

void HelloWorld(void)
{
    printf("[DEMO] Hello world.\n");
}

SYS_RUN(HelloWorld);

也就是说所有用SYS_RUN() 宏设置的函数都会在使用MODULE_INIT(run); 的时候被调用。

为了验证这一点,我们可以加一些打印信息,如下:

我们重新编译后烧录。打开串口查看打印信息,如下:

可以看到在27行之后,就打印 hello world的信息。符合预期。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ......

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ......

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ......

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题

2.性能优化方向

3.架构方向

4.鸿蒙开发系统底层方向

5.鸿蒙音视频开发方向

6.鸿蒙车载开发方向

7.鸿蒙南向开发方向

相关推荐
半开半落4 分钟前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt
理想不理想v32 分钟前
vue经典前端面试题
前端·javascript·vue.js
不收藏找不到我33 分钟前
浏览器交互事件汇总
前端·交互
- 羊羊不超越 -43 分钟前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
YBN娜1 小时前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=1 小时前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck1 小时前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!1 小时前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。1 小时前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax