/**
  ******************************************************************************
  * @file    can.c
  * @author  MCD Application Team
  * @version V1.0.2
  * @date    2-April-2024
  * @brief   This file provides firmware functions to manage the following 
  *          functionalities of the Controller area network (CAN) peripheral:
  *           + Initialization and Configuration 
  *           + CAN Frames Transmission
  *           + CAN Frames Reception
  *           + Operation modes switch
  *           + Error management
  *           + Interrupts and flags
  *
@verbatim
 ===============================================================================
                        ##### How to use this driver #####
 ===============================================================================
    [..]
      (#) Enable the CAN controller interface clock using 
          CGC_PER0PeriphClockCmd(CGC_PER0Periph_CAN0, ENABLE); for CAN0
      -@- In case you are using CAN1 only, you have to enable the CAN1 clock.
       
      (#) CAN pins configuration
        (++) Connect the involved CAN pins to GROUP_AF_CTXD/CRXD using the following function 
                GPIO_PinAFConfig(GPIO_PORTx, GPIO_Pin_y, GPIO_Pxy, GROUP_AF_CTXD);
                GPIO_PinAFConfig(GPIO_PORTx, GPIO_Pin_y, GPIO_Pxy, GROUP_AF_CRXD);
        (++) Configure these CAN pins in alternate function mode by calling
             the function  GPIO_Init();
        
      (#) Initialize and configure the CAN using CAN_Init() and 
          CAN_OperatingModeRequest() functions to configurate working mode.
        
      (#) Initialize and configure the CAN Message cache using 
          CAN_MessageCache_DeInit() and CAN_MessageCache_Init().

      (#) When we want to use ABT mode, on the base of fore used CAN_OperatingModeRequest() function
          to set CAN_OpMode_NormalABT mode, then to config ABT mode by CAN_ABTModeTransmitConfig()
          function to set each frame DBT value and trigger ABT mode start.
        (++) Use CAN_GetABTStatus() function to get the status by ABT.
        (++) ABT mode only support message cache from CAN0MSG00 to CAN0MSG07 and id must be continuous.
        
      (#) Transmit the desired CAN frame using CAN_Transmit() function.
        (++) CAN is in CAN_OpMode_Normal mode for polling type.
        
      (#) Receive a CAN frame using CAN_Receive() function.
        (++) CAN is in CAN_OpMode_Normal mode for polling type.
        
      (#) To control CAN events you can use one of the following two methods:
        (++) Check on CAN flags using the CAN_GetFlagStatus() function.  
        (++) Use CAN interrupts through the function CAN_ITConfig() at 
             initialization phase and CAN_GetITStatus() function into 
             interrupt routines to check if the event has occurred or not.
             After checking on a flag you should clear it using CAN_ClearFlag()
             function. 

@endverbatim
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2016 Cmsemicon.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */


/* Includes ------------------------------------------------------------------*/
#include "can.h"
#include "cgc.h"

/** @addtogroup BAT32G137xx_StdPeriph_Driver
  * @{
  */

/** @defgroup CAN 
  * @brief CAN driver modules
  * @{
  */ 
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/

/* Time out for Init operate */
#define INIT_TIMEOUT        ((uint32_t)0x0000FFFF)

/* Time out for mode setting */
#define SMODE_TIMEOUT       ((uint32_t)0x0000FFFF)

/* Time out for cache init */
#define CACHE_TIMEOUT       ((uint32_t)0x0000FFFF)

/**
  * @brief  Deinitializes the CAN peripheral registers to their default reset values.
  * @param  CANx: where x can be 0 select the CAN peripheral.
  * @retval None.
  */
void CAN_DeInit(CAN_Type* CANx)
{
    CGC_PER0PeriphClockCmd(CGC_PER0Periph_CAN0, ENABLE);
    CGC_PER0PeriphClockCmd(CGC_PER0Periph_CAN0, DISABLE);
}

/**
  * @brief  Initializes the CAN peripheral according to the specified
  *         parameters in the CAN_InitStruct.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  CAN_InitStruct: pointer to a CAN_InitTypeDef structure that contains
  *         the configuration information for the CAN peripheral.
  * @retval Constant indicates initialization succeed which will be 
  *         CAN_InitStatus_Failed or CAN_InitStatus_Success.
  */
uint8_t CAN_Init(CAN_Type* CANx, CAN_InitTypeDef* CAN_InitStruct)
{
    uint8_t InitStatus = CAN_InitStatus_Success;
    uint32_t wait_ack = 0x00000000;

    assert_param(IS_CAN_ALL_PERIPH(CANx));
    assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_TTCM));
    assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_ABTM));
    assert_param(IS_FUNCTIONAL_STATE(CAN_InitStruct->CAN_AWUM));
    assert_param(IS_CANOP_MODE(CAN_InitStruct->CAN_OperateMode));
    assert_param(IS_CAN_SJW(CAN_InitStruct->CAN_SJW));
    assert_param(IS_CAN_BS1(CAN_InitStruct->CAN_BS1));
    assert_param(IS_CAN_BS2(CAN_InitStruct->CAN_BS2));
    assert_param(IS_CAN_PRESCALER(CAN_InitStruct->CAN_Prescaler));
    assert_param(IS_CAN_BITRATEPRESCALER(CAN_InitStruct->CAN_BitRatePrescaler));

    /* Periphal clock enable for CAN module */
    if (CANx == CAN0)
    {
        CGC_PER0PeriphClockCmd(CGC_PER0Periph_CAN0, ENABLE);
    }
