IR Receive

该示例演示了如何在 IR 中断模式下接收数据。

使用 P2_6 引脚作为 PWM 输出引脚,输出 PWM 波形用于模拟发送端数据。将 PWM 输出引脚连接到 IR 输入引脚。

在 IR 中断内进行数据的接收,当数据量超过 IR 的 FIFO 容量时,需要多次连续接收数据。

环境需求

该示例的环境需求,可参考 环境需求

硬件连线

连接 PWM 输出引脚 P2_6 与 IR 接收引脚 P2_5。

配置选项

  1. 可配置如下宏修改引脚定义。

    #define IR_RX_PIN               P2_5
    #define PWM_OUT_PIN             P2_6
    
  2. 可配置如下宏修改 PWM 配置信息。

    #define PWM_PERIOD              26.3   //uint:us
    #define PWM_DUTY_CYCLE          50     //uint:percent
    #define PWM_HIGH_COUNT          ((((PWM_PERIOD)*(PWM_DUTY_CYCLE*40))/100)-1)
    #define PWM_LOW_COUNT           ((((PWM_PERIOD)*((100-PWM_DUTY_CYCLE)*40))/100)-1)
    

编译和下载

该示例的编译和下载流程,可参考 编译和下载

测试验证

  1. 初始化完毕后,PWM 开始输出波形,频率设置为 38KHz,与 IR 接收频率相同。在主程序中,每间隔 500us 反复使能和失能 TIM PWM 输出,模拟 500us 的有载波数据和无载波数据。PWM 波形如图所示。

这里应该是 IR PWM 波形的图片

PWM 输出波形

  1. 当 PWM 输出波形的时候,IR 接收器将 500us 的 PWM 波形解析为一个 mark 数据。当 PWM 不输出波形的时候,IR 接收器将 500us 的低电平解析为一个 space 数据。

  2. 当 IR 接收计数器的数据个数达到设置的阈值,会触发 IR IR_INT_RF_LEVEL 中断,在 IR 中断内接收当前数据。

  3. 在主程序中,PWM 输出变化数次后,会停止输出 PWM 波形,此时 IR 检测到低电平输入。当低电平输入时间达到设定的 counter 值时,触发 IR_INT_RX_CNT_THR 中断,将剩余数据全部接收完毕。

    IR_INT_RX_CNT_THR
    len = xx
    [io_ir]io_handle_ir_msg: IR RX data[0] = 0x80000012
    [io_ir]io_handle_ir_msg: IR RX data[1] = 0x00000012
    ...
    [io_ir]io_handle_ir_msg: IR RX data[xx] = 0x00000200
    

备注

其中载波数据 0x800000012 表示高电平输出为 12 个载波周期,0x12 代表低电平输出 12 个载波周期。

代码介绍

该章节主要介绍示例中的初始化和相应功能实现的代码和流程说明。

源码路径

工程文件和源码路径如下:

  • 工程路径: sdk\samples\peripheral\ir\rx\proj

  • 源码路径: sdk\samples\peripheral\ir\rx\src

初始化

外设的初始化流程可参考 General Introduction 中的 初始化流程 部分。

  1. 调用 Pad_Config()Pinmux_Config(),配置对应引脚的 PAD 和 PINMUX。

    void board_ir_init(void)
    {
        Pad_Config(IR_RX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_DISABLE, PAD_OUT_LOW);
    
        Pinmux_Config(IR_RX_PIN, IRDA_RX);
    }
    
    void board_pwm_init(void)
    {
        Pad_Config(PWM_OUT_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_NONE, PAD_OUT_ENABLE, PAD_OUT_HIGH);
    
        Pinmux_Config(PWM_OUT_PIN, PWM_OUT_PIN_PINMUX);
    }
    
  2. 调用 RCC_PeriphClockCmd(),开启 IR 时钟。

  3. 对 IR 外设进行初始化:

    1. 定义 IR_InitTypeDef 类型 IR_InitStruct,调用 IR_StructInit()IR_InitStruct 预填默认值。

    2. 根据需求修改 IR_InitStruct 参数,IR 的初始化参数配置如下表。

    3. 调用 IR_Init(),初始化 IR 外设。

