/**
  **************************************************************************
  * @file     at32f435_437_can.c
  * @brief    contains all the functions for the can firmware library
  **************************************************************************
  *                       Copyright notice & Disclaimer
  *
  * The software Board Support Package (BSP) that is made available to
  * download from Artery official website is the copyrighted work of Artery.
  * Artery authorizes customers to use, copy, and distribute the BSP
  * software and its related documentation for the purpose of design and
  * development in conjunction with Artery microcontrollers. Use of the
  * software is governed by this copyright notice and the following disclaimer.
  *
  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
  *
  **************************************************************************
  */

#include "at32f435_437_conf.h"

/** @addtogroup AT32F435_437_periph_driver
  * @{
  */

/** @defgroup CAN
  * @brief CAN driver modules
  * @{
  */

#ifdef CAN_MODULE_ENABLED

/** @defgroup CAN_private_functions
  * @{
  */

/**
  * @brief  deinitialize the can peripheral registers to their default reset values.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @retval none.
  */
void can_reset(can_type* can_x)
{
  if(can_x == CAN1)
  {
    crm_periph_reset(CRM_CAN1_PERIPH_RESET, TRUE);
    crm_periph_reset(CRM_CAN1_PERIPH_RESET, FALSE);
  }
  else if(can_x == CAN2)
  {
    crm_periph_reset(CRM_CAN2_PERIPH_RESET, TRUE);
    crm_periph_reset(CRM_CAN2_PERIPH_RESET, FALSE);
  }
}

/**
  * @brief  fill each can_baudrate_struct member with its default value.
  * @param  can_baudrate_struct: pointer to a can_baudrate_type structure which will be initialized.
  * @retval none.
  */
void can_baudrate_default_para_init(can_baudrate_type* can_baudrate_struct)
{
  /* reset can baudrate structure parameters values */

  /* baud rate division */
  can_baudrate_struct->baudrate_div = 1;

  /* resynchronization adjust width */
  can_baudrate_struct->rsaw_size = CAN_RSAW_2TQ;

  /* bit time segment 1 */
  can_baudrate_struct->bts1_size = CAN_BTS1_4TQ;

  /* bit time segment 2 */
  can_baudrate_struct->bts2_size = CAN_BTS2_3TQ;
}

/**
  * @brief  set the baudrate of the can peripheral
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  can_baudrate_struct: pointer to a can_baudrate_type structure which will be set.
  * @note   baudrate calculate method is:
  *         baudrate = fpclk/(baudrate_div *(3 + bts1_size + bts2_size))
  * @retval the result of baudrate set
  *         this parameter can be one of the following values:
  *         SUCCESS or ERROR
  */
error_status can_baudrate_set(can_type* can_x, can_baudrate_type* can_baudrate_struct)
{
  error_status status_index = ERROR;
  uint32_t wait_ack_index = 0x00000000;
  /* exit from doze mode */
  can_x->mctrl_bit.dzen = FALSE;

  /* request freeze mode */
  can_x->mctrl_bit.fzen = TRUE;

  /* wait the acknowledge */
  while((!can_x->msts_bit.fzc) && (wait_ack_index != FZC_TIMEOUT))
  {
    wait_ack_index++;
  }

  /* check acknowledge */
  if(can_x->msts_bit.fzc)
  {
    can_x->btmg_bit.brdiv = can_baudrate_struct->baudrate_div - 1;
    can_x->btmg_bit.rsaw = can_baudrate_struct->rsaw_size;
    can_x->btmg_bit.bts1 = can_baudrate_struct->bts1_size;
    can_x->btmg_bit.bts2 = can_baudrate_struct->bts2_size;

    /* request leave freeze mode */
    can_x->mctrl_bit.fzen = FALSE;

    /* wait the acknowledge */
    wait_ack_index = 0;
    while((can_x->msts_bit.fzc) && (wait_ack_index != FZC_TIMEOUT))
    {
      wait_ack_index++;
    }

    /* check acknowledged */
    if(can_x->msts_bit.fzc)
    {
      status_index = ERROR;
    }
    else
    {
      status_index = SUCCESS ;
    }
  }
  else
  {
    status_index = ERROR;
  }

  /* return the status of baudrate set */
  return status_index;
}

/**
  * @brief  fill each can_init_struct member with its default value.
  * @param  can_base_struct: pointer to a can_base_type structure which will be initialized.
  * @retval none.
  */
void can_default_para_init(can_base_type* can_base_struct)
{
  /* reset can init structure parameters values */

  /* initialize the time triggered communication mode */
  can_base_struct->ttc_enable = FALSE;

  /* initialize the automatic exit bus-off management */
  can_base_struct->aebo_enable = FALSE;

  /* initialize the automatic exit doze mode */
  can_base_struct->aed_enable = FALSE;

  /* initialize the prohibit retransmission when sending fails */
  can_base_struct->prsf_enable = FALSE;

  /* initialize the message discarding rule select when overflow */
  can_base_struct->mdrsel_selection = CAN_DISCARDING_FIRST_RECEIVED;

  /* initialize the multiple message sending sequence rule */
  can_base_struct->mmssr_selection = CAN_SENDING_BY_ID;

  /* initialize the can_mode */
  can_base_struct->mode_selection = CAN_MODE_COMMUNICATE;
}

