当前位置:   article > 正文

第5章 按下探索鸿蒙混沌世界的快捷键

第5章 按下探索鸿蒙混沌世界的快捷键

        本节将会介绍如何使用 HarmonyOS IoT 硬件子系统的 GPIO 模块的相关 API,通过按键控制 LED 灯的状态。

本节重点掌握要点

  • 清楚需要用到的头文件及其对应的路径
  • 熟练掌握 GPIO 模块与输入相关的 API 接口函数的使用
  • 掌握通过查询方式获取按键状态
  • 掌握通过中断方式获取按键状态

5.1 和按键相关头文件及其对应的路径与 API 接口函数

       在上一节中,我们点亮开发板上的 LED 灯,主要是通过 GPIO 模块输出的电平进行控制。在本节中,我们需要获取按键的相关状态,是通过 GPIO 模块的输入电平来判断按键的状态。GPIO 模块与输入相关的 API 接口函数、其对应的头文件以及头文件所在的路径如图5-1所示。

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

  •  unsigned int IoTGpioGetInputVal(unsigned int id, IotGpioValue *val); 

功能描述:获取 GPIO 引脚输入电平值。

参数讲解:该函数有两个参数。参数1: unsigned int 类型的参数 id,用于指定 GPIO 口的引脚,可取值 0-14。参数2:IotGpioValue 指针类型的参数 val,用于存储获取得到的 GPIO 引脚的电平值

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

  • unsigned int IoTGpioRegisterIsrFunc(unsigned int id, IotGpioIntType intType, IotGpioIntPolarity intPolarity, GpioIsrCallbackFunc func, char *arg);

功能描述:注册 GPIO 引脚中断。

参数讲解:该函数有5个参数。参数1: unsigned int 类型的参数 id,用于指定 GPIO 口的引脚,可取值 0-14。参数2:IotGpioIntType 类型的参数 intType,用于指定中断触发类型(边缘触发或者水平触发)。参数3:IotGpioIntPolarity 类型的参数intPolarity,用于指定触发中断具体的边缘类型(下降沿或上升沿)或者水平类型(高电平或低电平)。建议在我们编写程序代码时,IotGpioIntType 使用 IOT_INT_TYPE_EDGE 边沿触发,IotGpioIntPolarity 使用 IOT_GPIO_EDGE_RISE_LEVEL_HIGH 上升沿触发。参数4:GpioIsrCallbackFunc 类型的中断处理函数,中断触发后,执行该函数。参数5:char * 类型的指针,该参数用于指定中断处理函数的附加参数。

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

  1. typedef enum {
  2. IOT_INT_TYPE_LEVEL = 0,
  3. IOT_INT_TYPE_EDGE
  4. } IotGpioIntType;
  5. typedef enum {
  6. IOT_GPIO_EDGE_FALL_LEVEL_LOW = 0,
  7. IOT_GPIO_EDGE_RISE_LEVEL_HIGH
  8. } IotGpioIntPolarity;

说明:

IotGpioIntType:中断触发类型,IOT_INT_TYPE_LEVEL 表示水平触发,IOT_INT_TYPE_EDGE 表示边沿触发。

IotGpioIntPolarity:触发中断具体的边沿类型或者水平类型。

IOT_GPIO_EDGE_FALL_LEVEL_LOW:下降沿触发或者低电平触发;

IOT_GPIO_EDGE_RISE_LEVEL_HIGH:上升沿触发或者高电平触发。建议使用该方式。

  • hi_u32 hi_io_set_func(hi_io_name id, hi_u8 val);

功能描述:设置 GPIO 引脚功能。Hi3861 芯片的外设接口较多,引脚数量较少,因此存在部分引脚有多个功能的情况,该函数是用于设置某个引脚使用哪种功能。