#if defined(BAT32G1XX_100PIN) ||  defined(BAT32G1XX_80PIN)
    if (CANx == CAN1)
    {
        CGC_PER2PeriphClockCmd(CGC_PER2Periph_CAN1, ENABLE);
    }
#endif	
    /* CAN periphal Initialization process */
    /* 1. Set the clock prescaler */
	CANx->CGMCS = CAN_InitStruct->CAN_Prescaler - 1;

    /* 2. Set SET_GOM and reset CLR_GOM to Enable CAN */
	CANx->CGMCTRL |=  CAN_GMCTRL_SET_GOM;
    CANx->CGMCTRL &= ~CAN_GMCTRL_CLR_GOM;

    /* 3. Set CBRP and CBTR to get the baudrate */
	CANx->CBRP = CAN_InitStruct->CAN_BitRatePrescaler - 1;
	CANx->CBTR = (uint16_t) ((uint16_t)CAN_InitStruct->CAN_SJW << 12) | \
                            ((uint16_t)CAN_InitStruct->CAN_BS2 << 8) | \
                            ((uint16_t)CAN_InitStruct->CAN_BS1 << 0);

    /* 4. Set All Mask for CAN filter */
	CANx->CMASK1L = CAN_InitStruct->MASK1 & 0xFFFF;
	CANx->CMASK1H = (CAN_InitStruct->MASK1 >> 16) & 0x1FFF;
	CANx->CMASK2L = CAN_InitStruct->MASK2 & 0xFFFF;
	CANx->CMASK2H = (CAN_InitStruct->MASK2 >> 16) & 0x1FFF;
	CANx->CMASK3L = CAN_InitStruct->MASK3 & 0xFFFF;
	CANx->CMASK3H = (CAN_InitStruct->MASK3 >> 16) & 0x1FFF;
	CANx->CMASK4L = CAN_InitStruct->MASK4 & 0xFFFF;
	CANx->CMASK4H = (CAN_InitStruct->MASK4 >> 16) & 0x1FFF;

    /* 5. Set CAN operating mode */
    CANx->CCTRL |=  (uint16_t)(CAN_InitStruct->CAN_OperateMode << 8);
    CANx->CCTRL &= ~(uint16_t)(CAN_InitStruct->CAN_OperateMode);

    /* Wait the operate complete */
    while (((CANx->CCTRL & CAN_OPMODE_MASK) != CAN_InitStruct->CAN_OperateMode) && (wait_ack != INIT_TIMEOUT))
    {
        wait_ack++;
    }

    /* Check register setting success or fail */
    if ((CANx->CCTRL & CAN_OPMODE_MASK) != CAN_InitStruct->CAN_OperateMode)
    {
        InitStatus = CAN_InitStatus_Failed;
    }

    return InitStatus;
}

/** @defgroup CAN_Group5 CAN Bus Error management functions
 *  @brief    CAN Bus Error management functions 
 *
@verbatim    
 ===============================================================================
                ##### CAN Bus Error management functions #####
 ===============================================================================  
    [..] This section provides functions allowing to 
      (+) Return the CANx's last error code (LEC)
      (+) Return the CANx Receive Error Counter (REC)
      (+) Return the LSB of the 8-bit CANx Transmit Error Counter(TEC).
   
      -@- If TEC is greater than 255, The CAN is in bus-off state.
      -@- if REC or TEC are greater than 96, an Error warning flag occurs.
      -@- if REC or TEC are greater than 127, an Error Passive Flag occurs.
                        
@endverbatim
  * @{
  */
  
/**
  * @brief  Returns the CANx's last error code (LEC).
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @retval Error code: 
  *          - CAN_ERRORCODE_NoErr: No Error  
  *          - CAN_ERRORCODE_StuffErr: Stuff Error
  *          - CAN_ERRORCODE_FormErr: Form Error
  *          - CAN_ERRORCODE_ACKErr : Acknowledgment Error
  *          - CAN_ERRORCODE_BitRecessiveErr: Bit Recessive Error
  *          - CAN_ERRORCODE_BitDominantErr: Bit Dominant Error
  *          - CAN_ERRORCODE_CRCErr: CRC Error
  */
uint8_t CAN_GetLastErrorCode(CAN_Type* CANx)
{
    uint8_t errorcode = 0;

    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));

    /* Get the error code*/
    errorcode = (((uint8_t)CANx->CLEC) & (uint8_t)CAN_CLEC_ERRNDEF_MASK);

    /* Return the error code*/
    return errorcode;
}

/**
  * @brief  Returns the CANx's error status.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @retval Error Status: 
  *          - CAN_ErrorStat_ErrActive: Error Active Status
  *          - CAN_ErrorStat_ErrPassive: Error Passive Status
  *          - CAN_ErrorStat_BusOff: Bus-Off Error
  */