/**
  * @brief  initialize the can peripheral according to the specified
  *         parameters in the can_init_struct.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  can_base_struct: pointer to a can_base_struct structure that contains the configuration information for the can peripheral.
  * @retval the status of initialization
  *         this parameter can be one of the following values:
  *         SUCCESS or ERROR
  */
error_status can_base_init(can_type* can_x, can_base_type* can_base_struct)
{
  error_status init_status_index = ERROR;
  uint32_t wait_ack_index = 0x00000000;
  /* exit from doze mode */
  can_x->mctrl_bit.dzen = FALSE;

  /* request freeze mode */
  can_x->mctrl_bit.fzen = TRUE;

  /* wait the acknowledge */
  while((!can_x->msts_bit.fzc) && (wait_ack_index != FZC_TIMEOUT))
  {
    wait_ack_index++;
  }

  /* check acknowledge */
  if(can_x->msts_bit.fzc)
  {
    /* set the time triggered communication mode */
    can_x->mctrl_bit.ttcen = can_base_struct->ttc_enable;

    /* set the automatic exit bus-off management */
    can_x->mctrl_bit.aeboen = can_base_struct->aebo_enable;

    /* set the automatic automatic exit doze mode */
    can_x->mctrl_bit.aeden = can_base_struct->aed_enable;

    /* set the prohibit retransmission when sending fails */
    can_x->mctrl_bit.prsfen = can_base_struct->prsf_enable;

    /* set the message discarding rule select when overflow */
    can_x->mctrl_bit.mdrsel = can_base_struct->mdrsel_selection;

    /* set the multiple message sending sequence rule */
    can_x->mctrl_bit.mmssr = can_base_struct->mmssr_selection;

    /* set the test mode */
    can_x->btmg_bit.lben = can_base_struct->mode_selection & 0x01;
    can_x->btmg_bit.loen = (can_base_struct->mode_selection >> 1) & 0x01;

    /* request leave freeze mode */
    can_x->mctrl_bit.fzen = FALSE;

    /* wait the acknowledge */
    wait_ack_index = 0;
    while((can_x->msts_bit.fzc) && (wait_ack_index != FZC_TIMEOUT))
    {
      wait_ack_index++;
    }

    /* check acknowledged */
    if(can_x->msts_bit.fzc)
    {
      init_status_index = ERROR;
    }
    else
    {
      init_status_index = SUCCESS ;
    }
  }
  else
  {
    init_status_index = ERROR;
  }

  /* return the status of initialization */
  return init_status_index;
}

/**
  * @brief  fill each can_filter_init_struct member with its default value.
  * @param  can_filter_init_struct: pointer to a can_filter_init_type structure which will be initialized.
  * @retval none.
  */
void can_filter_default_para_init(can_filter_init_type* can_filter_init_struct)
{
  /* reset can filter init structure parameters values */

  /* initialize the filter activate state */
  can_filter_init_struct->filter_activate_enable = FALSE;

  /* filter mode */
  can_filter_init_struct->filter_mode = CAN_FILTER_MODE_ID_MASK;

  /* filter relation fifo select */
  can_filter_init_struct->filter_fifo = CAN_FILTER_FIFO0;

  /* filter number select */
  can_filter_init_struct->filter_number = 0;

  /* initialize the filter bit width */
  can_filter_init_struct->filter_bit = CAN_FILTER_16BIT;

  /* initialize the filters filter data bit */
  can_filter_init_struct->filter_id_high = 0;
  can_filter_init_struct->filter_id_low = 0;
  can_filter_init_struct->filter_mask_high = 0;
  can_filter_init_struct->filter_mask_low = 0;
}

/**
  * @brief  initialize the can peripheral according to the specified
  *         parameters in the can_filter_init_struct.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  can_filter_init_struct: pointer to a can_filter_init_type structure that contains the configuration information.
  * @retval none.
  */
