GPIO Latch

该示例通过使用 ENHTIM,实现 GPIO 触发锁存器状态,并记录计数器计数值的功能。

P2_4 设定为 GPIO 输出引脚,设定 P2_4 引脚电平随时间变化不断翻转。

P2_2 设定为 GPIO 输入引脚,同时将该引脚设定为 ENHTIM 的计数触发引脚。

将 P2_2 与 P2_4 引脚连接,当 P2_2 电压发生上升沿变化时会触发计数。累计变化三次后,触发 ENHTIM 中断,在中断函数内打印三次触发时计数器数值。

环境需求

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

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

硬件连线

连接 P2_4(GPIO 输出)和 P2_2(GPIO 输入)。

编译和下载

该示例的工程路径如下:

Project file: board\evb\io_sample\TIM_ENHANCE\Latch_GPIO\mdk

Project file: board\evb\io_sample\TIM_ENHANCE\Latch_GPIO\gcc

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

  1. 打开工程文件。

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

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

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

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

测试验证

当 P2_2 和 P2_4 引脚连接后,在 Debug Analyzer 工具上,会不断打印 GPIO 触发锁存时的计数器数值,计数值不断递增。

ENH_TIM0 ENHTIM_INT_LATCH_CNT2_FIFO_THD
ENH_TIM0 fifo length = 3
ENH_TIM0 data[0] = 0x2f5
ENH_TIM0 data[1] = 0x125c6c
ENH_TIM0 data[2] = 0x1e91a7
...

代码介绍

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

  1. 源码路径

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

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

源码路径

  • 工程路径: sdk\board\evb\io_sample\TIM_ENHANCE\Latch_GPIO

  • 源码路径: sdk\src\sample\io_sample\TIM_ENHANCE\Latch_GPIO

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

└── Project: latch_gpio
    └── 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
            └── rtl876x_enh_tim.c
        ├── profile
        └── app                      includes the ble_peripheral user application implementation
            └── main.c

初始化

当 EVB 复位启动时,执行 main 函数,执行以下流程:

int main(void)
{
    __enable_irq();

    enhance_tim_demo();

    while (1)
    {
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
    }
}

enhance_tim_demo 中,包含 PAD/PINMUX 设置,GPIO 和 ENHTIM 外设的初始化等流程。

void enhance_tim_demo(void)
{
    board_gpio_enhtim_init();
    driver_gpio_enhtim_init();
    driver_enhance_timer_init();

    /* GPIO output is only used to simulate the input signal, only for demo debugging. */
    board_gpio_init();
    driver_gpio_init();
    ...
}

board_gpio_enhtim_init 为 GPIO 输入相关的 PAD/PINMUX 设置,包含如下流程:

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

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

void board_gpio_enhtim_init(void)
{
    Pad_Config(GPIO_INPUT_PIN_0, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
    Pinmux_Config(GPIO_INPUT_PIN_0,  DWGPIO);
}

board_gpio_init 为 GPIO 输出相关的 PAD/PINMUX 设置,包含如下流程:

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

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

void board_gpio_init(void)
{
    Pad_Config(GPIO_OUTPUT_PIN_0, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    Pinmux_Config(GPIO_OUTPUT_PIN_0, DWGPIO);
}

driver_gpio_enhtim_init 为 GPIO 输入外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 设置 GPIO 的引脚为输入模式。

void driver_gpio_enhtim_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_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_Init(&GPIO_InitStruct);

    GPIO_WriteBit(GPIO_PIN_OUTPUT_0, (BitAction)(1));
}

driver_gpio_init 为 GPIO 输出外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 设置 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_OUTPUT_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(&GPIO_InitStruct);

    GPIO_WriteBit(GPIO_PIN_OUTPUT_0, (BitAction)(1));
}

driver_gpio_init 为 ENHTIM 外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 设置 ENHTIM 时钟一分频。

  3. 设置 ENHTIM 为自由运行模式。

  4. 设置 ENHTIM 启用 GPIO 触发锁存功能。

  5. 设置 GPIO 上升沿触发锁存,触发中断 FIFO 阈值为 3。

  6. 使能 GPIO 触发计时功能。

  7. 配置和使能 ENHTIM 的 IRQ 通道。

  8. 配置 ENHTIM 计时中断,FIFO 满中断和达到 FIFO 阈值中断。

  9. 使能 ENHTIM 外设。

