/*
 * THE FOLLOWING FIRMWARE IS PROVIDED: (1) "AS IS" WITH NO WARRANTY; AND
 * (2)TO ENABLE ACCESS TO CODING INFORMATION TO GUIDE AND FACILITATE CUSTOMER.
 * CONSEQUENTLY, CMOSTEK SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR
 * CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
 * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION
 * CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
 *
 * Copyright (C) CMOSTEK SZ.
 */

/*!
 * @file    cmt2300a.c
 * @brief   CMT2300A transceiver RF chip driver
 *
 * @version 1.3
 * @date    Jul 17 2017
 * @author  CMOSTEK R@D
 */

#include "cmt2300a.h"

/*! ********************************************************
 * @name    CMT2300A_SoftReset
 * @desc    Soft reset.
 * *********************************************************/
void CMT2300A_SoftReset(void)
{
    CMT2300A_WriteReg(0x7F, 0xFF);
}

/*! ********************************************************
 * @name    CMT2300A_GetChipStatus
 * @desc    Get the chip status.
 * @return
 *          CMT2300A_STA_PUP
 *          CMT2300A_STA_SLEEP
 *          CMT2300A_STA_STBY
 *          CMT2300A_STA_RFS
 *          CMT2300A_STA_TFS
 *          CMT2300A_STA_RX
 *          CMT2300A_STA_TX
 *          CMT2300A_STA_EEPROM
 *          CMT2300A_STA_ERROR
 *          CMT2300A_STA_CAL
 * *********************************************************/
uint8_t CMT2300A_GetChipStatus(void)
{
    return CMT2300A_ReadReg(CMT2300A_CUS_MODE_STA) & CMT2300A_MASK_CHIP_MODE_STA;
}

/*! ********************************************************
 * @name    CMT2300A_AutoSwitchStatus
 * @desc    Auto switch the chip status, and 10 ms as timeout.
 * @param   nGoCmd: the chip next status
 * @return  TRUE or FALSE
 * *********************************************************/
bool CMT2300A_AutoSwitchStatus(uint8_t nGoCmd)
{
#ifdef ENABLE_AUTO_SWITCH_CHIP_STATUS
    uint32_t nBegTick = CMT2300A_GetTickCount();
    uint8_t nWaitStatus = 0;

    switch (nGoCmd) {
    case CMT2300A_GO_SLEEP:
        nWaitStatus = CMT2300A_STA_SLEEP;
        break;
    case CMT2300A_GO_STBY:
        nWaitStatus = CMT2300A_STA_STBY;
        break;
    case CMT2300A_GO_TFS:
        nWaitStatus = CMT2300A_STA_TFS;
        break;
    case CMT2300A_GO_TX:
        nWaitStatus = CMT2300A_STA_TX;
        break;
    case CMT2300A_GO_RFS:
        nWaitStatus = CMT2300A_STA_RFS;
        break;
    case CMT2300A_GO_RX:
        nWaitStatus = CMT2300A_STA_RX;
        break;
    }

    CMT2300A_WriteReg(CMT2300A_CUS_MODE_CTL, nGoCmd);

    while (CMT2300A_GetTickCount() - nBegTick < 10) {
        CMT2300A_DelayUs(100);

        if (nWaitStatus == CMT2300A_GetChipStatus())
            return true;

        if (CMT2300A_GO_TX == nGoCmd) {
            CMT2300A_DelayUs(100);

            if (CMT2300A_MASK_TX_DONE_FLG & CMT2300A_ReadReg(CMT2300A_CUS_INT_CLR1))
                return true;
        }

        if (CMT2300A_GO_RX == nGoCmd) {
            CMT2300A_DelayUs(100);

            if (CMT2300A_MASK_PKT_OK_FLG & CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG))
                return true;
        }
    }

    return false;

#else
    CMT2300A_WriteReg(CMT2300A_CUS_MODE_CTL, nGoCmd);
    return true;
#endif
}

/*! ********************************************************
 * @name    CMT2300A_GoSleep
 * @desc    Entry SLEEP mode.
 * @return  TRUE or FALSE
 * *********************************************************/
bool CMT2300A_GoSleep(void)
{
    return CMT2300A_AutoSwitchStatus(CMT2300A_GO_SLEEP);
}

/*! ********************************************************
 * @name    CMT2300A_GoStby
 * @desc    Entry Sleep mode.
 * @return  TRUE or FALSE
 * *********************************************************/
