/*
 * @file        apm32a0xx_can.c
 *
 * @brief       This file contains all the functions for the CAN peripheral
 *
 * @version     V1.0.0
 *
 * @date        2023-03-30
 *
 * @attention
 *
 *  Copyright (C) 2023 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* include */
#include "apm32a0xx_can.h"
#include "apm32a0xx_rcm.h"

/** @addtogroup APM32A0xx_StdPeriphDriver
  @{
*/

/** @addtogroup CAN_Driver
  @{
*/

/** @defgroup CAN_Macros Macros
  @{
*/

/**@} end of group CAN_Macros */

/** @defgroup CAN_Enumerations Enumerations
  @{
*/

/**@} end of group CAN_Enumerations */

/** @defgroup CAN_Structures Structures
  @{
*/

/**@} end of group CAN_Structures */

/** @defgroup CAN_Variables Variables
  @{
*/

/**@} end of group CAN_Variables */

/** @defgroup CAN_Functions Functions
  @{
*/

/*!
 * @brief       Reset CAN peripheral registers to their default values.
 *
 * @param       None
 *
 * @retval      None
 *
 */
void CAN_Reset(void)
{
    RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_CAN);
    RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_CAN);
}

/*!
 * @brief       Congfig the CAN peripheral using the specified parameters in the canConfig.
 *
 * @param       canConfig : pointer to a CAN_Config_T structure that contains
 *                          the configuration information for the CAN peripheral.
 *
 * @retval      Configuration status (SET or RESET)
 */
uint8_t CAN_Config(CAN_Config_T* canConfig)
{
    uint8_t InitStatus = RESET;
    uint32_t ackWait = 0x00000000;

    CAN->MCTRL_B.SLEEPREQ = BIT_RESET;

    CAN->MCTRL_B.INITREQ = BIT_SET;

    while ((CAN->MSTS_B.INITFLG != BIT_SET) && (ackWait != (uint32_t)0x00FFFFFF))
    {
        ackWait++;
    }

    if (CAN->MSTS_B.INITFLG != BIT_SET)
    {
        InitStatus = RESET;
    }
    else
    {
        if (canConfig->timeTrigComMode == ENABLE)
        {
            CAN->MCTRL_B.TTCM = BIT_SET;
        }
        else
        {
            CAN->MCTRL_B.TTCM = BIT_RESET;
        }

        if (canConfig->autoBusOffManage == ENABLE)
        {
            CAN->MCTRL_B.ALBOFFM = BIT_SET;
        }
        else
        {
            CAN->MCTRL_B.ALBOFFM = BIT_RESET;
        }

        if (canConfig->autoWakeUpMode == ENABLE)
        {
            CAN->MCTRL_B.AWUPCFG = BIT_SET;
        }
        else
        {
            CAN->MCTRL_B.AWUPCFG = BIT_RESET;
        }

        if (canConfig->nonAutoRetran == ENABLE)
        {
            CAN->MCTRL_B.ARTXMD = BIT_SET;
        }
        else
        {
            CAN->MCTRL_B.ARTXMD = BIT_RESET;
        }

        if (canConfig->rxFIFOLockMode == ENABLE)
        {
            CAN->MCTRL_B.RXFLOCK = BIT_SET;
        }
        else
        {
            CAN->MCTRL_B.RXFLOCK = BIT_RESET;
        }

        if (canConfig->txFIFOPriority == ENABLE)
        {
            CAN->MCTRL_B.TXFPCFG = BIT_SET;
        }
        else
        {
            CAN->MCTRL_B.TXFPCFG = BIT_RESET;
        }

        CAN->BITTIM = (uint32_t)canConfig->mode << 30;
        CAN->BITTIM_B.RSYNJW     = canConfig->syncJumpWidth;
        CAN->BITTIM_B.TIMSEG1 = canConfig->timeSegment1;
        CAN->BITTIM_B.TIMSEG2 = canConfig->timeSegment2;
        CAN->BITTIM_B.BRPSC     = canConfig->prescaler - 1;

        CAN->MCTRL_B.INITREQ = BIT_RESET;

        ackWait = 0x00000000;

        while ((CAN->MSTS_B.INITFLG == BIT_SET) && (ackWait != (uint32_t)0x00FFFFFF))
        {
            ackWait++;
        }

        if (CAN->MSTS_B.INITFLG == BIT_RESET)
        {
            InitStatus = SET;
        }
        else
        {
            InitStatus = RESET;
        }
    }
    return InitStatus;
}

/*!
 * @brief       Congfig the CAN filter according to the specified parameters in the filterConfig.
 *
 * @param       filterConfig: pointer to a CAN_Filter_Config_T structure that
 *                            contains the configuration information.
 *
 * @retval      None
 */