void can_filter_init(can_type* can_x, can_filter_init_type* can_filter_init_struct)
{
  uint32_t filter_number_bit_pos = 0;
  filter_number_bit_pos = ((uint32_t)1) << can_filter_init_struct->filter_number;
  /* set the filter turn into configuration condition */
  can_x->fctrl_bit.fcs = TRUE;

  /* filter activate disable */
  can_x->facfg &= ~(uint32_t)filter_number_bit_pos;

  /* filter bit width */
  switch(can_filter_init_struct->filter_bit)
  {
    case CAN_FILTER_16BIT:
      can_x->fbwcfg &= ~(uint32_t)filter_number_bit_pos;
      /* first 16-bit identifier and first 16-bit mask or first 16-bit identifier and second 16-bit identifier */
      can_x->ffb[can_filter_init_struct->filter_number].ffdb1 = ((0x0000FFFF & (uint32_t)can_filter_init_struct->filter_mask_low) << 16);
      can_x->ffb[can_filter_init_struct->filter_number].ffdb1 |= (0x0000FFFF & (uint32_t)can_filter_init_struct->filter_id_low);

      /* second 16-bit identifier and second 16-bit mask or third 16-bit identifier and fourth 16-bit identifier */
      can_x->ffb[can_filter_init_struct->filter_number].ffdb2 = ((0x0000FFFF & (uint32_t)can_filter_init_struct->filter_mask_high) << 16);
      can_x->ffb[can_filter_init_struct->filter_number].ffdb2 |= (0x0000FFFF & (uint32_t)can_filter_init_struct->filter_id_high);

      break;
    case CAN_FILTER_32BIT:
      can_x->fbwcfg |= filter_number_bit_pos;
      /* 32-bit identifier or first 32-bit identifier */
      can_x->ffb[can_filter_init_struct->filter_number].ffdb1 = ((0x0000FFFF & (uint32_t)can_filter_init_struct->filter_id_high) << 16);
      can_x->ffb[can_filter_init_struct->filter_number].ffdb1 |= (0x0000FFFF & (uint32_t)can_filter_init_struct->filter_id_low);

      /* 32-bit mask or second 32-bit identifier */
      can_x->ffb[can_filter_init_struct->filter_number].ffdb2 = ((0x0000FFFF & (uint32_t)can_filter_init_struct->filter_mask_high) << 16);
      can_x->ffb[can_filter_init_struct->filter_number].ffdb2 |= (0x0000FFFF & (uint32_t)can_filter_init_struct->filter_mask_low);

      break;
    default:
      break;
  }

  /* filter mode */
  switch(can_filter_init_struct->filter_mode)
  {
    case CAN_FILTER_MODE_ID_MASK:
      can_x->fmcfg &= ~(uint32_t)filter_number_bit_pos;
      break;
    case CAN_FILTER_MODE_ID_LIST:
      can_x->fmcfg |= (uint32_t)filter_number_bit_pos;
      break;
    default:
      break;
  }

  /* filter relation fifo select */
  switch(can_filter_init_struct->filter_fifo)
  {
    case CAN_FILTER_FIFO0:
      can_x->frf &= ~(uint32_t)filter_number_bit_pos;
      break;
    case CAN_FILTER_FIFO1:
      can_x->frf |= (uint32_t)filter_number_bit_pos;
      break;
    default:
      break;
  }

  /* filter activate enable */
  switch(can_filter_init_struct->filter_activate_enable)
  {
    case TRUE:
      can_x->facfg |= (uint32_t)filter_number_bit_pos;
      break;
    case FALSE:
      can_x->facfg &= ~(uint32_t)filter_number_bit_pos;
      break;
    default:
      break;
  }

  /* set the filter turn into working condition */
  can_x->fctrl_bit.fcs = FALSE;
}

/**
  * @brief  enable or disable the debug transmission prohibit of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  new_state: new state of debug transmission prohibit.
  *         this parameter can be: TRUE or FALSE.
  * @retval none.
  */
void can_debug_transmission_prohibit(can_type* can_x, confirm_state new_state)
{
  can_x->mctrl_bit.ptd = new_state;
}

/**
  * @brief  enable or disable time trigger operation communication mode of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1 or CAN2.
  * @param  new_state : new state of time trigger operation communication mode.
  *         this parameter can be: TRUE or FALSE.
  * @note
  *         note1:
  *         when enabled, transmit mailbox time stamp(tmts[15:0]) value is sent in the last two data bytes of
  *         the 8-byte message: tmts[7:0] in data byte 6 and tmts[15:8] in data byte 7
  * @note
  *         note2:
  *         tmdtbl must be programmed as 8 in order time stamp (2 bytes) to be sent over the can bus.
  * @retval none
  */
void can_ttc_mode_enable(can_type* can_x, confirm_state new_state)
{
  /* config the ttc mode new_state */
  can_x->mctrl_bit.ttcen = new_state;

  /* config tmtsten bits new_state */
  can_x->tx_mailbox[0].tmc_bit.tmtsten = new_state;
  can_x->tx_mailbox[1].tmc_bit.tmtsten = new_state;
  can_x->tx_mailbox[2].tmc_bit.tmtsten = new_state;
}

