Send Code

该示例演示 IR 外设的 TX 功能和 IR 编码协议库实现 IR 编码协议发送。

利用 TIM 定时功能,定期发送定义的 RAW 编码协议。发送完成,通过 IR 中断向 app_task 发送消息事件,app_task 检测到消息事件,在 app 层解析消息,执行用户程序,发送 IR 编码协议。

使用逻辑分析仪观察 IR 发送波形,并观察发送数据是否符合预期。连接 P0_1 至 LED 灯,当 IR 发送数据后,P0_1 翻转,LED 灯由灭转亮。

环境需求

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

开发套件

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

更多信息请参考 快速入门

硬件连线

连接 IR 发送引脚 P2_5 至逻辑分析仪,P0_1 连接至 LED 灯。

编译和下载

该示例的工程路径如下:

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

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

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

  1. 打开工程文件。

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

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

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

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

测试验证

在逻辑分析仪内,观察 IR 发送波形是否符合预期。

  1. 发送一帧 ir_raw_data_buf 波形。

这里应该是IR发送波形的图片

ir_raw_data_buf 波形

  1. 连续发送 uir_param_info 波形。

这里应该是IR发送波形的图片

uir_param_info 连续波形

  1. 一帧 uir_param_info 波形如下图所示。

这里应该是IR发送波形的图片

uir_param_info 一帧波形

  1. Repeat Code 示意图如下图所示。

这里应该是IR发送波形的图片

Repeat Code 示意图

代码介绍

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

  1. 源码路径

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

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

源码路径

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

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

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

└── Project: ir_sendcode
    └── 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_ir.c
        ├── profile
        ├── app                      includes the ble_peripheral user application implementation
            ├── main.c
            ├── ancs.c
            ├── app.c
            ├── app_task.c
            └── ir_app.c
        ├── ir_protocol
        ├── ir_encode
        ├── ir_trans
        └── 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_All_LED_Init ,该函数为 GPIO(LED)相关引脚的 PAD/PINMUX 设置,包含如下流程:

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

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

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

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

    1. 使能 RCC 时钟。

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

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

    void Driver_All_LED_Init(void)
    {
        /* Enable GPIO clock */
        RCC_PeriphClockCmd(APBPeriph_GPIO, APBPeriph_GPIO_CLOCK, ENABLE);
    
        /* Initialize GPIO */
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_StructInit(&GPIO_InitStruct);
        GPIO_InitStruct.GPIO_Pin  = LED_IR_SEND_GPIO_INDEX | LED_IR_LEARN_GPIO_INDEX;
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_Init(&GPIO_InitStruct);
        GPIO_ResetBits(LED_IR_SEND_GPIO_INDEX);
        GPIO_ResetBits(LED_IR_LEARN_GPIO_INDEX);
    }
    
  4. 在主任务内,执行 IR_Send_Init ,在 UIR_TransInit 内对 IR 和 TIM 外设进行初始化配置。

    1. 配置 PAD/PINMUX:配置 IR 发送引脚,PINMUX 模式,PowerOn,无内部上拉,输出高,分配引脚为 IRDA_TX 功能。

    2. 重置 IR 发送数据。

    3. 执行 Driver_IR_Init 对 IR 外设进行初始化配置:

      1. 使能 RCC 时钟。

      2. 设置 IR 发送频率为 38kHz。

      3. 设置 IR 载波占空比为 1/2。

      4. 设置 IR 为发送模式。

      5. 设置 IR 发送 FIFO 阈值为 2。

      void Driver_IR_Init(uint32_t freq)
      {
          /* Disable IR clock */
          RCC_PeriphClockCmd(APBPeriph_IR, APBPeriph_IR_CLOCK, DISABLE);
          /* 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           = freq;
          IR_InitStruct.IR_DutyCycle      = 2; /* !< 1/2 duty cycle */
          IR_InitStruct.IR_Mode           = IR_MODE_TX;
          IR_InitStruct.IR_TxInverse      = IR_TX_DATA_NORMAL;
          IR_InitStruct.IR_TxFIFOThrLevel = IR_TX_FIFO_THR_LEVEL;
          IR_InitStruct.IR_TxIdleLevel    = IR_IDLE_OUTPUT_LOW;
          IR_Init(&IR_InitStruct);
          IR_Cmd(IR_MODE_TX, ENABLE);
          /* Modify IR interrupt handle */
          RamVectorTableUpdate(IR_VECTORn, IR_Handler);
      
          /* 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. 执行 Driver_IR_TIM_Init ,对 TIM 外设进行初始化,包含如下流程:

      1. 使能 RCC 时钟。

      2. 设置 TIM 时钟为 4 分频,即时钟为 10MHz。

      3. 设置 TIM 为用户自定义模式,配置 TIM 中断。

      void Driver_IR_TIM_Init(void)
      {
          /* Enable TIM clock */
          RCC_PeriphClockCmd(APBPeriph_TIMER, APBPeriph_TIMER_CLOCK, ENABLE);
      
          /* Initialize TIM */
          TIM_TimeBaseInitTypeDef TIM_InitStruct;
          TIM_StructInit(&TIM_InitStruct);
          /* Timer clock = 10MHz */
          TIM_InitStruct.TIM_SOURCE_DIV = TIM_CLOCK_DIVIDER_4;
          TIM_InitStruct.TIM_Period = 0xFFFFFFFF;
          TIM_InitStruct.TIM_Mode = TIM_Mode_UserDefine;
          TIM_TimeBaseInit(IR_TIM, &TIM_InitStruct);
          TIM_INTConfig(IR_TIM, ENABLE);
      
          /* Configure NVIC */
          NVIC_InitTypeDef NVIC_InitStruct;
          NVIC_InitStruct.NVIC_IRQChannel = IR_TIM_IRQ;
          NVIC_InitStruct.NVIC_IRQChannelPriority = 2;
          NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
          NVIC_Init(&NVIC_InitStruct);
      }
      
  5. 注册 IR 中断处理函数的回调函数。

