IR Receive

该示例演示红外接收功能的使用方法。

使用另一颗 EVB 下载 IR 发送的程序作为 IR 数据的发送端向该 EVB 发送 IR 数据。

在 IR 中断内进行数据的接收,若数据量大于 IR 的 FIFO,需要连续多次地接收数据。

IR 发送端的示例可参考 IR Transmit

环境需求

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

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

硬件连线

使用另一颗 EVB 下载 IR TX 程序,连接 IR 接收引脚 P2_5 和另一颗 EVB 的发送引脚。

配置选项

编译和下载

该示例的工程路径如下:

Project file: board\evb\io_sample\IR\Rx\mdk

Project file: board\evb\io_sample\IR\Rx\gcc

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

  1. 打开工程文件。

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

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

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

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

测试验证

当 IR 接收计数器的个数达到设置的阈值,代表 IR 数据接收完毕,进入 IR 中断,打印接收数据。接收的数据即为 IR 发送端定义的数据内容。

[io_ir]io_handle_ir_msg: IR RX data[0] = xxx
[io_ir]io_handle_ir_msg: IR RX data[1] = xxx
...

代码介绍

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

  1. 源码路径

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

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

源码路径

  • 工程路径: sdk\board\evb\io_sample\IR\Rx

  • 源码路径: sdk\src\sample\io_sample\IR\Rx

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

└── Project: ir_rx
    └── 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
            └── adc.lib
        ├── peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl876x_rcc.c
            ├── rtl876x_pinmux.c
            ├── rtl876x_nvic.c
            └── rtl876x_ir.c
        ├── profile
        └── app                      includes the ble_peripheral user application implementation
            ├── main.c
            ├── ancs.c
            ├── app.c
            ├── app_task.c
            └── io_ir.c

初始化

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

int main(void)
{
    extern uint32_t random_seed_value;
    srand(random_seed_value);
    global_data_init();
    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. global_data_init 中,执行 global_data_ir_init ,该函数为全局初始化,初始化全局变量 IR_Rx_Data ,重置 IR 接收数据。

    void global_data_ir_init(void)
    {
        /* Initialize adc k value! */
        APP_PRINT_INFO0("[io_ir]global_data_ir_init");
        memset(&IR_Rx_Data, 0, sizeof(IR_Rx_Data));
        IR_RX_Count = 0;
    }
    
  2. board_init 中,执行 board_ir_init ,该函数为 PAD/PINMUX 设置,包含如下流程:

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

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

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

  4. driver_init 中执行 driver_ir_init ,该函数为 IR 外设的初始化,包含如下流程:

    1. 使能 RCC 时钟。

    2. 设置 IR 接收频率为 38kHz。

    3. 设置 IR 为自动接收模式。

    4. 设置 IR 接收阈值为 30,当 IR 接收 FIFO 满时丢弃最新数据,过滤低于 50ns 的杂波数据。

    5. 设置触发接收计数器阈值中断的电平类型为高电平,IR_RxCntThr 设置为 0x1F40。

    6. 使能 IR 外设接收功能,清除 IR 接收 FIFO。

    void driver_ir_init(void)
    {
        /* Enable IR clock */
        RCC_PeriphClockCmd(APBPeriph_IR, APBPeriph_IR_CLOCK, ENABLE);
    
        /* Initialize IR */
        IR_InitTypeDef IR_InitStruct;
        IR_StructInit(&IR_InitStruct);
    
        IR_InitStruct.IR_Freq               = 38000;/* IR carrier freqency is 38KHz */
        IR_InitStruct.IR_Mode               = IR_MODE_RX;/* IR receiveing mode */
        IR_InitStruct.IR_RxStartMode        = IR_RX_AUTO_MODE;
        IR_InitStruct.IR_RxFIFOThrLevel     =
            IR_RX_FIFO_THR_LEVEL; /* Configure RX FIFO threshold level to trigger IR_INT_RF_LEVEL interrupt */
        IR_InitStruct.IR_RxFIFOFullCtrl     =
            IR_RX_FIFO_FULL_DISCARD_NEWEST;/* Discard the lastest received dta if RX FIFO is full */
        IR_InitStruct.IR_RxFilterTime       =
            IR_RX_FILTER_TIME_50ns;/* If high to low or low to high transition time <= 50ns,Filter out it. */
        IR_InitStruct.IR_RxTriggerMode      = IR_RX_FALL_EDGE;/* Configure trigger type */
        IR_InitStruct.IR_RxCntThrType       =
            IR_RX_Count_High_Level;/* IR_RX_Count_High_Level is counting high level */
        IR_InitStruct.IR_RxCntThr           =
            0x1F40;/* Configure RX counter threshold.You can use it to decide to stop receiving IR data */
        IR_Init(&IR_InitStruct);
        IR_Cmd(IR_MODE_RX, ENABLE);
        IR_ClearRxFIFO();
    
        /* Configure NVIC */
        NVIC_InitTypeDef NVIC_InitStruct;
        NVIC_InitStruct.NVIC_IRQChannel = IR_IRQn;
        NVIC_InitStruct.NVIC_IRQChannelPriority = 3;
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStruct);
    
    }
    