void CAN_ConfigFilter(CAN_FilterConfig_T* filterConfig)
{
    uint16_t filterNumber = 0;

    filterNumber = BIT0 << filterConfig->filterNumber;

    CAN->FCTRL_B.FIM = BIT_SET;

    CAN->FACT &= ~filterNumber;

    if (filterConfig->filterScale == CAN_FILTER_SCALE_16BIT)
    {
        CAN->FSCFG &= ~filterNumber;

        CAN->sFilterRegister[filterConfig->filterNumber].R1 =
            ((0x0000FFFF & filterConfig->filterMaskIdLow) << 16) |
            (0x0000FFFF & filterConfig->filterIdLow);

        CAN->sFilterRegister[filterConfig->filterNumber].R2 =
            ((0x0000FFFF & filterConfig->filterMaskIdHigh) << 16) |
            (0x0000FFFF & filterConfig->filterIdHigh);
    }

    if (filterConfig->filterScale == CAN_FILTER_SCALE_32BIT)
    {
        CAN->FSCFG |= filterNumber;

        CAN->sFilterRegister[filterConfig->filterNumber].R1 =
            ((0x0000FFFF & filterConfig->filterIdHigh) << 16) |
            (0x0000FFFF & filterConfig->filterIdLow);

        CAN->sFilterRegister[filterConfig->filterNumber].R2 =
            ((0x0000FFFF & filterConfig->filterMaskIdHigh) << 16) |
            (0x0000FFFF & filterConfig->filterMaskIdLow);
    }

    if (filterConfig->filterMode == CAN_FILTER_MODE_IDMASK)
    {
        CAN->FMCFG &= ~filterNumber;
    }
    else
    {
        CAN->FMCFG |= filterNumber;
    }

    if (filterConfig->filterFIFO == CAN_FIFO_0)
    {
        CAN->FFASS &= ~filterNumber;
    }
    else
    {
        CAN->FFASS |= filterNumber;
    }

    if (filterConfig->filterActivation == ENABLE)
    {
        CAN->FACT |= filterNumber;
    }

    CAN->FCTRL_B.FIM = BIT_RESET;
}

/*!
 * @brief   Fills each canConfig member with its default value.
 *
 * @param   canConfig : Pointer to a CAN_Config_T structure which is used for configuration.
 *
 * @retval  None
 */
void CAN_ConfigStructInit(CAN_Config_T* canConfig)
{
    canConfig->timeTrigComMode  = DISABLE;

    canConfig->autoBusOffManage  = DISABLE;

    canConfig->autoWakeUpMode   = DISABLE;

    canConfig->nonAutoRetran   = DISABLE;

    canConfig->rxFIFOLockMode = DISABLE;

    canConfig->txFIFOPriority  = DISABLE;

    canConfig->mode = CAN_MODE_NORMAL;

    canConfig->syncJumpWidth = CAN_SJW_1;

    canConfig->timeSegment1 = CAN_TIME_SEGMENT1_4;

    canConfig->timeSegment2 = CAN_TIME_SEGMENT2_3;

    canConfig->prescaler = 1;
}

/*!
 * @brief     Select the start bank filter for slave CAN.
 *
 * @param     bankNumber:Select the start slave bank filter from 1 to 27.
 *
 * @retval    None
 *
 * @note      This function applies only to APM32 Connectivity line devices.
 */
void CAN_StartBankSlave(uint8_t bankNumber)
{
    CAN->FCTRL_B.FIM = BIT_SET;

    CAN->FCTRL &= 0xFFFFC0F1;
    CAN->FCTRL |= (uint32_t)(bankNumber) << 8;

    CAN->FCTRL_B.FIM = BIT_RESET;
}

/*!
 * @brief      Enable the Debug Freeze mdoe for CAN.
 *
 * @param      None
 *
 * @retval     None
 */
void CAN_EnableDebugFreeze(void)
{
    CAN->MCTRL_B.DBGFRZE = BIT_SET;
}

/*!
 * @brief      Disable the Debug Freeze mode for CAN.
 *
 * @param      None
 *
 * @retval     None
 */
void CAN_DisableDebugFreeze(void)
{
    CAN->MCTRL_B.DBGFRZE = BIT_RESET;
}

/*!
 * @brief      Enables the Time Triggered Communication mode for CAN.
 *
 * @param      None
 *
 * @retval     None
 */
void CAN_EnableTTComMode(void)
{
    CAN->MCTRL_B.TTCM = BIT_SET;

    CAN->sTxMailBox[0].TXDLEN_B.TXGT = BIT_SET;
    CAN->sTxMailBox[1].TXDLEN_B.TXGT = BIT_SET;
    CAN->sTxMailBox[2].TXDLEN_B.TXGT = BIT_SET;
}

/*!
 * @brief      Disables the Time Triggered Communication mode for CAN.
 *
 * @param      None
 *
 * @retval     None
 */
void CAN_DisableTTComMode(void)
{
    CAN->MCTRL_B.TTCM = BIT_RESET;

    CAN->sTxMailBox[0].TXDLEN_B.TXGT = BIT_RESET;
    CAN->sTxMailBox[1].TXDLEN_B.TXGT = BIT_RESET;
    CAN->sTxMailBox[2].TXDLEN_B.TXGT = BIT_RESET;
}

