> 文档中心 > 鸿蒙设备开发之Hello World

鸿蒙设备开发之Hello World


引言

每当学习一门新的编程语言或者上手一款新的开发板,我们经常写的第一个程序要么是在终端窗口中打印输出“Hello World !”,要么就是驱动GPIO点亮开发板上的LED灯。本文是在学习鸿蒙设备开发过程中,带大家写的第一个程序,通过这个程序,我们可以对鸿蒙设备开发的整个流程有一个初步的体验。

在阅读正文之前,建议大家先搭建好实验环境:

(1)按照《搭建鸿蒙设备开发环境:Ubuntu20.04+DevEco Device Tool Release 3.0》搭建鸿蒙开发环境。

(2)按照《获取OpenHarmony源码:从DevEco Marketplace获取(2)》创建一个鸿蒙项目。

(3)准备一块Hi3861处理器的开发板,我使用的是润和的Pegasus物联网开发套件。

建好的实验环境:

(1)Ubuntu20.04

(2)DevEco Device Tool Release 3.0(devicetool-linux-tool-3.0.0.401)

(3)用Open Harmony发行版@ohos/hispark_pegasus 3.1.2创建了鸿蒙工程hispark_pegasus_312。

(4)润和的Pegasus物联网开发套件

一、编写源程序

1、新建文件夹experiment 。

单击DevEco Device Tool左侧工具栏中的Explore图标,打开鸿蒙工程hispark_pegasus_312的目录树,选中applications/sample/wifi-iot下的app文件夹,在其上单击鼠标右键,选择New Folder,新建一个文件夹experiment,以后每次实验的代码可以放在这个文件夹下。

在这里插入图片描述

2、新建文件夹B01_hello_world 。

在文件夹experiment中再新建文件夹B01_hello_world,用于存放本次实验的代码。

3、新建文件hello_world.c 。

选中文件夹B01_hello_world,在其上单击鼠标右键,选择New File,新建一个文件hello_world.c。

4、编写源程序。

在文件hello_world.c中编写本次实验的源程序。

(1)头文件

#include #include "ohos_init.h"#include "cmsis_os2.h"

(2)宏定义

#define TASK_STACK_SIZE     1024    // 任务堆栈的大小,单位:字节     #define TASK_PRIO    osPriorityNormal1     // 任务优先级

osPriorityNormal1是在头文件 cmsis_os2.h 中定义的枚举量,值为25。

(3)任务函数

可以把任务函数简单理解成主函数。本例程中的任务函数很简单,就是每隔2秒打印输出Hello world!

//任务函数static void HelloTaskFun(const char *arg){    (void)arg;    unsigned int cnt = 0;    printf("\n");    while(1)    { printf(">>>Hello world! cnt=%d \n", cnt); cnt++; osDelay(200);    }}

osDelay是在 cmsis_os2.h 中声明的函数。

(4)任务入口函数

任务入口函数的作用就是把任务函数创建成一个任务,并使之进入就绪状态,接受操作系统的调度。主要包括两个步骤:设置任务属性;创建任务。

//任务入口函数static void HelloTaskEntry(void){    //Step1:设置任务属性    osThreadAttr_t attr;    attr.name= "HelloTask";      //任务名称    attr.attr_bits  = 0U;    attr.cb_mem     = NULL;    attr.cb_size    = 0;    attr.stack_mem  = NULL;    attr.stack_size = TASK_STACK_SIZE;  //任务堆栈的大小,单位:字节    attr.priority   = TASK_PRIO; //任务优先级    //Step2:创建任务    if(osThreadNew((osThreadFunc_t)HelloTaskFun, NULL, &attr) == NULL)    { printf(">>> Error!!! Falied to create HelloTask!\n");    }}

osThreadAttr_t 是在 cmsis_os2.h 中定义的结构体类型。通常只需要设置任务名称、任务堆栈的大小和任务优先级,其他属性设置为0或NULL即可。

osThreadNew 是在 cmsis_os2.h 中声明的函数,用于把任务函数创建成一个任务,并使之进入就绪状态,接受操作系统的调度。传给函数的第一个参数是指向任务函数的指针;第二个参数也是一个指针,指向要传给任务函数的参数;第三个参数是指向任务属性结构体的指针。如果任务创建成功,该函数返回任务的ID;否则,函数的返回值是NULL。

osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);

(5)APP_FEATURE_INIT

APP_FEATURE_INIT(HelloTaskEntry);

APP_FEATURE_INIT是一个在头文件 ohos_init.h 中定义的带参数的宏,这个宏的作用就是指示编译器把某个函数(如:本例程中的 HelloTaskEntry ),编译到某个代码段中,系统启动之后,会遍历这个代码段中的所有函数并执行。

二、编写编译脚本

除了编源程序之外,我们还需要通过编译脚本告诉鸿蒙工程的编译构建子系统,如何处理我们在本文第二部分编写的源程序。关于鸿蒙工程的编译构建子系统,我准备专门开一个专栏写一些文章,敬请期待。

1、在B01_hello_world文件夹中新建一个BUILD.gn文件,填入以下内容:

static_library("B01_hello_world") {    sources = [ "hello_world.c",    ]    include_dirs = [ "//utils/native/lite/include", "//device/hisilicon/hispark_pegasus/hi3861_adapter/kal/cmsis",    ]}

注:这个编译脚本的文件名必须是BUILD.gn,不要用其他名字,这是由编译构建子系统决定的。

