/* 
* classCan.cpp
*
* Created: 23/05/2018 17:40:57
* Author: Ledo
*/


#include "classCan.h"

// default constructor
classCan::classCan()
{
} //classCan

// default destructor
classCan::~classCan()
{
} //~classCan


/**
 * \brief Configure CAN baudrate.
 *
 * \param p_can       Pointer to a CAN peripheral instance.
 * \param ul_mck      The input main clock for the CAN module.
 * \param ul_baudrate Baudrate value (kB/s), allowed values:
 *                    1000, 800, 500, 250, 125, 50, 25, 10, 5.
 *
 * \retval Set the baudrate successfully or not.
 */
uint32_t classCan::can_set_baudrate(Can *p_can, uint32_t ul_mck,
		uint32_t ul_baudrate)
{
	uint8_t uc_tq;
	uint8_t uc_prescale;
	uint32_t ul_mod;
	uint32_t ul_cur_mod;
	can_bit_timing_t *p_bit_time;

	/* Check whether the baudrate prescale will be greater than the max
	 * divide value. */
	if (((ul_mck + (ul_baudrate * CAN_MAX_TQ_NUM * 1000 - 1)) /
			(ul_baudrate * CAN_MAX_TQ_NUM * 1000)) >
			CAN_BAUDRATE_MAX_DIV) {
		return 0;
	}

	/* Check whether the input MCK is too small. */
	if ((ul_mck / 2)  < ul_baudrate * CAN_MIN_TQ_NUM * 1000) {
		return 0;
	}

	/* Initialize it as the minimum Time Quantum. */
	uc_tq = CAN_MIN_TQ_NUM;

	/* Initialize the remainder as the max value. When the remainder is 0,
	 *get the right TQ number. */
	ul_mod = 0xffffffff;
	/* Find out the approximate Time Quantum according to the baudrate. */
	for (uint8_t i = CAN_MIN_TQ_NUM; i <= CAN_MAX_TQ_NUM; i++) {
		if ((ul_mck / (ul_baudrate * i * 1000)) <=
				CAN_BAUDRATE_MAX_DIV) {
			ul_cur_mod = ul_mck % (ul_baudrate * i * 1000);
			if (ul_cur_mod < ul_mod) {
				ul_mod = ul_cur_mod;
				uc_tq = i;
				if (!ul_mod) {
					break;
				}
			}
		}
	}

	/* Calculate the baudrate prescale value. */
	uc_prescale = ul_mck / (ul_baudrate * uc_tq * 1000);
	if (uc_prescale < 2) {
		return 0;
	}

	/* Get the right CAN BIT Timing group. */
	p_bit_time = (can_bit_timing_t *)&can_bit_time[uc_tq - CAN_MIN_TQ_NUM];

	/* Before modifying the CANBR register, disable the CAN controller. */
	can_disable(p_can);

	/* Write into the CAN baudrate register. */
	p_can->CAN_BR = CAN_BR_PHASE2(p_bit_time->uc_phase2 - 1) |
			CAN_BR_PHASE1(p_bit_time->uc_phase1 - 1) |
			CAN_BR_PROPAG(p_bit_time->uc_prog - 1) |
			CAN_BR_SJW(p_bit_time->uc_sjw - 1) |
			CAN_BR_BRP(uc_prescale - 1);
	return 1;
}

/**
 * \brief Initialize CAN controller.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 * \param ul_mck CAN module input clock.
 * \param ul_baudrate CAN communication baudrate in kbs.
 *
 * \retval 0 If failed to initialize the CAN module; otherwise successful.
 *
 * \note PMC clock for CAN peripheral should be enabled before calling this
 *function.
 */