参数讲解:该函数有两个参数。参数1: hi_io_name 类型的参数 id,用于指定 GPIO 口的引脚,是一个枚举类型。参数2:hi_u8 类型的参数 val,用于设置 GPIO 引脚的功能。

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

  1. typedef enum {
  2. HI_IO_NAME_GPIO_0, /**< GPIO0 */
  3. HI_IO_NAME_GPIO_1, /**< GPIO1 */
  4. HI_IO_NAME_GPIO_2, /**< GPIO2 */
  5. HI_IO_NAME_GPIO_3, /**< GPIO3 */
  6. HI_IO_NAME_GPIO_4, /**< GPIO4 */
  7. HI_IO_NAME_GPIO_5, /**< GPIO5 */
  8. HI_IO_NAME_GPIO_6, /**< GPIO6 */
  9. HI_IO_NAME_GPIO_7, /**< GPIO7 */
  10. HI_IO_NAME_GPIO_8, /**< GPIO8 */
  11. HI_IO_NAME_GPIO_9, /**< GPIO9 */
  12. HI_IO_NAME_GPIO_10, /**< GPIO10 */
  13. HI_IO_NAME_GPIO_11, /**< GPIO11 */
  14. HI_IO_NAME_GPIO_12, /**< GPIO12 */
  15. HI_IO_NAME_GPIO_13, /**< GPIO13 */
  16. HI_IO_NAME_GPIO_14, /**< GPIO14 */
  17. HI_IO_NAME_SFC_CSN, /**< SFC_CSN */
  18. HI_IO_NAME_SFC_IO1, /**< SFC_IO1 */
  19. HI_IO_NAME_SFC_IO2, /**< SFC_IO2 */
  20. HI_IO_NAME_SFC_IO0, /**< SFC_IO0 */
  21. HI_IO_NAME_SFC_CLK, /**< SFC_CLK */
  22. HI_IO_NAME_SFC_IO3, /**< SFC_IO3 */
  23. HI_IO_NAME_MAX,
  24. } hi_io_name;

说明:第二个参数 val 从以下枚举类型中选择相应功能,以下每一个功能都是一个枚举类型。

  1. hi_io_func_gpio_0,
  2. hi_io_func_gpio_1,
  3. hi_io_func_gpio_2,
  4. hi_io_func_gpio_3,
  5. hi_io_func_gpio_4,
  6. hi_io_func_gpio_5,
  7. hi_io_func_gpio_6,
  8. hi_io_func_gpio_7,
  9. hi_io_func_gpio_8,
  10. hi_io_func_gpio_9,
  11. hi_io_func_gpio_10,
  12. hi_io_func_gpio_11,
  13. hi_io_func_gpio_12,
  14. hi_io_func_gpio_13,
  15. hi_io_func_gpio_14,
  16. hi_io_func_sfc_csn,
  17. hi_io_func_sfc_io_1,
  18. hi_io_func_sfc_io_2,
  19. hi_io_func_sfc_io_0,
  20. hi_io_func_sfc_clk,
  21. hi_io_func_sfc_io_3
  1. typedef enum {
  2. HI_IO_FUNC_GPIO_8_GPIO,
  3. HI_IO_FUNC_GPIO_8_UART1_RTS_N = 2,
  4. HI_IO_FUNC_GPIO_8_SPI0_TXD,
  5. HI_IO_FUNC_GPIO_8_PWM1_OUT = 5,
  6. HI_IO_FUNC_GPIO_8_I2S0_WS,
  7. HI_IO_FUNC_GPIO_8_WLAN_ACTIVE,
  8. } hi_io_func_gpio_8;

说明:举个例子,hi_io_func_gpio_8 枚举值如左边所示,并不是所有的管脚都默认是 GPIO 功能,我们需要将引脚08设置为普通的 GPIO 引脚,因此,val 取值:HI_IO_FUNC_GPIO_8_GPIO。完整语句:hi_io_set_func(HI_IO_NAME_GPIO_8, HI_IO_FUNC_GPIO_8_GPIO)。

  • hi_u32 hi_io_set_pull(hi_io_name id, hi_io_pull val);

功能描述:设置 GPIO 引脚上拉或下拉状态。上拉可以理解为引脚电平状态为高电平,下拉可以理解为引脚电平状态为低电平。

参数讲解:该函数有两个参数。参数1: hi_io_name 类型的参数 id,用于指定 GPIO 口的引脚,是一个枚举类型,和前面函数 hi_io_set_func 一样。参数2:hi_io_pull 类型的参数 val,是一个枚举类型,用于设置 GPIO 引脚为上拉还是下拉。

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

  1. typedef enum {
  2. HI_IO_PULL_NONE,
  3. HI_IO_PULL_UP,
  4. HI_IO_PULL_DOWN,
  5. HI_IO_PULL_MAX,
  6. } hi_io_pull;