/**
  * @brief  fill the transmission message and transmit of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  tx_message_struct: pointer to a structure which contains the message to be trans.
  * @retval the number of the mailbox that is used for transmission:
  *         this parameter can be one of the following values:
  *         - CAN_TX_MAILBOX0
  *         - CAN_TX_MAILBOX1
  *         - CAN_TX_MAILBOX2
  *         - CAN_TX_STATUS_NO_EMPTY <meanings there is no empty mailbox, message cannot be filled>
  */
uint8_t can_message_transmit(can_type* can_x, can_tx_message_type* tx_message_struct)
{
  uint8_t transmit_mailbox = CAN_TX_STATUS_NO_EMPTY;

  /* select one empty transmit mailbox */
  if(can_x->tsts_bit.tm0ef)
  {
    transmit_mailbox = CAN_TX_MAILBOX0;
  }
  else if(can_x->tsts_bit.tm1ef)
  {
    transmit_mailbox = CAN_TX_MAILBOX1;
  }
  else if(can_x->tsts_bit.tm2ef)
  {
    transmit_mailbox = CAN_TX_MAILBOX2;
  }
  else
  {
    transmit_mailbox = CAN_TX_STATUS_NO_EMPTY;
  }

  if(transmit_mailbox != CAN_TX_STATUS_NO_EMPTY)
  {
    /* set up the id */
    can_x->tx_mailbox[transmit_mailbox].tmi &= 0x00000001;
    can_x->tx_mailbox[transmit_mailbox].tmi_bit.tmidsel = tx_message_struct->id_type;
    switch(tx_message_struct->id_type)
    {
      case CAN_ID_STANDARD:
        can_x->tx_mailbox[transmit_mailbox].tmi_bit.tmsid = tx_message_struct->standard_id;
        break;
      case CAN_ID_EXTENDED:
        can_x->tx_mailbox[transmit_mailbox].tmi |= (tx_message_struct->extended_id << 3);
        break;
      default:
        break;
    }
    can_x->tx_mailbox[transmit_mailbox].tmi_bit.tmfrsel = tx_message_struct->frame_type;
    /* set up the dlc */
    can_x->tx_mailbox[transmit_mailbox].tmc_bit.tmdtbl = (tx_message_struct->dlc & ((uint8_t)0x0F));

    /* set up the data field */
    can_x->tx_mailbox[transmit_mailbox].tmdtl = (((uint32_t)tx_message_struct->data[3] << 24) |
                                                ((uint32_t)tx_message_struct->data[2] << 16) |
                                                ((uint32_t)tx_message_struct->data[1] << 8) |
                                                ((uint32_t)tx_message_struct->data[0]));
    can_x->tx_mailbox[transmit_mailbox].tmdth = (((uint32_t)tx_message_struct->data[7] << 24) |
                                                ((uint32_t)tx_message_struct->data[6] << 16) |
                                                ((uint32_t)tx_message_struct->data[5] << 8) |
                                                ((uint32_t)tx_message_struct->data[4]));

    /* request transmission */
    can_x->tx_mailbox[transmit_mailbox].tmi_bit.tmsr = TRUE;
  }
  return transmit_mailbox;
}

/**
  * @brief  check the transmission state of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1 or CAN2.
  * @param  transmit_mailbox: the number of the mailbox that is used for transmission.
  *         this parameter can be one of the following values:
  *         - CAN_TX_MAILBOX0
  *         - CAN_TX_MAILBOX1
  *         - CAN_TX_MAILBOX2
  * @retval can transmit status
  *         this parameter can be one of the following values:
  *         - CAN_TX_STATUS_SUCCESSFUL
  *         - CAN_TX_STATUS_FAILED
  *         - CAN_TX_STATUS_PENDING
  */
can_transmit_status_type can_transmit_status_get(can_type* can_x, can_tx_mailbox_num_type transmit_mailbox)
{
  can_transmit_status_type state_index = CAN_TX_STATUS_FAILED;
  switch(transmit_mailbox)
  {
    case CAN_TX_MAILBOX0:
      if(can_x->tsts_bit.tm0tcf != RESET)
      {
        if(can_x->tsts_bit.tm0tsf != RESET)
        {
          state_index = CAN_TX_STATUS_SUCCESSFUL;
        }
        else
        {
          state_index = CAN_TX_STATUS_FAILED;
        }
      }
      else
      {
        state_index = CAN_TX_STATUS_PENDING;
      }
      break;
    case CAN_TX_MAILBOX1:
      if(can_x->tsts_bit.tm1tcf != RESET)
      {
        if(can_x->tsts_bit.tm1tsf != RESET)
        {
          state_index = CAN_TX_STATUS_SUCCESSFUL;
        }
        else
        {
          state_index = CAN_TX_STATUS_FAILED;
        }
      }
      else
      {
        state_index = CAN_TX_STATUS_PENDING;
      }
      break;
    case CAN_TX_MAILBOX2:
      if(can_x->tsts_bit.tm2tcf != RESET)
      {
        if(can_x->tsts_bit.tm2tsf != RESET)
        {
          state_index = CAN_TX_STATUS_SUCCESSFUL;
        }
        else
        {
          state_index = CAN_TX_STATUS_FAILED;
        }
      }
      else
      {
        state_index = CAN_TX_STATUS_PENDING;
      }
      break;
    default:
      state_index = CAN_TX_STATUS_FAILED;
      break;
  }
  return state_index;
}