uint32_t classCan::can_init(Can *p_can, uint32_t ul_mck, uint32_t ul_baudrate)
{
	uint32_t ul_flag;
	uint32_t ul_tick;

	/* Initialize the baudrate for CAN module. */
	ul_flag = can_set_baudrate(p_can, ul_mck, ul_baudrate);
	if (ul_flag == 0) {
		return 0;
	}

	/* Reset the CAN eight message mailbox. */
	can_reset_all_mailbox(p_can);

	/* Enable the CAN controller. */
	can_enable(p_can);

	/* Wait until the CAN is synchronized with the bus activity. */
	ul_flag = 0;
	ul_tick = 0;
	while (!(ul_flag & CAN_SR_WAKEUP) && (ul_tick < CAN_TIMEOUT)) {
		ul_flag = can_get_status(p_can);
		ul_tick++;
	}

	/* Timeout or the CAN module has been synchronized with the bus. */
	if (CAN_TIMEOUT == ul_tick) {
		return 0;
	} else {
		return 1;
	}
}

/**
 * \brief Enable CAN Controller.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_enable(Can *p_can)
{
	p_can->CAN_MR |= CAN_MR_CANEN;
}

/**
 * \brief Disable CAN Controller.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_disable(Can *p_can)
{
	p_can->CAN_MR &= ~CAN_MR_CANEN;
}

/**
 * \brief Disable CAN Controller low power mode.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_disable_low_power_mode(Can *p_can)
{
	p_can->CAN_MR &= ~CAN_MR_LPM;
}

/**
 * \brief Enable CAN Controller low power mode.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_enable_low_power_mode(Can *p_can)
{
	p_can->CAN_MR |= CAN_MR_LPM;
}

/**
 * \brief Disable CAN Controller autobaud/listen mode.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_disable_autobaud_listen_mode(Can *p_can)
{
	p_can->CAN_MR &= ~CAN_MR_ABM;
}

/**
 * \brief Enable CAN Controller autobaud/listen mode.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_enable_autobaud_listen_mode(Can *p_can)
{
	p_can->CAN_MR |= CAN_MR_ABM;
}

/**
 * \brief CAN Controller won't generate overload frame.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_disable_overload_frame(Can *p_can)
{
	p_can->CAN_MR &= ~CAN_MR_OVL;
}

/**
 * \brief CAN Controller will generate an overload frame after each successful
 *        reception for mailboxes configured in Receive mode, Producer and
 *        Consumer.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_enable_overload_frame(Can *p_can)
{
	p_can->CAN_MR |= CAN_MR_OVL;
}

/**
 * \brief Configure the timestamp capture point, at the start or the end of
 *        frame.
 *
 * \param p_can   Pointer to a CAN peripheral instance.
 * \param ul_flag 0: Timestamp is captured at each start of frame;
 *                1: Timestamp is captured at each end of frame.
 */
void classCan::can_set_timestamp_capture_point(Can *p_can, uint32_t ul_flag)
{
	if (ul_flag) {
		p_can->CAN_MR |= CAN_MR_TEOF;
	} else {
		p_can->CAN_MR &= ~CAN_MR_TEOF;
	}
}

/**
 * \brief Disable CAN Controller time triggered mode.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_disable_time_triggered_mode(Can *p_can)
{
	p_can->CAN_MR &= ~CAN_MR_TTM;
}

/**
 * \brief Enable CAN Controller time triggered mode.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_enable_time_triggered_mode(Can *p_can)
{
	p_can->CAN_MR |= CAN_MR_TTM;
}

/**
 * \brief Disable CAN Controller timer freeze.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_disable_timer_freeze(Can *p_can)
{
	p_can->CAN_MR &= ~CAN_MR_TIMFRZ;
}

/**
 * \brief Enable CAN Controller timer freeze.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_enable_timer_freeze(Can *p_can)
{
	p_can->CAN_MR |= CAN_MR_TIMFRZ;
}

/**
 * \brief Disable CAN Controller transmit repeat function.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_disable_tx_repeat(Can *p_can)
{
	p_can->CAN_MR |= CAN_MR_DRPT;
}

/**
 * \brief Enable CAN Controller transmit repeat function.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 */