void driver_enhance_timer_init(void)
{
    RCC_PeriphClockCmd(APBPeriph_ENHTIMER, APBPeriph_ENHTIMER_CLOCK, ENABLE);

    ENHTIM_InitTypeDef ENHTIM_InitStruct;
    ENHTIM_StructInit(&ENHTIM_InitStruct);

    ENHTIM_InitStruct.ENHTIM_ClockDiv               = ENHTIM_CLOCK_DIVIDER_1;
    ENHTIM_InitStruct.ENHTIM_Mode                   = ENHTIM_MODE_FreeRun;
    /* Only enhtim_ ENHTIM_LatchCountEn[2] latch triggered by GPIO. */
    ENHTIM_InitStruct.ENHTIM_LatchCountEn[2]        = ENHTIM_LATCH_COUNT_ENABLE;
    ENHTIM_InitStruct.ENHTIM_LatchCountTrigger[2]   = ENHTIM_LATCH_TRIGGER_RISING_EDGE;
    ENHTIM_InitStruct.ENHTIM_LatchCount2Thd         = 3;
    ENHTIM_InitStruct.ENHTIM_LatchTriggerPad        = GPIO_INPUT_PIN_0;
    ENHTIM_InitStruct.ENHTIM_TimerGPIOTriggerEn     = ENABLE;

    ENHTIM_Init(ENHTIMER_NUM, &ENHTIM_InitStruct);

    /*  Enable TIMER IRQ  */
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = ENHTIMER_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    ENHTIM_ClearINTPendingBit(ENHTIMER_NUM, ENHTIM_INT_TIM);
    ENHTIM_INTConfig(ENHTIMER_NUM, ENHTIM_INT_TIM, ENABLE);

    ENHTIM_ClearINTPendingBit(ENHTIMER_NUM, ENHTIM_INT_LATCH_CNT2_FIFO_FULL);
    ENHTIM_INTConfig(ENHTIMER_NUM, ENHTIM_INT_LATCH_CNT2_FIFO_FULL, ENABLE);

    ENHTIM_ClearINTPendingBit(ENHTIMER_NUM, ENHTIM_INT_LATCH_CNT2_FIFO_THD);
    ENHTIM_INTConfig(ENHTIMER_NUM, ENHTIM_INT_LATCH_CNT2_FIFO_THD, ENABLE);

    ENHTIM_Cmd(ENHTIMER_NUM, ENABLE);
}

功能实现

  1. 在主函数内循环执行 GPIO_WriteBit(),将 GPIO 输出引脚 P2_4 循环输出高低电平。

    void enhance_tim_demo(void)
    {
        ..
        while (1)
        {
            /* Simulate GPIO trigger signal */
            for (uint32_t i = 0; i < 100000; i++);
            GPIO_WriteBit(GPIO_PIN_OUTPUT_0, (BitAction)(1));
            for (uint32_t i = 0; i < 100000; i++);
            GPIO_WriteBit(GPIO_PIN_OUTPUT_0, (BitAction)(0));
        }
    }
    
  2. 将 P2_4(GPIO 输出)和 P2_2(GPIO 输入)引脚相连后,每次 P2_2 检测到 P2_4 输出上升沿时,触发 ENHTIM 的锁存计数功能。累计三次达到阈值时,触发 ENHTIM_INT_LATCH_CNT2_FIFO_THD 中断,进入中断处理函数 Enhanced_Timer0_Handler

    1. 失能 ENHTIM_INT_LATCH_CNT2_FIFO_THD 中断。

    2. 读取 ENHTIM 锁存计数 FIFO 内的数据长度,读取锁存计数值并打印。

    3. 清除中断标志位,重新使能中断。

    void Enhanced_Timer0_Handler()
    {
        ...
        if (ENHTIM_GetINTStatus(ENH_TIM0, ENHTIM_INT_LATCH_CNT2_FIFO_THD))
        {
            APP_PRINT_INFO0("ENH_TIM0 ENHTIM_INT_LATCH_CNT2_FIFO_THD\r\n");
            ENHTIM_INTConfig(ENHTIMER_NUM, ENHTIM_INT_LATCH_CNT2_FIFO_THD, DISABLE);
            uint8_t length = ENHTIM_GetLatchCount2FIFOLength(ENH_TIM0);
            uint32_t data[4] = {0};
            ENHTIM_ReadLatchCount2FIFO(ENH_TIM0, data, length);
            /* Only for debugging, removed in actual application. */
            APP_PRINT_INFO1("ENH_TIM0 fifo length = %d\r\n", length);
            for (uint8_t i = 0; i < length; i++)
            {
                /* Only for debugging, removed in actual application. */
                APP_PRINT_INFO2("ENH_TIM0 data[%d] = 0x%x\r\n", i, data[i]);
            }
            //add user code here
            ENHTIM_ClearINTPendingBit(ENH_TIM0, ENHTIM_INT_LATCH_CNT2_FIFO_THD);
            ENHTIM_INTConfig(ENH_TIM0, ENHTIM_INT_LATCH_CNT2_FIFO_THD, ENABLE);
        }
    }