电子产业一站式赋能平台

PCB联盟网

搜索
查看: 704|回复: 0
收起左侧

HarmonyOS开发,从hello world开始

[复制链接]

2607

主题

2607

帖子

7472

积分

高级会员

Rank: 5Rank: 5

积分
7472
发表于 2020-11-10 17:56:20 | 显示全部楼层 |阅读模式
HarmonyOS开发,从hello world开始, 按照官方教程操作没有坑,我使用的编译环境是 VM12+Ubuntu18.04并且配置了SSH和Samba。

在Windows下使用VSCODE和ssh,这样的好处是只要虚拟机开机,其他的操作都可以在Windows下进行,并且可以不用在同一台电脑上,有需要可以在云上搭建。

(让人难过的是,我家里用的电脑出现了故障,在公司用的又不能安装VM,所以之后准备换成docker)

下面这个代码树就是拿到开发板后,玩板子的人操作的空间,所有你打算写的代码都在这个。

10-9微信推送.png (11.59 KB, 下载次数: 0)

下载附件  保存到相册  

1 小时前 上传




1、新建文件夹

2.png (44.54 KB, 下载次数: 0)

下载附件  保存到相册  

1 小时前 上传




2、编写业务代码

  • #include <stdio.h>
      
  • #include “ohos_init.h“
      
  • #include “ohos_types.h“
      

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

复制代码

这段代码引用了三个头文件:

stdio.h:学过C语言的都知道,调用printf的时候就需要使用; ohos_types.h:将C语言的变量做了重新定义; ohos_types.h: SYS_RUN这个宏就在这个文件里定义;

在例程的gn文件中提示了头文件的路径:

//utils/native/lite/include



3.png (17.87 KB, 下载次数: 0)

下载附件  保存到相册  

1 小时前 上传