这个BUILD.gn文件中的static_library是告诉编译构建子系统,将应用程序编译生成一个静态库,主要内容包括三部分:

(1)在static_library后面的括号中,指定要编译生成的静态库的目标名称,例如:B01_hello_world。在本文第三部分编译工程之后,会在 out/hispark_pegasus/wifiiot_hispark_pegasus/libs 路径下生成一个静态库文件:libB01_hello_world.a(在指定的静态库目标名称前会默认加上lib)。

(2)在sources列表中,列出编译生成静态库所需要用到的源文件,多个文件之间用逗号隔开。

(3)在include_dirs列表中,列出源文件中所引用的头文件的路径,多个路径之间用逗号隔开。

无论是sources列表中的文件,还是include_dirs列表中的路径,若包含"//“则表示使用绝对路径(”//“代表鸿蒙项目根目录的全路径名),若不包含”//"则表示使用相对路径(相对于当前这个文件所在的文件夹)。

2、修改app文件夹下的BUILD.gn文件。

在这个BUILD.gn文件的features列表中,添加一项,如下图红框中所示:

在这里插入图片描述

我们知道,鸿蒙系统是按照“系统(子系统集)”、“子系统(Subsystem)”、“组件/模块(Component)”逐次展开的,详见:《迈出学习鸿蒙操作系统的第一步》中的“鸿蒙操作系统的技术架构”。其实,一个组件/模块(Component)也可以进一步分成若干个Feature。在本文中,由源程序 hello_world.c 生成的静态库B01_hello_world 就被作为一个feature加入到了名为app的Component中;app这个Component又是子系统applications 里面的。

这个BUILD.gn文件就是告诉编译构建子系统怎么去构建一个模块(Component)。

第14行中的import语句类似于C程序中的#include,因为第16行用到的 lite_component 是在这个.gni文件中定义的。

第16行,lite_component后面的括号中是要构建生成的模块的目标名称(Target Name)。

在feature列表中列出参与构建这个Component的目标(Target),这个列表中的每个目标都可以被看作构成这个Component的一个Feature,其中就包括由源程序hello_world.c生成的目标:静态库B01_hello_world。

冒号后面是要参与构建这个Component的目标。冒号前面是目标的路径,里面没有包含"//",所以这是一个相对路径。在编译构建时,编译构建子系统会到这个路径下寻找名为 BUILD.gn 的文件,再根据这个 BUILD.gn将源程序 hello_world.c 编译成一个目标,即:静态库 B01_hello_world 。

注1:冒号前后不能有空格。

注2:如果冒号后面的目标名字与目标的BUILD.gn文件所在文件夹名字相同,可以省略冒号和其后面的目标名称,如下图所示:

在这里插入图片描述

现在,我们基本上知道了,如何通过编写BUILD.gn文件,指示编译构建子系统构建一个Feature,以及将Feature加入到Component中,构建一个Component。那么,怎么把Component加入到Subsystem中,以及怎么让某个Subsystem参与整个鸿蒙系统的构建呢?在编译构建子系统专栏中,我会把这些问题讲清楚。

三、编译工程

点击左侧工具栏中的DevEco图标,在“PROJECT TASKS”–鸿蒙工程hispark_pegasus_312–hi3861下,单击Build按钮,开始编译。

在这里插入图片描述

编译成功后,终端(TERMINAL)窗口的输出如下图所示。可以在工程的out目录下,查看编译生成的文件,用于后续的Hi3861开发板烧录。

在这里插入图片描述

四、烧写程序

1、连接Hi3861开发板和开发主机。

将Hi3861开发板通过USB线与电脑连接后,Ubuntu虚拟机会弹出下图所示窗口,选择连接到虚拟机,选择虚拟机名称,单击“确定”。

在这里插入图片描述

2、配置工程的烧录选项(Upload Options)。

在这里插入图片描述

点击左侧工具栏中的DevEco图标;打开Projetc Settings页面;打开hi3861标签页;设置烧录选项(Upload Options),包括upload_partitions、upload_port和upload_protocol。

upload_partitions:选择待烧录的分区列表,默认选择 hi3861_app 。

upload_port:选择串口设备 /dev/ttyUSB0 。

upload_protocol:选择 hiburn-serial 。

最后,单击右上角的Save按钮,保存配置信息。

3、启动烧录

(1)点击左侧工具栏中的DevEco图标,在“PROJECT TASKS”–鸿蒙工程hispark_pegasus_312–hi3861下,单击Upload按钮。

在这里插入图片描述

(2)终端(TERMINAL)窗口的输出如下图所示:

在这里插入图片描述

根据提示信息,按开发板上的复位键,对开发板进行复位(注:如果终端窗口没反应,就再按一次),直到开始烧录。

(3)烧录完毕后,终端窗口如下图所示:

在这里插入图片描述

单击终端窗口,看到实心白色光标块后,根据最后一行提示,按任意键关闭终端窗口。

五、测试

(1)点击左侧工具栏中的DevEco图标,在“PROJECT TASKS”–鸿蒙工程hispark_pegasus_312–hi3861下,单击Monitor按钮。
在这里插入图片描述

终端窗口如下图所示。

在这里插入图片描述

(2)按开发板上的复位键,对开发板进行复位,烧录到开发板上的程序开始执行,在终端窗口中打印输出一些信息,如下图所示:

在这里插入图片描述

单击终端窗口,看到实心白色光标块后,按ctrl+c结束程序,然后按任意键可关闭终端窗口。

本文在CSDN、公众号、头条号和知乎同步发布,感谢关注。