Receive Interrupt

该示例演示使用 UART 中断方式与 PC 终端进行数据通信。

PC 终端程序(如 PUTTY 或 UartAssist)发送数据。

SoC 接收数据并触发中断。在 UART 中断处理函数中,在 UART_INT_RD_AVA 中断中将接收到的数据存储到缓冲区中,在 UART_FLAG_RX_IDLE 中断中置位 receive_flag 以表示接收完成。

一旦 receive_flag 被设置,SoC 将缓冲区数据发送回 PC 终端。

环境需求

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

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

硬件连线

连接 P3_0(UART TX)和 FT232 的 RX,P3_1(UART RX)和 FT232 的 TX。

编译和下载

该示例的工程路径如下:

Project file: board\evb\io_sample\UART\Interrupt\mdk

Project file: board\evb\io_sample\UART\Interrupt\gcc

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

  1. 打开工程文件。

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

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

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

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

测试验证

准备阶段

启动 PuTTY 或 UartAssist 等 PC 终端,连接到使用的 COM 端口,并进行以下 UART 设置:

  • 波特率: 115200

  • 8 数据位

  • 1 停止位

  • 无校验

  • 无硬件流控

测试阶段

  1. 该示例开始发送 ### Uart interrupt demo ###\r\n,观察 PC 终端上出现的字符串。

  2. 在 PC 终端上输入数据,观察 PC 终端上 SoC 是否回复了相同的数据。

  3. PC 端输入数据的长度未达到 Rx 接收阈值时(默认 16 字节),产生的中断类型为 UART_INT_ID_RX_DATA_TIMEOUT,在 Debug Analyzer 内打印中断信息与接收到的数据信息。

    UART_INT_ID_RX_TMEOUT
    data = 0x..
    ...
    
  4. PC 端输入数据的长度超过 Rx 接收阈值时(默认 16 字节),产生的中断类型为 UART_INT_ID_RX_LEVEL_REACH,在 Debug Analyzer 内打印中断信息与接收到的数据信息。

    UART_INT_ID_RX_LEVEL_REACH
    data = 0x..
    ...
    

代码介绍

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

  1. 源码路径

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

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

源码路径

  • 工程路径: sdk\board\evb\io_sample\UART\Interrupt

  • 源码路径: sdk\src\sample\io_sample\UART\Interrupt

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

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

初始化

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

int main(void)
{
    extern uint32_t random_seed_value;
    srand(random_seed_value);
    __enable_irq();
    uart_demo();
}

uart_demo 中,包含 PAD/PINMUX 设置,UART 外设的初始化等流程。

void uart_demo(void)
{
    uint16_t demo_str_len = 0;

    board_uart_init();
    driver_uart_init();

    ...
}

board_uart_init 为 UART 相关的 PAD/PINMUX 设置,包含如下流程:

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

  2. 配置 PINMUX:配置引脚分别为 UART0_TX 和 UART0_RX 功能。

driver_uart_init 为 UART 外设的初始化,包含如下流程:

  1. 使能 RCC 时钟。

  2. 配置 UART 的波特率为 115200,默认接收阈值为 16。

  3. 配置 UART 接收中断 UART_INT_RD_AVA 和 UART 接收空闲中断 UART_INT_RX_IDLE

void driver_uart_init(void)
{
    RCC_PeriphClockCmd(APBPeriph_UART0, APBPeriph_UART0_CLOCK, ENABLE);

    /* uart init */
    UART_InitTypeDef UART_InitStruct;
    UART_StructInit(&UART_InitStruct);

    UART_Init(UART0, &UART_InitStruct);

    //enable rx interrupt and line status interrupt
    UART_INTConfig(UART0, UART_INT_RD_AVA, ENABLE);
    UART_INTConfig(UART0, UART_INT_RX_IDLE, ENABLE);

    /*  Enable UART IRQ  */
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel         = UART0_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd      = (FunctionalState)ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
    NVIC_Init(&NVIC_InitStruct);
}