void classCan::can_enable_tx_repeat(Can *p_can)
{
	p_can->CAN_MR &= ~CAN_MR_DRPT;
}



/**
 * \brief Enable CAN interrupt.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 * \param dw_mask Interrupt to be enabled.
 */
void classCan::can_enable_interrupt(Can *p_can, uint32_t dw_mask)
{
	p_can->CAN_IER = dw_mask;
}

/**
 * \brief Disable CAN interrupt.
 *
 * \param p_can  Pointer to a CAN peripheral instance.
 * \param dw_mask Interrupt to be disabled.
 */
void classCan::can_disable_interrupt(Can *p_can, uint32_t dw_mask)
{
	p_can->CAN_IDR = dw_mask;
}

/**
 * \brief Get CAN Interrupt Mask.
 *
 * \param p_can Pointer to a CAN peripheral instance.
 *
 * \retval CAN interrupt mask.
 */
uint32_t classCan::can_get_interrupt_mask(Can *p_can)
{
	return (p_can->CAN_IMR);
}

/**
 * \brief Get CAN status.
 *
 * \param p_can Pointer to a CAN peripheral instance.
 *
 * \retval CAN status.
 */
uint32_t classCan::can_get_status(Can *p_can)
{
	return (p_can->CAN_SR);
}

/**
 * \brief Get the 16-bit free-running internal timer count.
 *
 * \param p_can Pointer to a CAN peripheral instance.
 *
 * \retval The internal CAN free-running timer counter.
 */
uint32_t classCan::can_get_internal_timer_value(Can *p_can)
{
	return (p_can->CAN_TIM);
}

/**
 * \brief Get CAN timestamp register value.
 *
 * \param p_can Pointer to a CAN peripheral instance.
 *
 * \retval The timestamp value.
 */
uint32_t classCan::can_get_timestamp_value(Can *p_can)
{
	return (p_can->CAN_TIMESTP);
}

/**
 * \brief Get CAN transmit error counter.
 *
 * \param p_can Pointer to a CAN peripheral instance.
 *
 * \retval Transmit error counter.
 */
uint8_t classCan::can_get_tx_error_cnt(Can *p_can)
{
	return (uint8_t)(p_can->CAN_ECR >> CAN_ECR_TEC_Pos);
}

/**
 * \brief Get CAN receive error counter.
 *
 * \param p_can Pointer to a CAN peripheral instance.
 *
 * \retval Receive error counter.
 */
uint8_t classCan::can_get_rx_error_cnt(Can *p_can)
{
	return (uint8_t)(p_can->CAN_ECR >> CAN_ECR_REC_Pos);
}

/**
 * \brief Reset the internal free-running 16-bit timer.
 *
 * \param p_can Pointer to a CAN peripheral instance.
 *
 * \note If the internal timer counter is frozen, this function automatically
 * re-enables it.
 */
void classCan::can_reset_internal_timer(Can *p_can)
{
	p_can->CAN_TCR |= CAN_TCR_TIMRST;
}

/**
 * \brief Send global transfer request.
 *
 * \param p_can   Pointer to a CAN peripheral instance.
 * \param uc_mask Mask for mailboxes that are requested to transfer.
 */
void classCan::can_global_send_transfer_cmd(Can *p_can, uint8_t uc_mask)
{
	uint32_t ul_reg;

	ul_reg = p_can->CAN_TCR & ((uint32_t) ~GLOBAL_MAILBOX_MASK);
	p_can->CAN_TCR = ul_reg | uc_mask;
}

/**
 * \brief Send global abort request.
 *
 * \param p_can   Pointer to a CAN peripheral instance.
 * \param uc_mask Mask for mailboxes that are requested to abort.
 */
void classCan::can_global_send_abort_cmd(Can *p_can, uint8_t uc_mask)
{
	uint32_t ul_reg;

	ul_reg = p_can->CAN_ACR & ((uint32_t) ~GLOBAL_MAILBOX_MASK);
	p_can->CAN_ACR = ul_reg | uc_mask;
}

