`
上一次报告主要是环境搭建,并且完成了代码获取与代码的编译烧写,本次报告从嵌入式系统最基本的GPIO引脚控制讲起。
动手之前肯定要熟悉下代码目录,为了直观的查看代码,推荐一个生成目录树的网站:“http://dir.yardtea.cc/ ”,只要把目录拖到网站相应位置即可生成目录树。点击配置按钮可以配置行程目录树的深度。
下边是我对代码目录下文件的简单理解,鸿蒙的文档很多,没有一个一个熟悉,还是比较喜欢先动起来,基本上是摸索出来的,发出来抛砖引玉。
下边会做三个逐渐复杂起来的简单程序:
1、hello world程序
2、LED控制程序(熟悉IO输出操作)
3、按键控制LED程序(熟悉IO输入操作)
一、hello world程序
添加用户APP程序代码,添加在applicationssamplewifi-iotapp下面,建立代码目录,本报告以“xapp“为例,在此目录下建立程序C代码文件xstart.c,并且在新建立的目录里面增加一个编译文件(BUILD.gn),鸿蒙采用gn构建系统,可以通过https://zhuanlan.zhihu.com/p/136954435做一个初步了解(我也是现学现卖)。建立好后用户目录如下图所示:
其中xstart.c文件如下编写,主要是使用printf函数输出“[xDEMO] Hello world”到串口。
- #include <stdio.h>
- #include "ohos_init.h"
- #include "ohos_types.h"
- void HelloWorld(void)
- {
- printf("[xDEMO] Hello world.
- ");
- }
- SYS_RUN(HelloWorld);
复制代码
新建立的目录里面增加的编译文件(BUILD.gn)内容如下图,关键点如图中红色标记。
修改applicationssamplewifi-iotapp目录下编译文件(BUILD.gn)内容如下图:静态库名称跟文件夹名称要对应建立的文件夹名称以及上一个编译文件生成静态库的名称,否则编译会出错,无法通过。
在鸿蒙操作系统根目录下使用“python build.py wifiiot”命令即可完成编译。将“Hi3861_wifiiot_app_allinone.bin”文件使用DecEco工具烧入开发板后,使用串口工具查看串口输出如下:
ready to OS start sdkver:Hi3861V100R001C00SPC025 2020-09-03 18:10:00 FileSystem mount ok.
[Xwind]wifi init success!
[xDEMO] Hello world. // printf输出内容
00 00:00:00 0 132 D 0/HIVIEW: hilog initsuccess.
00 00:00:00 0 132 D 0/HIVIEW: log limitinit success.
00 00:00:00 0 132 I 1/SAMGR: Bootstrapcore services(count:3).
00 00:00:00 0 132 I 1/SAMGR: Initservice:0x4ae54c TaskPool:0xfa1e4
00 00:00:00 0 132 I 1/SAMGR: Initservice:0x4ae570 TaskPool:0xfa854
00 00:00:00 0 132 I 1/SAMGR: Initservice:0x4ae680 TaskPool:0xfaa14
00 00:00:00 0 164 I 1/SAMGR: Init service0x4ae570 <time: 0ms> success!
00 00:00:00 0 64 I 1/SAMGR: Init service0x4ae54c <time: 0ms> success!
00 00:00:00 0 8 D 0/HIVIEW: hiview initsuccess.
00 00:00:00 0 8 I 1/SAMGR: Init service0x4ae680 <time: 0ms> success!
00 00:00:00 0 8 I 1/SAMGR: Initialized allcore system services!
00 00:00:00 0 64 I 1/SAMGR: Bootstrapsystem and application services(count:0).
00 00:00:00 0 64 I 1/SAMGR: Initializedall system and application services!
00 00:00:00 0 64 I 1/SAMGR: Bootstrapdynamic registered services(count:0).
至此helloworld程序算是成功添加并编译运行了。
二、LED控制程序(熟悉IO输出操作)
上一个程序仅仅使用了printf函数输出串口信息,除了MCU内核、串口外未使用其他MCU资源,接下来这个程序将通过程序代码控制核心板上的蓝色LED灯。
查看核心板原理图,如LED灯相关连的是GPIO09以及跳线J3,此次实验需要确保J3处于短接状态。电路图如下:
修改xstart.c文件内容如下,上一次未使用任务方式,程序仅仅会运行一次,本次会使用建立task的方式实现LED控制,修改内容代码中有注解:
- #include <stdio.h>
- #include "ohos_init.h"
- #include "ohos_types.h"
- #include <unistd.h>
- #include "cmsis_os2.h" // 操作系统任务相关头文件
- #include "wifiiot_gpio.h" // gpio操作函数头文件
- #include "wifiiot_gpio_ex.h" // gpio操作函数头文件
- static void * LedTask(const char *arg) // 任务函数
- {
- (void)arg;
- while(1) // 操作系统任务函数中典型的循环
- {
- printf("[LedExample]LED OFF!
- "); // 串口输出LED状态
- GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_9, 1);// GPIO09输出高电平LED熄灭
- usleep(500000); // 延时500MS
- printf("[LedExample]LED ON!
- ");// 串口输出LED状态
- GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_9, 0); // GPIO09输出高电平LED点亮
- usleep(500000); // 延时500MS
- }
- return NULL;
- }
- void HelloWorld(void)
- {
- printf("[xDEMO] Hello world.
- ");
- osThreadAttr_t attr; // task配置结构体 在cmsis_os2.h
- GpioInit(); // GPIO初始化在 wifiiot_gpio.h
- //复用引脚,设置GPIO09为GPIO模式
- IoSetFunc(WIFI_IOT_IO_NAME_GPIO_9, WIFI_IOT_IO_FUNC_GPIO_9_GPIO);//wifiiot_gpio_ex.h
- //设置为输出 设置GPIO09为输出
- GpioSetDir(WIFI_IOT_IO_NAME_GPIO_9, WIFI_IOT_GPIO_DIR_OUT);
- attr.name ="LedTask";
- attr.attr_bits = 0U;
- attr.cb_mem = NULL;
- attr.cb_size = 0U;
- attr.stack_mem = NULL;
- attr.stack_size = 1024; //512; // 此处为任务栈深度
- attr.priority = 26;
- // 创建LedTask
- if(osThreadNew((osThreadFunc_t)LedTask, NULL, &attr) == NULL)
- {
- printf("[LedExample]Falied to create LedTask!
- ");
- }
- }
- SYS_RUN(HelloWorld);
复制代码代码中在helloworld函数运行后会创建LedTask,修改App/xapp/文件夹下BUILD.gn,将kernelliteos_mcomponentscmsis2.0、
aseiot_hardwareinterfaceskitswifiiot_lite两个头文件目录加入include
-dirs里面,否则后续编译会出现找不到头文件而报错终止编译。
static_library("xstart")
{
sources =
[
"xstart.c"
include_dirs =
[
"//utils/native/lite/include" ,
"//kernel/liteos_m/components/cmsis/2.0",
"//base/iot_hardware/interfaces/kits/wifiiot_lite",
}
特别说明一下,网上其他教程里面,栈深度一般写的是512,而使用printf函数需要的栈空间较大,需要将栈深度更改为1024.否则编译烧录运行后有栈溢出问题。串口会输出如下内容。
=======KERNEL PANIC=======
[ERR] CURRENT task LedTask stack overflow!
栈深度修改为1024后编译烧录运行会发现核心板上的蓝色LED以1hz(500ms亮、500ms灭)在闪烁,同时串口监视会收到打印信息如下:
ready to OS start sdkver:Hi3861V100R001C00SPC025 2020-09-03 18:10:00 formatting spiffs...
FileSystem mount ok.
[Xwind]wifi init success!!
[xDEMO] Hello world.
[LedExample]LED OFF!
00 00:00:00 0 132 D 0/HIVIEW: hilog initsuccess.
00 00:00:00 0 132 D 0/HIVIEW: log limitinit success.
00 00:00:00 0 132 I 1/SAMGR: Bootstrapcore services(count:3).
00 00:00:00 0 132 I 1/SAMGR: Initservice:0x4ae78c TaskPool:0xfa608
00 00:00:00 0 132 I 1/SAMGR: Init service:0x4ae7b0TaskPool:0xfac78
00 00:00:00 0 132 I 1/SAMGR: Initservice:0x4ae8c0 TaskPool:0xfae38
00 00:00:00 0 8 I 1/SAMGR: Init service0x4ae7b0<time: 0ms> success!
00 00:00:00 0 164 I 1/SAMGR: Init service0x4ae78c <time: 0ms> success!
00 00:00:00 0 108 D 0/HIVIEW: hiview initsuccess.
00 00:00:00 0 108 I 1/SAMGR: Init service0x4ae8c0 <time: 0ms> success!
00 00:00:00 0 108 I 1/SAMGR: Initializedall core system services!
00 00:00:00 0 164 I 1/SAMGR: Bootstrapsystem and application services(count:0).
00 00:00:00 0 164 I 1/SAMGR: Initializedall system and application services!
00 00:00:00 0 164 I 1/SAMGR: Bootstrapdynamic registered services(count:0).
[LedExample]LED ON!
[LedExample]LED OFF!
.
.
.
至此LED控制实验算是结束了,此次实验主要使用了GPIO的IO输出功能,相关函数在aseiot_hardwareframeworkswifiiot_litesrc目录的wifiiot_gpio.c, wifiiot_gpio_ex.c文件中。
三、按键控制LED程序(熟悉IO输入操作)
上一个实验使用了GPIO的输出功能,那本次实验会通过按键输入来实现按键控制LED的亮灭,从而熟悉GPIO输入功能。
可以通过查看wifiiot_gpio wifiiot_gpio_ex两个GPIO驱动文件熟悉GPIO的所有操作,此处不细讲了,关键代码部分摘录注释如下,报告会附上本次实验的代码文件。
查看原理图,核心板上的user按键是连接的MCU GPIO05
按键直接与MCU连接,因此此次按键输入要在mcu内部配置上拉,另外此次按键输入采用边沿扫描中断方式,在xstart.s文件的void HelloWorld(void)函数中添加按键IO配置代码如下:
- //设置KEY IO为 GPIO模式
- IoSetFunc(WIFI_IOT_IO_NAME_GPIO_5, WIFI_IOT_IO_FUNC_GPIO_5_GPIO);
- //设置KEY IO为输入状态
- GpioSetDir(WIFI_IOT_IO_NAME_GPIO_5, WIFI_IOT_GPIO_DIR_IN);
- //设置KEY IO上拉
- IoSetPull(WIFI_IOT_IO_NAME_GPIO_5, WIFI_IOT_IO_PULL_UP);
- //设置KEY 输入中断模式为边沿扫描、下降沿、并指定按键中断处理函数KeyHandle
- GpioRegisterIsrFunc(WIFI_IOT_IO_NAME_GPIO_5, WIFI_IOT_INT_TYPE_EDGE, WIFI_IOT_GPIO_EDGE_FALL_LEVEL_LOW, KeyHandle, NULL);
复制代码使用全局变量传递信息,定义一个全局变量:
staticint Keystate = 0;
实现按键中断处理函数,此处的中断处理函数仅仅是中断函数调用的应用层处理函数,不需要考虑清中断之类操作,但是也不能处理过多的事务,因此函数内仅仅设置标记。
- static void KeyHandle(char *arg)
- {
- (void)arg; // 此处若不添加编译时会报错。
- if(Keystate>0)
- {
- Keystate = 0;
- }
- else
- {
- Keystate++;
- }
- }
复制代码将LedTask任务函数更改如下:
- static void * LedTask(const char *arg)
- {
- (void)arg;
- static int keystateback = 0xffff;
- while(1)
- {
- usleep(50000);
- if(keystateback!= Keystate) // 查看是否有新的状态
- {
- keystateback = Keystate;
- if(keystateback ==0) //根据新状态设置LED亮灭
- {
- printf("[LedExample]LED OFF!
- ");
- GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_9, 1);
- }
- else
- {
- printf("[LedExample]LED ON!
- ");
- GpioSetOutputVal(WIFI_IOT_IO_NAME_GPIO_9, 0);
- }
- }
- }
- return NULL;
- }
复制代码至此修改完成,编译后烧写,就可以使用核心板user按键控制LED的亮灭了,按键按下一次,LED亮灭状态就会改变一次,同时串口会输出LED亮灭状态。
[2020-10-15 15:46:55.121]# RECVASCII>
[LedExample]LED ON!
[2020-10-15 15:46:56.120]# RECVASCII>
[LedExample]LED OFF!
[2020-10-15 15:46:56.821]# RECVASCII>
[LedExample]LED ON!
[2020-10-15 15:46:57.370]# RECVASCII>
[LedExample]LED OFF!
综上三个实验,基本上熟悉了HI381的GPIO输入输出编写方式,GPIO操作是每个单片机编程的基本操作,在HarmonyOS HiSpark Wi-Fi IoT 套件中按键、led、蜂鸣器控制均属于GPIO操作。
`