bool CMT2300A_GoStby(void)
{
    return CMT2300A_AutoSwitchStatus(CMT2300A_GO_STBY);
}

/*! ********************************************************
 * @name    CMT2300A_GoTFS
 * @desc    Entry TFS mode.
 * @return  TRUE or FALSE
 * *********************************************************/
bool CMT2300A_GoTFS(void)
{
    return CMT2300A_AutoSwitchStatus(CMT2300A_GO_TFS);
}

/*! ********************************************************
 * @name    CMT2300A_GoRFS
 * @desc    Entry RFS mode.
 * @return  TRUE or FALSE
 * *********************************************************/
bool CMT2300A_GoRFS(void)
{
    return CMT2300A_AutoSwitchStatus(CMT2300A_GO_RFS);
}

/*! ********************************************************
 * @name    CMT2300A_GoTx
 * @desc    Entry Tx mode.
 * @return  TRUE or FALSE
 * *********************************************************/
bool CMT2300A_GoTx(void)
{
    return CMT2300A_AutoSwitchStatus(CMT2300A_GO_TX);
}

/*! ********************************************************
 * @name    CMT2300A_GoRx
 * @desc    Entry Rx mode.
 * @return  TRUE or FALSE
 * *********************************************************/
bool CMT2300A_GoRx(void)
{
    return CMT2300A_AutoSwitchStatus(CMT2300A_GO_RX);
}

/*! ********************************************************
 * @name    CMT2300A_ConfigGpio
 * @desc    Config GPIO pins mode.
 * @param   nGpioSel: GPIO1_SEL | GPIO2_SEL | GPIO3_SEL | GPIO4_SEL
 *          GPIO1_SEL:
 *            CMT2300A_GPIO1_SEL_DOUT/DIN
 *            CMT2300A_GPIO1_SEL_INT1
 *            CMT2300A_GPIO1_SEL_INT2
 *            CMT2300A_GPIO1_SEL_DCLK
 *
 *          GPIO2_SEL:
 *            CMT2300A_GPIO2_SEL_INT1
 *            CMT2300A_GPIO2_SEL_INT2
 *            CMT2300A_GPIO2_SEL_DOUT/DIN
 *            CMT2300A_GPIO2_SEL_DCLK
 *
 *          GPIO3_SEL:
 *            CMT2300A_GPIO3_SEL_CLKO
 *            CMT2300A_GPIO3_SEL_DOUT/DIN
 *            CMT2300A_GPIO3_SEL_INT2
 *            CMT2300A_GPIO3_SEL_DCLK
 *
 *          GPIO4_SEL:
 *            CMT2300A_GPIO4_SEL_RSTIN
 *            CMT2300A_GPIO4_SEL_INT1
 *            CMT2300A_GPIO4_SEL_DOUT
 *            CMT2300A_GPIO4_SEL_DCLK
 * *********************************************************/
void CMT2300A_ConfigGpio(uint8_t nGpioSel)
{
    CMT2300A_WriteReg(CMT2300A_CUS_IO_SEL, nGpioSel);
}

/*! ********************************************************
 * @name    CMT2300A_ConfigInterrupt
 * @desc    Config interrupt on INT1 and INT2.
 * @param   nInt1Sel, nInt2Sel
 *            CMT2300A_INT_SEL_RX_ACTIVE
 *            CMT2300A_INT_SEL_TX_ACTIVE
 *            CMT2300A_INT_SEL_RSSI_VLD
 *            CMT2300A_INT_SEL_PREAM_OK
 *            CMT2300A_INT_SEL_SYNC_OK
 *            CMT2300A_INT_SEL_NODE_OK
 *            CMT2300A_INT_SEL_CRC_OK
 *            CMT2300A_INT_SEL_PKT_OK
 *            CMT2300A_INT_SEL_SL_TMO
 *            CMT2300A_INT_SEL_RX_TMO
 *            CMT2300A_INT_SEL_TX_DONE
 *            CMT2300A_INT_SEL_RX_FIFO_NMTY
 *            CMT2300A_INT_SEL_RX_FIFO_TH
 *            CMT2300A_INT_SEL_RX_FIFO_FULL
 *            CMT2300A_INT_SEL_RX_FIFO_WBYTE
 *            CMT2300A_INT_SEL_RX_FIFO_OVF
 *            CMT2300A_INT_SEL_TX_FIFO_NMTY
 *            CMT2300A_INT_SEL_TX_FIFO_TH
 *            CMT2300A_INT_SEL_TX_FIFO_FULL
 *            CMT2300A_INT_SEL_STATE_IS_STBY
 *            CMT2300A_INT_SEL_STATE_IS_FS
 *            CMT2300A_INT_SEL_STATE_IS_RX
 *            CMT2300A_INT_SEL_STATE_IS_TX
 *            CMT2300A_INT_SEL_LED
 *            CMT2300A_INT_SEL_TRX_ACTIVE
 *            CMT2300A_INT_SEL_PKT_DONE
 * *********************************************************/