uint8_t CAN_GetErrorStatus(CAN_Type* CANx)
{
    uint8_t errorstat = 0;

    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));

    if (CANx->CINFO & CAN_CINFO_BOFF_MASK)
    {
        /* Bus Off Status: Transmit error counter greater than 255 */
        if (CAN_GET_TECS(CANx->CINFO) == 0x03)
        {
            errorstat = CAN_ErrorStat_BusOff;
        }
    }
    else
    {
        if ((CANx->CERC & CAN_CERC_REPS_MASK) && \
            ((CAN_GET_TECS(CANx->CINFO) == 0x03) || CAN_GET_RECS(CANx->CINFO) == 0x03))
        {
            /* Error Passive Status: */
            errorstat = CAN_ErrorStat_ErrPassive;
        }
        else
        {
            /* Error Active Status: */
            errorstat = CAN_ErrorStat_ErrActive;
        }
    }

    return errorstat;
}

/**
  * @brief  Returns the CANx Receive Error Counter (REC).
  * @note   In case of an error during reception, this counter is incremented 
  *         by 1 or by 8 depending on the error condition as defined by the CAN 
  *         standard. After every successful reception, the counter is 
  *         decremented by 1 or reset to 120 if its value was higher than 128. 
  *         When the counter value exceeds 127, the CAN controller enters the 
  *         error passive state.  
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @retval CAN Receive Error Counter from 0 to 127. 
  */
uint8_t CAN_GetReceiveErrorCounter(CAN_Type* CANx)
{
    uint8_t counter = 0;

    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));

    /* Get the Receive Error Counter*/
    counter = CAN_GET_REC(CANx->CERC);

    if (CAN_GET_RECS(CANx->CINFO) == 0x03)
    {
        counter = 128;
    }

    /* Return the Receive Error Counter*/
    return counter;
}

/**
  * @brief  Returns the LSB of the CANx Transmit Error Counter(TEC).
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @retval LSB of the 8-bit CAN Transmit Error Counter. 
  */
uint8_t CAN_GetTransmitErrorCounter(CAN_Type* CANx)
{
    uint8_t counter = 0;

    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));

    /* Get the LSB of the CANx Transmit Error Counter(TEC) */
    counter = CAN_GET_TEC(CANx->CERC);

    /* Return the LSB of the CANx Transmit Error Counter(TEC) */
    return counter;
}

/**
  * @brief  Enables or disables the specified CANx interrupts.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  CAN_IT: specifies the CAN interrupt sources to be enabled or disabled.
  *          This parameter can be: 
  *            @arg CAN_IT_TRX:         Transmit or receive from cache Interrupt 
  *            @arg CAN_IT_REC:         Cache receive frame Interrupt
  *            @arg CAN_IT_ERR_STATE:   CAN state error Interrupt
  *            @arg CAN_IT_ERR_PROTO:   CAN protocol error Interrupt
  *            @arg CAN_IT_ERR_ARBLOST: CAN Arbitration lost error Interrupt
  *            @arg CAN_IT_WKU:         Wake-up Interrupt
  * @param  NewState: new state of the CAN interrupts.
  * @note   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void CAN_ITConfig(CAN_Type* CANx, uint8_t CAN_IT, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));
    assert_param(IS_CAN_IT(CAN_IT));
    assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)
    {
        /* Enable the selected CANx interrupt */
        CANx->CIE |=  (uint16_t)(CAN_IT << 8);
        CANx->CIE &= ~(uint16_t)(CAN_IT);
    }
    else
    {
        /* Disable the selected CANx interrupt */
        CANx->CIE &= ~(uint16_t)(CAN_IT << 8);
        CANx->CIE |=  (uint16_t)(CAN_IT);
    }
}

/**
  * @brief  Get CAN flag status for events.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  CAN_FLAG: specifies the CAN flag can be set or reset.
  *          This parameter can be: 
  *            @arg CAN_FLAG_TRX:         Transmit or receive from cache flag.
  *            @arg CAN_FLAG_REC:         Cache receive frame flag.
  *            @arg CAN_FLAG_ERR_STATE:   CAN state error flag.
  *            @arg CAN_FLAG_ERR_PROTO:   CAN protocol error flag.
  *            @arg CAN_FLAG_ERR_ARBLOST: CAN Arbitration lost error flag.
  *            @arg CAN_FLAG_WKU:         Wake-up flag.
  * @retval This parameter can be: SET or RESET.
  */
FlagStatus CAN_GetFlagStatus(CAN_Type* CANx, uint8_t CAN_FLAG)
{
    FlagStatus bitstatus = RESET;

    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));
    assert_param(IS_CAN_FLAG(CAN_FLAG));

    /* get the interrupt flag bit value */
    if ((CAN_FLAG & CANx->CINTS) != (uint16_t)RESET)
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }

    return bitstatus;
}