说明:

HI_IO_PULL_NONE:无拉,即没有上拉也没有下拉。

HI_IO_PULL_UP:设置为上拉。

HI_IO_PULL_DOWN:设置为下拉。

HI_IO_PULL_MAX:无效值。

5.2 开发板按键原理图说明

        开发板按键部分的原理如图5-2所示,开发板中设计了两个按键 SW1 和 S3,它们其中一端接地,另一端分别连接主控芯片的 GPIO08 和 GPIO12。因此,当按键被按下时,GPIO08 和 GPIO12 引脚将会接地,即处于低电平状态,我们只需要使用函数 IoTGpioGetInputVal 读取GPIO08 和 GPIO12 引脚电平值就可以判断出按键是否被按下,若按下,则读取到的电平值为 0 ,若没有被按下,读取到的电平值为 1。当然,在使用函数读取 IoTGpioGetInputVal 电平值之前,需要先用函数 hi_io_set_pull 将 GPIO08 和 GPIO12 引脚设置为上拉,即GPIO08 和 GPIO12 引脚状态为高电平。

        通过 HarmonyOS IoT 硬件子系统的 GPIO 模块的相关 API 实现输入功能,蛀主要有两种方式:

(1)查询方式。应用代码通过 IoTGpioGetInputVal 主动获取引脚状态。

(2)中断方式。应用代码通过 IoTGpioRegisterIsrFunc 向系统注册一个中断处理函数。当状态发生改变时,该中断处理函数会被系统调用,相应的代码就会被执行。

在本节中,我们将会通过代码实例演示如何使用以上两种方式,实现通过按键控制上一节中 LED3 灯亮和灭。其中按键 S1 使用查询方式,按键 SW2 使用中断方式。

图5-2 按键原理图

5.3 通过查询方式控制 LED 灯

1. 创建名为 key_example.c 文件

        在 OpenHarmony 工程项目的 src/applications/sample/wifi-iot/app/ 目录下,创建 02_key_demo 文件夹,在该文件夹下创建名为 key_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. #include "hi_io.h"
  7. #define LED_TEST_GPIO 9
  8. #define KEY_TEST_GPIO 8
  9. static void MykeyTask(void *arg)
  10. {
  11. (void) arg;
  12. IoTGpioInit(LED_TEST_GPIO);
  13. IoTGpioInit(KEY_TEST_GPIO);
  14. IoTGpioSetDir(LED_TEST_GPIO, IOT_GPIO_DIR_OUT);
  15. IoTGpioSetDir(KEY_TEST_GPIO, IOT_GPIO_DIR_IN);
  16. hi_io_set_func(KEY_TEST_GPIO, HI_IO_FUNC_GPIO_8_GPIO);
  17. hi_io_set_pull(KEY_TEST_GPIO, HI_IO_PULL_UP);
  18. while (1)
  19. {
  20. IotGpioValue value = IOT_GPIO_VALUE0;
  21. IoTGpioGetInputVal(KEY_TEST_GPIO, &value); //获取 GPIO08 引脚电平状态值,存储在value中
  22. IoTGpioSetOutputVal(LED_TEST_GPIO, value); //设置 GPIO09 引脚电平
  23. }
  24. }
  25. static void KeyExampleEntry(void)
  26. {
  27. osThreadAttr_t attr = {0};
  28. attr.name = "MykeyTask";
  29. attr.stack_size = 4096;
  30. attr.priority = osPriorityNormal;
  31. if(osThreadNew((osThreadFunc_t)MykeyTask, NULL, &attr) == NULL)
  32. {
  33. printf("[MykeyEntry] create MykeyTask failed! \n");
  34. }
  35. }
  36. SYS_RUN(KeyExampleEntry);

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

hi_io_set_func(KEY_TEST_GPIO, HI_IO_FUNC_GPIO_8_GPIO) 函数将按键SW1即 GPIO08 引脚设置为普通 GPIO 引脚功能。

IoTGpioSetDir(KEY_TEST_GPIO, IOT_GPIO_DIR_IN) 函数将按键SW1即 GPIO08 引脚设置为输入模式。

