/**
  ******************************************************************************
  * @file    bl702_ir.c
  * @version V1.0
  * @date
  * @brief   This file is the standard driver c file
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2020 Bouffalo Lab</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of Bouffalo Lab nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

#include "bl702_ir.h"
#include "bl702_glb.h"

/** @addtogroup  BL702_Peripheral_Driver
 *  @{
 */

/** @addtogroup  IR
 *  @{
 */

/** @defgroup  IR_Private_Macros
 *  @{
 */
#define NEC_HEAD_H_MIN          17000
#define NEC_HEAD_H_MAX          19000
#define NEC_HEAD_L_MIN          8400
#define NEC_HEAD_L_MAX          9600
#define NEC_BIT0_H_MIN          525
#define NEC_BIT0_H_MAX          1725
#define RC5_ONE_PLUSE_MIN       1175
#define RC5_ONE_PLUSE_MAX       2375
#define RC5_TWO_PLUSE_MIN       2955
#define RC5_TWO_PLUSE_MAX       4155
#define IR_TX_INT_TIMEOUT_COUNT (100 * 160 * 1000)
#define IR_RX_INT_TIMEOUT_COUNT (100 * 160 * 1000)

/*@} end of group IR_Private_Macros */

/** @defgroup  IR_Private_Types
 *  @{
 */

/*@} end of group IR_Private_Types */

/** @defgroup  IR_Private_Variables
 *  @{
 */
static intCallback_Type *irIntCbfArra[IR_INT_ALL] = { NULL, NULL };

/*@} end of group IR_Private_Variables */

/** @defgroup  IR_Global_Variables
 *  @{
 */

/*@} end of group IR_Global_Variables */

/** @defgroup  IR_Private_Fun_Declaration
 *  @{
 */

/*@} end of group IR_Private_Fun_Declaration */

/** @defgroup  IR_Private_Functions
 *  @{
 */

/*@} end of group IR_Private_Functions */

/** @defgroup  IR_Public_Functions
 *  @{
 */

/****************************************************************************/ /**
 * @brief  IR RX IRQ handler function
 *
 * @param  None
 *
 * @return None
 *
*******************************************************************************/
#ifndef BFLB_USE_HAL_DRIVER
void IRRX_IRQHandler(void)
{
    uint32_t tmpVal;

    tmpVal = BL_RD_REG(IR_BASE, IRRX_INT_STS);

    if (BL_IS_REG_BIT_SET(tmpVal, IRRX_END_INT) && !BL_IS_REG_BIT_SET(tmpVal, IR_CR_IRRX_END_MASK)) {
        BL_WR_REG(IR_BASE, IRRX_INT_STS, BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_END_CLR));

        if (irIntCbfArra[IR_INT_RX] != NULL) {
            irIntCbfArra[IR_INT_RX]();
        }
    }
}
#endif

/****************************************************************************/ /**
 * @brief  IR TX IRQ handler function
 *
 * @param  None
 *
 * @return None
 *
*******************************************************************************/
#ifndef BFLB_USE_HAL_DRIVER
void IRTX_IRQHandler(void)
{
    uint32_t tmpVal;

    tmpVal = BL_RD_REG(IR_BASE, IRTX_INT_STS);

    if (BL_IS_REG_BIT_SET(tmpVal, IRTX_END_INT) && !BL_IS_REG_BIT_SET(tmpVal, IR_CR_IRTX_END_MASK)) {
        BL_WR_REG(IR_BASE, IRTX_INT_STS, BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_END_CLR));

        if (irIntCbfArra[IR_INT_TX] != NULL) {
            irIntCbfArra[IR_INT_TX]();
        }
    }
}
#endif

/****************************************************************************/ /**
 * @brief  IR tx initialization function
 *
 * @param  irTxCfg: IR tx configuration structure pointer
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_TxInit(IR_TxCfg_Type *irTxCfg)
{
    uint32_t tmpVal;

    /* Disable clock gate */
    GLB_AHB_Slave1_Clock_Gate(DISABLE, BL_AHB_SLAVE1_IRR);

    tmpVal = BL_RD_REG(IR_BASE, IRTX_CONFIG);
    /* Set data bit */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_DATA_NUM, irTxCfg->dataBits - 1);
    /* Set tail pulse */
    ENABLE == irTxCfg->tailPulseInverse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_TAIL_HL_INV)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_TAIL_HL_INV));
    ENABLE == irTxCfg->tailPulse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_TAIL_EN)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_TAIL_EN));
    /* Set head pulse */
    ENABLE == irTxCfg->headPulseInverse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_HEAD_HL_INV)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_HEAD_HL_INV));
    ENABLE == irTxCfg->headPulse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_HEAD_EN)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_HEAD_EN));
    /* Enable or disable logic 1 and 0 pulse inverse */
    ENABLE == irTxCfg->logic1PulseInverse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_LOGIC1_HL_INV)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_LOGIC1_HL_INV));
    ENABLE == irTxCfg->logic0PulseInverse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_LOGIC0_HL_INV)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_LOGIC0_HL_INV));
    /* Enable or disable data pulse */
    ENABLE == irTxCfg->dataPulse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_DATA_EN)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_DATA_EN));
    /* Enable or disable output modulation */
    ENABLE == irTxCfg->outputModulation ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_MOD_EN)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_MOD_EN));
    /* Enable or disable output inverse */
    ENABLE == irTxCfg->outputInverse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_OUT_INV)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_OUT_INV));

    /* Write back */
    BL_WR_REG(IR_BASE, IRTX_CONFIG, tmpVal);

