Send Code
This example demonstrates the TX function of the IR peripheral and the implementation of the IR encoding protocol library for sending IR encoding protocols.
By utilizing the TIM timing function, a defined RAW encoding protocol is periodically sent. After the transmission is completed, an IR interrupt sends a message event to the app_task. When app_task detects the message event, it parses the message at the app level, executes the user program, and sends the IR encoding protocol.
A logic analyzer is used to observe the IR transmission waveform and to check if the transmitted data meets expectations. Connect P0_1 to an LED; after the IR data is transmitted, P0_1 will toggle, causing the LED to turn 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
Connect IR transmission pin P2_5 to the logic analyzer, and connect P0_1 to the LED light.
Building and Downloading
This sample can be found in the SDK folder:
Project file: board\evb\io_sample\IR\SendCode\mdk
Project file: board\evb\io_sample\IR\SendCode\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
In the logic analyzer, observe whether the IR transmission waveform meets expectations.
Transmit one frame of
ir_raw_data_buf
waveform.

ir_raw_data_buf waveform
Continuously transmit
uir_param_info
waveform.

uir_param_info continuous waveform
One frame of
uir_param_info
waveform as shown below.

uir_param_info one frame waveform
Repeat Code diagram as shown below.

Repeat Code diagram
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\SendCode
Source code directory:
sdk\src\sample\io_sample\IR\SendCode
Source files are currently categorized into several groups as below.
└── 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
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_All_LED_Init
. This function sets the PAD/PINMUX for GPIO (LED) related pins, including the following steps:Configure PAD: set the pin, PINMUX mode, PowerOn, internal pull-none, and output high.
Configure PINMUX: assign the pin to the DWGPIO function.
After executing
os_sched_start()
to start task scheduling, executedriver_init
in the main taskapp_main_task
to initialize and configure the peripheral drivers.In
driver_init
, executeDriver_All_LED_Init
. This function initializes the GPIO peripherals, including the following steps:Enable the RCC clock.
Set the GPIO pin to output mode.
Set the pin to output low level.
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); }
In the main task, execute
IR_Send_Init
to initialize the IR and TIM peripherals inUIR_TransInit
.Configure PAD/PINMUX: Configure IR transmission pin, PINMUX mode, PowerOn, internal pull-none, output high, assign pin to IRDA_TX function.
Reset IR transmission data.
Execute
Driver_IR_Init
to initialize the IR peripheral:Enable RCC clock.
Set IR transmission frequency to 38kHz.
Set IR carrier duty cycle to 1/2.
Set IR to transmission mode.
Set the IR transmission FIFO threshold to 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); }
Execute
Driver_IR_TIM_Init
to initialize the TIM peripheral, including the following steps:
Enable the RCC clock.
Set the TIM clock to 4 prescalers, i.e., the clock is 10MHz.
Set the TIM to user-defined mode and configure the TIM interrupt.
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); }
Register the callback function for the IR interrupt handler.
Functional Implementation
Execute
os_sched_start()
to start task scheduling. When the stack is ready, executeapp_handle_dev_state_evt
and then executeIR_Raw_Packet_Send
.Define the IR transmission waveform protocol, data content, and length.
Execute
UIR_OneFrameTransmitting
to start sending the IR encoding protocol.In
UIR_SendIRWaveform
, enable the IR transmission FIFO data count less than the set transmission threshold interruptIR_INT_TF_LEVEL
.When the status is NO_ERROR, toggle 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); } }
When the number of FIFO data sent by IR is less than the set send threshold, the
IR_INT_TF_LEVEL
interrupt is triggered. Or when the IR send FIFO is empty, theIR_INT_TF_EMPTY
interrupt is triggered, entering the interrupt handlerIR_Handler
.Mask the
IR_INT_TF_LEVEL
andIR_INT_TF_EMPTY
interrupts.If the triggered interrupt is determined to be
IR_INT_TF_LEVEL
, it means IR is sending data. Since the IR data being sent may be large (greater than the IR TX FIFO depth), it needs to be sent in batches.If the remaining number of data to be sent is greater than the size of the send FIFO, fill the IR send FIFO with (IR_TX_FIFO_SIZE - IR_TX_FIFO_THR_LEVEL) pieces of data and send, which means the remaining data is still greater than the IR TX FIFO depth.
Otherwise, if the remaining number of data to be sent is greater than 0, fill the IR send FIFO with the remaining data, which means the remaining data is less than the IR TX FIFO depth, i.e., sending the last piece of data.
Otherwise, if there is no remaining data, disable the
IR_INT_TF_LEVEL
interrupt, indicating that all IR data has been sent.Unmask the
IR_INT_TF_LEVEL
interrupt.
If the triggered interrupt is determined to be
IR_INT_TF_EMPTY
, execute the following process:Send the Repeat code.
Disable the
IR_INT_TF_EMPTY
interrupt and clear the interrupt pending bit.Call the
pfnIRIntrHandlerCB
callback function.
Unmask the
IR_INT_TF_EMPTY
andIR_INT_TF_LEVEL
interrupts.
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); }
Execute
UIR_BurstSendCmd
, send Repeat Code. Within the function, configure the corresponding TIM period, enable TIM. When the timer expires, trigger an interrupt. In the interrupt function, executeUIR_SendRepeatData
, send 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); }
Within the callback function, define the message type and send the message to the app task. The main task determines that the message type is
IO_MSG_TYPE_IR_SEND_COMPLETE
and executesir_send_msg_proc
, then executesNEC_SendCode
.Define the IR sending data protocol type and data content.
Send 1 frame of IR encoded protocol.
Send continuous IR encoded protocol.
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(); ... }