当前位置:   article > 正文

第4章 点亮鸿蒙混沌世界的第一盏灯_点亮鸿蒙的第一站等

点亮鸿蒙的第一站等

        本节将会介绍如何使用 HarmonyOS IoT 硬件子系统的 GPIO 模块的相关 API,控制 Hi3861 开发板上可编程 LED 灯亮或灭。

        GPIO 是 General Purpose Input/Output 的英文缩写。Hi3861 芯片内部包含了 GPIO 模块,用于实现芯片引脚上的数字输入、输出功能。所谓的数字输入、输出,是指状态只能是0或者1两种状态,通常使用低电平表示0,高电平表示1。

本节重点掌握要点

  • 掌握 OpenHarmony 工程项目目录结构,清楚在哪个路径下编写代码
  • 清楚需要用到的头文件及其对应的路径
  • 熟练掌握 GPIO 模块与输出相关的 API 接口函数的使用
  • 熟练掌握程序编写的流程

4.1 OpenHarmony 工程项目目录结构

        打开 OpenHarmony 工程项目,找到 src/applications/sample/wifi-iot/app 目录,该目录就是我们后续需要编写代码的路径,每学习一节课程,我们就在该目录下新建一个文件夹,把我们这一节要写的代码放到该文件夹中,该文件夹包含我们自己编写的 .c 文件和 BUILD.gn 文件,BUILD.gn 文件为编译脚本文件。代码量大的话,还会包括 .h 头文件或者新的文件夹,详细结构如图4-1和图4-2所示。细心的读者会发现,这里有两个 BUILD.gn 文件,其中一个是在 app 目录下,另外一个是在我们编写代码文件夹路径下,为什么会有两个 BUILD.gn 文件呢?我们先不着急,下面我们会重点讲解该问题,这个问题非常重要, 后面讲解的时候,读者务必细心研读。

图4-1

图4-2

4.2 GPIO模块相关头文件及其对应的路径与 API 接口函数

     HarmonyOS IoT 硬件子系统提供了控制外设硬件的应用程序编程接口(Application Programming Interface, API)。其中,GPIO 模块的相关 API 接口函数可用于控制芯片引脚的数字输入和数字输出。编程前一定要清楚知道自己需要用到哪些 API 接口函数,这些函数是在哪个头文件,头文件的路径是什么。这些在程序编写时都要编写正确,编译的时候才不会出现各种错误。GPIO 模块与输出相关的 API 接口函数、其对应的头文件以及头文件所在的路径如图4-3所示。

图4-3 GPIO模块相关头文件及其对应的路径、API 接口函数

  • unsigned int IoTGpioInit(unsigned int id);

功能描述:对 GPIO 引脚进行初始化。

参数讲解:该函数有一个 unsigned int 类型的参数 id,用于指定 GPIO 口的引脚,可取值 0-14。

函数返回值:初始化成功则返回0,初始化失败则返回-1。

  • unsigned int IoTGpioSetDir(unsigned int id, IotGpioDir dir);

功能描述:设置 GPIO 引脚方向,GPIO 可用作于输出和输入,该函数用来设置 GPIO 引脚是用于输出还是输入。

参数讲解:该函数有两个参数。参数1: unsigned int 类型的参数 id,用于指定 GPIO 口的引脚,可取值 0-14。参数2:IotGpioDir 类型的参数 dir,用于指定 GPIO 输入还是输出。IotGpioDir 类型其实是一个枚举类型,可取 IOT_GPIO_DIR_IN (输入)或者 IOT_GPIO_DIR_OUT (输出)

函数返回值:成功则返回0,失败则返回-1。

  1. typedef enum {
  2. /** Input */
  3. IOT_GPIO_DIR_IN = 0,
  4. /** Output */
  5. IOT_GPIO_DIR_OUT
  6. } IotGpioDir;

说明:IotGpioDir 是一个 enum 类型取的新名字,也就是说dir 是一个 enum 类型参数,取值为 IOT_GPIO_DIR_IN(输入) 或者 IOT_GPIO_DIR_OUT(输出)。

  • unsigned int IoTGpioSetOutputVal(unsigned int id, IotGpioValue val);

功能描述:设置 GPIO 引脚输出的电平值,是输出高电平还是低电平 。

参数讲解:该函数有两个参数。参数1: unsigned int 类型的参数 id,用于指定 GPIO 口的引脚,可取值 0-14。参数2:IotGpioValue 类型的参数 val,用于指定 GPIO 引脚输出的电平值。IotGpioValue 类型其实是一个枚举类型,可取 IOT_GPIO_VALUE0(低电平)或者 IOT_GPIO_VALUE1(高电平)。