#ifndef BFLB_USE_HAL_DRIVER
    Interrupt_Handler_Register(IRTX_IRQn, IRTX_IRQHandler);
#endif
    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR tx pulse width configure function
 *
 * @param  irTxPulseWidthCfg: IR tx pulse width configuration structure pointer
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_TxPulseWidthConfig(IR_TxPulseWidthCfg_Type *irTxPulseWidthCfg)
{
    uint32_t tmpVal;

    tmpVal = BL_RD_REG(IR_BASE, IRTX_PW);
    /* Set logic 0 pulse phase 0 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_LOGIC0_PH0_W, irTxPulseWidthCfg->logic0PulseWidth_0 - 1);
    /* Set logic 0 pulse phase 1 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_LOGIC0_PH1_W, irTxPulseWidthCfg->logic0PulseWidth_1 - 1);
    /* Set logic 1 pulse phase 0 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_LOGIC1_PH0_W, irTxPulseWidthCfg->logic1PulseWidth_0 - 1);
    /* Set logic 1 pulse phase 1 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_LOGIC1_PH1_W, irTxPulseWidthCfg->logic1PulseWidth_1 - 1);
    /* Set head pulse phase 0 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_HEAD_PH0_W, irTxPulseWidthCfg->headPulseWidth_0 - 1);
    /* Set head pulse phase 1 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_HEAD_PH1_W, irTxPulseWidthCfg->headPulseWidth_1 - 1);
    /* Set tail pulse phase 0 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_TAIL_PH0_W, irTxPulseWidthCfg->tailPulseWidth_0 - 1);
    /* Set tail pulse phase 1 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_TAIL_PH1_W, irTxPulseWidthCfg->tailPulseWidth_1 - 1);
    BL_WR_REG(IR_BASE, IRTX_PW, tmpVal);

    tmpVal = BL_RD_REG(IR_BASE, IRTX_PULSE_WIDTH);
    /* Set modulation phase 0 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_MOD_PH0_W, irTxPulseWidthCfg->moduWidth_0 - 1);
    /* Set modulation phase 1 width */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_MOD_PH1_W, irTxPulseWidthCfg->moduWidth_1 - 1);
    /* Set pulse width unit */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_PW_UNIT, irTxPulseWidthCfg->pulseWidthUnit - 1);
    BL_WR_REG(IR_BASE, IRTX_PULSE_WIDTH, tmpVal);

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR tx software mode pulse width(multiples of pulse width unit) configure function
 *
 * @param  irTxSWMPulseWidthCfg: IR tx software mode pulse width configuration structure pointer
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_TxSWMPulseWidthConfig(IR_TxSWMPulseWidthCfg_Type *irTxSWMPulseWidthCfg)
{
    /* Set swm pulse width,multiples of pulse width unit */
    BL_WR_REG(IR_BASE, IRTX_SWM_PW_0, irTxSWMPulseWidthCfg->swmData0);
    BL_WR_REG(IR_BASE, IRTX_SWM_PW_1, irTxSWMPulseWidthCfg->swmData1);
    BL_WR_REG(IR_BASE, IRTX_SWM_PW_2, irTxSWMPulseWidthCfg->swmData2);
    BL_WR_REG(IR_BASE, IRTX_SWM_PW_3, irTxSWMPulseWidthCfg->swmData3);
    BL_WR_REG(IR_BASE, IRTX_SWM_PW_4, irTxSWMPulseWidthCfg->swmData4);
    BL_WR_REG(IR_BASE, IRTX_SWM_PW_5, irTxSWMPulseWidthCfg->swmData5);
    BL_WR_REG(IR_BASE, IRTX_SWM_PW_6, irTxSWMPulseWidthCfg->swmData6);
    BL_WR_REG(IR_BASE, IRTX_SWM_PW_7, irTxSWMPulseWidthCfg->swmData7);

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR rx initialization function
 *
 * @param  irRxCfg: IR rx configuration structure pointer
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_RxInit(IR_RxCfg_Type *irRxCfg)
{
    uint32_t tmpVal;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_RXMODE_TYPE(irRxCfg->rxMode));

    /* Disable clock gate */
    GLB_AHB_Slave1_Clock_Gate(DISABLE, BL_AHB_SLAVE1_IRR);

    tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);

    /* Set rx mode */
    switch (irRxCfg->rxMode) {
        case IR_RX_NEC:
            tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_MODE, 0x0);
            break;

        case IR_RX_RC5:
            tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_MODE, 0x1);
            break;

        case IR_RX_SWM:
            tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_MODE, 0x2);
            break;

        default:
            break;
    }

    /* Enable or disable input inverse */
    ENABLE == irRxCfg->inputInverse ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_IN_INV)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRRX_IN_INV));
    /* Enable or disable rx input de-glitch function */
    ENABLE == irRxCfg->rxDeglitch ? (tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_DEG_EN)) : (tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRRX_DEG_EN));
    /* Set de-glitch function cycle count */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_DEG_CNT, irRxCfg->DeglitchCnt);
    /* Write back */
    BL_WR_REG(IR_BASE, IRRX_CONFIG, tmpVal);

    tmpVal = BL_RD_REG(IR_BASE, IRRX_PW_CONFIG);
    /* Set pulse width threshold to trigger end condition */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_END_TH, irRxCfg->endThreshold - 1);
    /* Set pulse width threshold for logic0/1 detection */
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_DATA_TH, irRxCfg->dataThreshold - 1);
    /* Write back */
    BL_WR_REG(IR_BASE, IRRX_PW_CONFIG, tmpVal);