/**
 * \brief Configure the timemark for the mailbox.
 *
 * \param p_can    Pointer to a CAN peripheral instance.
 * \param uc_index Indicate which mailbox is to be configured.
 * \param us_cnt   The timemark to be set.
 *
 * \note The timemark is active in Time Triggered mode only.
 */
void classCan::can_mailbox_set_timemark(Can *p_can, uint8_t uc_index, uint16_t us_cnt)
{
	uint32_t ul_reg;

	ul_reg = p_can->CAN_MB[uc_index].CAN_MMR & ((uint32_t) ~TIMEMARK_MASK);
	p_can->CAN_MB[uc_index].CAN_MMR = ul_reg | us_cnt;
}

/**
 * \brief Get status of the mailbox.
 *
 * \param p_can    Pointer to a CAN peripheral instance.
 * \param uc_index Indicate which mailbox is to be read.
 *
 * \retval The mailbox status.
 */
uint32_t classCan::can_mailbox_get_status(Can *p_can, uint8_t uc_index)
{
	return (p_can->CAN_MB[uc_index].CAN_MSR);
}

/**
 * \brief Send single mailbox transfer request.
 *
 * \param p_can   Pointer to a CAN peripheral instance.
 * \param p_mailbox Pointer to a CAN mailbox instance.
 */
void classCan::can_mailbox_send_transfer_cmd(Can *p_can, can_mb_conf_t *p_mailbox)
{
	uint8_t uc_index;

	uc_index = (uint8_t)p_mailbox->ul_mb_idx;

	p_can->CAN_MB[uc_index].CAN_MCR = CAN_MCR_MTCR |
			CAN_MCR_MDLC(p_mailbox->uc_length);;
}

/**
 * \brief Send single mailbox abort request.
 *
 * \param p_can    Pointer to a CAN peripheral instance.
 * \param p_mailbox Pointer to a CAN mailbox instance.
 */
void classCan::can_mailbox_send_abort_cmd(Can *p_can, can_mb_conf_t *p_mailbox)
{
	uint8_t uc_index;

	uc_index = (uint8_t)p_mailbox->ul_mb_idx;

	p_can->CAN_MB[uc_index].CAN_MCR = CAN_MCR_MACR |
			CAN_MCR_MDLC(p_mailbox->uc_length);;
}

/**
 * \brief Initialize the mailbox in different mode and set up related
 *        configuration.
 *
 * \param p_can    Pointer to a CAN peripheral instance.
 * \param p_mailbox Pointer to a CAN mailbox instance.
 */
void classCan::can_mailbox_init(Can *p_can, can_mb_conf_t *p_mailbox)
{
	uint8_t uc_index;

	uc_index = (uint8_t)p_mailbox->ul_mb_idx;
	/* Check the object type of the mailbox. If it's used to disable the
	 * mailbox, reset the whole mailbox. */
	if (!p_mailbox->uc_obj_type) {
		p_can->CAN_MB[uc_index].CAN_MMR = 0;
		p_can->CAN_MB[uc_index].CAN_MAM = 0;
		p_can->CAN_MB[uc_index].CAN_MID = 0;
		p_can->CAN_MB[uc_index].CAN_MDL = 0;
		p_can->CAN_MB[uc_index].CAN_MDH = 0;
		p_can->CAN_MB[uc_index].CAN_MCR = 0;
		return;
	}

	/* Set the priority in Transmit mode. */
	p_can->CAN_MB[uc_index].CAN_MMR = (p_can->CAN_MB[uc_index].CAN_MMR &
			~CAN_MMR_PRIOR_Msk) |
			(p_mailbox->uc_tx_prio << CAN_MMR_PRIOR_Pos);

	/* Set the message ID and message acceptance mask for the mailbox in
	 * other modes. */
	if (p_mailbox->uc_id_ver) {
		p_can->CAN_MB[uc_index].CAN_MAM = p_mailbox->ul_id_msk |
				CAN_MAM_MIDE;
		p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id |
				CAN_MAM_MIDE;
	} else {
		p_can->CAN_MB[uc_index].CAN_MAM = p_mailbox->ul_id_msk;
		p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id;
	}

	/* Set up mailbox in one of the five different modes. */
	p_can->CAN_MB[uc_index].CAN_MMR = (p_can->CAN_MB[uc_index].CAN_MMR &
			~CAN_MMR_MOT_Msk) |
			(p_mailbox->uc_obj_type << CAN_MMR_MOT_Pos);
}

