OpenHarmony设备开发基于命令行开发轻量系统(基于Hi3861开发板)
版本:v3.2 Beta
安装Hi3861开发板特有环境
除上述安装库和工具集和安装编译工具外,针对Hi3861开发板还需要安装特定的编译工具。
工具要求
表1 Hi3861 WLAN模组需要安装的编译工具
开发工具 | 用途 |
SCons3.0.4+ | 编译构建工具 |
python模块:setuptools、kconfiglib、pycryptodome、six、ecdsa | 编译构建工具 |
gcc riscv32 | 编译构建工具 |
操作步骤
相关操作在Ubuntu环境下进行。
安装Scons
1.运行如下命令,安装SCons安装包。
python3 -m pip install scons
2.运行如下命令,查看是否安装成功。如果安装成功,查询结果下图所示。
scons -v
图1 SCons安装成功界面,版本要求3.0.4以上
安装python模块
1.运行如下命令,安装python模块setuptools。
pip3 install setuptools
2.安装GUI menuconfig工具(Kconfiglib),建议安装Kconfiglib 13.2.0+版本,任选如下一种方式。
● 命令行方式:
sudo pip3 install kconfiglib
● 安装包方式:
● 下载.whl文件(例如:kconfiglib-13.2.0-py2.py3-none-any.whl)。 下载路径:“https://pypi.org/project/kconfiglib#files”
● 运行如下命令,安装.whl文件。
sudo pip3 install kconfiglib-13.2.0-py2.py3-none-any.whl
3.安装pycryptodome,任选如下一种方式。
安装升级文件签名依赖的Python组件包,包括:pycryptodome、six、ecdsa。安装ecdsa依赖six,请先安装six,再安装ecdsa。
● 命令行方式:
sudo pip3 install pycryptodome
● 安装包方式:
● 下载.whl文件(例如:pycryptodome-3.9.9-cp38-cp38-manylinux1_x86_64.whl)。 下载路径:“https://pypi.org/project/pycryptodome/#files”。
● 运行如下命令,安装.whl文件。
sudo pip3 install pycryptodome-3.9.9-cp38-cp38-manylinux1_x86_64.whl
4.安装six,任选如下一种方式。
● 命令行方式:
sudo pip3 install six --upgrade --ignore-installed six
● 安装包方式:
● 下载.whl文件(例如:six-1.12.0-py2.py3-none-any.whl)。 下载路径:“https://pypi.org/project/six/#files”
● 运行如下命令,安装.whl文件。
sudo pip3 install six-1.12.0-py2.py3-none-any.whl
4.安装ecdsa,任选如下一种方式。
● 命令行方式:
sudo pip3 install ecdsa
● 安装包方式:
● 下载.whl文件(例如:ecdsa-0.14.1-py2.py3-none-any.whl)。 下载路径:“https://pypi.org/project/ecdsa/#files”
● 运行如下命令,安装.whl文件。
sudo pip3 install ecdsa-0.14.1-py2.py3-none-any.whl
安装gcc_riscv32(WLAN模组类编译工具链)
须知:● Hi3861开发板平台仅支持使用libgcc运行时库的静态链接,不建议开发者使用libgcc运行时库的动态链接,以免产品需遵从GPLV3许可证。
● 通过下述步骤2-14,编译好了gcc_riscv32镜像,提供给开发者直接下载使用。直接下载gcc_riscv32镜像的开发者可省略下述2-14步。
1.打开Linux编译服务器终端。
2.下载riscv-gnu-toolchain交叉编译工具链。
git clone --recursive https://gitee.com/mirrors/riscv-gnu-toolchain.git
3.打开文件夹riscv-gnu-toolchain,先删除空文件夹,以防止下载newlib,binutils,gcc时冲突。
cd riscv-gnu-toolchain && rm -rf riscv-newlib && rm -rf riscv-binutils && rm -rf riscv-gcc
4.下载riscv-newlib-3.0.0。
git clone -b riscv-newlib-3.0.0 https://github.com/riscv/riscv-newlib.git
5.下载riscv-binutils-2.31.1。
git clone -b riscv-binutils-2.31.1 https://github.com/riscv/riscv-binutils-gdb.git
6.下载riscv-gcc-7.3.0。
git clone -b riscv-gcc-7.3.0 https://github.com/riscv/riscv-gcc
7.添加riscv-gcc-7.3.0补丁。 访问gcc官方补丁链接89411,86724,按照补丁链接中要求的修改,手动将变更添加到对应的.c和.h文件中,注意由于patch版本与下载的gcc版本有所偏差,行数有可能对应不上,请自行查找patch中的关键字定位到对应行。
8.下载GMP 6.1.2,并解压安装。
tar -xvf gmp-6.1.2.tar.bz2 && mkdir build_gmp && cd build_gmp && ../gmp-6.1.2/configure --prefix=/usr/local/gmp-6.1.2 --disable-shared --enable-cxx && make && make install
9.下载mpfr-4.0.2 ,并解压安装。
tar -xvf mpfr-4.0.2.tar.gz && mkdir build_mpfr && cd build_mpfr && ../mpfr-4.0.2/configure --prefix=/usr/local/mpfr-4.0.2 --with-gmp=/usr/local/gmp-6.1.2 --disable-shared && make && make install
10.下载mpc-1.1.0 ,并解压安装。
tar -xvf mpc-1.1.0.tar.gz && mkdir build_mpc && cd build_mpc && ../mpc-1.1.0/configure --prefix=/usr/local/mpc-1.1.0 --with-gmp=/usr/local/gmp-6.1.2 --with-mpfr=/usr/local/mpfr-4.0.2 --disable-shared && make && make install
11.打开文件夹riscv-gnu-toolchain,新建工具链输出目录。
cd /opt && mkdir gcc_riscv32
12.编译binutils。
mkdir build_binutils && cd build_binutils && ../riscv-binutils-gdb/configure --prefix=/opt/gcc_riscv32 --target=riscv32-unknown-elf --with-arch=rv32imc --with-abi=ilp32 --disable-__cxa_atexit --disable-libgomp --disable-libmudflap --enable-libssp --disable-libstdcxx-pch --disable-nls --disable-shared --disable-threads --disable-multilib --enable-poison-system-directories --enable-languages=c,c++ --with-gnu-as --with-gnu-ld --with-newlib --with-system-zlib CFLAGS="-fstack-protector-strong -O2 -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack -fPIE" CXXFLAGS="-fstack-protector-strong -O2 -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack -fPIE" CXXFLAGS_FOR_TARGET="-Os -mcmodel=medlow -Wall -fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -Wtrampolines -fno-short-enums -fno-short-wchar" CFLAGS_FOR_TARGET="-Os -mcmodel=medlow -Wall -fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -Wtrampolines -fno-short-enums -fno-short-wchar" --bindir=/opt/gcc_riscv32/bin --libexecdir=/opt/gcc_riscv32/riscv32 --libdir=/opt/gcc_riscv32 --includedir=/opt/gcc_riscv32 && make -j16 && make install && cd ..
13.编译newlib。
mkdir build_newlib && cd build_newlib && ../riscv-newlib/configure --prefix=/opt/gcc_riscv32 --target=riscv32-unknown-elf --with-arch=rv32imc --with-abi=ilp32 --disable-__cxa_atexit --disable-libgomp --disable-libmudflap --enable-libssp --disable-libstdcxx-pch --disable-nls --disable-shared --disable-threads --disable-multilib --enable-poison-system-directories --enable-languages=c,c++ --with-gnu-as --with-gnu-ld --with-newlib --with-system-zlib CFLAGS="-fstack-protector-strong -O2 -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack -fPIE" CXXFLAGS="-fstack-protector-strong -O2 -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack -fPIE" \CXXFLAGS_FOR_TARGET="-Os -mcmodel=medlow -Wall -fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -Wtrampolines -fno-short-enums -fno-short-wchar" CFLAGS_FOR_TARGET="-Os -mcmodel=medlow -Wall -fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -Wtrampolines -fno-short-enums -fno-short-wchar" --bindir=/opt/gcc_riscv32/bin --libexecdir=/opt/gcc_riscv32 --libdir=/opt/gcc_riscv32 --includedir=/opt/gcc_riscv32 && make -j16 && make install && cd ..
14.编译gcc。
mkdir build_gcc && cd build_gcc && ../riscv-gcc/configure --prefix=/opt/gcc_riscv32 --target=riscv32-unknown-elf --with-arch=rv32imc --with-abi=ilp32 --disable-__cxa_atexit --disable-libgomp --disable-libmudflap --enable-libssp --disable-libstdcxx-pch --disable-nls --disable-shared --disable-threads --disable-multilib --enable-poison-system-directories --enable-languages=c,c++ --with-gnu-as --with-gnu-ld --with-newlib --with-system-zlib CFLAGS="-fstack-protector-strong -O2 -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack -fPIE" CXXFLAGS="-fstack-protector-strong -O2 -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now,-z,noexecstack -fPIE" LDFLAGS="-Wl,-z,relro,-z,now,-z,noexecstack" CXXFLAGS_FOR_TARGET="-Os -mcmodel=medlow -Wall -fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -Wtrampolines -fno-short-enums -fno-short-wchar" CFLAGS_FOR_TARGET="-Os -mcmodel=medlow -Wall -fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -Wtrampolines -fno-short-enums -fno-short-wchar" --with-headers="/opt/gcc-riscv32/riscv32-unknown-elf/include" --with-mpc=/usr/local/mpc-1.1.0 --with-gmp=/usr/local/gmp-6.1.2 --with-mpfr=/usr/local/mpfr-4.0.2 && make -j16 && make install
15.设置环境变量。
说明:
如果直接采用编译好的riscv32 gcc包,请先执行以下命令将压缩包解压到根目录:
tar -xvf gcc_riscv32-linux-7.3.0.tar.gz -C ~
vim ~/.bashrc
将以下命令拷贝到.bashrc文件的最后一行,保存并退出。
export PATH=~/gcc_riscv32/bin:$PATH
16.生效环境变量。
source ~/.bashrc
17.Shell命令行中输入如下命令,如果能正确显示编译器版本号,表明编译器安装成功。
riscv32-unknown-elf-gcc -v
编写“Hello World”程序
下方将通过修改源码的方式展示如何编写简单程序,输出“Hello world”。请在下载的源码目录中进行下述操作。
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
2.编写业务代码。
新建./applications/sample/wifi-iot/app/my_first_app下的hello_world.c文件,在hello_world.c中新建业务入口函数HelloWorld,并实现业务逻辑。并在代码最下方,使用OpenHarmony启动恢复模块接口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文件。
新建./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/lite/components/communication.json,添加组件hello_world_app的配置,如下所示为communication.json文件片段,"##start##“和”##end##“之间为新增配置(”##start##“和”##end##"仅用来标识位置,添加完配置后删除这两行):
{
"components": [
{
"component": "camera_sample_communication",
"description": "Communication related samples.",
"optional": "true",
"dirs": [
"applications/sample/camera/communication"
],
"targets": [
"//applications/sample/camera/communication:sample"
],
"rom": "",
"ram": "",
"output": [],
"adapted_kernel": [ "liteos_a" ],
"features": [],
"deps": {
"components": [],
"third_party": []
}
},
##start##
{
"component": "hello_world_app",
"description": "hello world samples.",
"optional": "true",
"dirs": [
"applications/sample/wifi-iot/app/my_first_app"
],
"targets": [
"//applications/sample/wifi-iot/app/my_first_app:myapp"
],
"rom": "",
"ram": "",
"output": [],
"adapted_kernel": [ "liteos_m" ],
"features": [],
"deps": {
"components": [],
"third_party": []
}
},
##end##
{
"component": "camera_sample_app",
"description": "Camera related samples.",
"optional": "true",
"dirs": [
"applications/sample/camera/launcher",
"applications/sample/camera/cameraApp",
"applications/sample/camera/setting",
"applications/sample/camera/gallery",
"applications/sample/camera/media"
],
5.修改单板配置文件。
修改文件vendor/hisilicon/hispark_pegasus/config.json,新增hello_world_app组件的条目,如下所示代码片段为applications子系统配置,"##start##“和”##end##“之间为新增条目(”##start##“和”##end##"仅用来标识位置,添加完配置后删除这两行):
{
"subsystem": "applications",
"components": [
##start##
{ "component": "hello_world_app", "features":[] },
##end##
{ "component": "wifi_iot_sample_app", "features":[] }
]
},
编译
OpenHarmony支持hb和build.sh两种编译方式。此处介绍hb方式,build.sh脚本编译方式请参考使用build.sh脚本编译源码。
想要详细了解OpenHarmony编译构建模块功能的开发者可参考编译构建指南。
前提条件
- 已正确安装库和工具集。
- 已正确安装编译工具。
- 已正确安装Hi3861特有工具。
- “Hello World”程序已编写完成。
- 可正常登录Ubuntu环境。
操作步骤
在Ubuntu环境下进入源码根目录,执行如下命令进行编译:
1.设置编译路径。
hb set
2.选择当前路径。
.
3.在hisilicon下选择wifiiot_hispark_pegasus并回车。
说明:
同样的开发板在适配不同的场景时,要采用的编译形态不同(即此处要选择的product不同),请参考编译形态整体说明。
图1 Hi3861编译设置图例
4.执行编译。
说明:
- 单独编译一个部件(例如hello),可使用“hb build -T目标名称”进行编译。
- 增量编译整个产品,可使用“hb build”进行编译。
- 完整编译整个产品,可使用“hb build -f”进行编译。
此处以完整编译整个产品为例进行说明。
hb build -f
5.编译结束后,出现“build success”字样,则证明构建成功。
须知:
编译结果文件及编译日志获取路径:out/hispark_pegasus/wifiiot_hispark_pegasus。
烧录
针对Hi3861开发板,除了DevEco Device Tool(操作方法请参考烧录)外,还可以使用Hiburn进行烧录。
前提条件
- 开发板相关源码已编译完成,已形成烧录文件。
- 客户端(操作平台,例如Windows系统)已下载并安装HiBurn工具。
- 客户端(操作平台,例如Windows系统)已安装USB驱动,可参考USB驱动安装指导。
- 客户端已安装串口终端工具(例如IPOP)。
- 使用USB线缆连接客户端与开发板。
操作步骤
1.准备烧录相关文件。
1.在客户端新建用于保存烧录文件的文件夹,例如“D:\hi3861”。
2.将编译完成的源码包下载至客户端并解压,将烧录相关文件拷贝至步骤1中新建的文件夹。 烧写所需文件为:Hi3861_wifiiot_app_allinone.bin、Hi3861_loader_signed.bin
2.使用HiBurn烧录。
1.打开HiBurn。
2.设置HiBurn参数,根据实际情况选择COM口,勾选“Select all”和“Auto burn”。
图1 HiBurn烧写界面参数设置
3.单击Select file在步骤1创建的文件夹中选择烧写文件Hi3861_wifiiot_app_allinone.bin。
图2 hiburn文件选择完成
4.确保COM口选择正确,然后单击Connect后,按一下开发板上串口旁的按钮(Reset键)开始烧写。 烧录开始后,可以在HiBurn工具下方的控制台区域观察到烧录过程中的打印信息。
烧录完成后,控制台区域会打印提示"Execution Successful"。
图3 单击"Connect"后
- 图4单击开发板重置按键后,烧写中
- 图5烧写完成
5.单击Disconnect断开连接。
运行
联网配置
由于Hi3861为WLAN模组,您可以在版本编译及烧录后,通过如下操作,使开发板实现联网功能。
1.保持Windows工作台和Hi3861 WLAN模组的连接状态,确认串口终端显示正常。
2.复位Hi3861 WLAN模组,终端界面显示“ready to OS start”,则启动成功。
图1 Hi3861 WLAN模组复位成功示意图
3.依次执行如下AT命令,启动STA模式,连接指定AP热点,并开启DHCP功能。
AT+STARTSTA # 启动STA模式
AT+SCAN # 扫描周边AP
AT+SCANRESULT # 显示扫描结果
AT+CONN="SSID",,2,"PASSWORD" # 连接指定AP,其中SSID/PASSWORD为待连接的热点名称和密码
AT+STASTAT # 查看连接结果
AT+DHCP=wlan0,1 # 通过DHCP向AP请求wlan0的IP地址
4.查看Hi3861 WLAN模组与网关联通是否正常,如下图所示。
AT+IFCFG # 查看模组接口IP
AT+PING=X.X.X.X # 检查模组与网关的联通性,其中X.X.X.X需替换为实际的网关地址
图2 Hi3861 WLAN模组联网成功示意图
调测验证
完成烧录及联网之后,用户可根据需要进行调试验证。目前调试验证的方法有以下两种,开发者可以根据具体业务情况选择。
- 通过printf打印日志
- 通过asm文件定位panic问题
由于本示例业务简单,采用printf打印日志的调试方式即可。下方将介绍这两种调试手段的使用方法。
printf打印
代码中增加printf维测,信息会直接打印到串口上。开发者可在业务关键路径或业务异常位置增加日志打印,如下所示:
void HelloWorld(void)
{
printf("[DEMO] Hello world.\n");
}
根据asm文件进行问题定位
系统异常退出时,会在串口上打印异常退出原因调用栈信息,如下文所示。通过解析异常栈信息可以定位异常位置。
=======KERNEL PANIC=======
**Call Stack*
Call Stack 0 -- 4860d8 addr:f784c
Call Stack 1 -- 47b2b2 addr:f788c
Call Stack 2 -- 3e562c addr:f789c
Call Stack 3 -- 4101de addr:f78ac
Call Stack 4 -- 3e5f32 addr:f78cc
Call Stack 5 -- 3f78c0 addr:f78ec
Call Stack 6 -- 3f5e24 addr:f78fc
Call Stack end***
为解析上述调用栈信息,需要使用到Hi3861_wifiiot_app.asm文件,该文件记录了代码中函数在Flash上的符号地址以及反汇编信息。asm文件会随版本打包一同构建输出,存放在./out/wifiiot/路径下。
1.将调用栈CallStack信息保存到txt文档中,以便于编辑。(可选)
2.打开asm文件,并搜索CallStack中的地址,列出对应的函数名信息。通常只需找出前几个栈信息对应的函数,就可明确异常代码方向。
Call Stack 0 -- 4860d8 addr:f784c -- WadRecvCB
Call Stack 1 -- 47b2b2 addr:f788c -- wal_sdp_process_rx_data
Call Stack 2 -- 3e562c addr:f789c
Call Stack 3 -- 4101de addr:f78ac
Call Stack 4 -- 3e5f32 addr:f78cc
Call Stack 5 -- 3f78c0 addr:f78ec
Call Stack 6 -- 3f5e24 addr:f78fc
3.根据以上调用栈信息,可以定位WadRecvCB函数中出现了异常。
4.完成代码排查及修改。
运行结果
示例代码编译、烧录、运行、调测后,重启开发板后将自动在界面输出如下结果:
ready to OS start
FileSystem mount ok.
wifi init success!
[DEMO] Hello world.