功能实现

  1. 在主函数中执行 os_sched_start() ,开启任务调度。当 stack 准备好时,执行 app_handle_dev_state_evt ,执行 ir_demo

    1. 配置 IR 接收 FIFO 数据个数大于设置的接收阈值中断 IR_INT_RF_LEVEL 和接收电平超时中断 IR_INT_RX_CNT_THR

    2. 取消屏蔽 IR_INT_RF_LEVELIR_INT_RX_CNT_THR 中断。

    void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause)
    {
        ...
        if (gap_dev_state.gap_init_state != new_state.gap_init_state)
        {
            if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY)
            {
                APP_PRINT_INFO0("GAP stack ready");
                /*stack ready*/
                ir_demo();
            }
        }
        ...
    }
    
    void ir_demo(void)
    {
        /* Enable IR threshold interrupt. when RX FIFO offset >= threshold value, trigger interrupt*/
        /* Enable IR counter threshold interrupt to stop receiving data */
        IR_INTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, ENABLE);
        IR_MaskINTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, DISABLE);
    }
    
  2. 向 IR 收发模块发送 IR 数据。当 IR 接收 FIFO 的数据个数达到设置的接收阈值(此例中设为 30)时,触发 IR_INT_RF_LEVEL 中断。或当接收计数器的个数达到设置的阈值(此例中设为 0x1F40)时,触发 IR_INT_RX_CNT_THR 中断,进入中断处理函数 IR_Handler

    1. 屏蔽 IR_INT_TF_LEVELIR_INT_RX_CNT_THR 中断。

    2. 如果触发的中断为 IR_INT_TF_LEVEL ,则需要 IR 从 FIFO 中接收数据。将数据存入 DataBuf 内,并记录已经接收的数据长度。

    3. 如果触发的中断为 IR_INT_RX_CNT_THR ,则代表触发了 IR 接收数据的停止条件。将剩余数据存入 DataBuf 内,记录已经接收的数据长度,并打印数据内容。

    4. 清除 IR_INT_RX_CNT_THR 中断挂起位。

    5. 取消屏蔽 IR_INT_RF_LEVELIR_INT_RX_CNT_THR 中断。

    void IR_Handler(void)
    {
        uint16_t len = 0;
        ITStatus int_status_rfl = IR_GetINTStatus(IR_INT_RF_LEVEL);
        ITStatus int_status_rxcnt = IR_GetINTStatus(IR_INT_RX_CNT_THR);
    
        /* Mask IR all interrupt */
        IR_MaskINTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, ENABLE);
    
        /* Receive by interrupt */
        if (int_status_rfl == SET)
        {
            len = IR_GetRxDataLen();
            IR_ReceiveBuf(IR_Rx_Data.DataBuf + IR_RX_Count, len);
            IR_Rx_Data.DataLen += len;
            IR_RX_Count += len;
    
            IR_ClearINTPendingBit(IR_INT_RF_LEVEL_CLR);
        }
    
        /* Stop to receive IR data */
        if (int_status_rxcnt == SET)
        {
            /* Read remaining data */
            len = IR_GetRxDataLen();
            IR_ReceiveBuf(IR_Rx_Data.DataBuf + IR_RX_Count, len);
            IR_Rx_Data.DataLen += len;
            IR_RX_Count += len;
    
            T_IO_MSG int_ir_msg;
            int_ir_msg.type = IO_MSG_TYPE_IR;
            int_ir_msg.subtype = 0;
            int_ir_msg.u.buf = (void *)(&IR_Rx_Data);
            if (false == app_send_msg_to_apptask(&int_ir_msg))
            {
                APP_PRINT_ERROR0("[io_ir]IR_INT_RX_CNT_THR: Send int_ir_msg failed!");
                //Add user code here!
                IR_ClearINTPendingBit(IR_INT_RX_CNT_THR_CLR);
                return;
            }
            IR_ClearINTPendingBit(IR_INT_RX_CNT_THR_CLR);
        }
    
        /* Unmask IR all interrupt */
        IR_MaskINTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, DISABLE);
    }
    
    void io_handle_ir_msg(T_IO_MSG *io_ir_msg)
    {
        IR_Data_TypeDef *p_buf = io_ir_msg->u.buf;
        for (uint16_t i = 0; i < p_buf->DataLen; i++)
        {
            APP_PRINT_INFO2("[io_ir]io_handle_ir_msg: IR RX data[%d] = 0x%x", i, p_buf->DataBuf[i]);
        }
        memset(&IR_Rx_Data, 0, sizeof(IR_Rx_Data));
        IR_RX_Count = 0;
    }