hi_io_set_pull(KEY_TEST_GPIO, HI_IO_PULL_UP) 函数将按键SW1即 GPIO08 引脚设置为上拉模式,在该模式下,GPIO08 引脚的电平状态为高电平。

IoTGpioGetInputVal(KEY_TEST_GPIO, &value) 函数将从按键SW1即 GPIO08 引脚读取到的电平值存储在参数 value 中。这一条语句比较巧妙,第二个参数类型要求是一个指针类型,value 取地址 &value 就是一个指针变量,这里涉及到了指针的知识,C语言知识不是很扎实的读者可能理解起来有点费劲,建议再复习一下C语言指针的相关知识。总之,只需要记住第二个参数设置为 &value,则读取到的电平值就存储在了变量 value 中,value 的值就是我们读取到的电平值。

IoTGpioSetOutputVal(LED_TEST_GPIO, value) 函数将从按键SW1即 GPIO08 引脚读取到的电平值 value 设置给LED3 即 GPIO09 引脚。当 value 值为0即按键SW1按下时, LED3 灯将会亮,当 value 值为1即按键SW1松开后,LED3 灯将会熄灭。

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

        在 OpenHarmony 工程项目的 src/applications/sample/wifi-iot/app/02_key_demo 文件夹下,创建 BUILD.gn 文件,文件内容如下:

  1. static_library("my_key") {
  2. sources = [
  3. "key_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. "//device/hisilicon/hispark_pegasus/sdk_liteos/include",
  10. ]
  11. }
3. 编写模块级编译脚本 BUILD.gn 文件

        修改 OpenHarmony 工程项目的 src/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. "02_key_demo:my_key",
  6. ]
  7. }
4. 代码编译和烧录

        按照3.2节介绍的方法进行代码编译和烧录,在烧录完成后,按下复位按键,此时 LED3 灯是灭的状态,因为我们在代码中对 GPIO08 引脚设置了上拉,因此在按键没有被按下时,读取到该引脚电平为高,将高电平设置给 GPIO09 引脚,此时 LED3 灯为熄灭状态。

        此时,按下 SW1 按键, LED3 灯将会亮,松开 SW1按键后,LED3 灯将会熄灭。

5.4 通过中断方式控制 LED 灯

1. 创建名为 key_inter_example.c 文件

        在 OpenHarmony 工程项目的 src/applications/sample/wifi-iot/app/02_key_demo 目录下,创建名为 key_inter_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. #include "hi_io.h"
  7. #define LED_TEST_GPIO 9
  8. #define KEY_TEST_GPIO 12
  9. static volatile IotGpioValue ledPinValue = IOT_GPIO_VALUE0;
  10. static void OnButtonPressed(char* arg)
  11. {
  12. (void) arg;
  13. ledPinValue = !ledPinValue;
  14. printf("\r\n===== ledPinValue = %d =====\r\n", ledPinValue);
  15. }
  16. static void MykeyinterTask(void *arg)
  17. {
  18. (void) arg;
  19. IoTGpioInit(LED_TEST_GPIO);
  20. IoTGpioInit(KEY_TEST_GPIO);
  21. IoTGpioSetDir(LED_TEST_GPIO, IOT_GPIO_DIR_OUT);
  22. IoTGpioSetDir(KEY_TEST_GPIO, IOT_GPIO_DIR_IN);
  23. hi_io_set_func(KEY_TEST_GPIO, HI_IO_FUNC_GPIO_12_GPIO);
  24. hi_io_set_pull(KEY_TEST_GPIO, HI_IO_PULL_UP);
  25. IoTGpioRegisterIsrFunc(KEY_TEST_GPIO, IOT_INT_TYPE_EDGE,IOT_GPIO_EDGE_RISE_LEVEL_HIGH,
  26. OnButtonPressed, NULL); //注册 GPIO12 中断处理函数
  27. while (1)
  28. {
  29. IoTGpioSetOutputVal(LED_TEST_GPIO, ledPinValue);
  30. }
  31. }
  32. static void KeyinterExampleEntry(void)
  33. {
  34. osThreadAttr_t attr = {0};
  35. attr.name = "MykeyinterTask";
  36. attr.stack_size = 4096;
  37. attr.priority = osPriorityNormal;
  38. if(osThreadNew((osThreadFunc_t)MykeyinterTask, NULL, &attr) == NULL)
  39. {
  40. printf("[MykeyinterEntry] create MykeyinterTask failed! \n");
  41. }
  42. }
  43. SYS_RUN(KeyinterExampleEntry);

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