void CMT2300A_ConfigInterrupt(uint8_t nInt1Sel, uint8_t nInt2Sel)
{
    nInt1Sel &= CMT2300A_MASK_INT1_SEL;
    nInt1Sel |= (~CMT2300A_MASK_INT1_SEL) & CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL);
    CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, nInt1Sel);

    nInt2Sel &= CMT2300A_MASK_INT2_SEL;
    nInt2Sel |= (~CMT2300A_MASK_INT2_SEL) & CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL);
    CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, nInt2Sel);
}

/*! ********************************************************
 * @name    CMT2300A_SetInterruptPolar
 * @desc    Set the polarity of the interrupt.
 * @param   bEnable(TRUE): active-high (default)
 *          bEnable(FALSE): active-low
 * *********************************************************/
void CMT2300A_SetInterruptPolar(bool bActiveHigh)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL);

    if (bActiveHigh)
        tmp &= ~CMT2300A_MASK_INT_POLAR;
    else
        tmp |= CMT2300A_MASK_INT_POLAR;

    CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_SetFifoThreshold
 * @desc    Set FIFO threshold.
 * @param   nFifoThreshold
 * *********************************************************/
void CMT2300A_SetFifoThreshold(uint8_t nFifoThreshold)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_PKT29);

    tmp &= ~CMT2300A_MASK_FIFO_TH;
    tmp |= nFifoThreshold & CMT2300A_MASK_FIFO_TH;

    CMT2300A_WriteReg(CMT2300A_CUS_PKT29, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableAntennaSwitch
 * @desc    Enable antenna switch, output TX_ACTIVE/RX_ACTIVE
 *          via GPIO1/GPIO2.
 * @param   nMode
 *            0: RF_SWT1_EN=1, RF_SWT2_EN=0
 *               GPIO1: RX_ACTIVE, GPIO2: TX_ACTIVE
 *            1: RF_SWT1_EN=0, RF_SWT2_EN=1
 *               GPIO1: RX_ACTIVE, GPIO2: ~RX_ACTIVE
 * *********************************************************/
void CMT2300A_EnableAntennaSwitch(uint8_t nMode)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL);

    if (0 == nMode) {
        tmp |= CMT2300A_MASK_RF_SWT1_EN;
        tmp &= ~CMT2300A_MASK_RF_SWT2_EN;
    } else if (1 == nMode) {
        tmp &= ~CMT2300A_MASK_RF_SWT1_EN;
        tmp |= CMT2300A_MASK_RF_SWT2_EN;
    }

    CMT2300A_WriteReg(CMT2300A_CUS_INT1_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableInterrupt
 * @desc    Enable interrupt.
 * @param   nEnable
 *            CMT2300A_MASK_SL_TMO_EN   |
 *            CMT2300A_MASK_RX_TMO_EN   |
 *            CMT2300A_MASK_TX_DONE_EN  |
 *            CMT2300A_MASK_PREAM_OK_EN |
 *            CMT2300A_MASK_SYNC_OK_EN  |
 *            CMT2300A_MASK_NODE_OK_EN  |
 *            CMT2300A_MASK_CRC_OK_EN   |
 *            CMT2300A_MASK_PKT_DONE_EN
 * *********************************************************/
void CMT2300A_EnableInterrupt(uint8_t nEnable)
{
    CMT2300A_WriteReg(CMT2300A_CUS_INT_EN, nEnable);
}

/*! ********************************************************
 * @name    CMT2300A_EnableRxFifoAutoClear
 * @desc    Auto clear Rx FIFO before entry Rx mode.
 * @param   bEnable(TRUE): Enable it(default)
 *          bEnable(FALSE): Disable it
 * *********************************************************/
void CMT2300A_EnableRxFifoAutoClear(bool bEnable)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL);

    if (bEnable)
        tmp &= ~CMT2300A_MASK_FIFO_AUTO_CLR_DIS;
    else
        tmp |= CMT2300A_MASK_FIFO_AUTO_CLR_DIS;

    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableFifoMerge
 * @desc    Enable FIFO merge.
 * @param   bEnable(TRUE): use a single 64-byte FIFO for either Tx or Rx
 *          bEnable(FALSE): use a 32-byte FIFO for Tx and another 32-byte FIFO for Rx(default)
 * *********************************************************/