/**
  * @brief  cancel a transmit request of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1 or CAN2.
  * @param  mailbox:  mailbox number.
  *         this parameter can be one of the following values:
  *         - CAN_TX_MAILBOX0
  *         - CAN_TX_MAILBOX1
  *         - CAN_TX_MAILBOX2
  * @retval none.
  */
void can_transmit_cancel(can_type* can_x, can_tx_mailbox_num_type transmit_mailbox)
{
  switch (transmit_mailbox)
  {
    case CAN_TX_MAILBOX0:
      can_x->tsts = CAN_TSTS_TM0CT_VAL;
      break;
    case CAN_TX_MAILBOX1:
      can_x->tsts = CAN_TSTS_TM1CT_VAL;
      break;
    case CAN_TX_MAILBOX2:
      can_x->tsts = CAN_TSTS_TM2CT_VAL;
      break;
    default:
      break;
  }
}

/**
  * @brief  receive message of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  fifo_number: receive fifo number.
  *         this parameter can be one of the following values:
  *         - CAN_RX_FIFO0
  *         - CAN_RX_FIFO1
  * @param  rx_message_struct: pointer to a structure which store the receive message.
  * @retval none.
  */
void can_message_receive(can_type* can_x, can_rx_fifo_num_type fifo_number, can_rx_message_type* rx_message_struct)
{
  /* get the id type */
  rx_message_struct->id_type = (can_identifier_type)can_x->fifo_mailbox[fifo_number].rfi_bit.rfidi;
  switch (rx_message_struct->id_type)
  {
    case CAN_ID_STANDARD:
      rx_message_struct->standard_id = can_x->fifo_mailbox[fifo_number].rfi_bit.rfsid;
      break;
    case CAN_ID_EXTENDED:
      rx_message_struct->extended_id = 0x1FFFFFFF & (can_x->fifo_mailbox[fifo_number].rfi >> 3);
      break;
    default:
      break;
  }
  rx_message_struct->frame_type = (can_trans_frame_type)can_x->fifo_mailbox[fifo_number].rfi_bit.rffri;
  /* get the dlc */
  rx_message_struct->dlc = can_x->fifo_mailbox[fifo_number].rfc_bit.rfdtl;

  /* get the filter match number */
  rx_message_struct->filter_index = can_x->fifo_mailbox[fifo_number].rfc_bit.rffmn;

  /* get the data field */
  rx_message_struct->data[0] = can_x->fifo_mailbox[fifo_number].rfdtl_bit.rfdt0;
  rx_message_struct->data[1] = can_x->fifo_mailbox[fifo_number].rfdtl_bit.rfdt1;
  rx_message_struct->data[2] = can_x->fifo_mailbox[fifo_number].rfdtl_bit.rfdt2;
  rx_message_struct->data[3] = can_x->fifo_mailbox[fifo_number].rfdtl_bit.rfdt3;
  rx_message_struct->data[4] = can_x->fifo_mailbox[fifo_number].rfdth_bit.rfdt4;
  rx_message_struct->data[5] = can_x->fifo_mailbox[fifo_number].rfdth_bit.rfdt5;
  rx_message_struct->data[6] = can_x->fifo_mailbox[fifo_number].rfdth_bit.rfdt6;
  rx_message_struct->data[7] = can_x->fifo_mailbox[fifo_number].rfdth_bit.rfdt7;

  /* release the fifo */
  can_receive_fifo_release(can_x, fifo_number);
}

/**
  * @brief  release the specified fifo of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  fifo_number: fifo to be release.
  *         this parameter can be one of the following values:
  *         - CAN_RX_FIFO0
  *         - CAN_RX_FIFO1
  * @retval none.
  */
void can_receive_fifo_release(can_type* can_x, can_rx_fifo_num_type fifo_number)
{
  switch (fifo_number)
  {
    case CAN_RX_FIFO0:
      can_x->rf0 = CAN_RF0_RF0R_VAL;
      break;
    case CAN_RX_FIFO1:
      can_x->rf1 = CAN_RF1_RF1R_VAL;
      break;
    default:
      break;
  }
}

/**
  * @brief  return the number of pending messages of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  fifo_number: receive fifo number.
  *         this parameter can be one of the following values:
  *         - CAN_RX_FIFO0
  *         - CAN_RX_FIFO1
  * @retval the number of message pending in the receive fifo.
  */