/*!
 * @brief     Transmit a message from TxMessage of CAN_Tx_Message structure.
 *
 * @param     TxMessage : Pointer to a structure which contains CAN Id, CAN DLCODE and CAN data.
 *
 * @retval    Returns the number of the transmit mailbox state.It can be one of value:
 *                 @arg 0: Transmit mailbox 0
 *                 @arg 1: Transmit mailbox 1
 *                 @arg 2: Transmit mailbox 2
 *                 @arg 4: There is no empty mailbox
 */
uint8_t CAN_TxMessage(CAN_Tx_Message* TxMessage)
{
    uint8_t TxMailbox = 0;

    if ((CAN->TXSTS_B.TXMEFLG0 == BIT_SET))
    {
        TxMailbox = 0;
    }
    else if ((CAN->TXSTS_B.TXMEFLG1 == BIT_SET))
    {
        TxMailbox = 1;
    }
    else if ((CAN->TXSTS_B.TXMEFLG2 == BIT_SET))
    {
        TxMailbox = 2;
    }
    else
    {
        TxMailbox = CAN_TX_MAILBOX_FULL;
    }

    if (TxMailbox != CAN_TX_MAILBOX_FULL)
    {
        /* Set up the Id */
        CAN->sTxMailBox[TxMailbox].TXMID &= BIT0;
        if (TxMessage->typeID == CAN_TYPEID_STD)
        {
            CAN->sTxMailBox[TxMailbox].TXMID |= (((uint32_t)TxMessage->stanID << 21) | \
                                                 TxMessage->typeID        | \
                                                 TxMessage->remoteTxReq);
        }
        else
        {
            CAN->sTxMailBox[TxMailbox].TXMID |= (((uint32_t)TxMessage->extenID << 3) | \
                                                 TxMessage->typeID       | \
                                                 TxMessage->remoteTxReq);
        }

        /* Set up the DLCODE */
        CAN->sTxMailBox[TxMailbox].TXDLEN_B.DLCODE = (TxMessage->dataLengthCode & 0x0F);

        /* Set up the data field */
        CAN->sTxMailBox[TxMailbox].TXMDH_B.DATABYTE7 = TxMessage->data[7];
        CAN->sTxMailBox[TxMailbox].TXMDH_B.DATABYTE6 = TxMessage->data[6];
        CAN->sTxMailBox[TxMailbox].TXMDH_B.DATABYTE5 = TxMessage->data[5];
        CAN->sTxMailBox[TxMailbox].TXMDH_B.DATABYTE4 = TxMessage->data[4];

        CAN->sTxMailBox[TxMailbox].TXMDL_B.DATABYTE3 = TxMessage->data[3];
        CAN->sTxMailBox[TxMailbox].TXMDL_B.DATABYTE2 = TxMessage->data[2];
        CAN->sTxMailBox[TxMailbox].TXMDL_B.DATABYTE1 = TxMessage->data[1];
        CAN->sTxMailBox[TxMailbox].TXMDL_B.DATABYTE0 = TxMessage->data[0];

        /* Request transmission */
        CAN->sTxMailBox[TxMailbox].TXMID |= BIT0;
    }

    return TxMailbox;
}

/*!
 * @brief     Reads the status of a transmission.
 *
 * @param     TxMailbox: the number of the mailbox
 *              This parameter can be one of the following values:
 *              @arg CAN_MAILBOX_0 : Transmit mailbox 0
 *              @arg CAN_MAILBOX_1 : Transmit mailbox 1
 *              @arg CAN_MAILBOX_2 : Transmit mailbox 2
 *
 * @retval    Returns the transmission state.
 *              It can be one of the following values:
 *              @arg CAN_TX_FAILED
 *              @arg CAN_TX_OK
 *              @arg CAN_TX_WAITING
 */
uint8_t CAN_TxMessageStatus(CAN_MAILBOX_T TxMailbox)
{
    uint8_t state = CAN_TX_FAILED;

    switch (TxMailbox)
    {
        case (CAN_MAILBOX_0):
            if (CAN->TXSTS_B.REQCFLG0 && CAN->TXSTS_B.TXSUSFLG0 && CAN->TXSTS_B.TXMEFLG0)
            {
                state = CAN_TX_OK;
            }
            else if (!(CAN->TXSTS_B.REQCFLG0 || CAN->TXSTS_B.TXSUSFLG0 || CAN->TXSTS_B.TXMEFLG0))
            {
                state = CAN_TX_WAITING;
            }
            break;

        case (CAN_MAILBOX_1):
            if (CAN->TXSTS_B.REQCFLG1 && CAN->TXSTS_B.TXSUSFLG1 && CAN->TXSTS_B.TXMEFLG1)
            {
                state = CAN_TX_OK;
            }
            else if (!(CAN->TXSTS_B.REQCFLG1 || CAN->TXSTS_B.TXSUSFLG1 || CAN->TXSTS_B.TXMEFLG1))
            {
                state = CAN_TX_WAITING;
            }
            break;

        case (CAN_MAILBOX_2):
            if (CAN->TXSTS_B.REQCFLG2 && CAN->TXSTS_B.TXSUSFLG2 && CAN->TXSTS_B.TXMEFLG2)
            {
                state = CAN_TX_OK;
            }
            else if (!(CAN->TXSTS_B.REQCFLG2 || CAN->TXSTS_B.TXSUSFLG2 || CAN->TXSTS_B.TXMEFLG2))
            {
                state = CAN_TX_WAITING;
            }
            break;

        default:
            break;
    }

    return state;
}