#ifndef BFLB_USE_HAL_DRIVER
    Interrupt_Handler_Register(IRRX_IRQn, IRRX_IRQHandler);
#endif

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR set default value of all registers function
 *
 * @param  None
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_DeInit(void)
{
    GLB_AHB_Slave1_Reset(BL_AHB_SLAVE1_IRR);

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR enable function
 *
 * @param  direct: IR direction type
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_Enable(IR_Direction_Type direct)
{
    uint32_t tmpVal;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_DIRECTION_TYPE(direct));

    if (direct == IR_TX || direct == IR_TXRX) {
        /* Enable ir tx unit */
        tmpVal = BL_RD_REG(IR_BASE, IRTX_CONFIG);
        BL_WR_REG(IR_BASE, IRTX_CONFIG, BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_EN));
    }

    if (direct == IR_RX || direct == IR_TXRX) {
        /* Enable ir rx unit */
        tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);
        BL_WR_REG(IR_BASE, IRRX_CONFIG, BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_EN));
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR disable function
 *
 * @param  direct: IR direction type
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_Disable(IR_Direction_Type direct)
{
    uint32_t tmpVal;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_DIRECTION_TYPE(direct));

    if (direct == IR_TX || direct == IR_TXRX) {
        /* Disable ir tx unit */
        tmpVal = BL_RD_REG(IR_BASE, IRTX_CONFIG);
        BL_WR_REG(IR_BASE, IRTX_CONFIG, BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_EN));
    }

    if (direct == IR_RX || direct == IR_TXRX) {
        /* Disable ir rx unit */
        tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);
        BL_WR_REG(IR_BASE, IRRX_CONFIG, BL_CLR_REG_BIT(tmpVal, IR_CR_IRRX_EN));
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR tx software mode enable or disable function
 *
 * @param  txSWM: Enable or disable
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_TxSWM(BL_Fun_Type txSWM)
{
    uint32_t tmpVal;

    /* Enable or disable tx swm */
    tmpVal = BL_RD_REG(IR_BASE, IRTX_CONFIG);

    if (ENABLE == txSWM) {
        BL_WR_REG(IR_BASE, IRTX_CONFIG, BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_SWM_EN));
    } else {
        BL_WR_REG(IR_BASE, IRTX_CONFIG, BL_CLR_REG_BIT(tmpVal, IR_CR_IRTX_SWM_EN));
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR clear rx fifo function
 *
 * @param  None
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_RxFIFOClear(void)
{
    uint32_t tmpVal;

    /* Clear rx fifo */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_SWM_FIFO_CONFIG_0);
    BL_WR_REG(IR_BASE, IRRX_SWM_FIFO_CONFIG_0, BL_SET_REG_BIT(tmpVal, IR_RX_FIFO_CLR));

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR send data function
 *
 * @param  irWord: IR tx data word 0 or 1
 * @param  data: data to send
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_SendData(IR_Word_Type irWord, uint32_t data)
{
    /* Check the parameters */
    CHECK_PARAM(IS_IR_WORD_TYPE(irWord));

    /* Write word 0 or word 1 */
    if (IR_WORD_0 == irWord) {
        BL_WR_REG(IR_BASE, IRTX_DATA_WORD0, data);
    } else {
        BL_WR_REG(IR_BASE, IRTX_DATA_WORD1, data);
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR software mode send pulse width data function
 *
 * @param  data: data to send
 * @param  length: Length of send buffer
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_SWMSendData(uint16_t *data, uint8_t length)
{
    uint8_t i = 0, j = 0;
    uint16_t minData = data[0];
    uint32_t tmpVal;
    uint32_t pwVal = 0;
    uint32_t count = (length + 7) / 8;

    /* Search for min value */
    for (i = 1; i < length; i++) {
        if (minData > data[i] && data[i] != 0) {
            minData = data[i];
        }
    }

    /* Set pulse width unit */
    tmpVal = BL_RD_REG(IR_BASE, IRTX_PULSE_WIDTH);
    BL_WR_REG(IR_BASE, IRTX_PULSE_WIDTH, BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_PW_UNIT, minData));

    /* Set tx SWM pulse width data as multiples of pulse width unit */
    for (i = 0; i < count; i++) {
        pwVal = 0;

        if (i < count - 1) {
            for (j = 0; j < 8; j++) {
                tmpVal = ((2 * data[j + i * 8] + minData) / (2 * minData) - 1) & 0xf;
                pwVal |= tmpVal << (4 * j);
            }

            *(volatile uint32_t *)(IR_BASE + IRTX_SWM_PW_0_OFFSET + i * 4) = pwVal;
        } else {
            for (j = 0; j < length % 8; j++) {
                tmpVal = ((2 * data[j + i * 8] + minData) / (2 * minData) - 1) & 0xf;
                pwVal |= tmpVal << (4 * j);
            }

            *(volatile uint32_t *)(IR_BASE + IRTX_SWM_PW_0_OFFSET + i * 4) = pwVal;
        }
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR send command function
 *
 * @param  word1: IR send data word 1
 * @param  word0: IR send data word 0
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_SendCommand(uint32_t word1, uint32_t word0)
{
    uint32_t timeoutCnt = IR_TX_INT_TIMEOUT_COUNT;

    /* Write data */
    IR_SendData(IR_WORD_1, word1);
    IR_SendData(IR_WORD_0, word0);

    /* Mask tx interrupt */
    IR_IntMask(IR_INT_TX, MASK);

    /* Clear tx interrupt */
    IR_ClrIntStatus(IR_INT_TX);

    /* Enable ir tx */
    IR_Enable(IR_TX);

    /* Wait for tx interrupt */
    while (SET != IR_GetIntStatus(IR_INT_TX)) {
        timeoutCnt--;

        if (timeoutCnt == 0) {
            IR_Disable(IR_TX);

            return TIMEOUT;
        }
    }

    /* Disable ir tx */
    IR_Disable(IR_TX);

    /* Clear tx interrupt */
    IR_ClrIntStatus(IR_INT_TX);

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR send command in software mode function
 *
 * @param  data: IR fifo data to send
 * @param  length: Length of data
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_SWMSendCommand(uint16_t *data, uint8_t length)
{
    uint32_t timeoutCnt = IR_TX_INT_TIMEOUT_COUNT;

    /* Write fifo */
    IR_SWMSendData(data, length);

    /* Mask tx interrupt */
    IR_IntMask(IR_INT_TX, MASK);

    /* Clear tx interrupt */
    IR_ClrIntStatus(IR_INT_TX);

    /* Enable ir tx */
    IR_Enable(IR_TX);

    /* Wait for tx interrupt */
    while (SET != IR_GetIntStatus(IR_INT_TX)) {
        timeoutCnt--;

        if (timeoutCnt == 0) {
            IR_Disable(IR_TX);

            return TIMEOUT;
        }
    }

    /* Disable ir tx */
    IR_Disable(IR_TX);

    /* Clear tx interrupt */
    IR_ClrIntStatus(IR_INT_TX);

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR send in NEC protocol
 *
 * @param  address: Address
 * @param  command: Command
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_SendNEC(uint8_t address, uint8_t command)
{
    uint32_t tmpVal = ((~command & 0xff) << 24) + (command << 16) + ((~address & 0xff) << 8) + address;

    IR_SendCommand(0, tmpVal);

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR interrupt mask or unmask function
 *
 * @param  intType: IR interrupt type
 * @param  intMask: Mask or unmask
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_IntMask(IR_INT_Type intType, BL_Mask_Type intMask)
{
    uint32_t tmpVal;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_INT_TYPE(intType));

    if (intType == IR_INT_TX || intType == IR_INT_ALL) {
        /* Mask or unmask tx interrupt */
        tmpVal = BL_RD_REG(IR_BASE, IRTX_INT_STS);
        BL_WR_REG(IR_BASE, IRTX_INT_STS, BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_END_MASK, intMask));
    }

    if (intType == IR_INT_RX || intType == IR_INT_ALL) {
        /* Mask or unmask rx interrupt */
        tmpVal = BL_RD_REG(IR_BASE, IRRX_INT_STS);
        BL_WR_REG(IR_BASE, IRRX_INT_STS, BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_END_MASK, intMask));
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  Clear ir interrupt function
 *
 * @param  intType: IR interrupt type
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_ClrIntStatus(IR_INT_Type intType)
{
    uint32_t tmpVal;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_INT_TYPE(intType));

    if (intType == IR_INT_TX || intType == IR_INT_ALL) {
        /* Clear tx interrupt */
        tmpVal = BL_RD_REG(IR_BASE, IRTX_INT_STS);
        BL_WR_REG(IR_BASE, IRTX_INT_STS, BL_SET_REG_BIT(tmpVal, IR_CR_IRTX_END_CLR));
    }

    if (intType == IR_INT_RX || intType == IR_INT_ALL) {
        /* Clear rx interrupt */
        tmpVal = BL_RD_REG(IR_BASE, IRRX_INT_STS);
        BL_WR_REG(IR_BASE, IRRX_INT_STS, BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_END_CLR));
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR install interrupt callback function
 *
 * @param  intType: IR interrupt type
 * @param  cbFun: Pointer to interrupt callback function. The type should be void (*fn)(void)
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_Int_Callback_Install(IR_INT_Type intType, intCallback_Type *cbFun)
{
    /* Check the parameters */
    CHECK_PARAM(IS_IR_INT_TYPE(intType));

    irIntCbfArra[intType] = cbFun;

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR get interrupt status function
 *
 * @param  intType: IR int type
 *
 * @return IR tx or rx interrupt status
 *
*******************************************************************************/
BL_Sts_Type IR_GetIntStatus(IR_INT_Type intType)
{
    uint32_t tmpVal = 0;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_INT_TYPE(intType));

    /* Read tx or rx interrupt status */
    if (IR_INT_TX == intType) {
        tmpVal = BL_RD_REG(IR_BASE, IRTX_INT_STS);
        tmpVal = BL_GET_REG_BITS_VAL(tmpVal, IRTX_END_INT);
    } else if (IR_INT_RX == intType) {
        tmpVal = BL_RD_REG(IR_BASE, IRRX_INT_STS);
        tmpVal = BL_GET_REG_BITS_VAL(tmpVal, IRRX_END_INT);
    }

    if (tmpVal) {
        return SET;
    } else {
        return RESET;
    }
}

/****************************************************************************/ /**
 * @brief  IR get rx fifo underflow or overflow status function
 *
 * @param  fifoSts: IR fifo status type
 *
 * @return IR rx fifo status
 *
*******************************************************************************/
BL_Sts_Type IR_GetRxFIFOStatus(IR_FifoStatus_Type fifoSts)
{
    uint32_t tmpVal;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_FIFOSTATUS_TYPE(fifoSts));

    /* Read rx fifo status */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_SWM_FIFO_CONFIG_0);

    if (fifoSts == IR_RX_FIFO_UNDERFLOW) {
        tmpVal = BL_GET_REG_BITS_VAL(tmpVal, IR_RX_FIFO_UNDERFLOW);
    } else {
        tmpVal = BL_GET_REG_BITS_VAL(tmpVal, IR_RX_FIFO_OVERFLOW);
    }

    if (tmpVal) {
        return SET;
    } else {
        return RESET;
    }
}

/****************************************************************************/ /**
 * @brief  IR receive data function
 *
 * @param  irWord: IR rx data word 0 or 1
 *
 * @return Data received
 *
*******************************************************************************/
uint32_t IR_ReceiveData(IR_Word_Type irWord)
{
    uint32_t tmpVal;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_WORD_TYPE(irWord));

    /* Read word 0 or word 1 */
    if (IR_WORD_0 == irWord) {
        tmpVal = BL_RD_REG(IR_BASE, IRRX_DATA_WORD0);
    } else {
        tmpVal = BL_RD_REG(IR_BASE, IRRX_DATA_WORD1);
    }

    return tmpVal;
}

/****************************************************************************/ /**
 * @brief  IR software mode receive pulse width data function
 *
 * @param  data: Data received
 * @param  length: Max length of receive buffer
 *
 * @return Length of datas received
 *
*******************************************************************************/
uint8_t IR_SWMReceiveData(uint16_t *data, uint8_t length)
{
    uint8_t rxLen = 0;

    while (rxLen < length && IR_GetRxFIFOCount() > 0) {
        /* Read data */
        data[rxLen++] = BL_RD_REG(IR_BASE, IRRX_SWM_FIFO_RDATA) & 0xffff;
    }

    return rxLen;
}

/****************************************************************************/ /**
 * @brief  IR receive in NEC protocol
 *
 * @param  address: Address
 * @param  command: Command
 *
 * @return SUCCESS or ERROR
 *
*******************************************************************************/
BL_Err_Type IR_ReceiveNEC(uint8_t *address, uint8_t *command)
{
    uint32_t tmpVal = IR_ReceiveData(IR_WORD_0);

    *address = tmpVal & 0xff;
    *command = (tmpVal >> 16) & 0xff;

    if ((~(*address) & 0xff) != ((tmpVal >> 8) & 0xff) || (~(*command) & 0xff) != ((tmpVal >> 24) & 0xff)) {
        return ERROR;
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR get rx data bit count function
 *
 * @param  None
 *
 * @return IR rx data bit count
 *
*******************************************************************************/
uint8_t IR_GetRxDataBitCount(void)
{
    uint32_t tmpVal;

    /* Read rx data bit count */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_DATA_COUNT);
    tmpVal = BL_GET_REG_BITS_VAL(tmpVal, IR_STS_IRRX_DATA_CNT);

    return tmpVal;
}

/****************************************************************************/ /**
 * @brief  IR get rx fifo count function
 *
 * @param  None
 *
 * @return IR rx fifo available count
 *
*******************************************************************************/
uint8_t IR_GetRxFIFOCount(void)
{
    uint32_t tmpVal;

    /* Read rx fifo count */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_SWM_FIFO_CONFIG_0);
    tmpVal = BL_GET_REG_BITS_VAL(tmpVal, IR_RX_FIFO_CNT);

    return tmpVal;
}

/****************************************************************************/ /**
 * @brief  IR learning to set rx and tx mode function
 *
 * @param  data: Buffer to save data
 * @param  length: Length of data
 *
 * @return Protocol type
 *
*******************************************************************************/
IR_RxMode_Type IR_LearnToInit(uint32_t *data, uint8_t *length)
{
    uint32_t tmpVal;
    uint32_t timeoutCnt = IR_RX_INT_TIMEOUT_COUNT;

    /* Disable clock gate */
    GLB_AHB_Slave1_Clock_Gate(DISABLE, BL_AHB_SLAVE1_IRR);

    /* Disable rx,set rx in software mode and enable rx input inverse */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);
    tmpVal = BL_CLR_REG_BIT(tmpVal, IR_CR_IRRX_EN);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_MODE, 0x2);
    tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_IN_INV);
    BL_WR_REG(IR_BASE, IRRX_CONFIG, tmpVal);
    /* Set pulse width threshold to trigger end condition */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_PW_CONFIG);
    BL_WR_REG(IR_BASE, IRRX_PW_CONFIG, BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_END_TH, 19999));

    /* Clear and mask rx interrupt */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_INT_STS);
    tmpVal = BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_END_MASK);
    BL_WR_REG(IR_BASE, IRRX_INT_STS, BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_END_CLR));

    /* Enable rx */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);
    BL_WR_REG(IR_BASE, IRRX_CONFIG, BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_EN));

    /* Wait for rx interrupt */
    while (SET != IR_GetIntStatus(IR_INT_RX)) {
        timeoutCnt--;

        if (timeoutCnt == 0) {
            IR_Disable(IR_RX);

            return IR_RX_SWM;
        }
    }

    /* Disable rx */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);
    BL_WR_REG(IR_BASE, IRRX_CONFIG, BL_CLR_REG_BIT(tmpVal, IR_CR_IRRX_EN));

    /* Clear rx interrupt */
    tmpVal = BL_RD_REG(IR_BASE, IRRX_INT_STS);
    BL_WR_REG(IR_BASE, IRRX_INT_STS, BL_SET_REG_BIT(tmpVal, IR_CR_IRRX_END_CLR));

    /*Receive data */
    *length = IR_GetRxFIFOCount();
    *length = IR_SWMReceiveData((uint16_t *)data, *length);

    /* Judge protocol type */
    if (NEC_HEAD_H_MIN < (data[0] & 0xffff) && (data[0] & 0xffff) < NEC_HEAD_H_MAX && NEC_HEAD_L_MIN < (data[0] >> 16) && (data[0] >> 16) < NEC_HEAD_L_MAX && NEC_BIT0_H_MIN < (data[1] & 0xffff) && (data[1] & 0xffff) < NEC_BIT0_H_MAX) {
        /* Set rx in NEC mode */
        tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);
        BL_WR_REG(IR_BASE, IRRX_CONFIG, BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_MODE, 0x0));
        /* Set pulse width threshold to trigger end condition and pulse width threshold for logic0/1 detection */
        BL_WR_REG(IR_BASE, IRRX_PW_CONFIG, 0x23270d47);
        /* Set tx in NEC mode */
        /* Tx configure */
        BL_WR_REG(IR_BASE, IRTX_CONFIG, 0x1f514);
        /* Set logic 0,logic 1,head and tail pulse width */
        BL_WR_REG(IR_BASE, IRTX_PW, 0x7f2000);
        /* Set modulation phase width and pulse width unit */
        BL_WR_REG(IR_BASE, IRTX_PULSE_WIDTH, 0x22110464);

        return IR_RX_NEC;
    } else if (RC5_ONE_PLUSE_MIN < (data[0] & 0xffff) && (data[0] & 0xffff) < RC5_ONE_PLUSE_MAX && ((RC5_ONE_PLUSE_MIN < (data[0] >> 16) && (data[0] >> 16) < RC5_ONE_PLUSE_MAX) || (RC5_TWO_PLUSE_MIN < (data[0] >> 16) && (data[0] >> 16) < RC5_TWO_PLUSE_MAX)) &&
               ((RC5_ONE_PLUSE_MIN < (data[1] & 0xffff) && (data[1] & 0xffff) < RC5_ONE_PLUSE_MAX) || (RC5_TWO_PLUSE_MIN < (data[1] & 0xffff) && (data[1] & 0xffff) < RC5_TWO_PLUSE_MAX))) {
        /* Set rx in RC-5 mode */
        tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);
        BL_WR_REG(IR_BASE, IRRX_CONFIG, BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_MODE, 0x1));
        /* Set pulse width threshold to trigger end condition and pulse width threshold for logic0/1 detection */
        BL_WR_REG(IR_BASE, IRRX_PW_CONFIG, 0x13870a6a);
        /* Set tx in RC-5 mode */
        /* Tx configure */
        BL_WR_REG(IR_BASE, IRTX_CONFIG, 0xc134);
        /* Set logic 0,logic 1,head and tail pulse width */
        BL_WR_REG(IR_BASE, IRTX_PW, 0);
        /* Set modulation phase width and pulse width unit */
        BL_WR_REG(IR_BASE, IRTX_PULSE_WIDTH, 0x221106f1);

        return IR_RX_RC5;
    } else if ((data[0] >> 16) != 0) {
        /* Set tx in software mode */
        /* Tx configure */
        BL_WR_REG(IR_BASE, IRTX_CONFIG, *length << 12 | 0xc);
        /* Set modulation phase width */
        BL_WR_REG(IR_BASE, IRTX_PULSE_WIDTH, 0x22110000);

        return IR_RX_SWM;
    } else {
        tmpVal = BL_RD_REG(IR_BASE, IRRX_CONFIG);
        tmpVal = BL_GET_REG_BITS_VAL(tmpVal, IR_CR_IRRX_MODE);

        if (tmpVal == 0) {
            return IR_RX_NEC;
        } else if (tmpVal == 1) {
            return IR_RX_RC5;
        } else {
            return IR_RX_SWM;
        }
    }
}

