IR Learn

该示例使用 IR 接收功能,实现 IR 学习。

使用另一颗 EVB 下载 IR 发送的程序作为 IR 数据的发送端向该 EVB 发送 IR 数据,在 IR 中断内进行数据的接收。

在中断函数内向 app_task 发送消息事件,app_task 检测到消息事件,在 app 层解析消息,执行用户程序,对 IR 学习解码。在 Debug Analyzer 内,打印 IR 学习的解码后数据信息。

连接 P0_2 至 LED 灯,当 IR 接收数据后,P0_2 翻转,LED 灯由灭转亮。

环境需求

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

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

硬件连线

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

编译和下载

该示例的工程路径如下:

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

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

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

  1. 打开工程文件。

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

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

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

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

测试验证

在 Debug Analyzer 工具上,打印 IR 学习的解码后数据信息。

[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP, duty_cycle: xxx
[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP, frequence: xxx
[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP, learn data lenth: xxx
[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP,learn data_0: xxx
[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP,learn data_1: xxx
...

代码介绍

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

  1. 源码路径

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

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

源码路径

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

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

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

└── Project: learn
    └── 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_tim.c
            ├── rtl876x_rtc.c
            └── rtl876x_ir.c
        ├── profile
        ├── app                      includes the ble_peripheral user application implementation
            ├── main.c
            ├── ancs.c
            ├── app.c
            └── app_task.c
        ├── ir_learn
        ├── ir_trans_rx
        └── ir_led

初始化

当 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. board_init 中,执行 board_ir_led_init ,该函数为 GPIO(LED)相关引脚的 PAD/PINMUX 设置,包含如下流程:

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

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

  2. board_init 中,执行 board_ir_learn_init ,该函数为 IR 相关引脚的 PAD/PINMUX 设置,包含如下流程:

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

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

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

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

    1. 使能 RCC 时钟。

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

    3. 设置引脚输出低电平。

    void driver_ir_led_init(void)
    {
        /* Enable GPIO clock */
        RCC_PeriphClockCmd(APBPeriph_GPIO, APBPeriph_GPIO_CLOCK, ENABLE);
    
        /* Initialize GPIO */
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_StructInit(&GPIO_InitStruct);
    #if (IR_FUN_LEARN_EN)
        GPIO_InitStruct.GPIO_Pin  = LED_IR_SEND_OUT_PIN | LED_IR_LEARN_OUT_PIN;
    #else
        GPIO_InitStruct.GPIO_Pin  = LED_IR_SEND_OUT_PIN;
    #endif
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_Init(&GPIO_InitStruct);
    
        GPIO_ResetBits(LED_IR_SEND_OUT_PIN);
    #if (IR_FUN_LEARN_EN)
        GPIO_ResetBits(LED_IR_LEARN_OUT_PIN);
    #endif
    }
    
  5. driver_init 中执行 ir_learn_timer_init,创建软件定时器。定时时间 20s,定时结束后执行回调函数 ir_learn_timer_callback,超时退出 IR 学习。

    void ir_learn_timer_init(void)
    {
        APP_PRINT_INFO0("[ir_learn_app]ir_learn_timer_init");
        /* IR_Learn_Timer is used for ir learn */
        if (false == os_timer_create(&IR_Learn_Timer, "ir_learn_timer",  1, \
                                    IR_LEARN_TIMEOUT, false, ir_learn_timer_callback))
        {
            APP_PRINT_ERROR0("[ir_learn_app]ir_learn_timer_init: creat IR_Learn_Timer fail!");
        }
    }
    
    static void ir_learn_timer_callback(TimerHandle_t pxTimer)
    {
        APP_PRINT_INFO0("[ir_learn_app]ir_learn_timer_callback: ir learn time out");
        ir_learn_exit();
    
        //Add application code here!!!
    }
    
  6. driver_init 中执行 ir_learn_module_init ,包含如下流程:

    1. 启动软件定时器。

    2. 注册 IR 中断处理回调函数,实现 IR 中断处理函数中发送 msg 到 app task。

    3. 执行 ir_learn_init 初始化 IR 外设:

      1. 初始化 loop queue。

      2. 使能 RCC 时钟。

      3. 设置 IR 接收频率为 40MHz,占空比 1/2,自动接收,下降沿触发,接收 FIFO 阈值为 20,计数器阈值为 240000 等。

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

      5. 使能 IR 接收 FIFO 的数据个数达到设置的接收阈值中断 IR_INT_RF_LEVEL 和接收计数器的个数达到设置的阈值中断 IR_INT_RX_CNT_THR

      void ir_trans_rx_init(void)
      {
          /* Initialize loop queue */
          ir_loop_queue_init(&IR_RX_Queue);
      
          /* Modify IR interrupt handle */
          RamVectorTableUpdate(IR_VECTORn, IR_RX_Handler);
      
          /* Initialize IR peripheral */
          driver_ir_rx_init();
      }
      
      void driver_ir_rx_init(void)
      {
          /* Enable IR clock */
          RCC_PeriphClockCmd(APBPeriph_IR, APBPeriph_IR_CLOCK, DISABLE);
          RCC_PeriphClockCmd(APBPeriph_IR, APBPeriph_IR_CLOCK, ENABLE);
      
          /* Initialize IR */
          IR_InitTypeDef IR_InitStruct;
          IR_StructInit(&IR_InitStruct);
          /* IR carrier freqency is 38KHz */
          IR_InitStruct.IR_Freq               = IR_LEARN_FREQ;
          /* Duty ratio = 1/IR_DutyCycle */
          IR_InitStruct.IR_DutyCycle          = 2;
          /* IR receiveing mode */
          IR_InitStruct.IR_Mode               = IR_MODE_RX;
          IR_InitStruct.IR_RxStartMode        = IR_RX_AUTO_MODE;
          /* Configure RX FIFO threshold level to trigger IR_INT_RF_LEVEL interrupt */
          IR_InitStruct.IR_RxFIFOThrLevel     = IR_RX_FIFO_THR_LEVEL;
          /* Discard the lastest received dta if RX FIFO is full */
          IR_InitStruct.IR_RxFIFOFullCtrl     = IR_RX_FIFO_FULL_DISCARD_NEWEST;
          /* Configure trigger type */
          IR_InitStruct.IR_RxTriggerMode      = IR_RX_FALL_EDGE;
          /* If high to low or low to high transition time <= 50ns,Filter out it. */
          IR_InitStruct.IR_RxFilterTime       = IR_RX_FILTER_TIME_200ns;
          /* IR_RX_Count_Low_Level is counting low level */
          IR_InitStruct.IR_RxCntThrType       = IR_RX_Count_High_Level;
          /* Configure RX counter threshold.You can use it to decide to stop receiving IR data */
          IR_InitStruct.IR_RxCntThr           = IR_LEARN_NO_WAVEFORM_TIME_MAX;
          IR_Init(&IR_InitStruct);
      
          IR_Cmd(IR_MODE_RX, ENABLE);
          IR_ClearRxFIFO();
      
          IR_INTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, ENABLE);
          IR_MaskINTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, DISABLE);
      
          /* Configure NVIC */
          NVIC_InitTypeDef NVIC_InitStruct;
          NVIC_InitStruct.NVIC_IRQChannel = IR_IRQn;
          NVIC_InitStruct.NVIC_IRQChannelPriority = 2;
          NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
          NVIC_Init(&NVIC_InitStruct);
      }
      

功能实现

  1. 当 IR 接收 FIFO 的数据个数达到设置的接收阈值(此例中设为 20)时,触发 IR_INT_RF_LEVEL 中断。或当接收计数器的个数达到设置的阈值(此例中设为 240000)时,触发 IR_INT_RX_CNT_THR 中断,进入中断处理函数 IR_Handler

    1. 屏蔽 IR_INT_TF_LEVELIR_INT_RX_CNT_THR 中断。

    2. 如果触发的中断为 IR_INT_TF_LEVEL ,则需要 IR 从 FIFO 中接收数据,将数据入列到 IR_RX_Queue 中,清除中断标志位。

    3. 如果触发的中断为 IR_INT_RX_CNT_THR ,则代表触发了 IR 接收数据的停止条件。将剩余数据入列到 IR_RX_Queue 中,清除中断标志位。

    4. 调用 pFn_IR_RX_Handler_CB ,定义消息子类型为 IO_MSG_TYPE_IR_LEARN_STOPIO_MSG_TYPE_IR_LEARN_DATA ,发送至 app task。

    5. 取消屏蔽 IR_INT_RF_LEVELIR_INT_RX_CNT_THR 中断。

    void IR_RX_Handler(void)
    {
        uint16_t data_len = 0;
        uint8_t ir_learn_end_flag = false;
        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);
    
        /* Received interrupt */
        if (int_status_rfl == SET)
        {
            data_len = IR_GetRxDataLen();
            ir_loop_queue_data_in(&IR_RX_Queue, data_len);
            IR_ClearINTPendingBit(IR_INT_RF_LEVEL_CLR);
        }
    
        /* Stop to receive IR data */
        if (int_status_rxcnt == SET)
        {
            /* Read remaining data */
            data_len = IR_GetRxDataLen();
            ir_loop_queue_data_in(&IR_RX_Queue, data_len);
            IR_ClearINTPendingBit(IR_INT_RX_CNT_THR_CLR);
    
            /* Send ir learn end signal. */
            //Add application code here
            ir_learn_end_flag = true;
        }
    
        if (pFn_IR_RX_Handler_CB)
        {
            pFn_IR_RX_Handler_CB(ir_learn_end_flag);
        }
    
        /* Unmask IR all interrupt */
        IR_MaskINTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, DISABLE);
    
    }
    
    static void ir_learn_send_msg_from_isr(uint8_t vIsEnd)
    {
        //Add applcation code here which send msg from IR ISR to application layer to notify handling data
        T_IO_MSG bee_io_msg;
        bee_io_msg.type = IO_MSG_TYPE_IR;
    
        if (vIsEnd)
        {
            /* send ir learn data message to app */
            bee_io_msg.subtype = IO_MSG_TYPE_IR_LEARN_STOP;
        }
        else
        {
            /* send ir learn data message to app */
            bee_io_msg.subtype = IO_MSG_TYPE_IR_LEARN_DATA;
        }
        ir_send_msg_to_apptask(&bee_io_msg);
    }
    
  2. 主 task 检测到消息后,在 ir_learn_handle_msg 内对消息进行处理。

    1. 如果检测发送的消息子类型为 IO_MSG_TYPE_IR_LEARN_DATA,对 IR 数据进行解码。如果 IR 学习解码状态为错误状态,停止 IR 学习。

    2. 如果检测发送的消息子类型为 IO_MSG_TYPE_IR_LEARN_STOP,对最后一笔数据进行解码。IR 学习获取频率,数据转换等,打印相关数据。

    3. 退出 IR 学习。

    4. 重新执行 ir_learn_module_init,重新初始化 IR 外设。

    bool ir_learn_handle_msg(T_IO_MSG *io_ir_msg)
    {
        bool ret = false;
        uint16_t sub_type = io_ir_msg->subtype;
    
        if (sub_type == IO_MSG_TYPE_IR_LEARN_DATA)
        {
            IR_Learn_Status status = IR_LEARN_OK;
            status = ir_learn_decode(&IR_Learn_Packet);
    
            if (status == IR_LEARN_EXCEED_SIZE)
            {
                APP_PRINT_WARN0("[ir_learn_app]ir_learn_handle_msg: IR learn exceed size!");
                /* Close IR learn */
                ir_learn_deinit();
                ret = false;
            }
            else if (status == IR_LEARN_EXIT)
            {
                APP_PRINT_WARN0("[ir_learn_app]ir_learn_handle_msg: IR learn stop time error!");
                /* Close IR learn */
                ir_learn_deinit();
                ret = false;
            }
        }
        else if (sub_type == IO_MSG_TYPE_IR_LEARN_STOP)
        {
            /* Pick up the last ir data*/
            ir_learn_decode(&IR_Learn_Packet);
            /* Decode IR carrier freqency */
            ir_learn_freq(&IR_Learn_Packet);
            /* Data reduction */
            ir_learn_data_convert(&IR_Learn_Packet);
            /* Print decode result */
    #ifdef IR_LEARN_DUTY_CYCLE_SUPPORT
            APP_PRINT_INFO1("[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP, duty_cycle: %d/1000.",
                            (uint32_t)(IR_Learn_Packet.duty_cycle * 1000));
    #endif
            APP_PRINT_INFO1("[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP, frequence: %d.",
                            (uint32_t)(IR_Learn_Packet.freq * 1000));
            APP_PRINT_INFO1("[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP, learn data lenth: %d.",
                            IR_Learn_Packet.buf_index);
            for (uint32_t i = 0; i < IR_Learn_Packet.buf_index; i++)
            {
                APP_PRINT_INFO2("[ir_learn_app]ir_learn_handle_msg:IO_MSG_TYPE_IR_LEARN_STOP,learn data%d: 0x%x.",
                                i, IR_Learn_Packet.ir_buf[i]);
            }
            ir_learn_exit();
            ret = true;
    
            // Add application code here!!!
            ir_learn_module_init();
        }
        return ret;
    }