/*!
 * @brief     Cancels a transmit request.
 *
 * @param     TxMailbox: the number of the mailbox
 *              This parameter can be one of the following values:
 *                @arg CAN_MAILBOX_0 : Abort transmission request for mailbox 0
 *                @arg CAN_MAILBOX_1 : Abort transmission request for mailbox 1
 *                @arg CAN_MAILBOX_2 : Abort transmission request for mailbox 2
 *
 * @retval    None
 */
void CAN_CancelTx(CAN_MAILBOX_T TxMailbox)
{
    switch (TxMailbox)
    {
        case 0:
            CAN->TXSTS_B.ABREQFLG0 = BIT_SET;
            break;

        case 1:
            CAN->TXSTS_B.ABREQFLG1 = BIT_SET;
            break;

        case 2:
            CAN->TXSTS_B.ABREQFLG2 = BIT_SET;
            break;

        default:
            break;
    }
}

/*!
 * @brief     Receive a valid message through the filter.
 *
 * @param     FIFONumber: The number of Receive FIFO
 *              This parameter can be one of the following values:
 *                @arg CAN_FIFO_0 : CAN FIFO 0 used to receive
 *                @arg CAN_FIFO_1 : CAN FIFO 1 used to receive
 *
 * @param     RxMessage : pointer to a structure that is used to receive message
 *
 * @retval    None
 */
void CAN_RxMessage(uint8_t FIFONumber, CAN_Rx_Message* RxMessage)
{
    RxMessage->typeID = (CAN_TYPEID_T)(BIT2 & CAN->sRxFIFO[FIFONumber].RXMID);

    if (RxMessage->typeID == CAN_TYPEID_STD)
    {
        RxMessage->stanID = CAN->sRxFIFO[FIFONumber].RXMID_B.STDID;
    }
    else
    {
        RxMessage->extenID = (CAN->sRxFIFO[FIFONumber].RXMID_B.STDID << 18);
        RxMessage->extenID |= CAN->sRxFIFO[FIFONumber].RXMID_B.EXTID;
    }

    RxMessage->remoteTxReq = (CAN_RTXR_T)(BIT1 & CAN->sRxFIFO[FIFONumber].RXMID);
    RxMessage->dataLengthCode = CAN->sRxFIFO[FIFONumber].RXDLEN_B.DLCODE;
    RxMessage->filterMatchIndex = CAN->sRxFIFO[FIFONumber].RXDLEN_B.FMIDX;

    RxMessage->data[7] = CAN->sRxFIFO[FIFONumber].RXMDH_B.DATABYTE7;
    RxMessage->data[6] = CAN->sRxFIFO[FIFONumber].RXMDH_B.DATABYTE6;
    RxMessage->data[5] = CAN->sRxFIFO[FIFONumber].RXMDH_B.DATABYTE5;
    RxMessage->data[4] = CAN->sRxFIFO[FIFONumber].RXMDH_B.DATABYTE4;

    RxMessage->data[3] = CAN->sRxFIFO[FIFONumber].RXMDL_B.DATABYTE3;
    RxMessage->data[2] = CAN->sRxFIFO[FIFONumber].RXMDL_B.DATABYTE2;
    RxMessage->data[1] = CAN->sRxFIFO[FIFONumber].RXMDL_B.DATABYTE1;
    RxMessage->data[0] = CAN->sRxFIFO[FIFONumber].RXMDL_B.DATABYTE0;

    if (FIFONumber == CAN_FIFO_0)
    {
        CAN->RXF0_B.RFOM0 = BIT_SET;
    }
    else
    {
        CAN->RXF1_B.RFOM1 = BIT_SET;
    }
}

/*!
 * @brief     Releases the specified receive FIFO.
 *
 * @param     FIFONumber : the number of receive FIFO
 *              This parameter can be one of the following values:
 *                @arg CAN_FIFO_0 : CAN FIFO 0 used to receive
 *                @arg CAN_FIFO_1 : CAN FIFO 1 used to receive
 *
 * @retval    None
 */
void CAN_ReleaseFIFO(uint8_t FIFONumber)
{
    if (FIFONumber == CAN_FIFO_0)
    {
        CAN->RXF0_B.RFOM0 = BIT_SET;
    }
    else
    {
        CAN->RXF1_B.RFOM1 = BIT_SET;
    }
}

/*!
 * @brief    Reads the number of pending messages.
 *
 * @param    FIFONumber : the number of receive FIFO
 *              This parameter can be one of the following values:
 *                @arg CAN_FIFO_0 : CAN FIFO 0 used to receive
 *                @arg CAN_FIFO_1 : CAN FIFO 1 used to receive
 *
 * @retval   which is the number of pending message.
 */
