IR Receive

This example demonstrates the usage method of the infrared reception function.

Use another EVB to download the program that sends IR data as the IR data sender to send IR data to this EVB.

Receive data within the IR interrupt. If the amount of data is greater than the FIFO of the IR, it is necessary to receive data continuously multiple times.

For examples of the IR sender, please refer to IR Transmit.

Requirements

The sample supports the following development kits:

Development Kits

Hardware Platforms

Board Name

RTL8752H HDK

RTL8752H EVB

For more requirements, please refer to Quick Start.

Wiring

Use another EVB to download the IR TX program, and connect the IR receive pin P2_5 to the transmit pin of the other EVB.

Building and Downloading

This sample can be found in the SDK folder:

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

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

Please follow these steps to build and run the example:

  1. Open sample project file.

  2. To build the target, follow the steps listed on the Generating App Image in Quick Start.

  3. After a successful compilation, the app bin app_MP_xxx.bin will be generated in the directory mdk\bin or gcc\bin.

  4. To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.

  5. Press reset button on EVB board and it will start running.

Experimental Verification

When the number of IR reception counters reaches the set threshold, it indicates that the IR data reception is complete. The system then enters the IR interrupt and prints the received data. The received data is the content defined by the IR transmitter.

[io_ir]io_handle_ir_msg: IR RX data[0] = xxx
[io_ir]io_handle_ir_msg: IR RX data[1] = xxx
...

Code Overview

This chapter will be introduced according to the following several parts:

  1. Source Code Directory.

  2. Peripheral initialization will be introduced in chapter Initialization.

  3. Functional implementation after initialization will be introduced in chapter Function Implementation.

Source Code Directory

  • Project directory: sdk\board\evb\io_sample\IR\Rx

  • Source code directory: sdk\src\sample\io_sample\IR\Rx

Source files are currently categorized into several groups as below.

