/*******************************************************************************
 * Copyright (C) 2020, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by HDSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 */
/******************************************************************************/
/** \file hc32f460_can.c
 **
 ** A detailed description is available at
 ** @link CanGroup CAN description @endlink
 **
 **   - 2018-12-13  CDT First version for Device Driver Library of CAN.
 **
 ******************************************************************************/

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "hc32f460_can.h"
#include "hc32f460_utility.h"

/**
 *******************************************************************************
 ** \addtogroup CanGroup
 ******************************************************************************/
//@{

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
#define CAN_RESET_ENABLE()                  (M4_CAN->CFG_STAT_f.RESET = 1u)
#define CAN_RESET_DISABLE()                                                     \
do{                                                                             \
    do{                                                                         \
        M4_CAN->CFG_STAT_f.RESET = 0u;                                          \
}while(M4_CAN->CFG_STAT_f.RESET);                                               \
}while(0)

#define CAN_RW_MEM32(addr)                  (*(__IO uint32_t *)(addr))

#define CAN_ACF_ID_REG_SEL                  ((uint8_t)0x00u)
#define CAN_ACF_MASK_REG_SEL                ((uint8_t)0x01u)


/*! Parameter validity check for CAN Mode \a CanMode. */
#define IS_CAN_MODE_VALID(CanMode)                                             \
(       (CanExternalLoopBackMode  == (CanMode)) ||                             \
        (CanInternalLoopBackMode  == (CanMode)) ||                             \
        (CanTxSignalPrimaryMode   == (CanMode)) ||                             \
        (CanTxSignalSecondaryMode == (CanMode)) ||                             \
        (CanListenOnlyMode        == (CanMode))                                \
)

/*! Parameter validity check for CAN Tx Cmd \a TxCmd. */
#define IS_TX_CMD_VALID(TxCmd)                                                 \
(       (CanPTBTxCmd      == (TxCmd))           ||                             \
        (CanPTBTxAbortCmd == (TxCmd))           ||                             \
        (CanSTBTxOneCmd   == (TxCmd))           ||                             \
        (CanSTBTxAllCmd   == (TxCmd))           ||                             \
        (CanSTBTxAbortCmd == (TxCmd))                                          \
)

/*! Parameter validity check for CAN status \a enCanStatus. */
#define IS_CAN_STATUS_VALID(enCanStatus)                                       \
(       (CanRxActive == (enCanStatus))          ||                             \
        (CanTxActive == (enCanStatus))          ||                             \
        (CanBusoff   == (enCanStatus))                                         \
)

/*! Parameter validity check for CAN Irq type \a enCanIrqType. */
#define IS_CAN_IRQ_TYPE_VALID(enCanIrqType)                                    \
(       (CanRxIrqEn              == (enCanIrqType)) ||                         \
        (CanRxOverIrqEn          == (enCanIrqType)) ||                         \
        (CanRxBufFullIrqEn       == (enCanIrqType)) ||                         \
        (CanRxBufAlmostFullIrqEn == (enCanIrqType)) ||                         \
        (CanTxPrimaryIrqEn       == (enCanIrqType)) ||                         \
        (CanTxSecondaryIrqEn     == (enCanIrqType)) ||                         \
        (CanErrorIrqEn           == (enCanIrqType)) ||                         \
        (CanErrorPassiveIrqEn    == (enCanIrqType)) ||                         \
        (CanArbiLostIrqEn        == (enCanIrqType)) ||                         \
        (CanBusErrorIrqEn        == (enCanIrqType))                            \
)

/*! Parameter validity check for CAN Irq flag type \a enCanIrqFLg. */
#define IS_CAN_IRQ_FLAG_VALID(enCanIrqFLg)                                     \
(       (CanTxBufFullIrqFlg        == (enCanIrqFLg))    ||                     \
        (CanRxIrqFlg               == (enCanIrqFLg))    ||                     \
        (CanRxOverIrqFlg           == (enCanIrqFLg))    ||                     \
        (CanRxBufFullIrqFlg        == (enCanIrqFLg))    ||                     \
        (CanRxBufAlmostFullIrqFlg  == (enCanIrqFLg))    ||                     \
        (CanTxPrimaryIrqFlg        == (enCanIrqFLg))    ||                     \
        (CanTxSecondaryIrqFlg      == (enCanIrqFLg))    ||                     \
        (CanErrorIrqFlg            == (enCanIrqFLg))    ||                     \
        (CanAbortIrqFlg            == (enCanIrqFLg))    ||                     \
        (CanErrorWarningIrqFlg     == (enCanIrqFLg))    ||                     \
        (CanErrorPassivenodeIrqFlg == (enCanIrqFLg))    ||                     \
        (CanErrorPassiveIrqFlg     == (enCanIrqFLg))    ||                     \
        (CanArbiLostIrqFlg         == (enCanIrqFLg))    ||                     \
        (CanBusErrorIrqFlg         == (enCanIrqFLg))                           \
)

