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
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 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
...
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
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_init
,app_le_profile_init
等为 privacy 管理模块相关的初始化,参考 LE Peripheral Privacy 中的初始化流程介绍。
与外设相关的初始化流程具体如下:
在
board_init
中,执行board_ir_led_init
,该函数为 GPIO(LED)相关引脚的 PAD/PINMUX 设置,包含如下流程:配置 PAD:设置引脚、PINMUX 模式、PowerOn、无内部上拉、输出高。
配置 PINMUX:分配引脚为 DWGPIO 功能。
在
board_init
中,执行board_ir_learn_init
,该函数为 IR 相关引脚的 PAD/PINMUX 设置,包含如下流程:配置 PAD:设置引脚、PINMUX 模式、PowerOn、内部上拉、输出失能。
配置 PINMUX:分配引脚为 IRDA_RX 功能。
在执行
os_sched_start()
开启任务调度后,在app_main_task
主任务内,执行driver_init
对外设驱动进行初始化配置。在
driver_init
中执行driver_ir_led_init
,该函数为 GPIO 外设的初始化,包含如下流程:使能 RCC 时钟。
设置 GPIO 引脚为输出模式。
设置引脚输出低电平。
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 }
在
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!!! }
在
driver_init
中执行ir_learn_module_init
,包含如下流程:启动软件定时器。
注册 IR 中断处理回调函数,实现 IR 中断处理函数中发送 msg 到 app task。
执行
ir_learn_init
初始化 IR 外设:初始化 loop queue。
使能 RCC 时钟。
设置 IR 接收频率为 40MHz,占空比 1/2,自动接收,下降沿触发,接收 FIFO 阈值为 20,计数器阈值为 240000 等。
使能 IR 外设接收功能,清除 IR 接收 FIFO。
使能 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); }
功能实现
当 IR 接收 FIFO 的数据个数达到设置的接收阈值(此例中设为 20)时,触发
IR_INT_RF_LEVEL
中断。或当接收计数器的个数达到设置的阈值(此例中设为 240000)时,触发IR_INT_RX_CNT_THR
中断,进入中断处理函数IR_Handler
。屏蔽
IR_INT_TF_LEVEL
和IR_INT_RX_CNT_THR
中断。如果触发的中断为
IR_INT_TF_LEVEL
,则需要 IR 从 FIFO 中接收数据,将数据入列到IR_RX_Queue
中,清除中断标志位。如果触发的中断为
IR_INT_RX_CNT_THR
,则代表触发了 IR 接收数据的停止条件。将剩余数据入列到IR_RX_Queue
中,清除中断标志位。调用
pFn_IR_RX_Handler_CB
,定义消息子类型为IO_MSG_TYPE_IR_LEARN_STOP
或IO_MSG_TYPE_IR_LEARN_DATA
,发送至 app task。取消屏蔽
IR_INT_RF_LEVEL
和IR_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); }
主 task 检测到消息后,在
ir_learn_handle_msg
内对消息进行处理。如果检测发送的消息子类型为
IO_MSG_TYPE_IR_LEARN_DATA
,对 IR 数据进行解码。如果 IR 学习解码状态为错误状态,停止 IR 学习。如果检测发送的消息子类型为
IO_MSG_TYPE_IR_LEARN_STOP
,对最后一笔数据进行解码。IR 学习获取频率,数据转换等,打印相关数据。退出 IR 学习。
重新执行
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; }