uint8_t CAN_PendingMessage(uint8_t FIFONumber)
{
    if (FIFONumber == CAN_FIFO_0)
    {
        return  CAN->RXF0_B.FMNUM0;
    }
    else
    {
        return  CAN->RXF1_B.FMNUM1;
    }
}

/*!
 * @brief     Select the CAN Operation mode.
 *
 * @param     operatingMode: CAN Operating Mode
 *              This parameter can be one of the following values:
 *              @arg CAN_OPERATING_MODE_INIT  : Initialization mode
 *              @arg CAN_OPERATING_MODE_NORMAL: Normal mode
 *              @arg CAN_OPERATING_MODE_SLEEP : sleep mode
 *
 * @retval    status: 0 : CAN failed entering the specific mode
 *                    1 : CAN Succeed entering the specific mode
 */
uint8_t CAN_OperatingMode(CAN_OPERATING_MODE_T operatingMode)
{
    uint8_t status = RESET;
    uint32_t timeout = 0x00FFFFFF;
    uint8_t sleepflg, initfig;

    sleepflg = CAN->MSTS_B.SLEEPFLG;
    initfig = CAN->MSTS_B.INITFLG;

    switch ((uint8_t)operatingMode)
    {
        case CAN_OPERATING_MODE_INIT:
            CAN->MCTRL_B.SLEEPREQ  = BIT_RESET;
            CAN->MCTRL_B.INITREQ = BIT_SET;

            while ((sleepflg | !initfig) && (timeout != 0))
            {
                sleepflg = CAN->MSTS_B.SLEEPFLG;
                initfig = CAN->MSTS_B.INITFLG;
                timeout--;
            }

            if (sleepflg | !initfig)
            {
                status = RESET;
            }
            else
            {
                status = SET;
            }
            break;

        case CAN_OPERATING_MODE_NORMAL:

            CAN->MCTRL_B.INITREQ = BIT_RESET;
            CAN->MCTRL_B.SLEEPREQ     = BIT_RESET;

            while ((sleepflg | initfig) && (timeout != 0))
            {
                sleepflg = CAN->MSTS_B.SLEEPFLG;
                initfig = CAN->MSTS_B.INITFLG;
                timeout--;
            }

            if (sleepflg | initfig)
            {
                status = RESET;
            }
            else
            {
                status = SET;
            }
            break;

        case CAN_OPERATING_MODE_SLEEP:
            CAN->MCTRL_B.INITREQ = BIT_RESET;
            CAN->MCTRL_B.SLEEPREQ     = BIT_SET;

            while ((!sleepflg | initfig) && (timeout != 0))
            {
                sleepflg = CAN->MSTS_B.SLEEPFLG;
                initfig = CAN->MSTS_B.INITFLG;
                timeout--;
            }

            if (!sleepflg | initfig)
            {
                status = RESET;
            }
            else
            {
                status = SET;
            }
            break;

        default:
            status = RESET;
            break;
    }

    return status;
}

/*!
 * @brief     Enters the sleep mode.
 *
 * @param     None
 *
 * @retval    status: 0 : Into sleep fail
 *                    1 : Into sleep success
 */
uint8_t CAN_SleepMode(void)
{
    CAN_SLEEP_T sleepstatus = CAN_SLEEP_FAILED;

    CAN->MCTRL_B.INITREQ = BIT_RESET;
    CAN->MCTRL_B.SLEEPREQ     = BIT_SET;

    if ((CAN->MSTS_B.SLEEPFLG == BIT_SET) && (CAN->MSTS_B.INITFLG == BIT_RESET))
    {
        sleepstatus =  CAN_SLEEP_OK;
    }

    return (uint8_t)sleepstatus;
}

/*!
 * @brief     Wakes up the CAN from sleep mode.
 *
 * @param     None
 *
 * @retval    status: 0 : WakeUp CAN fail
 *                    1 : WakeUp CAN success
 */
uint8_t CAN_WakeUpMode(void)
{
    uint32_t slakWait = 0x00FFFFFF;
    CAN_WUP_T wakeupstatus = CAN_WAKEUP_FAILED;

    CAN->MCTRL_B.SLEEPREQ = BIT_RESET;

    while ((CAN->MSTS_B.SLEEPFLG == BIT_SET) && (slakWait != 0x00))
    {
        slakWait--;
    }
    if (CAN->MSTS_B.SLEEPFLG != BIT_SET)
    {
        wakeupstatus = CAN_WAKEUP_OK;
    }
    return (uint8_t)wakeupstatus;
}

/*!
 * @brief     Read the last error code of CAN.
 *
 * @param     None
 *
 * @retval    CAN_ErrorCode
 */
uint8_t CAN_ReadLastErrorCode(void)
{
    uint8_t errorcode = 0;
    errorcode = CAN->ERRSTS_B.LERRC;
    return errorcode;
}