/*! Parameter validity check for CAN filter \a enCanFilter. */
#define IS_CAN_FILTER_VALID(enCanFilter)                                       \
(       (enCanFilter) <= CanFilterSel8)

/*! Parameter validity check for CAN filter count \a u8Count. */
#define IS_CAN_FILTER_COUNT_VALID(u8Count)                                     \
(       (u8Count) <= 8u)

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 *******************************************************************************
 ** \brief  Configures the can base functions
 **
 ** \param  [in] pstcCanInitCfg            The can initial config struct.
 **
 ** \retval None
 **
 ** \note   None
 **
 ******************************************************************************/
void CAN_Init(const stc_can_init_config_t *pstcCanInitCfg)
{
    uint8_t i;
    if (NULL != pstcCanInitCfg)
    {
        DDL_ASSERT(IS_CAN_FILTER_COUNT_VALID(pstcCanInitCfg->u8FilterCount));

        CAN_RESET_ENABLE();
        M4_CAN->BT_f.PRESC     = pstcCanInitCfg->stcCanBt.PRESC;
        M4_CAN->BT_f.SEG_1     = pstcCanInitCfg->stcCanBt.SEG_1;
        M4_CAN->BT_f.SEG_2     = pstcCanInitCfg->stcCanBt.SEG_2;
        M4_CAN->BT_f.SJW       = pstcCanInitCfg->stcCanBt.SJW;
        M4_CAN->TCTRL_f.TSMODE = pstcCanInitCfg->enCanSTBMode;
        CAN_FilterConfig(pstcCanInitCfg->pstcFilter, pstcCanInitCfg->u8FilterCount);

        CAN_RESET_DISABLE();
        M4_CAN->RCTRL_f.RBALL  = pstcCanInitCfg->enCanRxBufAll;
        M4_CAN->RCTRL_f.ROM    = pstcCanInitCfg->enCanRxBufMode;
        M4_CAN->RCTRL_f.SACK   = pstcCanInitCfg->enCanSAck;
        M4_CAN->LIMIT_f.AFWL   = pstcCanInitCfg->stcWarningLimit.CanWarningLimitVal;
        M4_CAN->LIMIT_f.EWL    = pstcCanInitCfg->stcWarningLimit.CanErrorWarningLimitVal;

        // Enable filters.
        for (i=0u; i<pstcCanInitCfg->u8FilterCount; i++)
        {
            CAN_FilterCmd(pstcCanInitCfg->pstcFilter[i].enFilterSel, Enable);
        }
    }
}

/**
 *******************************************************************************
 ** \brief  De-Init (RESET CAN register)
 **
 ** \param  None
 **
 ** \retval None
 **
 ** \note   None
 **
 ******************************************************************************/
void CAN_DeInit(void)
{
    CAN_RESET_ENABLE();
}

/**
 *******************************************************************************
 ** \brief  Configures the can Mode
 **
 ** \param  [in] enMode                 The can mode enum. @ref en_can_mode_t
 ** \param  [in] enNewState             The new state of the can filter chanel.
 ** \arg    Enable                      Enable filter.
 ** \arg    Disable                     Disable filter.
 ** \retval None
 **
 ** \note   None
 **
 ******************************************************************************/
void CAN_ModeConfig(en_can_mode_t enMode, en_functional_state_t enNewState)
{
    DDL_ASSERT(IS_CAN_MODE_VALID(enMode));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    if(CanListenOnlyMode == enMode)
    {
        M4_CAN->TCMD_f.LOM = enNewState;
    }else
    {
        if(Enable == enNewState)
        {
            M4_CAN->CFG_STAT |= enMode;
        }else
        {
            M4_CAN->CFG_STAT &= ~enMode;
        }
    }
}


/**
 *******************************************************************************
 ** \brief  Configures the can acceptance filter
 **
 ** \param  [in] pstcFilter             Pointer to a stc_can_filter_t type array.
 **                                     @ref stc_can_filter_t
 ** \param  [in] u8FilterCount          Number of filters that to be configured.
 **
 ** \retval None
 **
 ** \note   None
 **
 ******************************************************************************/