函数返回值:成功则返回0,失败则返回-1。

  1. typedef enum {
  2. /** Low GPIO level */
  3. IOT_GPIO_VALUE0 = 0,
  4. /** High GPIO level */
  5. IOT_GPIO_VALUE1
  6. } IotGpioValue;

说明:IotGpioValue 是一个 enum 类型取的新名字,也就是说 val 是一个 enum 类型参数,取值为 IOT_GPIO_VALUE0(低电平)或者 IOT_GPIO_VALUE1(高电平)

  • int usleep(unsigned)

功能描述:系统延时函数,单位为 us。

参数讲解:该函数有一个 unsigned 类型的参数,用于设置延时的时长。

函数返回值:返回 int 类型的值。

  • SYS_RUN(func)

功能描述:系统启动函数,SYS_RUN 函数是系统启动后的第一个入口函数,整个程序的运行就是从这里开始,该函数的功能是调用 func 函数。

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

功能描述:该函数用于创建线程。

参数讲解:该函数有三个参数。第一个 参数 func 是线程的运行函数,也就是我们要编写的任务函数,第二个参数 argument 是线程函数的参数,一般设置为 NULL,第三个参数 attr 通过类型 osThreadAttr_t 指定了该线程的属性。

函数返回值:函数运行成功后,返回该线程的线程 ID。运行失败,返回 NULL。

  1. typedef struct {
  2. const char *name;
  3. uint32_t attr_bits;
  4. void *cb_mem;
  5. uint32_t cb_size;
  6. void *stack_mem;
  7. uint32_t stack_size;
  8. osPriority_t priority;
  9. TZ_ModuleId_t tz_module;
  10. uint32_t reserved;
  11. } osThreadAttr_t;

说明:

name:线程名

attr_bits:线程属性位

cb_mem:线程控制块的内存初始地址,默认为系统自动分配。

cb_size:线程控制块的内存大小。

stack_mem:线程栈的内存初始地址,默认为系统自动分配。

stack_size:线程栈的内存大小。

priority:线程优先级,默认为osPriorityNormal。

tz_module: TrustZone模块标识符,默认不指定TrustZone。reserved: Reserved 保留,必须为0。

在程序中,我们只需要设置线程的名字、线程栈内存大小和线程优先级即可,其他的都使用默认值。线程栈内存 stack_size 如果设置过小,编译会出现报错。

2.2.3 开发板 LED 灯原理图说明

        Hi3861 开发板上可编程 LED 灯部分的原理图如图4-4所示。

图4-4

 LED2 即开发板上可编程 LED 灯,它的一端通过电阻 R2 连接到3.3V 电源,另一端和主控芯片的 GPIO09 引脚连接,因此主控芯片 GPIO09 引脚输出不同的电平即可控制 LED2 的状态。结合原理图分析可知,主控芯片 GPIO09 引脚状态和 LED2 状态的对应关系如表4-1所示。

表4-1

GPIO09 引脚状态

LED2 状态

低电平

高电平

4.3 程序编写步骤

图4-5 程序编写步骤

4.4 编写 Led Demo 程序源代码

1. 创建名为 led_example.c 文件

        在 OpenHarmony 工程项目的 src/applications/sample/wifi-iot/app/ 目录下,创建 01_led_demo 文件夹,在该文件夹下创建名为 led_example.c 文件。文件内容如下:

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include "ohos_init.h"
  4. #include "cmsis_os2.h"
  5. #include "iot_gpio.h"
  6. #define LED_TEST_GPIO 9
  7. #define LED_INTERVAL_TIME_US 1000000
  8. static void MyledTask(void *arg)
  9. {
  10. (void) arg;
  11. IoTGpioInit(LED_TEST_GPIO);
  12. IoTGpioSetDir(LED_TEST_GPIO, IOT_GPIO_DIR_OUT);
  13. while (1)
  14. {
  15. IoTGpioSetOutputVal(LED_TEST_GPIO, IOT_GPIO_VALUE0);
  16. usleep(LED_INTERVAL_TIME_US);
  17. printf("\r\n===== Led test success! =====\r\n");
  18. }
  19. }
  20. static void LedExampleEntry(void)
  21. {
  22. osThreadAttr_t attr = {0};
  23. attr.name = "MyledTask";
  24. attr.stack_size = 4096;
  25. attr.priority = osPriorityNormal;
  26. if(osThreadNew((osThreadFunc_t)MyledTask, NULL, &attr) == NULL)
  27. {
  28. printf("[MyledEntry] create MyledTask failed! \n");
  29. }
  30. }
  31. SYS_RUN(LedExampleEntry);

图4-6 程序运行流程图

以上代码的部分代码说明如下。

程序运行流程如图2.2-6所示,SYS_RUN 函数是系统系统启动后的第一个入口函数。程序启动后,首先运行SYS_RUN(LedExampleEntry),接着运行LedExampleEntry(void) 函数,该函数通过osThreadNew 函数创建 MyledTask 线程函数,MyledTask 函数就是我们自己需要重点编写的程序代码了。