我们主要来看一下关于SYS_RUN的定义:

  • typedef void (*InitCall)(void);
      
  • #define USED_ATTR __attribute__((used))
      
  • #ifdef LAYER_INIT_SHARED_LIB
      
  • #define LAYER_INIT_LEVEL_0 0
      
  • #define LAYER_INIT_LEVEL_1 1
      
  • #define LAYER_INIT_LEVEL_2 2
      
  • #define LAYER_INIT_LEVEL_3 3
      
  • #define LAYER_INIT_LEVEL_4 4
      
  • #define CTOR_VALUE_device 100
      
  • #define CTOR_VALUE_core 110
      
  • #define CTOR_VALUE_sys_service 120
      
  • #define CTOR_VALUE_sys_feature 130
      
  • #define CTOR_VALUE_app_service 140
      
  • #define CTOR_VALUE_app_feature 150
      
  • #define CTOR_VALUE_run  700
      
  • #define LAYER_INITCALL(func, layer, clayer, priority)                                     \
      
  •     static __attribute__((constructor(CTOR_VALUE_##layer + LAYER_INIT_LEVEL_##priority))) \
      
  •         void BOOT_##layer##priority##func() {func();}
      
  • #else
      
  • #define LAYER_INITCALL(func, layer, clayer, priority)            \
      
  •     static const InitCall USED_ATTR __zinitcall_##layer##_##func \
      
  •         __attribute__((section(“.zinitcall.“ clayer #priority “.init“))) = func
      
  • #endif
      
  • // Default priority is 2, priority range is [0, 4]
      
  • #define LAYER_INITCALL_DEF(func, layer, clayer) \
      
  • LAYER_INITCALL(func, layer, clayer, 2)
      

  •   
  • #define SYS_RUN(func) LAYER_INITCALL_DEF(func, run, “run“)

复制代码

这段代码需要从下往上看

  • (1)SYS_RUN(func)  
      
  • func = HelloWorld
      
  • (2) LAYER_INITCALL_DEF(func, run, “run“)
      
  • func = HelloWorld
      
  • layer = run
      
  • clayer = “run”
      
  • (3) LAYER_INITCALL(func, layer, clayer, 2)
      
  • func = HelloWorld
      
  • layer = run
      
  • clayer = “run”
      
  • priority = 2

复制代码

LAYER_INITCALL有一个条件编译宏:



  • 定义了LAYER_INIT_SHARED_LIB
      


  • #define LAYER_INITCALL(func, layer, clayer, priority)                                     \
      
  •     static __attribute__((constructor(CTOR_VALUE_##layer + LAYER_INIT_LEVEL_##priority))) \
      
  •         void BOOT_##layer##priority##func() {func();}

复制代码

整理得

  • #define LAYER_INITCALL(func, layer, clayer, priority)                                     \
      
  •     static __attribute__((constructor(CTOR_VALUE_run + 2))) \
      
  •         void BOOT_ run2HelloWorld { HelloWorld ();}

复制代码

这里运用GUN 文档中对构造属性的说明

  • constructor
      
  • destructor
      
  • constructor (priority)
      
  • destructor (priority)
      
  • The constructor attribute causes the function to be calLED automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () completes or exit () is called. Functions with these attributes are useful for initializing data that is used implicitly during the execution of the program.
      
  • You may provide an optional integer priority to control the order in which constructor and destructor functions are run. A constructor with a smaller priority number runs before a constructor with a larger priority number; the opposite relationship holds for destructors. So, if you have a constructor that allocates a resource and a destructor that deallocates the same resource, both functions typically have the same priority. The priorities for constructor and destructor functions are the same as those specified for namespace-scope C++ objects (see C++ Attributes).

复制代码

就是在这里定义了一个函数BOOT_ run2HelloWorld, 在这个函数里调用应用层中的HelloWorld, 且这个函数的执行顺序是在main之前,其优先级为702。

2. 没定义LAYER_INIT_SHARED_LIB

  • #define LAYER_INITCALL(func, layer, clayer, priority)            \
      
  •     static const InitCall USED_ATTR __zinitcall_##layer##_##func \
      
  •         __attribute__((section(“.zinitcall.“ clayer #priority “.init“))) = func

复制代码

整理得

  • #define LAYER_INITCALL(func, layer, clayer, priority)            \
      
  •     static const InitCall USED_ATTR __zinitcall_run_HelloWorld \
      
  •         __attribute__((section(“.zinitcall.“ “run” “2”“.init“))) = HelloWorld

复制代码

最终得

  • #define LAYER_INITCALL(func, layer, clayer, priority)            \
      
  •     static const InitCall USED_ATTR __zinitcall_run_HelloWorld \
      
  •         __attribute__((section(“.zinitcall.run2.init“))) = HelloWorld

复制代码 InitCall 是一个没有参数没有返回值的函数指针类型,#define USED_ATTR __attribute__((used)) 用来修饰一个变量或者函数,目的是告诉编译器,修饰的这个变量或者函数是被使用的,即使没有使用也不要优化掉 __attribute__((section(“.zinitcall.run2.init“))) 用来修饰一个变量或者函数,目的是告诉编译器,修饰的这个变量或者函数要被编译连接到.zinitcall.run2.init 这个代码段里, 在系统运行后,.zinitcall.run2.init 代码段的内容会被逐个执行,就会执行到我们的HelloWorld 函数。



那么原宏定义就是定义了一个静态常量__zinitcall_run_HelloWorld ,这个常量的类型是个InitCall的指针类型,这个常量的值为HelloWorld ,对应一个函数,就是这个函数的地址。并将这个常量编译连接到.zinitcall.run2.init 代码段中。

熟悉Linux的都知道,这个是Linux的常规操作,将一系列初始化函数编译链接到一起,运行时候整块取出去执行。

在官方例程的这个脚本文件下

code-1.0\vendor\hisi\hi3861\hi3861\build\build_tmp\scripts\link.lds 包含下面内容:

  • __zinitcall_run_start = .;
      
  •        KEEP (*(.zinitcall.run0.init))
      
  •        KEEP (*(.zinitcall.run1.init))
      
  •        KEEP (*(.zinitcall.run2.init))
      
  •        KEEP (*(.zinitcall.run3.init))
      
  •        KEEP (*(.zinitcall.run4.init))
      
  •        __zinitcall_run_end = .;

复制代码

刚刚被定义的那个函数指针,编译后就会按照声明的位置,保存在目标代码.zinitcall.run2.init 代码段中

结合连老师的链接更清晰:

https://bbs.elecfans.com/jishu_2001989_1_1.html



补充一下关于宏定义#和##

#:字符串化

##:把宏参数连接在一起,参数在中间两边都需要##

宏定义用#和##的地方宏参数不会再展开, 如果需要将传入参数的宏再次展开,需要中间增加一层转换, 把所有宏的参数在这层里全部展开, 在最下层使用#或者##。




3、业务构建成静态库的BUILD.gn文件,跟刚刚的.c文件在同一个目录下

  • static_library(“myapp“) {
      
  •     sources = [
      
  •         “hello_world.c“
      
  •     ]
      
  •     include_dirs = [
      
  •         “//utils/native/lite/include“
      
  •     ]
      
  • }

复制代码

BUILD.gn文件由三部分内容(目标、源文件、头文件路径)构成:

· static_library中指定业务模块的编译结果,为静态库文件libmyapp.a,开发者根据实际情况完成填写。 · sources中指定静态库.a所依赖的.c文件及其路径,若路径中包含“//“则表示绝对路径(此处为代码根路径),若不包含“//“则表示相对路径。

· include_dirs中指定source所需要依赖的.h文件路径。


4、编写模块BUILD.gn文件,指定需参与构建的特性模块,在app这个目录下,和刚刚新建的那个文件夹(my_first_app)在同一个文件夹下

  • <p class=“MsoNormal“>import(“//build/lite/config/component/lite_component.gni“)<o:p></o:p></p><p class=“MsoNormal“><o:p> </o:p></p><p class=“MsoNormal“>lite_component(“app“) {<o:p></o:p></p><p class=“MsoNormal“>    features = [<o:p></o:p></p><p class=“MsoNormal“>        “my_first_app:myapp“,<o:p></o:p></p><p class=“MsoNormal“>    ]<o:p></o:p></p><p class=“MsoNormal“>}</p>

复制代码

将“my_first_app:myapp“添加到features字段中,刚刚写的那个代码就能被编译了。

· 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.png (64.67 KB, 下载次数: 0)

下载附件  保存到相册  

1 小时前 上传
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


联系客服 关注微信 下载APP 返回顶部 返回列表