IR Learn
This example uses the IR receiving function to achieve IR learning.
Use another EVB to download the IR sending program, which acts as the IR data sender to send IR data to this EVB, and receive data within the IR interrupt.
In the interrupt function, send a message event to app_task. When app_task detects the message event, it parses the message at the app layer, executes the user program, and decodes the IR learning. In the Debug Analyzer, print the decoded IR learning data information.
Connect P0_2 to an LED light. When IR data is received, P0_2 toggles, and the LED light turns from off to on.
Requirements
The sample supports the following development kits:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
For more requirements, please refer to Quick Start.
Wiring
Download the IR TX program using another EVB, connect the IR receiving pin P2_5 and the transmitting pin of the other EVB. Connect P0_2 to the LED light.
Building and Downloading
This sample can be found in the SDK folder:
Project file: board\evb\io_sample\IR\Learn\mdk
Project file: board\evb\io_sample\IR\Learn\gcc
Please follow these steps to build and run the example:
Open sample project file.
To build the target, follow the steps listed on the Generating App Image in Quick Start.
After a successful compilation, the app bin
app_MP_xxx.bin
will be generated in the directorymdk\bin
orgcc\bin
.To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.
Press reset button on EVB board and it will start running.
Experimental Verification
Print the decoded data information learned by IR on the Debug Analyzer.
[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
...
Code Overview
This chapter will be introduced according to the following several parts:
Peripheral initialization will be introduced in chapter Initialization.
Functional implementation after initialization will be introduced in chapter Function Implementation.
Source Code Directory
Project directory:
sdk\board\evb\io_sample\IR\Learn
Source code directory:
sdk\src\sample\io_sample\IR\Learn
Source files are currently categorized into several groups as below.
└── 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
Initialization
When the EVB reset is initiated, the main()
function is called, and the following process will be executed:
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;
}
Note
le_gap_init()
, gap_lib_init()
, app_le_gap_init
, and app_le_profile_init
are related to the initialization of the privacy management module. Refer to the initialization process description in LE Peripheral Privacy.
The specific initialization process related to peripherals is as follows:
In
board_init
, executeboard_ir_led_init
. This function configures the PAD/PINMUX settings for the GPIO (LED) related pins, including the following steps:Configure PAD: Set pin, PINMUX mode, PowerOn, internal pull-none, and output high.
Configure PINMUX: Assign pin to DWGPIO function.
In
board_init
, executeboard_ir_learn_init
. This function configures the PAD/PINMUX settings for the IR related pins, including the following steps:Configure PAD: Set pin, PINMUX mode, PowerOn, internal pull-up, and output disable.
Configure PINMUX: Assign pin to IRDA_RX function.
After executing
os_sched_start()
to start task scheduling, executedriver_init
within theapp_main_task
main task to initialize the peripheral driver configuration.In
driver_init
, executedriver_ir_led_init
. This function initializes the GPIO peripheral, including the following steps:Enable RCC clock.
Set GPIO pin to output mode.
Set pin to output low level.
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 }
In
driver_init
, executeir_learn_timer_init
to create a software timer. The timer duration is 20 seconds, and upon expiration, it executes the callback functionir_learn_timer_callback
, exiting the IR learning mode upon timeout.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!!! }
In
driver_init
, executeir_learn_module_init
, which includes the following processes:Start the software timer.
Register the IR interrupt handling callback function to send a message to the app task within the IR interrupt handler.
Execute
ir_learn_init
to initialize the IR peripheral:Initialize the loop queue.
Enable the RCC clock.
Set the IR receiving frequency to 40MHz, duty cycle to 1/2, automatic reception, triggering on the falling edge, receiving FIFO threshold to 20, and counter threshold to 240000, etc.
Enable the IR peripheral reception function and clear the IR receiving FIFO.
Enable the IR receiving FIFO data count to reach the set receiving threshold interrupt
IR_INT_RF_LEVEL
and the receiving counter count to reach the set threshold interruptIR_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); }
Functional Implementation
When the number of data in the IR receiving FIFO reaches the set receiving threshold (set to 20 in this example), the
IR_INT_RF_LEVEL
interrupt is triggered. Alternatively, when the number of the receiving counter reaches the set threshold (set to 240000 in this example), theIR_INT_RX_CNT_THR
interrupt is triggered, entering the interrupt handler functionIR_Handler
.Mask the
IR_INT_TF_LEVEL
andIR_INT_RX_CNT_THR
interrupts.If the triggered interrupt is
IR_INT_TF_LEVEL
, the IR needs to receive data from the FIFO, enqueue the data intoIR_RX_Queue
, and clear the interrupt flag.If the triggered interrupt is
IR_INT_RX_CNT_THR
, it indicates that the IR receiving data stop condition is triggered. Enqueue the remaining data intoIR_RX_Queue
, and clear the interrupt flag.Execute
pFn_IR_RX_Handler_CB
, define the message sub-type asIO_MSG_TYPE_IR_LEARN_STOP
orIO_MSG_TYPE_IR_LEARN_DATA
, and send it to the app task.Unmask the
IR_INT_RF_LEVEL
andIR_INT_RX_CNT_THR
interrupts.
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); }
After the main task detects a message, it processes the message within
ir_learn_handle_msg
.If the detected message subtype is
IO_MSG_TYPE_IR_LEARN_DATA
, decode the IR data. If the IR learning decode status is an error state, stop IR learning.If the detected message subtype is
IO_MSG_TYPE_IR_LEARN_STOP
, decode the last batch of data. IR learning obtains frequency, data conversion, etc., and prints the relevant data.Exit IR learning.
Re-execute
ir_learn_module_init
, reinitialize the IR peripherals.
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; }