void CMT2300A_EnableFifoMerge(bool bEnable)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL);

    if (bEnable)
        tmp |= CMT2300A_MASK_FIFO_MERGE_EN;
    else
        tmp &= ~CMT2300A_MASK_FIFO_MERGE_EN;

    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableReadFifo
 * @desc    Enable SPI to read the FIFO.
 * *********************************************************/
void CMT2300A_EnableReadFifo(void)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL);
    tmp &= ~CMT2300A_MASK_SPI_FIFO_RD_WR_SEL;
    tmp &= ~CMT2300A_MASK_FIFO_RX_TX_SEL;
    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableWriteFifo
 * @desc    Enable SPI to write the FIFO.
 * *********************************************************/
void CMT2300A_EnableWriteFifo(void)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL);
    tmp |= CMT2300A_MASK_SPI_FIFO_RD_WR_SEL;
    tmp |= CMT2300A_MASK_FIFO_RX_TX_SEL;
    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_RestoreFifo
 * @desc    Restore the FIFO.
 * *********************************************************/
void CMT2300A_RestoreFifo(void)
{
    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_RESTORE);
}

/*! ********************************************************
 * @name    CMT2300A_ClearFifo
 * @desc    Clear the Tx FIFO.
 * @return  FIFO flags
 *            CMT2300A_MASK_RX_FIFO_FULL_FLG |
 *            CMT2300A_MASK_RX_FIFO_NMTY_FLG |
 *            CMT2300A_MASK_RX_FIFO_TH_FLG   |
 *            CMT2300A_MASK_RX_FIFO_OVF_FLG  |
 *            CMT2300A_MASK_TX_FIFO_FULL_FLG |
 *            CMT2300A_MASK_TX_FIFO_NMTY_FLG |
 *            CMT2300A_MASK_TX_FIFO_TH_FLG
 * *********************************************************/
uint8_t CMT2300A_ClearTxFifo(void)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_FLAG);
    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_CLR_TX);
    return tmp;
}

/*! ********************************************************
 * @name    CMT2300A_ClearFifo
 * @desc    Clear the Rx FIFO.
 * @return  FIFO flags
 *            CMT2300A_MASK_RX_FIFO_FULL_FLG |
 *            CMT2300A_MASK_RX_FIFO_NMTY_FLG |
 *            CMT2300A_MASK_RX_FIFO_TH_FLG   |
 *            CMT2300A_MASK_RX_FIFO_OVF_FLG  |
 *            CMT2300A_MASK_TX_FIFO_FULL_FLG |
 *            CMT2300A_MASK_TX_FIFO_NMTY_FLG |
 *            CMT2300A_MASK_TX_FIFO_TH_FLG
 * *********************************************************/
uint8_t CMT2300A_ClearRxFifo(void)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_FLAG);
    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CLR, CMT2300A_MASK_FIFO_CLR_RX);
    return tmp;
}

/*! ********************************************************
 * @name    CMT2300A_ClearInterruptFlags
 * @desc    Clear all interrupt flags.
 * @return  Some interrupt flags
 *            CMT2300A_MASK_SL_TMO_EN    |
 *            CMT2300A_MASK_RX_TMO_EN    |
 *            CMT2300A_MASK_TX_DONE_EN   |
 *            CMT2300A_MASK_PREAM_OK_FLG |
 *            CMT2300A_MASK_SYNC_OK_FLG  |
 *            CMT2300A_MASK_NODE_OK_FLG  |
 *            CMT2300A_MASK_CRC_OK_FLG   |
 *            CMT2300A_MASK_PKT_OK_FLG
 * *********************************************************/