IoTGpioRegisterIsrFunc(KEY_TEST_GPIO,IOT_INT_TYPE_EDGE,IOT_GPIO_EDGE_RISE_LEVEL_HIGH, OnButtonPressed, NULL)。该函数的作用是注册 GPIO12 引脚中断,中断触发方式为上升沿触发,中断处理函数为 OnButtonPressed 。该函数总共5个参数,具体参数讲解如下:

参数1:KEY_TEST_GPIO,注册 GPIO12 引脚中断;

参数2:IOT_INT_TYPE_EDGE,设置中断触发方式为边沿触发。中断触发有两种触发,分别为边沿触发和水平触发,建议使用边沿触发;

参数3:IOT_GPIO_EDGE_RISE_LEVEL_HIGH,设置边沿触发中断方式为上升沿触发,除了上升沿触发,还有下降沿触发。下降沿触发是指按键按下时触发中断,按键松开时不会触发中断。上升沿触发是指按键松开后触发中断,按键按下时不会触发中断。理想情况下,这两种触发方式都可以,但实际情况可能会由于硬件按钮会存在抖动等各种未可预料的原因导致有些触发方式会出现两次触发中断的情况。在本例中,开始使用的是 IOT_GPIO_EDGE_FALL_LEVEL_LOW 下降沿触发中断,但程序烧录到开发板后,发现除了按键按下时会触发中断,按键松开时也会触发中断,不是我们预想的效果,因此改成了 IOT_GPIO_EDGE_RISE_LEVEL_HIGH 上升沿触发,修改后按键松开时触发中断,按键按下时不触发中断,达到我们预期想要的效果。

参数4:OnButtonPressed,中断处理函数。在该函数中,我们对 ledPinValue 值进行取反,这样每当按键按下后,LED3 灯的亮和灭状态就会发生一次改变。

参数5: NULL,这里参数5取 NULL 值。

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

        在 OpenHarmony 工程项目的 src/applications/sample/wifi-iot/app/02_key_demo 文件夹下,修改原来的 BUILD.gn 文件,将 sources 里面的 key_example.c 替换成 key_inter_example.c ,修改后的内容如下:

  1. static_library("my_key") {
  2. sources = [
  3. #"key_example.c" // #为注释符号,该处注释了上一节的.c文件,不参与编译
  4. "key_inter_example.c"
  5. ]
  6. include_dirs = [
  7. "//utils/native/lite/include",
  8. "//device/hisilicon/hispark_pegasus/hi3861_adapter/kal/cmsis",
  9. "//base/iot_hardware/peripheral/interfaces/kits",
  10. "//device/hisilicon/hispark_pegasus/sdk_liteos/include",
  11. ]
  12. }
3. 编写模块级编译脚本 BUILD.gn 文件

        OpenHarmony 工程项目的 src/applications/sample/wifi-iot/app 目录下的 BUILD.gn 文件内容不变,和通过查询方式控制 LED 灯的内容一样,具体如下:

  1. import("//build/lite/config/component/lite_component.gni")
  2. lite_component("app") {
  3. features = [
  4. #"01_led_demo:my_led", // #为注释符号,该处注释了上一节的应用程序,不参与编译
  5. "02_key_demo:my_key",
  6. ]
  7. }
4. 代码编译和烧录

        按照3.2节介绍的方法进行代码编译和烧录,在烧录完成后,按下复位按键,此时 LED3 灯是亮的状态,因为ledPinValue 变量的初始值为 IOT_GPIO_VALUE0 ,把 GPIO09 引脚的输出状态设置为该值后将会输出低电平。

按下 SW2 按键, LED3 灯状态不变还是点亮状态,松开 S3按键后,LED3 灯将会熄灭。再次按下 SW2 按键,LED3 灯状态不变还是熄灭状态,松开 SW2 按键后,LED3 灯将会被点亮。即每当按一次 SW2 按键,LED3 灯的亮和灭就会发生一次改变。

版权所有 © 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 配套视频
官方微信公众号

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/684058
推荐阅读
相关标签
  

闽ICP备14008679号