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:

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:

  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

In the logic analyzer, observe whether the IR transmission waveform meets expectations.

  1. Transmit one frame of ir_raw_data_buf waveform.

This should be an image of the IR transmission waveform

ir_raw_data_buf waveform

  1. Continuously transmit uir_param_info waveform.

This should be an image of the IR transmission waveform

uir_param_info continuous waveform

  1. One frame of uir_param_info waveform as shown below.

This should be an image of the IR transmission waveform

uir_param_info one frame waveform

  1. Repeat Code diagram as shown below.

This should be an image of the IR transmission waveform

Repeat Code diagram

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

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

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

    2. Configure PINMUX: assign the pin to the DWGPIO function.

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

  3. In driver_init, execute Driver_All_LED_Init. This function initializes the GPIO peripherals, including the following steps:

    1. Enable the RCC clock.

    2. Set the GPIO pin to output mode.

    3. 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);
    }
    
  4. In the main task, execute IR_Send_Init to initialize the IR and TIM peripherals in UIR_TransInit.

    1. Configure PAD/PINMUX: Configure IR transmission pin, PINMUX mode, PowerOn, internal pull-none, output high, assign pin to IRDA_TX function.

    2. Reset IR transmission data.

    3. Execute Driver_IR_Init to initialize the IR peripheral:

      1. Enable RCC clock.

      2. Set IR transmission frequency to 38kHz.

      3. Set IR carrier duty cycle to 1/2.

      4. Set IR to transmission mode.

      5. 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);
      }
      
    4. Execute Driver_IR_TIM_Init to initialize the TIM peripheral, including the following steps:

    1. Enable the RCC clock.

    2. Set the TIM clock to 4 prescalers, i.e., the clock is 10MHz.

    3. 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);
    }
    
  5. Register the callback function for the IR interrupt handler.

Functional Implementation

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

    1. Define the IR transmission waveform protocol, data content, and length.

    2. Execute UIR_OneFrameTransmitting to start sending the IR encoding protocol.

    3. In UIR_SendIRWaveform, enable the IR transmission FIFO data count less than the set transmission threshold interrupt IR_INT_TF_LEVEL.

    4. 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);
        }
    }
    
  2. 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, the IR_INT_TF_EMPTY interrupt is triggered, entering the interrupt handler IR_Handler.

    1. Mask the IR_INT_TF_LEVEL and IR_INT_TF_EMPTY interrupts.

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

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

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

      3. Otherwise, if there is no remaining data, disable the IR_INT_TF_LEVEL interrupt, indicating that all IR data has been sent.

      4. Unmask the IR_INT_TF_LEVEL interrupt.

    3. If the triggered interrupt is determined to be IR_INT_TF_EMPTY, execute the following process:

      1. Send the Repeat code.

      2. Disable the IR_INT_TF_EMPTY interrupt and clear the interrupt pending bit.

      3. Call the pfnIRIntrHandlerCB callback function.

    4. Unmask the IR_INT_TF_EMPTY and IR_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);
    }
    
  3. 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, execute UIR_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);
    }
    
  4. 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 executes ir_send_msg_proc, then executes NEC_SendCode.

    1. Define the IR sending data protocol type and data content.

    2. Send 1 frame of IR encoded protocol.

    3. 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();
    
        ...
    }