uint8_t CMT2300A_ClearInterruptFlags(void)
{
    uint8_t nFlag1, nFlag2;
    uint8_t nClr1 = 0;
    uint8_t nClr2 = 0;
    uint8_t nRet = 0;
    uint8_t nIntPolar;

    nIntPolar = CMT2300A_ReadReg(CMT2300A_CUS_INT1_CTL);
    nIntPolar = (nIntPolar & CMT2300A_MASK_INT_POLAR) ? 1 : 0;

    nFlag1 = CMT2300A_ReadReg(CMT2300A_CUS_INT_FLAG);
    nFlag2 = CMT2300A_ReadReg(CMT2300A_CUS_INT_CLR1);

    if (nIntPolar) {
        /* Interrupt flag active-low */
        nFlag1 = ~nFlag1;
        nFlag2 = ~nFlag2;
    }

    if (CMT2300A_MASK_LBD_FLG & nFlag1) {
        nClr2 |= CMT2300A_MASK_LBD_CLR; /* Clear LBD_FLG */
    }

    if (CMT2300A_MASK_COL_ERR_FLG & nFlag1) {
        nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear COL_ERR_FLG by PKT_DONE_CLR */
    }

    if (CMT2300A_MASK_PKT_ERR_FLG & nFlag1) {
        nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear PKT_ERR_FLG by PKT_DONE_CLR */
    }

    if (CMT2300A_MASK_PREAM_OK_FLG & nFlag1) {
        nClr2 |= CMT2300A_MASK_PREAM_OK_CLR; /* Clear PREAM_OK_FLG */
        nRet |= CMT2300A_MASK_PREAM_OK_FLG; /* Return PREAM_OK_FLG */
    }

    if (CMT2300A_MASK_SYNC_OK_FLG & nFlag1) {
        nClr2 |= CMT2300A_MASK_SYNC_OK_CLR; /* Clear SYNC_OK_FLG */
        nRet |= CMT2300A_MASK_SYNC_OK_FLG; /* Return SYNC_OK_FLG */
    }

    if (CMT2300A_MASK_NODE_OK_FLG & nFlag1) {
        nClr2 |= CMT2300A_MASK_NODE_OK_CLR; /* Clear NODE_OK_FLG */
        nRet |= CMT2300A_MASK_NODE_OK_FLG; /* Return NODE_OK_FLG */
    }

    if (CMT2300A_MASK_CRC_OK_FLG & nFlag1) {
        nClr2 |= CMT2300A_MASK_CRC_OK_CLR; /* Clear CRC_OK_FLG */
        nRet |= CMT2300A_MASK_CRC_OK_FLG; /* Return CRC_OK_FLG */
    }

    if (CMT2300A_MASK_PKT_OK_FLG & nFlag1) {
        nClr2 |= CMT2300A_MASK_PKT_DONE_CLR; /* Clear PKT_OK_FLG */
        nRet |= CMT2300A_MASK_PKT_OK_FLG; /* Return PKT_OK_FLG */
    }

    if (CMT2300A_MASK_SL_TMO_FLG & nFlag2) {
        nClr1 |= CMT2300A_MASK_SL_TMO_CLR; /* Clear SL_TMO_FLG */
        nRet |= CMT2300A_MASK_SL_TMO_EN; /* Return SL_TMO_FLG by SL_TMO_EN */
    }

    if (CMT2300A_MASK_RX_TMO_FLG & nFlag2) {
        nClr1 |= CMT2300A_MASK_RX_TMO_CLR; /* Clear RX_TMO_FLG */
        nRet |= CMT2300A_MASK_RX_TMO_EN; /* Return RX_TMO_FLG by RX_TMO_EN */
    }

    if (CMT2300A_MASK_TX_DONE_FLG & nFlag2) {
        nClr1 |= CMT2300A_MASK_TX_DONE_CLR; /* Clear TX_DONE_FLG */
        nRet |= CMT2300A_MASK_TX_DONE_EN; /* Return TX_DONE_FLG by TX_DONE_EN */
    }

    CMT2300A_WriteReg(CMT2300A_CUS_INT_CLR1, nClr1);
    CMT2300A_WriteReg(CMT2300A_CUS_INT_CLR2, nClr2);

    if (nIntPolar) {
        /* Interrupt flag active-low */
        nRet = ~nRet;
    }

    return nRet;
}