/**
  * @brief  Clear CAN flag status for events.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  CAN_FLAG: specifies the CAN flag can be set or reset.
  *          This parameter can be: 
  *            @arg CAN_FLAG_TRX:         Transmit or receive from cache flag.
  *            @arg CAN_FLAG_REC:         Cache receive frame flag.
  *            @arg CAN_FLAG_ERR_STATE:   CAN state error flag.
  *            @arg CAN_FLAG_ERR_PROTO:   CAN protocol error flag.
  *            @arg CAN_FLAG_ERR_ARBLOST: CAN Arbitration lost error flag.
  *            @arg CAN_FLAG_WKU:         Wake-up flag.
  * @retval None.
  */
void CAN_ClearFlag(CAN_Type* CANx, uint8_t CAN_FLAG)
{
    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));
    assert_param(IS_CAN_FLAG(CAN_FLAG));

    /* write 1 to CINTS bit to clear flag */
    CANx->CINTS = (uint16_t)CAN_FLAG;
}

/**
  * @brief  Selects the CAN Operation mode.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  CAN_OperateMode: CAN Operating Mode.
  *         This parameter can be one of @ref CAN_operating_mode.
  * @retval status of the requested mode which can be 
  *         - CAN_ModeStatus_Failed:  CAN failed entering the specific mode 
  *         - CAN_ModeStatus_Success: CAN Succeed entering the specific mode 
  */
uint8_t CAN_OperatingModeRequest(CAN_Type* CANx, uint8_t CAN_OperateMode)
{
    uint8_t status = CAN_ModeStatus_Failed;
    uint32_t timeout = SMODE_TIMEOUT; 

    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));
    assert_param(IS_CANOP_MODE(CAN_OperateMode));

    /* Set CCTRL set bit and Clear clr bit for OPMODE0~OPMODE2 register */
    CANx->CCTRL |=  (uint16_t)(CAN_OperateMode << 8);
    CANx->CCTRL &= ~(uint16_t)(CAN_OperateMode);

    /* Wait the operate complete */
    while (((CANx->CCTRL & CAN_OPMODE_MASK) != CAN_OperateMode) && (timeout != 0))
    {
        timeout--;
    }

    /* read register OPMODE0~OPMODE2 to check setting success or fail */
    if ((CANx->CCTRL & CAN_OPMODE_MASK) != 0)
    {
        status = CAN_ModeStatus_Failed;
    }
    else
    {
        status = CAN_ModeStatus_Success;
    }

    return status;
}

/**
  * @brief  Selects the CAN power save mode.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  CAN_PowersaveMode: CAN Powersave Mode.
  *         This parameter can be one of @ref CAN_powersave_mode.
  * @retval status of the requested mode which can be 
  *         - CAN_ModeStatus_Failed:  CAN failed entering the specific mode 
  *         - CAN_ModeStatus_Success: CAN Succeed entering the specific mode 
  */
uint8_t CAN_PowersaveModeRequest(CAN_Type* CANx, uint8_t CAN_PowersaveMode)
{
    uint8_t status = CAN_ModeStatus_Failed;
    uint32_t timeout = SMODE_TIMEOUT;

    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));
    assert_param(IS_CANPS_MODE(CAN_PowersaveMode));

    /* Set CCTRL set bit and Clear clr bit for PSMODE0~PSMODE1 register */
    CANx->CCTRL |=  (uint16_t)(CAN_PowersaveMode << 11);
    CANx->CCTRL &= ~(uint16_t)(CAN_PowersaveMode << 3);

    /* Wait the operate complete */
    while (((CANx->CCTRL & CAN_PSMODE_MASK) != CAN_PowersaveMode) && (timeout != 0))
    {
        timeout--;
    }

    /* read register PSMODE0~PSMODE1 to check setting success or fail */
    if ((CANx->CCTRL & CAN_PSMODE_MASK) != 0)
    {
        status = CAN_ModeStatus_Failed;
    }
    else
    {
        status = CAN_ModeStatus_Success;
    }

    return status;
}

/**
  * @brief  Configurate the CAN ABT transmit mode.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  DBT: CAN ABT transmit for each block delay time.
  * @retval status of the configurate which can be 
  *         - CAN_ModeStatus_Failed:  CAN failed configurate the specific mode 
  *         - CAN_ModeStatus_Success: CAN Succeed configurate the specific mode 
  */
uint8_t CAN_ABTModeTransmitConfig(CAN_Type* CANx, uint16_t DBT)
{
    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));
	assert_param(IS_CAN_ALL_DBT(DBT));
	
	/* When ABTTRG is set, operate will fail */
	if (CANx->CGMABT & CAN_GMABT_ABTTRG_MASK)
	{
		return CAN_ModeStatus_Failed;
	}
	
	/* When TSTAT is set, operate will fail */
	if (CANx->CCTRL & CAN_CCTRL_TSTAT_MASK)
	{
		return CAN_ModeStatus_Failed;
	}
	
	/* Set ABT time delay for each block transmit */
	CANx->CGMABTD = DBT;
	
	/* Set ABTTRG to start trigger ABT mode */
	CANx->CGMABT = CAN_GMABT_START_ABTTRG;
	
	return CAN_ModeStatus_Success;
}

/**
  * @brief  Get the CAN ABT transmit status.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @retval status of the configurate which can be 
  *         - CAN_ModeStatus_Failed:  CAN ABT is busy. 
  *         - CAN_ModeStatus_Success: CAN ABT is idle.
  */
