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:

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:

  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

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:

  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\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:

  1. In board_init, execute board_ir_led_init. This function configures the PAD/PINMUX settings for the GPIO (LED) related pins, including the following steps:

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

    2. Configure PINMUX: Assign pin to DWGPIO function.

  2. In board_init, execute board_ir_learn_init. This function configures the PAD/PINMUX settings for the IR related pins, including the following steps:

    1. Configure PAD: Set pin, PINMUX mode, PowerOn, internal pull-up, and output disable.

    2. Configure PINMUX: Assign pin to IRDA_RX function.

  3. After executing os_sched_start() to start task scheduling, execute driver_init within the app_main_task main task to initialize the peripheral driver configuration.

  4. In driver_init, execute driver_ir_led_init. This function initializes the GPIO peripheral, including the following steps:

    1. Enable RCC clock.

    2. Set GPIO pin to output mode.

    3. 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
    }
    
  5. In driver_init, execute ir_learn_timer_init to create a software timer. The timer duration is 20 seconds, and upon expiration, it executes the callback function ir_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!!!
    }
    
  6. In driver_init, execute ir_learn_module_init, which includes the following processes:

    1. Start the software timer.

    2. Register the IR interrupt handling callback function to send a message to the app task within the IR interrupt handler.

    3. Execute ir_learn_init to initialize the IR peripheral:

      1. Initialize the loop queue.

      2. Enable the RCC clock.

      3. 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.

      4. Enable the IR peripheral reception function and clear the IR receiving FIFO.

      5. 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 interrupt 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);
      }
      

Functional Implementation

  1. 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), the IR_INT_RX_CNT_THR interrupt is triggered, 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, the IR needs to receive data from the FIFO, enqueue the data into IR_RX_Queue, and clear the interrupt flag.

    3. 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 into IR_RX_Queue, and clear the interrupt flag.

    4. Execute pFn_IR_RX_Handler_CB, define the message sub-type as IO_MSG_TYPE_IR_LEARN_STOP or IO_MSG_TYPE_IR_LEARN_DATA, and send it to the app task.

    5. Unmask the IR_INT_RF_LEVEL and IR_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);
    }
    
  2. After the main task detects a message, it processes the message within ir_learn_handle_msg.

    1. 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.

    2. 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.

    3. Exit IR learning.

    4. 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;
    }