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
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
在逻辑分析仪内,观察 IR 发送波形是否符合预期。
发送一帧
ir_raw_data_buf
波形。

ir_raw_data_buf 波形
连续发送
uir_param_info
波形。

uir_param_info 连续波形
一帧
uir_param_info
波形如下图所示。

uir_param_info 一帧波形
Repeat Code 示意图如下图所示。

Repeat Code 示意图
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
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_init
,app_le_profile_init
等为 privacy 管理模块相关的初始化,参考 LE Peripheral Privacy 中的初始化流程介绍。
与外设相关的初始化流程具体如下:
在
board_init
中,执行Board_All_LED_Init
,该函数为 GPIO(LED)相关引脚的 PAD/PINMUX 设置,包含如下流程:配置 PAD:设置引脚、PINMUX 模式、PowerOn、无内部上拉、输出高。
配置 PINMUX:分配引脚为 DWGPIO 功能。
在执行
os_sched_start()
开启任务调度后,在app_main_task
主任务内,执行driver_init
对外设驱动进行初始化配置。在
driver_init
中执行Driver_All_LED_Init
,该函数为 GPIO 外设的初始化,包含如下流程:使能 RCC 时钟。
设置 GPIO 引脚为输出模式。
设置引脚输出低电平。
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); }
在主任务内,执行
IR_Send_Init
,在UIR_TransInit
内对 IR 和 TIM 外设进行初始化配置。配置 PAD/PINMUX:配置 IR 发送引脚,PINMUX 模式,PowerOn,无内部上拉,输出高,分配引脚为 IRDA_TX 功能。
重置 IR 发送数据。
执行
Driver_IR_Init
对 IR 外设进行初始化配置:使能 RCC 时钟。
设置 IR 发送频率为 38kHz。
设置 IR 载波占空比为 1/2。
设置 IR 为发送模式。
设置 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); }
执行
Driver_IR_TIM_Init
,对 TIM 外设进行初始化,包含如下流程:使能 RCC 时钟。
设置 TIM 时钟为 4 分频,即时钟为 10MHz。
设置 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); }
注册 IR 中断处理函数的回调函数。
功能实现
执行
os_sched_start()
,开启任务调度。当 stack 准备好时,执行app_handle_dev_state_evt
,执行IR_Raw_Packet_Send
。定义 IR 发送波形的协议,数据内容和长度等。
执行
UIR_OneFrameTransmitting
,启动 IR 编码协议发送。在
UIR_SendIRWaveform
内,使能 IR 发送 FIFO 数据个数小于设置的发送阈值中断IR_INT_TF_LEVEL
。判断状态为 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); } }
当 IR 发送 FIFO 数据个数小于设置的发送阈值时,触发
IR_INT_TF_LEVEL
中断。或 IR 发送 FIFO 为空时,触发IR_INT_TF_EMPTY
中断,进入中断处理函数IR_Handler
。屏蔽
IR_INT_TF_LEVEL
和IR_INT_TF_EMPTY
中断。若判断触发的中断为
IR_INT_TF_LEVEL
,代表 IR 正在发送数据。由于 IR 发送数据可能数据量较大(大于 IR TX FIFO 深度),因此需要分批进行发送。如果剩余发送数据个数大于发送 FIFO 大小,往 IR 发送 FIFO 中塞(IR_TX_FIFO_SIZE - IR_TX_FIFO_THR_LEVEL) 个数据并发送,代表剩余的数据量仍然大于 IR TX FIFO 深度。
否则如果剩余发送数据个数大于0,往 IR 发送 FIFO 中塞剩余数据,代表剩余的数据量小于 IR TX FIFO 深度,即发送最后一笔数据。
否则如果没有剩余数据,失能
IR_INT_TF_LEVEL
中断,代表 IR 所有数据发送完毕。取消屏蔽
IR_INT_TF_LEVEL
中断。
若判断触发的中断为
IR_INT_TF_EMPTY
,执行以下流程:发送 Repeat code。
失能
IR_INT_TF_EMPTY
中断,清除中断挂起位。调用
pfnIRIntrHandlerCB
回调函数。
取消屏蔽
IR_INT_TF_EMPTY
和IR_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); }
执行
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); }
在回调函数内,定义消息类型,发送消息至 app task。主 task 判断消息类型为
IO_MSG_TYPE_IR_SEND_COMPLETE
,执行ir_send_msg_proc
,执行NEC_SendCode
。定义 IR 发送数据协议类型和数据内容。
发送 1 帧 IR 编码协议。
发送连续 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(); ... }