uint8_t CAN_GetABTStatus(CAN_Type* CANx)
{
    /* Check the parameters */
    assert_param(IS_CAN_ALL_PERIPH(CANx));
	
	/* When ABTTRG is set, operate will fail */
	if (CANx->CGMABT & CAN_GMABT_ABTTRG_MASK)
	{
		return CAN_FuncStatus_Busy;
	}
	
	return CAN_FuncStatus_Idle;
}

/**
  * @brief  CAN message cache register deinit.
  * @param  CANxMSGy: where x can be 0 to select the CAN peripheral.
  *                   where y can be 0 to 15 to select the cache.
  * @retval status of the requested mode which can be 
  *         - CAN_ModeStatus_Failed:  CAN failed entering the specific mode 
  *         - CAN_ModeStatus_Success: CAN Succeed entering the specific mode 
  */
uint8_t CAN_MessageCache_DeInit(CANMSG_Type *CANxMSGy)
{
    uint32_t timeout = CACHE_TIMEOUT;

    /* Check the parameters */
    assert_param(IS_CAN_ALL_MSGCACHE(CANxMSGy));

    /* Check message cache RDY bit and clear it */
    if ((CANxMSGy->CMCTRL & CAN_MCTRL_RDY_MASK) != 0x00)
    {
        CANxMSGy->CMCTRL = CAN_MCTRL_CLR_RDY;

        /* Wait the operate complete */
        while (((CANxMSGy->CMCTRL & CAN_MCTRL_RDY_MASK) != 0x00) && (timeout != 0))
        {
            timeout--;
        }
    }

    if ((CANxMSGy->CMCTRL & CAN_MCTRL_RDY_MASK) == 0x00)
    {
        /* clear TRQ and DN bits for CMCTRL */
        CANxMSGy->CMCTRL = CAN_MCTRL_CLR_TRQ;
        CANxMSGy->CMCTRL = CAN_MCTRL_CLR_DN;

        /* clear message cache enable bit for MA0 */
        CANxMSGy->CMCONF &= ~CAN_MCONF_MA0;

        /* clear message cache mask select for MT0~MT2  */
        CANxMSGy->CMCONF &= ~CAN_MCONF_MT;
		
		/* clear all bit for message config register */
		CANxMSGy->CMCONF = 0x00;

        return CAN_ModeStatus_Success;
    }
    
    return CAN_ModeStatus_Failed;
}

/**
  * @brief  CAN message cache register init.
  * @param  CANxMSGy: where x can be 0 to select the CAN peripheral.
  *                   where y can be 0 to 15 to select the cache.
  * @param  TxRxMessage: the message for tx or rx message to change RTR ID and CacheType.
  * @retval status of the requested mode which can be 
  *         - CAN_MsgcacheInit_Failed:  CAN failed to init message cache
  *         - CAN_MsgcacheInit_Success: CAN Succeed to init message cache
  */
uint8_t CAN_MessageCache_Init(CANMSG_Type *CANxMSGy, CanTxRxMsg *TxRxMessage)
{
    /* Check the parameters */
    assert_param(IS_CAN_ALL_MSGCACHE(CANxMSGy));
    assert_param(IS_CAN_CACHETYPE(TxRxMessage->CacheType));
    assert_param(IS_CAN_IDTYPE(TxRxMessage->IDE));
    assert_param(IS_CAN_RTR(TxRxMessage->RTR));
    assert_param(IS_CAN_DLC(TxRxMessage->DLC));

    /* Set message cache used flag */
	CANxMSGy->CMCONF |= CAN_MCONF_MA0;

    /* Set message cache type */
    CANxMSGy->CMCONF |= (TxRxMessage->CacheType << 3);

    /* Message cache frame ID setting by IDType */
    if(TxRxMessage->IDE == CAN_Id_Extended)
    {
        CANxMSGy->CMIDL = TxRxMessage->Id & ((uint16_t)0xFFFF);
        CANxMSGy->CMIDH = ((TxRxMessage->Id >> 16U) & ((uint16_t)0xFFFF)) | ((uint16_t)0x8000);
    }
    else if(TxRxMessage->IDE == CAN_Id_Standard)
    {
        CANxMSGy->CMIDL = 0;
        CANxMSGy->CMIDH = (TxRxMessage->Id << 2U) & ((uint16_t)0x1FFC);
    }

    /* RTR setting to select standard frame or remote frame */
    if (TxRxMessage->RTR == CAN_RTR_Remote)
    {
        CANxMSGy->CMCONF |=  CAN_MCONF_RTR;
    }
    else if (TxRxMessage->RTR == CAN_RTR_Data)
    {
        CANxMSGy->CMCONF &= ~CAN_MCONF_RTR;
    }
	
    /* message cache overwrite config */	
	if (TxRxMessage->OverWriteConfig != DISABLE)
	{
		CANxMSGy->CMCONF |=  CAN_MCONF_OWS;
	}
	else
	{
		CANxMSGy->CMCONF &= ~CAN_MCONF_OWS;
	}
	
	/* When frame type is tx type, set frame data and length */
	if (TxRxMessage->CacheType == CAN_CacheType_Tx)
	{
		/* Set the frame data length */
		CANxMSGy->CMDLC = TxRxMessage->DLC;

		/* Set the frame data value from TxRxMessage->Data */
        CANxMSGy->CMDB0 = TxRxMessage->Data[0];
        CANxMSGy->CMDB1 = TxRxMessage->Data[1];
        CANxMSGy->CMDB2 = TxRxMessage->Data[2];
        CANxMSGy->CMDB3 = TxRxMessage->Data[3];
        CANxMSGy->CMDB4 = TxRxMessage->Data[4];
        CANxMSGy->CMDB5 = TxRxMessage->Data[5];
        CANxMSGy->CMDB6 = TxRxMessage->Data[6];
        CANxMSGy->CMDB7 = TxRxMessage->Data[7];
	}
	
	/* When the message cache interrupt enable, to set IE bit */
	if (TxRxMessage->Interrupt != DISABLE)
	{
		CANxMSGy->CMCTRL = CAN_MCTRL_SET_IE;
	}
	else
	{
		CANxMSGy->CMCTRL = CAN_MCTRL_CLR_IE;
	}
	
	/* When the message cache init, we should set RDY bit */
	CANxMSGy->CMCTRL = CAN_MCTRL_SET_RDY;
	
	return CAN_MsgcacheInit_Success;
}