IR 初始化参数

IR Hardware Parameters

Setting in the IR_InitStruct

IR

Sample Clock

IR_InitTypeDef::IR_Freq

38000

IR Mode

IR_InitTypeDef::IR_Mode

IR_MODE_RX

IR Rx Mode

IR_InitTypeDef::IR_RxStartMode

IR_RX_AUTO_MODE

FIFO Threshold Level

IR_InitTypeDef::IR_RxFIFOThrLevel

10

IR Rx Trigger Mode

IR_InitTypeDef::IR_RxTriggerMode

IR_RX_FALL_EDGE

IR Rx Counter Threshold Type

IR_InitTypeDef::IR_RxCntThrType

IR_RX_Count_Low_Level

IR Rx Counter Threshold

IR_InitTypeDef::IR_RxCntThr

0x200

  1. 调用 IR_Cmd() ,使能 IR 外设。

  2. 调用 IR_INTConfig()IR_MaskINTConfig() ,配置 IR 接收 FIFO 数据个数大于设置的接收阈值中断 IR_INT_RF_LEVEL 和接收电平超时中断 IR_INT_RX_CNT_THR 。配置 NVIC,详情参考参考 中断配置

  3. 调用 RCC_PeriphClockCmd() ,开启 TIM 时钟。

  4. 对 TIM 外设进行初始化:

    1. 定义 TIM_TimeBaseInitTypeDef 类型 TIM_InitStruct ,调用 TIM_StructInit()TIM_InitStruct 预填默认值。

    2. 根据需求修改 TIM_InitStruct 参数,TIM 的初始化参数配置如下表。

    3. 调用 TIM_TimeBaseInit(),初始化 TIM 外设。

TIM 初始化参数

TIM Hardware Parameters

Setting in the TIM_InitStruct

TIM

TIM Mode

TIM_TimeBaseInitTypeDef::TIM_Mode

TIM_Mode_UserDefine

PWM Enable

TIM_TimeBaseInitTypeDef::TIM_PWM_En

ENABLE

PWM High Count

TIM_TimeBaseInitTypeDef::TIM_PWM_High_Count

PWM_HIGH_COUNT

PWM Low Count

TIM_TimeBaseInitTypeDef::TIM_PWM_Low_Count

PWM_LOW_COUNT

功能实现

IR 通过中断接收数据的流程如图所示:

这里应该是 IR rx interrupt flow

IR 中断接收数据流程图

  1. ir_demo 中,循环使能与失能 PWM 输出,模拟 IR 载波数据。循环 50 次之后,PWM 会停止输出波形。

void ir_demo(void)
{
    ...
    for (uint32_t i = 0; i < 50; i++)
    {
        TIM_Cmd(PWM_TIMER_NUM, ENABLE);
        platform_delay_us(500);
        TIM_Cmd(PWM_TIMER_NUM, DISABLE);
        platform_delay_us(500);
    }
}
  1. 当 IR 接收 FIFO 的数据个数达到设置的接收阈值时,触发 IR_INT_RF_LEVEL 中断。在中断内调用 IR_GetRxDataLen()IR_ReceiveBuf(),接收当前 IR 数据。

  2. 当 PWM 停止输出波形后,当 IR 检测到超过设置的 IR_InitTypeDef::IR_RxCntThr 载波周期的低电平长度时,触发 IR_INT_RX_CNT_THR 中断。在中断内调用 IR_GetRxDataLen()IR_ReceiveBuf(),接收剩余的 IR 数据。

    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_ClearRxFIFO();
            IR_ClearINTPendingBit(IR_INT_RF_LEVEL_CLR);
        }
    
        /* Stop to receive IR data */
        if (int_status_rxcnt == SET)
        {
            DBG_DIRECT("IR_INT_RX_CNT_THR");
            /* 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;
            ...
    
            memset(&IR_Rx_Data, 0, sizeof(IR_Rx_Data));
            IR_RX_Count = 0;
    
            IR_ClearINTPendingBit(IR_INT_RX_CNT_THR_CLR);
        }
    
        /* Unmask IR all interrupt */
        IR_MaskINTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, DISABLE);
    }