uint8_t can_receive_message_pending_get(can_type* can_x, can_rx_fifo_num_type fifo_number)
{
  uint8_t message_pending = 0;
  switch (fifo_number)
  {
    case CAN_RX_FIFO0:
      message_pending = can_x->rf0_bit.rf0mn;
      break;
    case CAN_RX_FIFO1:
      message_pending = can_x->rf1_bit.rf1mn;
      break;
    default:
      break;
  }
  return message_pending;
}

/**
  * @brief  set the operation mode of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  can_operating_mode: can operating mode.
  *         this parameter can be one of the following values:
  *         - CAN_OPERATINGMODE_FREEZE
  *         - CAN_OPERATINGMODE_DOZE
  *         - CAN_OPERATINGMODE_COMMUNICATE
  * @retval status of operation mode set
  *         this parameter can be one of the following values:
  *         SUCCESS or ERROR
  */
error_status can_operating_mode_set(can_type* can_x, can_operating_mode_type can_operating_mode)
{
  error_status status = ERROR;
  uint32_t time_out_index = FZC_TIMEOUT;

  if (can_operating_mode == CAN_OPERATINGMODE_FREEZE)
  {
    /* request enter freeze mode */
    can_x->mctrl_bit.dzen = FALSE;
    can_x->mctrl_bit.fzen = TRUE;

    while(((can_x->msts_bit.dzc) || (!can_x->msts_bit.fzc)) && (time_out_index != 0))
    {
      time_out_index--;
    }
    if((can_x->msts_bit.dzc) || (!can_x->msts_bit.fzc))
    {
      status = ERROR;
    }
    else
    {
      status = SUCCESS;
    }
  }
  else if(can_operating_mode == CAN_OPERATINGMODE_DOZE)
  {
    /* request enter doze mode */
    can_x->mctrl_bit.dzen = TRUE;
    can_x->mctrl_bit.fzen = FALSE;

    while(((!can_x->msts_bit.dzc) || (can_x->msts_bit.fzc)) && (time_out_index != 0))
    {
      time_out_index--;
    }
    if((!can_x->msts_bit.dzc) || (can_x->msts_bit.fzc))
    {
      status = ERROR;
    }
    else
    {
      status = SUCCESS;
    }
  }
  else if(can_operating_mode == CAN_OPERATINGMODE_COMMUNICATE)
  {
    /* request enter normal mode */
    can_x->mctrl_bit.dzen = FALSE;
    can_x->mctrl_bit.fzen = FALSE;

    while(((can_x->msts_bit.dzc) || (can_x->msts_bit.fzc)) && (time_out_index != 0))
    {
      time_out_index--;
    }
    if((can_x->msts_bit.dzc) || (can_x->msts_bit.fzc))
    {
      status = ERROR;
    }
    else
    {
      status = SUCCESS;
    }
  }
  else
  {
    status = ERROR;
  }
  return status;
}

/**
  * @brief  enter the low power mode of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @retval status of doze mode enter, the returned value can be:
  *         - CAN_ENTER_DOZE_SUCCESSFUL  <it meaning enter the doze mode succeed>
  *         - CAN_ENTER_DOZE_FAILED  <it meaning enter the doze mode failed>
  */
can_enter_doze_status_type can_doze_mode_enter(can_type* can_x)
{
  can_enter_doze_status_type status = CAN_ENTER_DOZE_FAILED;
  uint32_t time_out_index = FZC_TIMEOUT;
  can_x->mctrl_bit.fzen = FALSE;
  can_x->mctrl_bit.dzen = TRUE;
  while(((!can_x->msts_bit.dzc) || (can_x->msts_bit.fzc)) && (time_out_index != 0))
  {
    time_out_index--;
  }
  if((!can_x->msts_bit.dzc) || (can_x->msts_bit.fzc))
  {
    status =  CAN_ENTER_DOZE_FAILED;
  }
  else
  {
    status =  CAN_ENTER_DOZE_SUCCESSFUL;
  }
  return status;
}

/**
  * @brief  exit the doze mode of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @retval status of doze mode enter, the returned value can be:
  *         - CAN_QUIT_DOZE_SUCCESSFUL  <it meaning exit the doze mode succeed>
  *         - CAN_QUIT_DOZE_FAILED  <it meaning exit the doze mode failed>
  */
can_quit_doze_status_type can_doze_mode_exit(can_type* can_x)
{
  can_quit_doze_status_type status = CAN_QUIT_DOZE_FAILED;
  uint32_t time_out_index = DZC_TIMEOUT;
  can_x->mctrl_bit.dzen = FALSE;
  while((can_x->msts_bit.dzc) && (time_out_index != 0))
  {
    time_out_index--;
  }
  if(!can_x->msts_bit.dzc)
  {
    status = CAN_QUIT_DOZE_SUCCESSFUL;
  }
  return status;
}