功能实现

  1. 执行 os_sched_start() ,开启任务调度。当 stack 准备好时,执行 app_handle_dev_state_evt ,执行 IR_Raw_Packet_Send

    1. 定义 IR 发送波形的协议,数据内容和长度等。

    2. 执行 UIR_OneFrameTransmitting,启动 IR 编码协议发送。

    3. UIR_SendIRWaveform 内,使能 IR 发送 FIFO 数据个数小于设置的发送阈值中断 IR_INT_TF_LEVEL

    4. 判断状态为 NO_ERROR 时,翻转 P0_1(LED0)。

    void IR_Raw_Packet_Send(uint32_t *pBuf, uint32_t len)
    {
        UIR_STATUS status;
    
        memset(&uir_param_info, 0, sizeof(uir_param_info));
        uir_param_info.uir_protocol_index   = UIR_CODESET_TYPE_RAW_Pack;
        uir_param_info.pRawPackAddr = pBuf;
        uir_param_info.raw_pack_len = len;
    
        status = UIR_OneFrameTransmitting(&uir_param_info, DISABLE);
        if (UIR_STATUS_NO_ERROR == status)
        {
            LED_IR_Send_Swap();
        }
    }
    
    static void UIR_SendIRWaveform(IR_DataTypeDef *pIR_DataStruct, uint16_t buf_index)
    {
        /* Start to send first bytes data of encoded data */
        if (pIR_DataStruct->bufLen >= IR_TX_FIFO_SIZE)
        {
            /* Enable IR threshold interrupt. Only when TX FIFO offset = threshold value, trigger interrupt */
            IR_SetTxThreshold(IR_TX_FIFO_THR_LEVEL);
    
            IR_SendBuf(&(pIR_DataStruct->irBuf[buf_index]), IR_TX_FIFO_SIZE, DISABLE);
    
            /* Record number which has been sent */
            ir_tx_count = IR_TX_FIFO_SIZE;
    
            IR_INTConfig(IR_INT_TF_LEVEL, ENABLE);
        }
        else
        {
            IR_SendBuf(&(pIR_DataStruct->irBuf[buf_index]), pIR_DataStruct->bufLen, DISABLE);
    
            /* Record number which has been sent */
            ir_tx_count = pIR_DataStruct->bufLen;
    
            /* Enable IR Tx FIFO empty interrupt */
            IR_INTConfig(IR_INT_TF_EMPTY, ENABLE);
        }
    }
    
  2. 当 IR 发送 FIFO 数据个数小于设置的发送阈值时,触发 IR_INT_TF_LEVEL 中断。或 IR 发送 FIFO 为空时,触发 IR_INT_TF_EMPTY 中断,进入中断处理函数 IR_Handler

    1. 屏蔽 IR_INT_TF_LEVELIR_INT_TF_EMPTY 中断。

    2. 若判断触发的中断为 IR_INT_TF_LEVEL ,代表 IR 正在发送数据。由于 IR 发送数据可能数据量较大(大于 IR TX FIFO 深度),因此需要分批进行发送。

      1. 如果剩余发送数据个数大于发送 FIFO 大小,往 IR 发送 FIFO 中塞(IR_TX_FIFO_SIZE - IR_TX_FIFO_THR_LEVEL) 个数据并发送,代表剩余的数据量仍然大于 IR TX FIFO 深度。

      2. 否则如果剩余发送数据个数大于0,往 IR 发送 FIFO 中塞剩余数据,代表剩余的数据量小于 IR TX FIFO 深度,即发送最后一笔数据。

      3. 否则如果没有剩余数据,失能 IR_INT_TF_LEVEL 中断,代表 IR 所有数据发送完毕。

      4. 取消屏蔽 IR_INT_TF_LEVEL 中断。

    3. 若判断触发的中断为 IR_INT_TF_EMPTY,执行以下流程:

      1. 发送 Repeat code。

      2. 失能 IR_INT_TF_EMPTY 中断,清除中断挂起位。

      3. 调用 pfnIRIntrHandlerCB 回调函数。

    4. 取消屏蔽 IR_INT_TF_EMPTYIR_INT_TF_LEVEL 中断。

    void IR_Handler(void)
    {
        ITStatus int_status_tfl = IR_GetINTStatus(IR_INT_TF_LEVEL);
        ITStatus int_status_tfe = IR_GetINTStatus(IR_INT_TF_EMPTY);
        /* Mask IR interrupt */
        IR_MaskINTConfig(IR_INT_TF_LEVEL, ENABLE);
        IR_MaskINTConfig(IR_INT_TF_EMPTY, ENABLE);
    
        /* Continue to send by interrupt */
        if (int_status_tfl == SET)
        {
            /* The remaining data is larger than the TX FIFO length */
            if ((IR_DataStruct.bufLen - ir_tx_count) >= IR_TX_FIFO_SIZE)
            {
                IR_SendBuf(IR_DataStruct.irBuf + ir_tx_count, IR_TX_FIFO_SIZE - IR_TX_FIFO_THR_LEVEL, DISABLE);
                ir_tx_count += (IR_TX_FIFO_SIZE - IR_TX_FIFO_THR_LEVEL);
            }
            else if ((IR_DataStruct.bufLen - ir_tx_count) > 0)
            {
                /* The remaining data is less than the TX FIFO length */
                IR_INTConfig(IR_INT_TF_LEVEL, DISABLE);
    
                /* Enable IR tx empty interrupt */
                IR_INTConfig(IR_INT_TF_EMPTY, ENABLE);
                IR_SendBuf(IR_DataStruct.irBuf + ir_tx_count, IR_DataStruct.bufLen - ir_tx_count, DISABLE);
                ir_tx_count = IR_DataStruct.bufLen;
            }
            else
            {
                /* Disable IR tx empty interrupt */
                IR_INTConfig(IR_INT_TF_LEVEL, DISABLE);
                /* Enable IR tx empty interrupt */
                IR_INTConfig(IR_INT_TF_EMPTY, ENABLE);
    
                //Add application code here!!!
    
            }
            /* Clear threshold interrupt */
            IR_ClearINTPendingBit(IR_INT_TF_LEVEL_CLR);
        }
    
        if (int_status_tfe == SET)
        {
            /* Configure repeat interval and repeat code sending function */
            if (IR_DataStruct.isSendRepeatcode)
            {
                if ((uir_param_info_base.uir_protocol_index == UIR_CODESET_TYPE_LC7464M) || \
                    (uir_param_info_base.uir_protocol_index == UIR_CODESET_TYPE_DVB) || \
                    (uir_param_info_base.uir_protocol_index == UIR_CODESET_TYPE_MIT_C8D8) || \
                    (uir_param_info_base.uir_protocol_index == UIR_CODESET_TYPE_KONICA))
                {
                    UIR_BurstSendCmd(ProtocolLib[uir_param_info_base.uir_protocol_index].repeat_interval, ENABLE);
                }
    
                if (uir_param_info_base.uir_protocol_index == UIR_CODESET_TYPE_DVB_40BIT)
                {
                    if (IR_DataStruct.isRepeatcodeEncoded)
                    {
                        UIR_BurstSendCmd(DVB_40BIT_TRANSMISION_RE_INTERVAL, ENABLE);
                    }
                    else
                    {
                        UIR_BurstSendCmd(ProtocolLib[uir_param_info_base.uir_protocol_index].repeat_interval, ENABLE);
                    }
                }
            }
    
            /* Tx completed */
            ir_tx_count = 0;
            /* Disable IR tx empty interrupt */
            IR_INTConfig(IR_INT_TF_EMPTY, DISABLE);
    
            /* Clear interrupt flag */
            IR_ClearINTPendingBit(IR_INT_TF_EMPTY_CLR);
    
            if (UIR_GetTIMStatus() == UIR_STATUS_ALREADY_IN_TRANSMITTING)
            {
                /* Unmask IR interrupt */
                IR_MaskINTConfig(IR_INT_TF_LEVEL, DISABLE);
                IR_MaskINTConfig(IR_INT_TF_EMPTY, DISABLE);
                return;
            }
            //Add application code here!!!
            //Send Msg to App task to inform that IR complete a complete transmission!!!
    
            if (pfnIRIntrHandlerCB)
            {
                pfnIRIntrHandlerCB();
            }
        }
    
        /* Unmask IR interrupt */
        IR_MaskINTConfig(IR_INT_TF_LEVEL, DISABLE);
        IR_MaskINTConfig(IR_INT_TF_EMPTY, DISABLE);
    }
    
  3. 执行 UIR_BurstSendCmd,发送 Repeat Code。在函数内,配置对应的 TIM 周期,使能 TIM。定时时间到时,触发中断。在中断函数内执行 UIR_SendRepeatData,发送 Repeat Code。

    void UIR_BurstSendCmd(uint32_t interval, FunctionalState NewState)
    {
        if (NewState != DISABLE)
        {
            /* Enable transmission continuously */
            TIM_Cmd(IR_TIM, DISABLE);
            TIM_ChangePeriod(IR_TIM, interval);
            TIM_Cmd(IR_TIM, ENABLE);
        }
        else
        {
            /* Disable transmission continuously */
            TIM_Cmd(IR_TIM, DISABLE);
        }
    }
    
    void IR_TIMIntrHandler(void)
    {
        // Add application code here
        if (ir_tx_count == 0)
        {
    #ifdef IR_WAVEFORM_INTERVAL_CTRL
            IR_DataStruct.isTimerOccured = true;
    
            if (IR_DataStruct.intervalCtrl)
            {
                UIR_BurstSendCmd(0, DISABLE);
                /* Stop to send repeat code */
                IR_DataStruct.isSendRepeatcode = false;
                IR_DataStruct.intervalCtrl = false;
                IR_DataStruct.isTimerOccured = false;
                TIM_ClearINT(IR_TIM);
    
                /* Notify To app task */
                if (pfnTIMIntrHandlerCB)
                {
                    pfnTIMIntrHandlerCB();
                }
                return;
            }
    #endif
            UIR_SendRepeatData(&uir_param_info_base, &IR_DataStruct);
        }
        else
        {
            //IR send error
        }
    
        TIM_ClearINT(IR_TIM);
    }
    
  4. 在回调函数内,定义消息类型,发送消息至 app task。主 task 判断消息类型为 IO_MSG_TYPE_IR_SEND_COMPLETE,执行 ir_send_msg_proc,执行 NEC_SendCode

    1. 定义 IR 发送数据协议类型和数据内容。

    2. 发送 1 帧 IR 编码协议。

    3. 发送连续 IR 编码协议。

    void NEC_SendCode(void)
    {
        UIR_STATUS status;
    
        memset(&uir_param_info, 0, sizeof(uir_param_info));
        uir_param_info.uir_protocol_index   = UIR_CODESET_TYPE_NEC;
        uir_param_info.custom_code_value[0] = 0x55;
        uir_param_info.custom_code_value[1] = (uint8_t)(~(0x55));
        uir_param_info.custom_code_length   = 16;
        uir_param_info.key_code_value[0]    = 0xAA;
        uir_param_info.key_code_value[1]    = (uint8_t)(~(0xAA));
        uir_param_info.key_code_length      = 16;
    
        UIR_OneFrameTransmitting(&uir_param_info, DISABLE);
    
        platform_delay_ms(2000);
    
        status = UIR_StartContinuouslyTransmitting(&uir_param_info);
        ...
        status = UIR_StopContinuouslyTransmitting();
    
        ...
    }