/**
  * @brief  CAN periphal for message cache over write config.
  * @param  CANxMSGy: where x can be 0 to select the CAN peripheral.
  *                   where y can be 0 to 15 to select the cache.
  * @param  NewState: new state of the CAN interrupts.
  * @note   This parameter can be: ENABLE or DISABLE.
  * @retval none.
  */
void CAN_MessageCache_OverWriteConfig(CANMSG_Type *CANxMSGy, FunctionalState NewState)
{
    /* Check the parameters */
    assert_param(IS_CAN_ALL_MSGCACHE(CANxMSGy));
    assert_param(IS_FUNCTIONAL_STATE(NewState));
	
	if (NewState != DISABLE)
	{
		CANxMSGy->CMCONF |=  CAN_MCONF_OWS;
	}
	else
	{
		CANxMSGy->CMCONF &= ~CAN_MCONF_OWS;
	}
}

/**
  * @brief  Initiates and transmits a CAN frame message.
  * @param  CANxMSGy: where x can be 0 to select the CAN peripheral.
  *                   where y can be 0 to 15 to select the cache.
  * @param  TxMessage: pointer to a structure which contains CAN Id, CAN DLC and CAN data.
  * @retval 0 is failed and true value is success for send data length.
  */
uint8_t CAN_Transmit(CANMSG_Type *CANxMSGy, CanTxRxMsg* TxMessage)
{
	uint32_t timeout = CACHE_TIMEOUT;
	
    /* Check the parameters */
    assert_param(IS_CAN_ALL_MSGCACHE(CANxMSGy));
    assert_param(IS_CAN_IDTYPE(TxMessage->IDE));
    assert_param(IS_CAN_RTR(TxMessage->RTR));
    assert_param(IS_CAN_DLC(TxMessage->DLC));
	
    /* Check message cache RDY bit and clear it */
    if ((CANxMSGy->CMCTRL & CAN_MCTRL_RDY_MASK) != 0x00)
    {
        CANxMSGy->CMCTRL = CAN_MCTRL_CLR_RDY;
		
        /* Wait the operate complete */
        while (((CANxMSGy->CMCTRL & CAN_MCTRL_RDY_MASK) != 0x00) && (timeout != 0))
        {
            timeout--;
        }
    }
	
	/* check RDY clear is or not success */
    if (CANxMSGy->CMCTRL & CAN_MCTRL_RDY_MASK)
    {
        return 0;
    }

    /* Set message cache used flag */
	CANxMSGy->CMCONF |= CAN_MCONF_MA0;
	
    /* clear mask select bits */
	CANxMSGy->CMCONF &= ~CAN_MCONF_MT;

    /* Set the frame data length */
    CANxMSGy->CMDLC = TxMessage->DLC;

    /* Set the frame data value from TxMessage->Data */
    if (TxMessage->RTR == CAN_RTR_Data)
    {
        CANxMSGy->CMDB0 = TxMessage->Data[0];
        CANxMSGy->CMDB1 = TxMessage->Data[1];
        CANxMSGy->CMDB2 = TxMessage->Data[2];
        CANxMSGy->CMDB3 = TxMessage->Data[3];
        CANxMSGy->CMDB4 = TxMessage->Data[4];
        CANxMSGy->CMDB5 = TxMessage->Data[5];
        CANxMSGy->CMDB6 = TxMessage->Data[6];
        CANxMSGy->CMDB7 = TxMessage->Data[7];
    }

    /* Message cache ready bit set */
    CANxMSGy->CMCTRL = CAN_MCTRL_SET_RDY;

    /* Message cache TRQ bit set */
    CANxMSGy->CMCTRL = CAN_MCTRL_SET_TRQ;
	
	return CANxMSGy->CMDLC;
}