/*! ********************************************************
 * @name    CMT2300A_ConfigTxDin
 * @desc    Used to select whether to use GPIO1 or GPIO2 or GPIO3
 *          as DIN in the direct mode. It only takes effect when
 *          call CMT2300A_EnableTxDin(TRUE) in the direct mode.
 * @param   nDinSel
 *            CMT2300A_TX_DIN_SEL_GPIO1
 *            CMT2300A_TX_DIN_SEL_GPIO2
 *            CMT2300A_TX_DIN_SEL_GPIO3
 * *********************************************************/
void CMT2300A_ConfigTxDin(uint8_t nDinSel)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL);
    tmp &= ~CMT2300A_MASK_TX_DIN_SEL;
    tmp |= nDinSel;
    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableTxDin
 * @desc    Used to change GPIO1/GPIO2/GPIO3 between DOUT and DIN.
 * @param   bEnable(TRUE): used as DIN
 *          bEnable(FALSE): used as DOUT(default)
 * *********************************************************/
void CMT2300A_EnableTxDin(bool bEnable)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FIFO_CTL);

    if (bEnable)
        tmp |= CMT2300A_MASK_TX_DIN_EN;
    else
        tmp &= ~CMT2300A_MASK_TX_DIN_EN;

    CMT2300A_WriteReg(CMT2300A_CUS_FIFO_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableTxDinInvert
 * @desc    Used to invert DIN data in direct mode.
 * @param   bEnable(TRUE): invert DIN
 *          bEnable(FALSE): not invert DIN(default)
 * *********************************************************/
void CMT2300A_EnableTxDinInvert(bool bEnable)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL);

    if (bEnable)
        tmp |= CMT2300A_MASK_TX_DIN_INV;
    else
        tmp &= ~CMT2300A_MASK_TX_DIN_INV;

    CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_IsExist
 * @desc    Chip indentify.
 * @return  TRUE: chip is exist, FALSE: chip not found
 * *********************************************************/
bool CMT2300A_IsExist(void)
{
    uint8_t back, dat;

    back = CMT2300A_ReadReg(CMT2300A_CUS_PKT17);
    CMT2300A_WriteReg(CMT2300A_CUS_PKT17, 0xAA);

    dat = CMT2300A_ReadReg(CMT2300A_CUS_PKT17);
    CMT2300A_WriteReg(CMT2300A_CUS_PKT17, back);

    if (0xAA == dat)
        return true;
    else
        return false;
}

/*! ********************************************************
 * @name    CMT2300A_GetRssiCode
 * @desc    Get RSSI code.
 * @return  RSSI code
 * *********************************************************/
uint8_t CMT2300A_GetRssiCode(void)
{
    return CMT2300A_ReadReg(CMT2300A_CUS_RSSI_CODE);
}

/*! ********************************************************
 * @name    CMT2300A_GetRssiDBm
 * @desc    Get RSSI dBm.
 * @return  dBm
 * *********************************************************/
int CMT2300A_GetRssiDBm(void)
{
    return (int)CMT2300A_ReadReg(CMT2300A_CUS_RSSI_DBM) - 128;
}

/*! ********************************************************
 * @name    CMT2300A_SetFrequencyChannel
 * @desc    This defines up to 255 frequency channel
 *          for fast frequency hopping operation.
 * @param   nChann: the frequency channel
 * *********************************************************/
void CMT2300A_SetFrequencyChannel(uint8_t nChann)
{
    CMT2300A_WriteReg(CMT2300A_CUS_FREQ_CHNL, nChann);
}

/*! ********************************************************
 * @name    CMT2300A_SetFrequencyStep
 * @desc    This defines the frequency channel step size
 *          for fast frequency hopping operation.
 *          One step size is 2.5 kHz.
 * @param   nOffset: the frequency step
 * *********************************************************/
void CMT2300A_SetFrequencyStep(uint8_t nOffset)
{
    CMT2300A_WriteReg(CMT2300A_CUS_FREQ_OFS, nOffset);
}

/*! ********************************************************
 * @name    CMT2300A_SetPayloadLength
 * @desc    Set payload length.
 * @param   nLength
 * *********************************************************/