/**
 * \brief Read receive information for the mailbox.
 *
 * \param p_can     Pointer to a CAN peripheral instance.
 * \param p_mailbox Pointer to a CAN mailbox instance.
 *
 * \retval Different CAN mailbox transfer status.
 *
 * \note Read the mailbox status before calling this function.
 */
uint32_t classCan::can_mailbox_read(Can *p_can, can_mb_conf_t *p_mailbox)
{
	uint32_t ul_status;
	uint8_t uc_index;
	uint32_t ul_retval;

	ul_retval = 0;
	uc_index = (uint8_t)p_mailbox->ul_mb_idx;
	ul_status = p_mailbox->ul_status;

	/* Check whether there is overwriting happening in Receive with
	 * Overwrite mode,
	 * or there're messages lost in Receive mode. */
	if ((ul_status & CAN_MSR_MRDY) && (ul_status & CAN_MSR_MMI)) {
		ul_retval = CAN_MAILBOX_RX_OVER;
	}

	/* Read the message family ID. */
	p_mailbox->ul_fid = p_can->CAN_MB[uc_index].CAN_MFID &
			CAN_MFID_MFID_Msk;

	/* Read received data length. */
	p_mailbox->uc_length
		= (ul_status & CAN_MSR_MDLC_Msk) >> CAN_MSR_MDLC_Pos;

	/* Read received data. */
	p_mailbox->ul_datal = p_can->CAN_MB[uc_index].CAN_MDL;
	if (p_mailbox->uc_length > 4) {
		p_mailbox->ul_datah = p_can->CAN_MB[uc_index].CAN_MDH;
	}

	/* Read the mailbox status again to check whether the software needs to
	 * re-read mailbox data register. */
	p_mailbox->ul_status = p_can->CAN_MB[uc_index].CAN_MSR;
	ul_status = p_mailbox->ul_status;
	if (ul_status & CAN_MSR_MMI) {
		ul_retval |= CAN_MAILBOX_RX_NEED_RD_AGAIN;
	} else {
		ul_retval |= CAN_MAILBOX_TRANSFER_OK;
	}

	/* Enable next receive process. */
	can_mailbox_send_transfer_cmd(p_can, p_mailbox);

	return ul_retval;
}

/**
 * \brief Prepare transmit information and write them into the mailbox.
 *
 * \param p_can     Pointer to a CAN peripheral instance.
 * \param p_mailbox Pointer to a CAN mailbox instance.
 *
 * \retval CAN_MAILBOX_NOT_READY: Failed because mailbox isn't ready.
 *       CAN_MAILBOX_TRANSFER_OK: Successfully write message into mailbox.
 *
 * \note After calling this function, the mailbox message won't be sent out
 *until
 * can_mailbox_send_transfer_cmd() is called.
 */