/**
  * @brief  A CAN frame message receive cache to memory to store.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  CANxMSGy: where x can be 0 to select the CAN peripheral.
  *                   where y can be 0 to 15 to select the cache.
  * @param  RxMessage: pointer to a structure which contains CAN Id, CAN DLC and CAN data.
  * @param  Timeout: wait timeout value for wait time.
  * @retval 0 is failed and true value is success for receive data length.
  * @note   This function is used by polling type.
  */
uint8_t CAN_Receive(CAN_Type* CANx, CanTxRxMsg* RxMessage, uint32_t Timeout)
{
	uint32_t timeout_temp = Timeout;
	uint16_t reg_crgpt = 0;
    uint8_t cache_num = 0;
    uint8_t recv_flag = 0;
	CANMSG_Type *CANxMSGy;
	
    /* Check the parameters */
	assert_param(IS_CAN_ALL_PERIPH(CANx));
    assert_param(IS_CAN_ALL_MSGCACHE(CANxMSGy));
	assert_param(RxMessage == NULL);

	/* wait interrupt flag set */
    while (timeout_temp--)
    {
        /* To wait CINTS receive flag set */
        if ((CAN_FLAG_REC & CANx->CINTS) != (uint16_t)RESET)
        {
			/* Read CRGPT register value to memory */
			reg_crgpt = CANx->CRGPT;
			
			/* clear interrupt status flag */
            CANx->CINTS = CAN_FLAG_REC;
            recv_flag = 1;
            break;
        }
    }

    /* When REC flag is not set, return 0 to completed this operate */
    if (!recv_flag)
    {
        return 0;
    }

	/* check ROVF register set or not and to clear it */
    if (reg_crgpt & CAN_CRGPT_ROVF_MASK)
    {
        CANx->CRGPT = CAN_CRGPT_CLR_ROVF;
    }
	
	/* When RHPM register is set, receive operate will finish. */
    if (reg_crgpt & CAN_CRGPT_RHPM_MASK)
    {
        return 0;
    }

	/* Get cache number and receive data length and valid data */
    cache_num = (((reg_crgpt) & CAN_CRGPT_RGPT_MASK) >> 8) & 0x0F;
	if(CANx == CAN0)
	{
		CANxMSGy = (CANMSG_Type*)CAN0MSG00 + cache_num;
	}
	else if(CANx == CAN1)
	{
		CANxMSGy = (CANMSG_Type*)CAN1MSG00 + cache_num;		
	}	
	/* clear DN register to enable next frame data cache */
    CANxMSGy->CMCTRL = CAN_MCTRL_CLR_DN;
	
	/* judge frame type is standard or extended */
	if (CANxMSGy->CMIDH & 0x8000)
	{
		/* Extended frame to fetch ID0~ID28 */
		RxMessage->IDE = CAN_Id_Extended;
		RxMessage->Id  = ((CANxMSGy->CMIDH & 0x1FFF) << 16) | (CANxMSGy->CMIDL);
	}
	else
	{
		/* Standard frame to fetch ID18~ID28 */
		RxMessage->IDE = CAN_Id_Standard;
		RxMessage->Id  = (CANxMSGy->CMIDH & 0x1FFC) >> 2;
	}
	
	/* Get receive frame data length */
    RxMessage->DLC = CANxMSGy->CMDLC;
	
	/* Get receive frame valid data to memory */
    for(int i = 0; i < RxMessage->DLC; i++)
    {
        RxMessage->Data[i] = *(((uint8_t *)&(CANxMSGy->CMDB0)) + i);
    }

	/* when DN or MUC register bit is set, the frame cache data is invalid */
    if ((CANxMSGy->CMCTRL & CAN_MCTRL_DN_MASK) || (CANxMSGy->CMCTRL & CAN_MCTRL_MUC_MASK))
    {
        return 0;
    }

    return RxMessage->DLC;
}

/**
  * @brief  obtain can mail num.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @retval CANMSG_Type for specific can mail num.
  * @note   This function is used by interrupt type.
  */
CANMSG_Type* CAN_Get_CANxMSGy(CAN_Type* CANx)
{
    uint8_t cache_num = 0;
    /* Check the parameters */
	assert_param(IS_CAN_ALL_PERIPH(CANx));

	/* Get cache number and receive data length and valid data */	
	cache_num = (((CANx->CRGPT) & CAN_CRGPT_RGPT_MASK) >> 8) & 0x0F;
	
	return ((CANMSG_Type*)CAN0MSG00+cache_num);	
}
/**
  * @brief  A CAN frame message receive cache to memory to store by interrupt.
  * @param  CANx: where x can be 0 to select the CAN peripheral.
  * @param  CANxMSGy: where x can be 0 to select the CAN peripheral.
  *                   where y can be 0 to 15 to select the cache.
  * @param  RxMessage: pointer to a structure which contains CAN Id, CAN DLC and CAN data.
  * @retval 0 is failed and true value is success for receive data length.
  * @note   This function is used by interrupt type.
  */
