Input Key

该示例使用 GPIO 中断功能,检测 GPIO 的输入信号。

首先将 GPIO 配置为输入并打开中断功能。当输入信号变化触发中断,中断服务处理函数会打印输入电平信息。

环境需求

该示例支持以下开发套件:

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

硬件连线

连接 P4_0 和外部输入信号。

编译和下载

该示例的工程路径如下:

Project file: board\evb\io_sample\GPIO\Input_key\mdk

Project file: board\evb\io_sample\GPIO\Input_key\gcc

请按照以下步骤操作构建并运行该示例:

  1. 打开工程文件。

  2. 按照 快速入门编译 APP Image 给出的步骤构建目标文件。

  3. 编译成功后,在路径 mdk\bingcc\bin 下会生成 app bin app_MP_xxx.bin 文件。

  4. 按照 快速入门MP Tool 给出的步骤将 app bin 烧录至 EVB 内。

  5. 按下 reset 按键,开始运行。

测试验证

控制外部输入信号从高电平变为低电平,P4_0 检测到下降沿信号触发中断,显示如下 log。

[app] app_handle_io_msg: GPIO input msg.

代码介绍

该章节分为以下几个部分:

  1. 源码路径

  2. 初始化函数将在 初始化 章节介绍。

  3. 初始化后的功能实现将在 功能实现 章节介绍。

源码路径

  • 工程路径: sdk\board\evb\io_sample\GPIO\Input_key

  • 源码路径: sdk\src\sample\io_sample\GPIO\Input_key

该工程的工程文件代码结构如下:

└── Project: input_key
    └── secure_only_app
        └── include
            ├── app_define.h
            └── rom_uuid.h
        ├── cmsis                    includes CMSIS header files and startup files
            ├── overlay_mgr.c
            ├── system_rtl876x.c
            └── startup_rtl876x.s
        ├── lib                      includes all binary symbol files that user application is built on
            ├── rtl8752h_sdk.lib
            ├── gap_utils.lib
            └── ROM.lib
        ├── peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl876x_rcc.c
            ├── rtl876x_pinmux.c
            ├── rtl876x_nvic.c
            └── rtl876x_gpio.c
        ├── profile
        └── app                      includes the ble_peripheral user application implementation
            ├── main.c
            ├── ancs.c
            ├── app.c
            ├── app_task.c
            └── io_gpio.c

初始化

当 EVB 复位启动时,调用 main() 函数,将执行以下流程:

int main(void)
{
    extern uint32_t random_seed_value;
    srand(random_seed_value);
    board_init();
    le_gap_init(APP_MAX_LINKS);
    gap_lib_init();
    app_le_gap_init();
    app_le_profile_init();
    pwr_mgr_init();
    task_init();
    os_sched_start();

    return 0;
}

备注

le_gap_init()gap_lib_init()app_le_gap_initapp_le_profile_init 等为 privacy 管理模块相关的初始化,参考 LE Peripheral Privacy 中的初始化流程介绍。

与外设相关的初始化流程具体如下:

  1. board_init 中,执行 board_gpio_init ,该函数为 PAD/PINMUX 设置,包含如下流程:

    1. 配置 PAD:设置引脚、PINMUX 模式、PowerOn、内部上拉、输出失能。

    2. 配置 PINMUX:分配引脚为 GPIO 功能。

  2. 在执行 os_sched_start() 开启任务调度后,在 app_main_task 主任务内,执行 driver_init 对外设驱动进行初始化配置。

  3. driver_init 中执行 driver_gpio_init ,该函数为 GPIO 外设的初始化,包含如下流程:

    1. 使能 RCC 时钟。

    2. 配置 GPIO 模式为输入模式。

    3. 使能 GPIO 中断。

    4. 设置 GPIO 中断触发模式为边沿触发,设置 GPIO 中断极性为下降沿触发。

    5. 开启 GPIO Debounce 功能,配置去抖时间。

    6. 屏蔽 GPIO 外部中断,使能 GPIO 中断,清除 GPIO 中断标志位,取消屏蔽 GPIO 外部中断。

    void driver_gpio_init(void)
    {
        /* Initialize GPIO peripheral */
        RCC_PeriphClockCmd(APBPeriph_GPIO, APBPeriph_GPIO_CLOCK, ENABLE);
    
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_StructInit(&GPIO_InitStruct);
        GPIO_InitStruct.GPIO_Pin        = GPIO_PIN_INPUT;
        GPIO_InitStruct.GPIO_Mode       = GPIO_Mode_IN;
        GPIO_InitStruct.GPIO_ITCmd      = ENABLE;
        GPIO_InitStruct.GPIO_ITTrigger  = GPIO_INT_Trigger_EDGE;
        GPIO_InitStruct.GPIO_ITPolarity = GPIO_INT_POLARITY_ACTIVE_LOW;
        GPIO_InitStruct.GPIO_ITDebounce = GPIO_INT_DEBOUNCE_ENABLE;
        GPIO_InitStruct.GPIO_DebounceTime = 10;/* unit:ms , can be 1~64 ms */
        GPIO_Init(&GPIO_InitStruct);
    
        GPIO_MaskINTConfig(GPIO_PIN_INPUT, ENABLE);
        GPIO_INTConfig(GPIO_PIN_INPUT, ENABLE);
        GPIO_ClearINTPendingBit(GPIO_PIN_INPUT);
        GPIO_MaskINTConfig(GPIO_PIN_INPUT, DISABLE);
    
        NVIC_InitTypeDef NVIC_InitStruct;
        NVIC_InitStruct.NVIC_IRQChannel = GPIO_PIN_INPUT_IRQN;
        NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStruct);
    }
    