/**
  * @brief  return the error type record (etr) of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @retval the value of the error code
  *         the return can be one of the follow values:
  *         - CAN_ERRORRECORD_NOERR
  *         - CAN_ERRORRECORD_STUFFERR,
  *         - CAN_ERRORRECORD_FORMERR,
  *         - CAN_ERRORRECORD_ACKERR,
  *         - CAN_ERRORRECORD_BITRECESSIVEERR,
  *         - CAN_ERRORRECORD_BITDOMINANTERR,
  *         - CAN_ERRORRECORD_CRCERR,
  *         - CAN_ERRORRECORD_SOFTWARESETERR
  */
can_error_record_type can_error_type_record_get(can_type* can_x)
{
  can_error_record_type error_code = CAN_ERRORRECORD_NOERR;

  error_code = (can_error_record_type)can_x->ests_bit.etr;
  return error_code;
}

/**
  * @brief  return the receive error counter (rec) of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @retval the value of receive error counter.
  */
uint8_t can_receive_error_counter_get(can_type* can_x)
{
  uint8_t counter = 0;
  counter = can_x->ests_bit.rec;
  return counter;
}

/**
  * @brief  return the transmit error counter of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @retval the value of transmit error counter.
  */
uint8_t can_transmit_error_counter_get(can_type* can_x)
{
  uint8_t counter = 0;
  counter = can_x->ests_bit.tec;
  return counter;
}

/**
  * @brief  enable or disable the interrupt of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  can_int: specifies the can interrupt sources to be enabled or disabled.
  *         this parameter can be one of the following values:
  *         - CAN_TCIEN_INT
  *         - CAN_RF0MIEN_INT
  *         - CAN_RF0FIEN_INT
  *         - CAN_RF0OIEN_INT
  *         - CAN_RF1MIEN_INT
  *         - CAN_RF1FIEN_INT
  *         - CAN_RF1OIEN_INT
  *         - CAN_EAIEN_INT
  *         - CAN_EPIEN_INT
  *         - CAN_BOIEN_INT
  *         - CAN_ETRIEN_INT
  *         - CAN_EOIEN_INT
  *         - CAN_QDZIEN_INT
  *         - CAN_EDZIEN_INT
  * @param  new_state: new state of the can interrupts.
  *         this parameter can be: TRUE or FALSE.
  * @retval none.
  */
void can_interrupt_enable(can_type* can_x, uint32_t can_int, confirm_state new_state)
{
  if (new_state != FALSE)
  {
    can_x->inten |= can_int;
  }
  else
  {
    can_x->inten &= ~can_int;
  }
}

/**
  * @brief  get flag of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  can_flag: select the flag.
  *         this parameter can be one of the following flags:
  *         - CAN_EAF_FLAG
  *         - CAN_EPF_FLAG
  *         - CAN_BOF_FLAG
  *         - CAN_ETR_FLAG
  *         - CAN_EOIF_FLAG
  *         - CAN_TM0TCF_FLAG
  *         - CAN_TM1TCF_FLAG
  *         - CAN_TM2TCF_FLAG
  *         - CAN_RF0MN_FLAG
  *         - CAN_RF0FF_FLAG
  *         - CAN_RF0OF_FLAG
  *         - CAN_RF1MN_FLAG
  *         - CAN_RF1FF_FLAG
  *         - CAN_RF1OF_FLAG
  *         - CAN_QDZIF_FLAG
  *         - CAN_EDZC_FLAG
  *         - CAN_TMEF_FLAG
  *         note:the state of CAN_EDZC_FLAG need to check dzc and edzif bit
  *         note:the state of CAN_TMEF_FLAG need to check rqc0,rqc1 and rqc2 bit
  * @retval status of can_flag, the returned value can be:SET or RESET.
  */