void CAN_Receive_IT(CAN_Type* CANx, CANBuffList_t *listbuf)
{
	uint16_t reg_crgpt = 0;
	uint8_t cache_num = 0;
	CANMSG_Type *CANxMSGy;
	CanTxRxMsg canMsgRec;
	

    /* Check the parameters */
	assert_param(IS_CAN_ALL_PERIPH(CANx));

    /* Read CRGPT register value to memory */
    reg_crgpt = CANx->CRGPT;
	
	/* check ROVF register set or not and to clear it */
    if (reg_crgpt & CAN_CRGPT_ROVF_MASK)
    {
        CANx->CRGPT = CAN_CRGPT_CLR_ROVF;
    }
		
	while(!(reg_crgpt & CAN_CRGPT_RHPM_MASK))
	{
	
		/* Get cache number and receive data length and valid data */
		cache_num = (((reg_crgpt) & CAN_CRGPT_RGPT_MASK) >> 8) & 0x0F;
		if(CANx == CAN0)
		{
			CANxMSGy = (CANMSG_Type*)CAN0MSG00 + cache_num;
		}
		else if(CANx == CAN1)
		{
			CANxMSGy = (CANMSG_Type*)CAN1MSG00 + cache_num;		
		}
		
		/* clear DN register to enable next frame data cache */
		CANxMSGy->CMCTRL = CAN_MCTRL_CLR_DN;

	
		/* judge frame type is standard or extended */
		if (CANxMSGy->CMIDH & 0x8000)
		{
			/* Extended frame to fetch ID0~ID28 */
			canMsgRec.IDE = CAN_Id_Extended;
			canMsgRec.Id = ((CANxMSGy->CMIDH & 0x1FFF) << 16) | (CANxMSGy->CMIDL);
		}
		else
		{
			/* Standard frame to fetch ID18~ID28 */
			canMsgRec.IDE = CAN_Id_Standard;
			canMsgRec.Id  = (CANxMSGy->CMIDH & 0x1FFC) >> 2;
		}
	
		/* Get receive frame data length */
			canMsgRec.DLC = CANxMSGy->CMDLC;
	
		/* Get receive frame valid data to memory */
			for(int i = 0; i < canMsgRec.DLC; i++)
			{
				canMsgRec.Data[i] = *(((uint8_t *)&(CANxMSGy->CMDB0)) + i);
			}
			
			/* Start user code. Do not edit comment generated here */
			if(listbuf->length >= LIST_BUF_MAX_NUM)
			{
				listbuf->length = 0;
			}
			listbuf->data[listbuf->Tail] = canMsgRec;
			listbuf->Tail = (listbuf->Tail+1)%LIST_BUF_MAX_NUM;
			listbuf->length++;
			
			/* End user code. Do not edit comment generated here */
			
			reg_crgpt = CANx->CRGPT;
		}
}

/***********************************************************************************************************************
* Function Name: CAN0Err_recover
* @brief  CAN error interrupt service routine
* @param  None
* @return None
***********************************************************************************************************************/
uint8_t CANErr_Recover(CAN_Type* CANx)
{
	uint8_t i;
	uint16_t can0ints;
	CANMSG_Type* pMsg;
	uint8_t canerr = 0;	
	/* Check the parameters */
	assert_param(IS_CAN_ALL_PERIPH(CANx));

	can0ints = CANx->CINTS&0x001C;//read error interrupt flag
	CANx->CINTS = can0ints;	//clear error interrupt flag
	
	if((can0ints & CAN_INTS_ERR_READ) && (can0ints & CAN_INTS_PERR_READ))
	{
		canerr = 1;
	}
	
#if 1	
	if(can0ints & CAN_INTS_ERR_READ)//ERR interrrupt?
	{
		if(CANx->CINFO & CAN_CINFO_BOFF_MASK)//bus off?
		{
			//recovery bus
			//clear all TRQ
			if(CANx == CAN0)
			{
				pMsg = (CANMSG_Type*)CAN0MSG00_BASE;
			}
			else
			{
				pMsg = (CANMSG_Type*)CAN1MSG00_BASE;			
			}
			
			for(i=0;i<16;i++)//clear all msg buffer TRQ
			{
				while(pMsg->CMCTRL & CAN_MCTRL_RDY_MASK)
				{
					pMsg->CMCTRL = CAN_MCTRL_CLR_RDY;//clear RDY
				}
				pMsg->CMCTRL = CAN_MCTRL_CLR_TRQ;//clear TRQ
				
				pMsg++;
			}
			
			CANx->CCTRL = CAN_CCTRL_CLR_CCERC| CAN_CCTRL_CLR_AL| CAN_CCTRL_CLR_VALID| CAN_CCTRL_PSMODE_IDLE| CAN_CCTRL_OPMODE_IDLE;//CAN initialize
			//Read can registor
			if(CANx->CLEC)
			{
				CANx->CLEC = 0x00;//clear CLEC
			}
			
			//set CCERC
			CANx->CCTRL = CAN_CCTRL_SET_CCERC;//clear CCERC
			
			CANx->CCTRL = CAN_CCTRL_OPMODE_NORMAL;//CAN resume
		}
	}
	
	if(can0ints & CAN_INTS_PERR_READ)//PERR interrrupt?
	{
		if(CANx->CLEC)
		{
			CANx->CLEC = 0x00;//clear CLEC
		}
	}
	
	return canerr;
#endif		
}