└── Project: ir_rx
    └── 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
            └── adc.lib
        ├── peripheral               includes all peripheral drivers and module code used by the application
            ├── rtl876x_rcc.c
            ├── rtl876x_pinmux.c
            ├── rtl876x_nvic.c
            └── rtl876x_ir.c
        ├── profile
        └── app                      includes the ble_peripheral user application implementation
            ├── main.c
            ├── ancs.c
            ├── app.c
            ├── app_task.c
            └── io_ir.c

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:

  1. In global_data_init, execute global_data_ir_init, which is a global initialization function that initializes the global variable IR_Rx_Data and resets the IR receive data.

    void global_data_adc_init(void)
    {
        /* Initialize adc k value! */
        APP_PRINT_INFO0("[io_adc] global_data_adc_init");
        bool adc_k_status = false;
        adc_k_status = ADC_CalibrationInit();
        if (false == adc_k_status)
        {
            APP_PRINT_ERROR0("[io_adc] global_data_adc_init: ADC_CalibrationInit fail!");
        }
        memset(&ADC_Recv_Buffer, 0, sizeof(ADC_Recv_Buffer));
    }
    
  2. In board_init, execute board_ir_init, which is responsible for PAD/PINMUX settings and includes the following process:

    1. Configure PAD: Set pin, PINMUX mode, PowerOn, internal pull-none, output disabled.

    2. Configure PINMUX: Assign the pin to the IRDA_RX function.

  3. After executing os_sched_start() to start task scheduling, in the app_main_task main task, execute driver_init to initialize and configure the peripheral drivers.

  4. In driver_init, execute driver_ir_init, which is the initialization function for the IR peripheral, including the following process:

    1. Enable RCC clock.

    2. Set IR receiving frequency to 38kHz.

    3. Set IR to auto-receive mode.

    4. Set IR receiving threshold to 30, discard the latest data when IR receiving FIFO is full, filter out noise data below 50ns.

    5. Set the level type for trigger receive counter threshold interrupt to high level, set IR_RxCntThr to 0x1F40.

    6. Enable IR peripheral receiving function, clear IR receiving FIFO.

    void driver_ir_init(void)
    {
        /* 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               = 38000;/* IR carrier freqency is 38KHz */
        IR_InitStruct.IR_Mode               = IR_MODE_RX;/* IR receiveing mode */
        IR_InitStruct.IR_RxStartMode        = IR_RX_AUTO_MODE;
        IR_InitStruct.IR_RxFIFOThrLevel     =
            IR_RX_FIFO_THR_LEVEL; /* Configure RX FIFO threshold level to trigger IR_INT_RF_LEVEL interrupt */
        IR_InitStruct.IR_RxFIFOFullCtrl     =
            IR_RX_FIFO_FULL_DISCARD_NEWEST;/* Discard the lastest received dta if RX FIFO is full */
        IR_InitStruct.IR_RxFilterTime       =
            IR_RX_FILTER_TIME_50ns;/* If high to low or low to high transition time <= 50ns,Filter out it. */
        IR_InitStruct.IR_RxTriggerMode      = IR_RX_FALL_EDGE;/* Configure trigger type */
        IR_InitStruct.IR_RxCntThrType       =
            IR_RX_Count_High_Level;/* IR_RX_Count_High_Level is counting high level */
        IR_InitStruct.IR_RxCntThr           =
            0x1F40;/* Configure RX counter threshold.You can use it to decide to stop receiving IR data */
        IR_Init(&IR_InitStruct);
        IR_Cmd(IR_MODE_RX, ENABLE);
        IR_ClearRxFIFO();
    
        /* 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);
    
    }
    

Functional Implementation

  1. Execute os_sched_start() to start task scheduling. When the stack is ready, execute app_handle_dev_state_evt and execute ir_demo.

    1. Configure the IR receive FIFO data count greater than the set receive threshold interrupt IR_INT_RF_LEVEL and receive level timeout interrupt IR_INT_RX_CNT_THR.

    2. Unmask the IR_INT_RF_LEVEL and IR_INT_RX_CNT_THR interrupts.

    void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause)
    {
        ...
        if (gap_dev_state.gap_init_state != new_state.gap_init_state)
        {
            if (new_state.gap_init_state == GAP_INIT_STATE_STACK_READY)
            {
                APP_PRINT_INFO0("GAP stack ready");
                /*stack ready*/
                ir_demo();
            }
        }
        ...
    }
    
    void ir_demo(void)
    {
        /* Enable IR threshold interrupt. when RX FIFO offset >= threshold value, trigger interrupt*/
        /* Enable IR counter threshold interrupt to stop receiving data */
        IR_INTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, ENABLE);
        IR_MaskINTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, DISABLE);
    }
    
  2. Send IR data to the IR transceiver module. When the number of data in the IR receive FIFO reaches the set reception threshold (set to 30 in this example), it triggers the IR_INT_RF_LEVEL interrupt. Alternatively, when the number of the receive counter reaches the set threshold (set to 0x1F40 in this example), it triggers the IR_INT_RX_CNT_THR interrupt, entering the interrupt handler function IR_Handler.

    1. Mask the IR_INT_TF_LEVEL and IR_INT_RX_CNT_THR interrupts.

    2. If the triggered interrupt is IR_INT_TF_LEVEL, IR needs to receive data from the FIFO. Store the data in DataBuf and record the length of the received data.

    3. If the triggered interrupt is IR_INT_RX_CNT_THR, it indicates that the IR data reception stop condition has been triggered. Store the remaining data in DataBuf, record the length of the received data, and print the data content.

    4. Clear the IR_INT_RX_CNT_THR interrupt pending bit.

    5. Unmask the IR_INT_RF_LEVEL and IR_INT_RX_CNT_THR interrupts.

    void IR_Handler(void)
    {
        uint16_t len = 0;
        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);
    
        /* Receive by interrupt */
        if (int_status_rfl == SET)
        {
            len = IR_GetRxDataLen();
            IR_ReceiveBuf(IR_Rx_Data.DataBuf + IR_RX_Count, len);
            IR_Rx_Data.DataLen += len;
            IR_RX_Count += len;
    
            IR_ClearINTPendingBit(IR_INT_RF_LEVEL_CLR);
        }
    
        /* Stop to receive IR data */
        if (int_status_rxcnt == SET)
        {
            /* Read remaining data */
            len = IR_GetRxDataLen();
            IR_ReceiveBuf(IR_Rx_Data.DataBuf + IR_RX_Count, len);
            IR_Rx_Data.DataLen += len;
            IR_RX_Count += len;
    
            T_IO_MSG int_ir_msg;
            int_ir_msg.type = IO_MSG_TYPE_IR;
            int_ir_msg.subtype = 0;
            int_ir_msg.u.buf = (void *)(&IR_Rx_Data);
            if (false == app_send_msg_to_apptask(&int_ir_msg))
            {
                APP_PRINT_ERROR0("[io_ir]IR_INT_RX_CNT_THR: Send int_ir_msg failed!");
                //Add user code here!
                IR_ClearINTPendingBit(IR_INT_RX_CNT_THR_CLR);
                return;
            }
            IR_ClearINTPendingBit(IR_INT_RX_CNT_THR_CLR);
        }
    
        /* Unmask IR all interrupt */
        IR_MaskINTConfig(IR_INT_RF_LEVEL | IR_INT_RX_CNT_THR, DISABLE);
    }
    
    void io_handle_ir_msg(T_IO_MSG *io_ir_msg)
    {
        IR_Data_TypeDef *p_buf = io_ir_msg->u.buf;
        for (uint16_t i = 0; i < p_buf->DataLen; i++)
        {
            APP_PRINT_INFO2("[io_ir]io_handle_ir_msg: IR RX data[%d] = 0x%x", i, p_buf->DataBuf[i]);
        }
        memset(&IR_Rx_Data, 0, sizeof(IR_Rx_Data));
        IR_RX_Count = 0;
    }