功能实现

  1. 定义字符串 ### Uart interrupt demo ###\r\n,执行 uart_senddata_continuous 将字符串内容发送至 PC 端。

    1. uart_senddata_continuous 内,轮询检测标志位 UART_FLAG_TX_FIFO_EMPTY,判断 UART TX FIFO 是否为空。

    2. 当 UART TX FIFO 空时,通过循环将数据塞入 TX FIFO 内,实现数据的多字节连续发送。

    3. 在 PC 端串口助手可以看到 SoC 发送的字符串数据。

    void uart_senddata_continuous(UART_TypeDef *UARTx, const uint8_t *pSend_Buf, uint16_t vCount)
    {
        uint8_t count;
    
        while (vCount / UART_TX_FIFO_SIZE > 0)
        {
            while (UART_GetFlagStatus(UARTx, UART_FLAG_TX_FIFO_EMPTY) == 0);
            for (count = UART_TX_FIFO_SIZE; count > 0; count--)
            {
                UARTx->RB_THR = *pSend_Buf++;
            }
            vCount -= UART_TX_FIFO_SIZE;
        }
    
        while (UART_GetFlagStatus(UARTx, UART_FLAG_TX_FIFO_EMPTY) == 0);
        while (vCount--)
        {
            UARTx->RB_THR = *pSend_Buf++;
        }
    }
    
  2. PC 端发送数据后,SoC 接收到数据时,触发 UART_INT_RD_AVAUART_INT_RX_IDLE 中断,进入中断处理函数。

  3. 在 UART 中断处理函数中,若 UART 正在接收数据会触发 UART_INT_RD_AVA 中断,处理流程如下:

    1. 关闭 UART_INT_RD_AVA 中断。

    2. 执行 UART_GetIID() ,获得中断标志 ID 类型。

      1. 当 ID 为 UART_INT_ID_RX_LEVEL_REACH (RX FIFO 数据长度达到 RX FIFO 阈值 UART_RxThdLevel )时,接收 FIFO 数据,保存到 UART_Recv_Buf 中。

      2. 当 ID 为 UART_INT_ID_RX_DATA_TIMEOUT (RX FIFO 中至少有一个 UART 数据,并且不再有数据进来保持 4 个字节时间)时,接收 FIFO 数据,保存到 UART_Recv_Buf 中。

    3. 开启 UART_INT_RD_AVA 中断。

    void UART0_Handler()
    {
        uint16_t lenth = 0;
        uint32_t int_status = UART_GetIID(UART0);
    
        UART_INTConfig(UART0, UART_INT_RD_AVA, DISABLE);
    
        ...
    
        switch (int_status & 0x0E)
        {
        case UART_INT_ID_RX_DATA_TIMEOUT:
            {
                DBG_DIRECT("UART_INT_ID_RX_TMEOUT");
                lenth = UART_GetRxFIFODataLen(UART0);
                UART_ReceiveData(UART0, UART_Recv_Buf, lenth);
                for (uint8_t i = 0; i < lenth; i++)
                {
                    DBG_DIRECT("data=0x%x", UART_Recv_Buf[i]);
                    UART_Send_Buf[UART_Recv_Buf_Lenth + i] = UART_Recv_Buf[i];
                }
                UART_Recv_Buf_Lenth += lenth;
                break;
            }
    
        case UART_INT_ID_RX_LEVEL_REACH:
            {
                DBG_DIRECT("UART_INT_ID_RX_LEVEL_REACH");
    
                lenth = UART_GetRxFIFODataLen(UART0);
                UART_ReceiveData(UART0, UART_Recv_Buf, lenth);
                for (uint8_t i = 0; i < lenth; i++)
                {
                    DBG_DIRECT("data=0x%x", UART_Recv_Buf[i]);
                    UART_Send_Buf[UART_Recv_Buf_Lenth + i] = UART_Recv_Buf[i];
                }
                UART_Recv_Buf_Lenth += lenth;
                break;
            }
        ...
        }
    
        UART_INTConfig(UART0, UART_INT_RD_AVA, ENABLE);
    }
    
  4. 在 UART 中断处理函数中,UART 完成接收数据会触发 UART_FLAG_RX_IDLE 中断(读空 RX FIFO 数据后,在 RX 空闲超时时间内没有数据进入 RX FIFO),工作流程如下:

    1. 失能 UART_INT_RX_IDLE 中断。

    2. 清除接收 FIFO。

    3. 重新使能 UART_INT_RX_IDLE 中断。

    4. 置位接收标志 receive_flagtrue

    void UART0_Handler()
    {
        ...
    
        if (UART_GetFlagStatus(UART0, UART_FLAG_RX_IDLE) == SET)
        {
            UART_INTConfig(UART0, UART_INT_RX_IDLE, DISABLE);
    
            UART_ClearRxFIFO(UART0);
            UART_INTConfig(UART0, UART_INT_RX_IDLE, ENABLE);
            receive_flag = true;
        }
        ...
    
    }
    
  5. 循环检测 receive_flag 位。当检测到该标志位被置为 true 时,代表 SoC 端数据接收完毕,将接收到的数据发回至 PC 端,并清空接收到的数据。

    while (1)
    {
        if (receive_flag == true)
        {
            receive_flag = false;
            uart_senddata_continuous(UART0, UART_Send_Buf, UART_Recv_Buf_Lenth);
    
            for (uint16_t i = 0; i < UART_Recv_Buf_Lenth; i++)
            {
                UART_Recv_Buf[i] = 0;
            }
            UART_Recv_Buf_Lenth = 0;
        }
    }