/****************************************************************************/ /**
 * @brief  IR receive data according to mode which is learned function
 *
 * @param  mode: Protocol type
 * @param  data: Buffer to save data
 *
 * @return Length of data
 *
*******************************************************************************/
uint8_t IR_LearnToReceive(IR_RxMode_Type mode, uint32_t *data)
{
    uint8_t length = 0;
    uint32_t timeoutCnt = IR_RX_INT_TIMEOUT_COUNT;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_RXMODE_TYPE(mode));

    /* Disable ir rx */
    IR_Disable(IR_RX);

    /* Clear and mask rx interrupt */
    IR_ClrIntStatus(IR_INT_RX);
    IR_IntMask(IR_INT_RX, MASK);

    /* Enable ir rx */
    IR_Enable(IR_RX);

    /* Wait for rx interrupt */
    while (SET != IR_GetIntStatus(IR_INT_RX)) {
        timeoutCnt--;

        if (timeoutCnt == 0) {
            IR_Disable(IR_RX);

            return TIMEOUT;
        }
    }

    /* Disable ir rx */
    IR_Disable(IR_RX);

    /* Clear rx interrupt */
    IR_ClrIntStatus(IR_INT_RX);

    /* Receive data according to mode */
    if (mode == IR_RX_NEC || mode == IR_RX_RC5) {
        /* Get data bit count */
        length = IR_GetRxDataBitCount();
        data[0] = IR_ReceiveData(IR_WORD_0);
    } else {
        /* Get fifo count */
        length = IR_GetRxFIFOCount();
        length = IR_SWMReceiveData((uint16_t *)data, length);
    }

    return length;
}