/*!
 * @brief    Read the Receive Error Counter of CAN.
 *
 * @param    None
 *
 * @retval   CAN Receive Error Counter.
 */
uint8_t CAN_ReadRxErrorCounter(void)
{
    uint8_t counter = 0;
    counter = CAN->ERRSTS_B.RXERRCNT;
    return counter;
}

/*!
 * @brief   Reads the Least significant byte of the 9-bit CAN Transmit Error Counter.
 *
 * @param   None
 *
 * @retval  Least significant byte of the 9-bit CAN Transmit Error Counter.
 */
uint8_t CAN_ReadLSBTxErrorCounter(void)
{
    uint8_t counter = 0;
    counter = CAN->ERRSTS_B.TXERRCNT;
    return counter;
}

/*!
 * @brief  Enables the specified CAN interrupts.
 *
 * @param  interrupt: Selects the CAN interrupt sources to be enabled.
 *          The parameter can be any combination of the following values:
 *            @arg CAN_INT_TXME : Transmit mailbox empty Interrupt
 *            @arg CAN_INT_F0MP : FIFO 0 message pending Interrupt
 *            @arg CAN_INT_F0FUL: FIFO 0 full Interrupt
 *            @arg CAN_INT_F0OVR: FIFO 0 overrun Interrupt
 *            @arg CAN_INT_F1MP : FIFO 1 message pending Interrupt
 *            @arg CAN_INT_F1FUL: FIFO 1 full Interrupt
 *            @arg CAN_INT_F1OVR: FIFO 1 overrun Interrupt
 *            @arg CAN_INT_EWIE : Error warning Interrupt
 *            @arg CAN_INT_EPIE : Error passive Interrupt
 *            @arg CAN_INT_BOIE : Bus-off Interrupt
 *            @arg CAN_INT_LEC  : Last error code Interrupt
 *            @arg CAN_INT_ERR  : Error Interrupt
 *            @arg CAN_INT_WUP  : Wake-up Interrupt
 *            @arg CAN_INT_SLE  : Sleep acknowledge Interrupt
 *
 * @retval  None
 */
void CAN_EnableInterrupt(uint32_t interrupt)
{
    CAN->INTEN |= interrupt;
}

/*!
 * @brief  Disables the specified CAN interrupts.
 *
 * @param  interrupt: Selects the CAN interrupt sources to be disabled.
 *          The parameter can be any combination of the following values:
 *            @arg CAN_INT_TXME : Transmit mailbox empty Interrupt
 *            @arg CAN_INT_F0MP : FIFO 0 message pending Interrupt
 *            @arg CAN_INT_F0FUL: FIFO 0 full Interrupt
 *            @arg CAN_INT_F0OVR: FIFO 0 overrun Interrupt
 *            @arg CAN_INT_F1MP : FIFO 1 message pending Interrupt
 *            @arg CAN_INT_F1FUL: FIFO 1 full Interrupt
 *            @arg CAN_INT_F1OVR: FIFO 1 overrun Interrupt
 *            @arg CAN_INT_WUP  : Wake-up Interrupt
 *            @arg CAN_INT_SLE  : Sleep acknowledge Interrupt
 *            @arg CAN_INT_EWIE : Error warning Interrupt
 *            @arg CAN_INT_EPIE : Error passive Interrupt
 *            @arg CAN_INT_BOIE : Bus-off Interrupt
 *            @arg CAN_INT_LEC  : Last error code Interrupt
 *            @arg CAN_INT_ERR  : Error Interrupt
 *
 * @retval None
 */
void CAN_DisableInterrupt(uint32_t interrupt)
{
    CAN->INTEN &= ~interrupt;
}

/*!
 * @brief  Reads the specified CAN flag status.
 *
 * @param  flag: specifies the flag of CAN.
 *          The parameter can be one of following values:
 *            @arg CAN_FLAG_EWF  : Error Warning Flag
 *            @arg CAN_FLAG_EPF  : Error Passive Flag
 *            @arg CAN_FLAG_BOF  : Bus-Off Flag
 *            @arg CAN_FLAG_LEC  : Last error code Flag
 *            @arg CAN_FLAG_WUP  : Wake up Flag
 *            @arg CAN_FLAG_SLAK : Sleep acknowledge Flag
 *            @arg CAN_FLAG_RQCP0: Request MailBox0 Flag
 *            @arg CAN_FLAG_RQCP1: Request MailBox1 Flag
 *            @arg CAN_FLAG_RQCP2: Request MailBox2 Flag
 *            @arg CAN_FLAG_FMP0 : FIFO 0 Message Pending Flag
 *            @arg CAN_FLAG_FF0  : FIFO 0 Full Flag
 *            @arg CAN_FLAG_FOV0 : FIFO 0 Overrun Flag
 *            @arg CAN_FLAG_FMP1 : FIFO 1 Message Pending Flag
 *            @arg CAN_FLAG_FF1  : FIFO 1 Full Flag
 *            @arg CAN_FLAG_FOV1 : FIFO 1 Overrun Flag
 *
 * @retval  staus : RESET or SET
 */