功能实现

  1. 当 P4_0 检测到外部有下降沿信号输入时,进入中断服务处理函数 GPIO_Input_Handler

    1. 关闭并屏蔽 GPIO 中断。退出中断函数时清除中断标志位、取消屏蔽和重新使能中断。

    2. 定义消息类型 IO_MSG_TYPE_GPIO ,发送 msg 给 task。在 msg 消息处理函数中,当检测到 GPIO 的消息时,打印 GPIO 信息。

    void GPIO_Input_Handler(void)
    {
        GPIO_INTConfig(GPIO_PIN_INPUT, DISABLE);
        GPIO_MaskINTConfig(GPIO_PIN_INPUT, ENABLE);
    
        T_IO_MSG int_gpio_msg;
    
        int_gpio_msg.type = IO_MSG_TYPE_GPIO;
        int_gpio_msg.subtype = 0;
        if (false == app_send_msg_to_apptask(&int_gpio_msg))
        {
            APP_PRINT_ERROR0("[io_gpio] GPIO_Input_Handler: Send int_gpio_msg failed!");
            GPIO_ClearINTPendingBit(GPIO_PIN_INPUT);
            return;
        }
    
        GPIO_ClearINTPendingBit(GPIO_PIN_INPUT);
        GPIO_MaskINTConfig(GPIO_PIN_INPUT, DISABLE);
        GPIO_INTConfig(GPIO_PIN_INPUT, ENABLE);
    }
    
    void app_handle_io_msg(T_IO_MSG io_msg)
    {
        uint16_t msg_type = io_msg.type;
    
        switch (msg_type)
        {
        ...
        case IO_MSG_TYPE_GPIO:
            {
                APP_PRINT_INFO0("[app] app_handle_io_msg: GPIO input msg.");
            }
            break;
        ...
        }
    }
    

常见问题

GPIO 误触发中断

GPIO 在如下四种模式下会误触发一次中断:

  • PAD 设置为输入高,采用上升沿触发中断。

  • PAD 设置为输入高,采用双边沿触发中断。

  • PAD 设置为输入低,采用下降沿触发中断。

  • PAD 设置为输入低,采用双边沿触发中断。

为避免中断误触发,在 GPIO 中断配置时应遵循如下设置流程:

  1. 屏蔽 GPIO 中断:GPIO_MaskINTConfig(GPIO_PIN, ENABLE)

  2. 使能 GPIO 中断:GPIO_INTConfig(GPIO_PIN, ENABLE)

  3. 清除 GPIO 中断标志位:GPIO_ClearPendingBit(GPIO_PIN)

  4. 取消屏蔽 GPIO 中断:GPIO_MastINTConfig(GPIO_PIN, DISABLE)

若需要更改 GPIO 中断配置时,应遵循如下设置流程:

  1. 屏蔽 GPIO 中断:GPIO_MaskINTConfig(GPIO_PIN, ENABLE)

  2. 修改 GPIO 中断配置(设置中断触发类型和极性):GPIO_SetITTrigger(GPIO_PIN, GPIO_TriggerMode) GPIO_SetITPolarity(GPIO_PIN, GPIO_ITPolarity)...

  3. 清除 GPIO 中断标志位:GPIO_ClearPendingBit(GPIO_PIN)

  4. 取消屏蔽 GPIO 中断:GPIO_MastINTConfig(GPIO_PIN, DISABLE)