flag_status can_flag_get(can_type* can_x, uint32_t can_flag)
{
  flag_status bit_status = RESET;
  switch(can_flag)
  {
    case CAN_EAF_FLAG:
      bit_status = (flag_status)can_x->ests_bit.eaf;
      break;
    case CAN_EPF_FLAG:
      bit_status = (flag_status)can_x->ests_bit.epf;
      break;
    case CAN_BOF_FLAG:
      bit_status = (flag_status)can_x->ests_bit.bof;
      break;
    case CAN_ETR_FLAG:
      if(can_x->ests_bit.etr != 0)
      {
        bit_status = SET;
      }
      else
      {
        bit_status = RESET;
      }
      break;
    case CAN_EOIF_FLAG:
      bit_status = (flag_status)can_x->msts_bit.eoif;
      break;
    case CAN_TM0TCF_FLAG:
      bit_status = (flag_status)can_x->tsts_bit.tm0tcf;
      break;
    case CAN_TM1TCF_FLAG:
      bit_status = (flag_status)can_x->tsts_bit.tm1tcf;
      break;
    case CAN_TM2TCF_FLAG:
      bit_status = (flag_status)can_x->tsts_bit.tm2tcf;
      break;
    case CAN_RF0MN_FLAG:
      if(can_x->rf0_bit.rf0mn != 0)
      {
        bit_status = SET;
      }
      else
      {
        bit_status = RESET;
      }
      break;
    case CAN_RF0FF_FLAG:
      bit_status = (flag_status)can_x->rf0_bit.rf0ff;
      break;
    case CAN_RF0OF_FLAG:
      bit_status = (flag_status)can_x->rf0_bit.rf0of;
      break;
    case CAN_RF1MN_FLAG:
      if(can_x->rf1_bit.rf1mn != 0)
      {
        bit_status = SET;
      }
      else
      {
        bit_status = RESET;
      }
      break;
    case CAN_RF1FF_FLAG:
      bit_status = (flag_status)can_x->rf1_bit.rf1ff;
      break;
    case CAN_RF1OF_FLAG:
      bit_status = (flag_status)can_x->rf1_bit.rf1of;
      break;
    case CAN_QDZIF_FLAG:
      bit_status = (flag_status)can_x->msts_bit.qdzif;
      break;
    case CAN_EDZC_FLAG:
      if((can_x->msts_bit.dzc != RESET) ||(can_x->msts_bit.edzif != RESET))
      {
        bit_status = SET;
      }
      else
      {
        bit_status = RESET;
      }
      break;
    case CAN_TMEF_FLAG:
      if((can_x->tsts_bit.tm0ef != RESET) || (can_x->tsts_bit.tm1ef != RESET) || (can_x->tsts_bit.tm2ef != RESET))
      {
        bit_status = SET;
      }
      else
      {
        bit_status = RESET;
      }
      break;
    default:
      bit_status = RESET;
      break;
  }
  return bit_status;
}

/**
  * @brief  clear flag of the specified can peripheral.
  * @param  can_x: select the can peripheral.
  *         this parameter can be one of the following values:
  *         CAN1,CAN2.
  * @param  can_flag: select the flag.
  *         this parameter can be one of the following flags:
  *         - CAN_EAF_FLAG
  *         - CAN_EPF_FLAG
  *         - CAN_BOF_FLAG
  *         - CAN_ETR_FLAG
  *         - CAN_EOIF_FLAG
  *         - CAN_TM0TCF_FLAG
  *         - CAN_TM1TCF_FLAG
  *         - CAN_TM2TCF_FLAG
  *         - CAN_RF0FF_FLAG
  *         - CAN_RF0OF_FLAG
  *         - CAN_RF1FF_FLAG
  *         - CAN_RF1OF_FLAG
  *         - CAN_QDZIF_FLAG
  *         - CAN_EDZC_FLAG
  *         - CAN_TMEF_FLAG
  *         note:CAN_RF0MN_FLAG and CAN_RF1MN_FLAG can not clear by this function
  * @retval none.
  */
void can_flag_clear(can_type* can_x, uint32_t can_flag)
{
  switch(can_flag)
  {
    case CAN_EAF_FLAG:
    case CAN_EPF_FLAG:
    case CAN_BOF_FLAG:
    case CAN_EOIF_FLAG:
      can_x->msts = CAN_MSTS_EOIF_VAL;
      break;
    case CAN_ETR_FLAG:
      can_x->msts = CAN_MSTS_EOIF_VAL;
      can_x->ests = 0;
      break;
    case CAN_TM0TCF_FLAG:
      can_x->tsts = CAN_TSTS_TM0TCF_VAL;
      break;
    case CAN_TM1TCF_FLAG:
      can_x->tsts = CAN_TSTS_TM1TCF_VAL;
      break;
    case CAN_TM2TCF_FLAG:
      can_x->tsts = CAN_TSTS_TM2TCF_VAL;
      break;
    case CAN_RF0FF_FLAG:
      can_x->rf0 = CAN_RF0_RF0FF_VAL;
      break;
    case CAN_RF0OF_FLAG:
      can_x->rf0 = CAN_RF0_RF0OF_VAL;
      break;
    case CAN_RF1FF_FLAG:
      can_x->rf1 = CAN_RF1_RF1FF_VAL;
      break;
    case CAN_RF1OF_FLAG:
      can_x->rf1 = CAN_RF1_RF1OF_VAL;
      break;
    case CAN_QDZIF_FLAG:
      can_x->msts = CAN_MSTS_QDZIF_VAL;
      break;
    case CAN_EDZC_FLAG:
      can_x->msts = CAN_MSTS_EDZIF_VAL;
      break;
    case CAN_TMEF_FLAG:
      can_x->tsts = CAN_TSTS_TM0TCF_VAL | CAN_TSTS_TM1TCF_VAL | CAN_TSTS_TM2TCF_VAL;
      break;
    default:
      break;
  }
}

/**
  * @}
  */

#endif

/**
  * @}
  */

/**
  * @}
  */