void CAN_FilterConfig(const stc_can_filter_t pstcFilter[], uint8_t u8FilterCount)
{
    uint8_t i;

    if(NULL != pstcFilter)
    {
        DDL_ASSERT(IS_CAN_FILTER_COUNT_VALID(u8FilterCount));

        for (i=0u; i<u8FilterCount; i++)
        {
            DDL_ASSERT(IS_CAN_FILTER_VALID(pstcFilter[i].enFilterSel));
            //<<Acceptance filter address
            M4_CAN->ACFCTRL_f.ACFADR  = pstcFilter[i].enFilterSel;
            //<<ID config
            M4_CAN->ACFCTRL_f.SELMASK = CAN_ACF_ID_REG_SEL;
            M4_CAN->ACF               = pstcFilter[i].u32CODE;
            //<<MASK config
            M4_CAN->ACFCTRL_f.SELMASK = CAN_ACF_MASK_REG_SEL;
            M4_CAN->ACF               = pstcFilter[i].u32MASK;
            //<<Frame format config
            M4_CAN->ACF_f.AIDEE = ((pstcFilter[i].enAcfFormat >> 1ul) & 0x01u);
            M4_CAN->ACF_f.AIDE  = (pstcFilter[i].enAcfFormat & 0x01ul);
        }
    }
}

/**
 *******************************************************************************
 ** \brief  Enable or disable the specified can acceptance filter.
 **
 ** \param  [in] enFilter               Specifies a filter.
 **                                     @ref en_can_filter_sel_t
 ** \param  [in] enNewState             The new state of the specified filter.
 ** \arg    Enable                      Enable.
 ** \arg    Disable                     Disable.
 **
 ** \retval None
 **
 ** \note   None
 **
 ******************************************************************************/
void CAN_FilterCmd(en_can_filter_sel_t enFilter, en_functional_state_t enNewState)
{
    uint8_t u8FilterSel;

    DDL_ASSERT(IS_CAN_FILTER_VALID(enFilter));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    u8FilterSel = (uint8_t)(1ul << enFilter);

    if(Enable == enNewState)
    {
        M4_CAN->ACFEN |= u8FilterSel;
    }else
    {
        M4_CAN->ACFEN &= (uint8_t)(~u8FilterSel);
    }
}

/**
 *******************************************************************************
 ** \brief  Configures the can Tx frame set
 **
 ** \param  [in] pstcTxFrame            The can Tx frame struct.
 **                                     @ref stc_can_txframe_t
 ** \retval None
 **
 ** \note   None
 **
 ******************************************************************************/
void CAN_SetFrame(stc_can_txframe_t *pstcTxFrame)
{
    uint32_t u32TBUFAddr;

    if(NULL != pstcTxFrame)
    {
        u32TBUFAddr = (uint32_t)&M4_CAN->TBUF;
        M4_CAN->TCMD_f.TBSEL = pstcTxFrame->enBufferSel;
        CAN_RW_MEM32(u32TBUFAddr)    = pstcTxFrame->TBUF32_0;
        CAN_RW_MEM32(u32TBUFAddr+4)  = pstcTxFrame->TBUF32_1;
        CAN_RW_MEM32(u32TBUFAddr+8)  = pstcTxFrame->TBUF32_2[0];
        CAN_RW_MEM32(u32TBUFAddr+12) = pstcTxFrame->TBUF32_2[1];

        if(CanSTBSel == pstcTxFrame->enBufferSel)
        {
            M4_CAN->TCTRL_f.TSNEXT = Enable;
        }
    }
}

/**
 *******************************************************************************
 ** \brief  Configures the can Tx Command
 **
 ** \param  [in] enTxCmd            The can Tx Command.
 **
 ** \retval Can Tx buffer status    @ref en_can_tx_buf_status_t
 **
 ** \note   None
 **
 ******************************************************************************/
en_can_tx_buf_status_t CAN_TransmitCmd(en_can_tx_cmd_t enTxCmd)
{
    DDL_ASSERT(IS_TX_CMD_VALID(enTxCmd));

    M4_CAN->TCMD |= enTxCmd;

    return (en_can_tx_buf_status_t)M4_CAN->TCTRL_f.TSSTAT;
}

/**
 *******************************************************************************
 ** \brief  Configures the can Rx frame
 **
 ** \param  [in] pstcRxFrame        The can Rx frame.
 **                                 @ref stc_can_rxframe_t
 ** \retval Can rx buffer status    @ref en_can_rx_buf_status_t
 **
 ** \note   None
 **
 ******************************************************************************/
en_can_rx_buf_status_t CAN_Receive(stc_can_rxframe_t *pstcRxFrame)
{
    uint32_t u32RBUFAddr;

    if(NULL != pstcRxFrame)
    {
        u32RBUFAddr = (uint32_t)&M4_CAN->RBUF;
        pstcRxFrame->RBUF32_0    = CAN_RW_MEM32(u32RBUFAddr);
        pstcRxFrame->RBUF32_1    = CAN_RW_MEM32(u32RBUFAddr+4);
        pstcRxFrame->RBUF32_2[0] = CAN_RW_MEM32(u32RBUFAddr+8);
        pstcRxFrame->RBUF32_2[1] = CAN_RW_MEM32(u32RBUFAddr+12);

        M4_CAN->RCTRL_f.RREL = 1u;
    }
    return (en_can_rx_buf_status_t)M4_CAN->RCTRL_f.RSTAT;
}