/****************************************************************************/ /**
 * @brief  IR send data according to mode which is learned function
 *
 * @param  mode: Protocol type
 * @param  data: Buffer of data to send
 * @param  length: Length of data
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_LearnToSend(IR_RxMode_Type mode, uint32_t *data, uint8_t length)
{
    uint32_t tmpVal;

    /* Check the parameters */
    CHECK_PARAM(IS_IR_RXMODE_TYPE(mode));

    /* Set send length */
    tmpVal = BL_RD_REG(IR_BASE, IRTX_CONFIG);
    tmpVal = BL_SET_REG_BITS_VAL(tmpVal, IR_CR_IRTX_DATA_NUM, length - 1);
    BL_WR_REG(IR_BASE, IRTX_CONFIG, tmpVal);

    if (mode == IR_RX_NEC || mode == IR_RX_RC5) {
        IR_SendCommand(0, data[0]);
    } else {
        IR_SWMSendCommand((uint16_t *)data, length);
    }

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR init to control led function
 *
 * @param  clk: Clock source
 * @param  div: Clock division(1~64)
 * @param  unit: Pulse width unit(multiples of clock pulse width, 1~4096)
 * @param  code0H: code 0 high level time(multiples of pulse width unit, 1~16)
 * @param  code0L: code 0 low level time(multiples of pulse width unit, 1~16)
 * @param  code1H: code 1 high level time(multiples of pulse width unit, 1~16)
 * @param  code1L: code 1 low level time(multiples of pulse width unit, 1~16)
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_LEDInit(HBN_XCLK_CLK_Type clk, uint8_t div, uint8_t unit, uint8_t code0H, uint8_t code0L, uint8_t code1H, uint8_t code1L)
{
    IR_TxCfg_Type txCfg = {
        24,      /* 24-bit data */
        DISABLE, /* Disable signal of tail pulse inverse */
        DISABLE, /* Disable signal of tail pulse */
        DISABLE, /* Disable signal of head pulse inverse */
        DISABLE, /* Disable signal of head pulse */
        DISABLE, /* Disable signal of logic 1 pulse inverse */
        DISABLE, /* Disable signal of logic 0 pulse inverse */
        ENABLE,  /* Enable signal of data pulse */
        DISABLE, /* Disable signal of output modulation */
        ENABLE   /* Enable signal of output inverse */
    };

    IR_TxPulseWidthCfg_Type txPWCfg = {
        code0L, /* Pulse width of logic 0 pulse phase 1 */
        code0H, /* Pulse width of logic 0 pulse phase 0 */
        code1L, /* Pulse width of logic 1 pulse phase 1 */
        code1H, /* Pulse width of logic 1 pulse phase 0 */
        1,      /* Pulse width of head pulse phase 1 */
        1,      /* Pulse width of head pulse phase 0 */
        1,      /* Pulse width of tail pulse phase 1 */
        1,      /* Pulse width of tail pulse phase 0 */
        1,      /* Modulation phase 1 width */
        1,      /* Modulation phase 0 width */
        unit    /* Pulse width unit */
    };

    HBN_Set_XCLK_CLK_Sel(clk);
    GLB_Set_IR_CLK(ENABLE, GLB_IR_CLK_SRC_XCLK, div - 1);

    /* Disable ir before config */
    IR_Disable(IR_TXRX);

    /* IR tx init */
    IR_TxInit(&txCfg);
    IR_TxPulseWidthConfig(&txPWCfg);

    return SUCCESS;
}

/****************************************************************************/ /**
 * @brief  IR send 24-bit data to control led function
 *
 * @param  data: Data to send(24-bit)
 *
 * @return SUCCESS
 *
*******************************************************************************/
BL_Err_Type IR_LEDSend(uint32_t data)
{
    /* Change MSB_first to LSB_first */
    data = ((data >> 1) & 0x55555555) | ((data << 1) & 0xaaaaaaaa);
    data = ((data >> 2) & 0x33333333) | ((data << 2) & 0xcccccccc);
    data = ((data >> 4) & 0x0f0f0f0f) | ((data << 4) & 0xf0f0f0f0);
    data = ((data >> 16) & 0xff) | (data & 0xff00) | ((data << 16) & 0xff0000);
    IR_SendCommand(0, data);

    return SUCCESS;
}

/*@} end of group IR_Public_Functions */

/*@} end of group IR */

/*@} end of group BL702_Peripheral_Driver */