uint32_t classCan::can_mailbox_write(Can *p_can, can_mb_conf_t *p_mailbox)
{
	uint32_t ul_status;
	uint8_t uc_index;

	uc_index = (uint8_t)p_mailbox->ul_mb_idx;
	/* Read the mailbox status firstly to check whether the mailbox is ready
	 *or not. */
	p_mailbox->ul_status = can_mailbox_get_status(p_can, uc_index);
	ul_status = p_mailbox->ul_status;
	if (!(ul_status & CAN_MSR_MRDY)) {
		return CAN_MAILBOX_NOT_READY;
	}

	/* Write transmit identifier. */
	if (p_mailbox->uc_id_ver) {
		p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id |
				CAN_MAM_MIDE;
	} else {
		p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id;
	}

	/* Write transmit data into mailbox data register. */
	p_can->CAN_MB[uc_index].CAN_MDL = p_mailbox->ul_datal;
	if (p_mailbox->uc_length > 4) {
		p_can->CAN_MB[uc_index].CAN_MDH = p_mailbox->ul_datah;
	}

	/* Write transmit data length into mailbox control register. */
	p_can->CAN_MB[uc_index].CAN_MCR = CAN_MCR_MDLC(p_mailbox->uc_length);

	return CAN_MAILBOX_TRANSFER_OK;
}

/**
 * \brief Require to send out a remote frame.
 *
 * \param p_can    Pointer to a CAN peripheral instance.
 * \param p_mailbox Pointer to a CAN mailbox instance.
 *
 * \retval CAN_MAILBOX_NOT_READY: Failed because mailbox isn't ready for
 *transmitting message.
 *       CAN_MAILBOX_TRANSFER_OK: Successfully send out a remote frame.
 */
uint32_t classCan::can_mailbox_tx_remote_frame(Can *p_can, can_mb_conf_t *p_mailbox)
{
	uint32_t ul_status;
	uint8_t uc_index;

	uc_index = (uint8_t)p_mailbox->ul_mb_idx;
	/* Read the mailbox status firstly to check whether the mailbox is ready
	 *or not. */
	p_mailbox->ul_status = p_can->CAN_MB[uc_index].CAN_MSR;
	ul_status = p_mailbox->ul_status;
	if (!(ul_status & CAN_MSR_MRDY)) {
		return CAN_MAILBOX_NOT_READY;
	}

	/* Write transmit identifier. */
	if (p_mailbox->uc_id_ver) {
		p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id |
				CAN_MAM_MIDE;
	} else {
		p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id;
	}

	/* Set the RTR bit in the sent frame. */
	p_can->CAN_MB[uc_index].CAN_MCR = CAN_MCR_MRTR |
			CAN_MCR_MDLC(p_mailbox->uc_length);

	/* Set the MBx bit in the Transfer Command Register to send out the
	 *remote frame. */
	can_global_send_transfer_cmd(p_can, (1 << uc_index));

	return CAN_MAILBOX_TRANSFER_OK;
}

/**
 * \brief Reset the eight mailboxes.
 *
 * \param p_can Pointer to a CAN peripheral instance.
 */
void classCan::can_reset_all_mailbox(Can *p_can)
{
	can_mb_conf_t mb_config_t;

	/* Set the mailbox object type parameter to disable the mailbox. */
	mb_config_t.uc_obj_type = CAN_MB_DISABLE_MODE;

	for (uint8_t i = 0; i < CANMB_NUMBER; i++) {
		mb_config_t.ul_mb_idx = i;
		can_mailbox_init(p_can, &mb_config_t);
	}
}

/**
 * \brief Reset mailbox configure structure.
 *
 *  \param p_mailbox Pointer to mailbox configure structure.
 */
void classCan::reset_mailbox_conf(can_mb_conf_t *p_mailbox)
{
	p_mailbox->ul_mb_idx = 0;
	p_mailbox->uc_obj_type = 0;
	p_mailbox->uc_id_ver = 0;
	p_mailbox->uc_length = 0;
	p_mailbox->uc_tx_prio = 0;
	p_mailbox->ul_status = 0;
	p_mailbox->ul_id_msk = 0;
	p_mailbox->ul_id = 0;
	p_mailbox->ul_fid = 0;
	p_mailbox->ul_datal = 0;
	p_mailbox->ul_datah = 0;
}