/**
 *******************************************************************************
 ** \brief  Get the can Error Status
 **
 ** \param  None
 **
 ** \retval en_can_error_t          The can error status
 **
 ** \note   None
 **
 ******************************************************************************/
en_can_error_t CAN_ErrorStatusGet(void)
{
    en_can_error_t enRet = UNKOWN_ERROR;

    if(6u > M4_CAN->EALCAP_f.KOER)
    {
        enRet = (en_can_error_t)M4_CAN->EALCAP_f.KOER;
    }
    return enRet;
}

/**
 *******************************************************************************
 ** \brief  Get the can Status
 **
 ** \param  enCanStatus             The can status
 ** \arg    true
 ** \arg    false
 ** \retval bool
 **
 ** \note   None
 **
 ******************************************************************************/
bool CAN_StatusGet(en_can_status_t enCanStatus)
{
    bool bRet = false;
    DDL_ASSERT(IS_CAN_STATUS_VALID(enCanStatus));

    if(M4_CAN->CFG_STAT & enCanStatus)
    {
        bRet = true;
    }
    return bRet;
}

/**
 *******************************************************************************
 ** \brief  Configures the can Interrupt enable
 **
 ** \param  [in] enCanIrqType           The can interrupt type.
 ** \param  [in] enNewState             The new state of the can interrupt.
 ** \arg    Enable                      Enable.
 ** \arg    Disable                     Disable.
 **
 ** \retval None
 **
 ** \note   None
 **
 ******************************************************************************/
void CAN_IrqCmd(en_can_irq_type_t enCanIrqType, en_functional_state_t enNewState)
{
    volatile uint32_t *u32pIE;

    DDL_ASSERT(IS_CAN_IRQ_TYPE_VALID(enCanIrqType));
    DDL_ASSERT(IS_FUNCTIONAL_STATE(enNewState));

    u32pIE = (volatile uint32_t*)(&M4_CAN->RTIE);

    if(Enable == enNewState)
    {
        *u32pIE |= enCanIrqType;
    }else
    {
        *u32pIE &= ~((uint32_t)enCanIrqType);
    }
}

/**
 *******************************************************************************
 ** \brief  Get the can Interrupt Flag
 **
 ** \param  [in] enCanIrqFlgType            The can interrupt Flag.
 **
 ** \retval bool
 **
 ** \note   None
 **
 ******************************************************************************/
bool CAN_IrqFlgGet(en_can_irq_flag_type_t enCanIrqFlgType)
{
    volatile uint32_t *u32pIE = NULL;
    bool bRet = false;

    DDL_ASSERT(IS_CAN_IRQ_FLAG_VALID(enCanIrqFlgType));

    u32pIE = (volatile uint32_t*)(&M4_CAN->RTIE);

    if( *u32pIE & enCanIrqFlgType)
    {
        bRet = true;
    }
    return bRet;
}

/**
 *******************************************************************************
 ** \brief  Clear the can Interrupt Flag
 **
 ** \param  [in] enCanIrqFlgType           The can interrupt type.
 **
 ** \retval None
 **
 ** \note   None
 **
 ******************************************************************************/
void CAN_IrqFlgClr(en_can_irq_flag_type_t enCanIrqFlgType)
{
    volatile uint32_t *u32pIE = NULL;
    uint32_t u32IETempMsk = 0xFF2A00FF;

    DDL_ASSERT(IS_CAN_IRQ_FLAG_VALID(enCanIrqFlgType));

    u32pIE = (volatile uint32_t*)(&M4_CAN->RTIE);

    *u32pIE = (((*u32pIE)&u32IETempMsk) | (uint32_t)enCanIrqFlgType);
}


/**
 *******************************************************************************
 ** \brief  Get the can Rx Error Counter
 **
 ** \param  None
 **
 ** \retval Error Counter(0~255)
 **
 ** \note   None
 **
 ******************************************************************************/
uint8_t CAN_RxErrorCntGet(void)
{
    return M4_CAN->RECNT;
}

/**
 *******************************************************************************
 ** \brief  Get the can Tx Error Counter
 **
 ** \param  None
 **
 ** \retval Error Counter(0~255)
 **
 ** \note   None
 **
 ******************************************************************************/
uint8_t CAN_TxErrorCntGet(void)
{
    return M4_CAN->TECNT;
}

/**
 *******************************************************************************
 ** \brief  Get the can Arbitration lost captrue
 **
 ** \param  None
 **
 ** \retval address(0~31)
 **
 ** \note   None
 **
 ******************************************************************************/
uint8_t CAN_ArbitrationLostCap(void)
{
    return M4_CAN->EALCAP_f.ALC;
}


//@} // CanGroup

/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/