在 LedExampleEntry 函数中,重点关注线程函数 osThreadNew 第三个参数 attr 。在这里,我们定义一个 osThreadAttr_t 类型参数 attr ,并对其 name、stack_size、priority 属性进行设置,其他属性使用默认值。

IoTGpioInit 函数对 LED_TEST_GPIO 引脚即9引脚进行了初始化。

IoTGpioSetDir 函数将引脚9设置为输出功能。

IoTGpioSetOutputVal 函数将引脚9输出值设置为0。

usleep 函数设置了延时1000000 us,即1000ms。

2. 创建 BUILD.gn 文件,编写应用级编译脚本

        在 OpenHarmony 源代码的 src/applications/sample/wifi-iot/app/01_led_demo 文件夹下,创建 BUILD.gn 文件,文件内容如下:

  1. static_library("my_led") {
  2. sources = [
  3. "led_example.c"
  4. ]
  5. include_dirs = [
  6. "//utils/native/lite/include",
  7. "//device/hisilicon/hispark_pegasus/hi3861_adapter/kal/cmsis",
  8. "//base/iot_hardware/peripheral/interfaces/kits",
  9. ]
  10. }

在该 BUILD.gn 文件中,定义了一个名为 “my_led”的静态库,同时指定了编译该静态库所需的 .c 源代码文件和头文件所在的目录。

  • static_library 中指定业务模块一个名为 “my_led”的静态库。
  • sources 中指定需要编译的 .c 源代码文件及其路径,若路径中包含"//“则表示绝对路径(此处为代码文件夹 src 路径),若不包含”//"则表示相对路径。
  • include_dirs 中指定 source 所需要依赖的.h文件路径,即我们 .c文件中 .h 头文件所在路径,相关头文件对应的路径如图2.2-3所示。
3. 编写模块级编译脚本 BUILD.gn 文件

        修改 OpenHarmony 工程项目的 arc/applications/sample/wifi-iot/app 目录下的 BUILD.gn 文件,将其内容修改如下:

  1. import("//build/lite/config/component/lite_component.gni")
  2. lite_component("app") {
  3. features = [
  4. "01_led_demo:my_led",
  5. ]
  6. }
4. 两个 BUILD.gn 文件的区别与联系

        看到这里,有些初学者可能会感到困惑,为什么有两个 BUILD.gn 文件?这两个 BUILD.gn 文件作用是什么呢?这里我们给大家进行详细讲解。

        首先,两个 BUILD.gn 文件所在的路径不一样。应用级编译脚本 BUILD.gn 文件位于我们编写 .c 文件代码所在的目录下:src/applications/sample/wifi-iot/app/01_led_demo,模块级编译脚本 BUILD.gn 文件位于其上一层目录,即 src/applications/sample/wifi-iot/app 目录下。

        其次,两个 BUILD.gn 文件的作用不一样。应用级编译脚本的作用是指定需要编译的 .c 源代码文件和所依赖的.h文件所在的路径,不在应用源码列表中的.c文件不会被编译。模块级编译脚本的作用是指定应用级编译脚本所在的路径和目标,即通过模块级编译脚本找到应用级编译脚本,它们之间的关系如图4-7所示,模块级编译脚本 BUILD.gn 文件 features 字段 的 01_led_demo 是应用级编译脚本 BUILD.gn 文件所在的目录,模块级编译脚本 BUILD.gn 文件 features 字段 的 my_led 是应用级编译脚本 BUILD.gn 文件 static_library 括号里的名称。不在features 列表中的应用程序不会被编译。

图4-7

5. 代码编译和烧录

        按照3.2节介绍的方法进行代码编译和烧录,在烧录完成后,按下复位按键,我们就可以看到 Hi3861 开发板上 LED 灯被点亮了,且串口上会输出 ===== Led test success! ===== 。

图4-8 输出结果

版权所有 © 2023 东莞龙元智能科技有限公司

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

附:本文档相关配套资料

1、配套开发板龙元智能 LY-01 淘宝链接:

https://item.taobao.com/item.htm?abbucket=20&id=792601994657&spm=a230r.7195193.1997079397.6.638a4fd8mRWYWP​​​​​​

2、配套视频教程 bilibili 链接:【鸿蒙3.1设备开发教程】龙元智能 HarmonyOS 3.1 鸿蒙设备开发实战教程_哔哩哔哩_bilibili

3、配套程序代码:在官方淘宝店购买开发板后,发货清单中有配套程序代码下载地址。

官方淘宝店
官方 bilibili 配套视频
官方微信公众号

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/666532
推荐阅读
相关标签
  

闽ICP备14008679号