uint8_t CAN_ReadStatusFlag(CAN_FLAG_T flag)
{
    uint8_t status = RESET;

    if ((flag & 0x00F00000) != (uint32_t)RESET)
    {
        if ((CAN->ERRSTS & (flag & 0x000FFFFF)) != (uint32_t)RESET)
        {
            status = SET;
        }
        else
        {
            status = RESET;
        }
    }
    else if ((flag & 0x01000000) != (uint32_t)RESET)
    {
        if ((CAN->MSTS & (flag & 0x000FFFFF)) != (uint32_t)RESET)
        {
            status = SET;
        }
        else
        {
            status = RESET;
        }
    }
    else if ((flag & 0x02000000) != (uint32_t)RESET)
    {
        if ((CAN->TXSTS & (flag & 0x000FFFFF)) != (uint32_t)RESET)
        {
            status = SET;
        }
        else
        {
            status = RESET;
        }
    }
    else if ((flag & 0x04000000) != (uint32_t)RESET)
    {
        if ((CAN->RXF0 & (flag & 0x000FFFFF)) != (uint32_t)RESET)
        {
            status = SET;
        }
        else
        {
            status = RESET;
        }
    }
    else
    {
        if ((CAN->RXF1 & (flag & 0x000FFFFF)) != (uint32_t)RESET)
        {
            status = SET;
        }
        else
        {
            status = RESET;
        }
    }
    return  status;
}

/*!
 * @brief  Clears the status flags of CAN.
 *
 * @param  flag : specifies the flag to clear.
 *           The parameter can be one of following values:
 *            @arg CAN_FLAG_LEC  : Last error code Flag
 *            @arg CAN_FLAG_WUP  : Wake up Flag
 *            @arg CAN_FLAG_SLAK : Sleep acknowledge Flag
 *            @arg CAN_FLAG_RQCP0: Request MailBox0 Flag
 *            @arg CAN_FLAG_RQCP1: Request MailBox1 Flag
 *            @arg CAN_FLAG_RQCP2: Request MailBox2 Flag
 *            @arg CAN_FLAG_FF0  : FIFO 0 Full Flag
 *            @arg CAN_FLAG_FOV0 : FIFO 0 Overrun Flag
 *            @arg CAN_FLAG_FF1  : FIFO 1 Full Flag
 *            @arg CAN_FLAG_FOV1 : FIFO 1 Overrun Flag
 *
 * @retval  None
 */
void CAN_ClearStatusFlag(CAN_FLAG_T flag)
{
    uint32_t flagtmp = 0;

    if (flag == CAN_FLAG_LEC)
    {
        CAN->ERRSTS_B.LERRC = 0;
    }
    else
    {
        flagtmp = flag & 0x000FFFFF;

        if ((flag & 0x01000000) != RESET)
        {
            CAN->MSTS = flagtmp;
        }
        else if ((flag & 0x02000000) != RESET)
        {
            CAN->TXSTS = flagtmp;
        }
        else if ((flag & 0x04000000) != RESET)
        {
            CAN->RXF0 = flagtmp;
        }
        else
        {
            CAN->RXF1 = flagtmp;
        }
    }
}

/*!
 * @brief  Reads the specified CAN interrupt flag status.
 *
 * @param  interrupt: specifies the CAN interrupt source.
 *          The parameter can be one of following values:
 *            @arg CAN_INT_TXME : Transmit mailbox empty Interrupt
 *            @arg CAN_INT_F0MP : FIFO 0 message pending Interrupt
 *            @arg CAN_INT_F0FUL: FIFO 0 full Interrupt
 *            @arg CAN_INT_F0OVR: FIFO 0 overrun Interrupt
 *            @arg CAN_INT_F1MP : FIFO 1 message pending Interrupt
 *            @arg CAN_INT_F1FUL: FIFO 1 full Interrupt
 *            @arg CAN_INT_F1OVR: FIFO 1 overrun Interrupt
 *            @arg CAN_INT_EWIE : Error warning Interrupt
 *            @arg CAN_INT_EPIE : Error passive Interrupt
 *            @arg CAN_INT_BOIE : Bus-off Interrupt
 *            @arg CAN_INT_LEC  : Last error code Interrupt
 *            @arg CAN_INT_ERR  : Error Interrupt
 *            @arg CAN_INT_WUP  : Wake-up Interrupt
 *            @arg CAN_INT_SLE  : Sleep acknowledge Interrupt
 *
 * @retval  status : SET or RESET
 */