void CMT2300A_SetPayloadLength(uint16_t nLength)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_PKT14);

    tmp &= ~CMT2300A_MASK_PAYLOAD_LENG_10_8;
    tmp |= (nLength >> 4) & CMT2300A_MASK_PAYLOAD_LENG_10_8;
    CMT2300A_WriteReg(CMT2300A_CUS_PKT14, tmp);

    tmp = nLength & CMT2300A_MASK_PAYLOAD_LENG_7_0;
    CMT2300A_WriteReg(CMT2300A_CUS_PKT15, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableLfosc
 * @desc    If you need use sleep timer, you should enable LFOSC.
 * @param   bEnable(TRUE): Enable it(default)
 *          bEnable(FALSE): Disable it
 * *********************************************************/
void CMT2300A_EnableLfosc(bool bEnable)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_SYS2);

    if (bEnable) {
        tmp |= CMT2300A_MASK_LFOSC_RECAL_EN;
        tmp |= CMT2300A_MASK_LFOSC_CAL1_EN;
        tmp |= CMT2300A_MASK_LFOSC_CAL2_EN;
    } else {
        tmp &= ~CMT2300A_MASK_LFOSC_RECAL_EN;
        tmp &= ~CMT2300A_MASK_LFOSC_CAL1_EN;
        tmp &= ~CMT2300A_MASK_LFOSC_CAL2_EN;
    }

    CMT2300A_WriteReg(CMT2300A_CUS_SYS2, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableLfoscOutput
 * @desc    LFOSC clock is output via GPIO3.
 * @param   bEnable(TRUE): Enable it
 *          bEnable(FALSE): Disable it(default)
 * *********************************************************/
void CMT2300A_EnableLfoscOutput(bool bEnable)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_INT2_CTL);

    if (bEnable)
        tmp |= CMT2300A_MASK_LFOSC_OUT_EN;
    else
        tmp &= ~CMT2300A_MASK_LFOSC_OUT_EN;

    CMT2300A_WriteReg(CMT2300A_CUS_INT2_CTL, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_EnableAfc
 * @desc    AFC enable or disanble.
 * @param   bEnable(TRUE): Enable it
 *          bEnable(FALSE): Disable it(default)
 * *********************************************************/
void CMT2300A_EnableAfc(bool bEnable)
{
    uint8_t tmp = CMT2300A_ReadReg(CMT2300A_CUS_FSK5);

    if (bEnable)
        tmp |= 0x10;
    else
        tmp &= ~0x10;

    CMT2300A_WriteReg(CMT2300A_CUS_FSK5, tmp);
}

/*! ********************************************************
 * @name    CMT2300A_SetAfcOvfTh
 * @desc    This is optional, only needed when using Rx fast frequency hopping.
 * @param   afcOvfTh: AFC_OVF_TH see AN142 and AN197 for details.
 * *********************************************************/
void CMT2300A_SetAfcOvfTh(uint8_t afcOvfTh)
{
    CMT2300A_WriteReg(CMT2300A_CUS_FSK4, afcOvfTh);
}

/*! ********************************************************
 * @name    CMT2300A_Init
 * @desc    Initialize chip status.
 * *********************************************************/
bool CMT2300A_Init(void)
{
    uint8_t tmp;

    CMT2300A_SoftReset();
    CMT2300A_DelayMs(20);

    if (!CMT2300A_GoStby())
        return false; // CMT2300A not switched to standby mode!

    if (!CMT2300A_IsExist())
        return false; // CMT2300A not found!

    tmp = CMT2300A_ReadReg(CMT2300A_CUS_MODE_STA);
    tmp |= CMT2300A_MASK_CFG_RETAIN; /* Enable CFG_RETAIN */
    tmp &= ~CMT2300A_MASK_RSTN_IN_EN; /* Disable RSTN_IN */
    CMT2300A_WriteReg(CMT2300A_CUS_MODE_STA, tmp);

    tmp = CMT2300A_ReadReg(CMT2300A_CUS_EN_CTL);
    tmp |= CMT2300A_MASK_LOCKING_EN; /* Enable LOCKING_EN */
    CMT2300A_WriteReg(CMT2300A_CUS_EN_CTL, tmp);

    CMT2300A_EnableLfosc(false); /* Disable LFOSC */

    CMT2300A_ClearInterruptFlags();

    return true;
}

/*! ********************************************************
 * @name    CMT2300A_ConfigRegBank
 * @desc    Config one register bank.
 * *********************************************************/
bool CMT2300A_ConfigRegBank(uint8_t base_addr, const uint8_t bank[], uint8_t len)
{
    uint8_t i;
    for (i = 0; i < len; i++)
        CMT2300A_WriteReg(i + base_addr, bank[i]);

    return true;
}