uint8_t CAN_ReadIntFlag(CAN_INT_T interrupt)
{
    uint8_t status = 0;

    if ((CAN->INTEN & interrupt) != RESET)
    {
        switch (interrupt)
        {
            case CAN_INT_TXME:
                status = CAN->TXSTS_B.REQCFLG0;
                status |= CAN->TXSTS_B.REQCFLG1;
                status |= CAN->TXSTS_B.REQCFLG2;
                break;
            case CAN_INT_F0MP:
                status = !(!CAN->RXF0_B.FMNUM0);
                break;
            case CAN_INT_F0FUL:
                status = CAN->RXF0_B.FFULLFLG0;
                break;
            case CAN_INT_F0OVR:
                status = CAN->RXF0_B.FOVRFLG0;
                break;
            case CAN_INT_F1MP:
                status = !(!CAN->RXF1_B.FMNUM1);
                break;
            case CAN_INT_F1FUL:
                status = CAN->RXF1_B.FFULLFLG1;
                break;
            case CAN_INT_F1OVR:
                status = CAN->RXF1_B.FOVRFLG1;
                break;
            case CAN_INT_EWIE:
                status = CAN->ERRSTS_B.ERRWFLG;
                break;
            case CAN_INT_EPIE:
                status = CAN->ERRSTS_B.ERRPFLG;
                break;
            case CAN_INT_BOIE:
                status = CAN->ERRSTS_B.BOFLG;
                break;
            case CAN_INT_LEC:
                status = !(!CAN->ERRSTS_B.LERRC);
                break;
            case CAN_INT_ERR:
                status = CAN->MSTS_B.ERRIFLG;
                break;
            case CAN_INT_WUP:
                status = CAN->MSTS_B.WUPIFLG;
                break;
            case CAN_INT_SLE:
                status = CAN->MSTS_B.SLEEPIFLG;
                break;
            default:
                status = RESET;
                break;
        }
    }
    else
    {
        status = RESET;
    }
    return status;
}

/*!
 * @brief  Clears the CAN's interrupt flag.
 *
 * @param  interrupt: specifies the interrupt flag to clear.
 *          The parameter can be any combination of the following values:
 *            @arg CAN_INT_TXME : Transmit mailbox empty Interrupt
 *            @arg CAN_INT_F0FUL: FIFO 0 full Interrupt
 *            @arg CAN_INT_F0OVR: FIFO 0 overrun Interrupt
 *            @arg CAN_INT_F1FUL: FIFO 1 full Interrupt
 *            @arg CAN_INT_F1OVR: FIFO 1 overrun Interrupt
 *            @arg CAN_INT_EWIE : Error warning Interrupt
 *            @arg CAN_INT_EPIE : Error passive Interrupt
 *            @arg CAN_INT_BOIE : Bus-off Interrupt
 *            @arg CAN_INT_LEC  : Last error code Interrupt
 *            @arg CAN_INT_ERR  : Error Interrupt
 *            @arg CAN_INT_WUP  : Wake-up Interrupt
 *            @arg CAN_INT_SLE  : Sleep acknowledge Interrupt
 *
 * @retval  None
 */
void CAN_ClearIntFlag(uint32_t interrupt)
{
    if (interrupt & CAN_INT_TXME)
    {
        CAN->TXSTS_B.REQCFLG0 = BIT_SET;
        CAN->TXSTS_B.REQCFLG1 = BIT_SET;
        CAN->TXSTS_B.REQCFLG2 = BIT_SET;
    }
    if (interrupt & CAN_INT_F0FUL)
    {
        CAN->RXF0_B.FFULLFLG0 = BIT_SET;
    }
    if (interrupt & CAN_INT_F0OVR)
    {
        CAN->RXF0_B.FOVRFLG0 = BIT_SET;
    }
    if (interrupt & CAN_INT_F1FUL)
    {
        CAN->RXF1_B.FFULLFLG1 = BIT_SET;
    }
    if (interrupt & CAN_INT_F1OVR)
    {
        CAN->RXF1_B.FOVRFLG1 = BIT_SET;
    }
    if (interrupt & CAN_INT_EWIE)
    {
        /**  Note : the corresponding Flag is cleared by hardware
         *          depending of the CAN Bus status
         */
        CAN->MSTS_B.ERRIFLG = BIT_SET;
    }
    if (interrupt & CAN_INT_EPIE)
    {
        /**  Note : the corresponding Flag is cleared by hardware
         *          depending of the CAN Bus status
         */
        CAN->MSTS_B.ERRIFLG = BIT_SET;
    }
    if (interrupt & CAN_INT_BOIE)
    {
        /**  Note : the corresponding Flag is cleared by hardware
         *          depending of the CAN Bus status
         */
        CAN->MSTS_B.ERRIFLG = BIT_SET;
    }
    if (interrupt & CAN_INT_LEC)
    {
        CAN->ERRSTS_B.LERRC = 0;
        CAN->MSTS_B.ERRIFLG = BIT_SET;
    }
    if (interrupt & CAN_INT_ERR)
    {
        CAN->ERRSTS_B.LERRC = 0;
        CAN->MSTS_B.ERRIFLG = BIT_SET;
    }
    if (interrupt & CAN_INT_WUP)
    {
        CAN->MSTS_B.WUPIFLG = BIT_SET;
    }
    if (interrupt & CAN_INT_SLE)
    {
        CAN->MSTS_B.SLEEPIFLG = BIT_SET;
    }
}

/**@} end of group CAN_Functions */
/**@} end of group CAN_Driver */
/**@} end of group APM32A0xx_StdPeriphDriver */
