mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-03 14:24:15 +01:00
777 lines
21 KiB
C
777 lines
21 KiB
C
/**
|
|
* \file
|
|
*
|
|
* \brief Controller Area Network (CAN) driver module for SAM.
|
|
*
|
|
* Copyright (c) 2011 - 2012 Atmel Corporation. All rights reserved.
|
|
*
|
|
* \asf_license_start
|
|
*
|
|
* \page License
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. The name of Atmel may not be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 4. This software may only be redistributed and used in connection with an
|
|
* Atmel microcontroller product.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
|
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* \asf_license_stop
|
|
*
|
|
*/
|
|
|
|
#include "../chip.h"
|
|
|
|
/// @cond 0
|
|
/**INDENT-OFF**/
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
/**INDENT-ON**/
|
|
/// @endcond
|
|
|
|
#if SAM3XA_SERIES
|
|
|
|
|
|
/** Define the timemark mask. */
|
|
#define TIMEMARK_MASK 0x0000ffff
|
|
|
|
/* CAN timeout for synchronization. */
|
|
#define CAN_TIMEOUT 100000
|
|
|
|
/** The max value for CAN baudrate prescale. */
|
|
#define CAN_BAUDRATE_MAX_DIV 128
|
|
|
|
/** Define the scope for TQ. */
|
|
#define CAN_MIN_TQ_NUM 8
|
|
#define CAN_MAX_TQ_NUM 25
|
|
|
|
/** Define the fixed bit time value. */
|
|
#define CAN_BIT_SYNC 1
|
|
#define CAN_BIT_IPT 2
|
|
|
|
typedef struct {
|
|
uint8_t uc_tq; //! CAN_BIT_SYNC + uc_prog + uc_phase1 + uc_phase2 = uc_tq, 8 <= uc_tq <= 25.
|
|
uint8_t uc_prog; //! Propagation segment, (3-bits + 1), 1~8;
|
|
uint8_t uc_phase1; //! Phase segment 1, (3-bits + 1), 1~8;
|
|
uint8_t uc_phase2; //! Phase segment 2, (3-bits + 1), 1~8, CAN_BIT_IPT <= uc_phase2;
|
|
uint8_t uc_sjw; //! Resynchronization jump width, (2-bits + 1), min(uc_phase1, 4);
|
|
uint8_t uc_sp; //! Sample point value, 0~100 in percent.
|
|
} can_bit_timing_t;
|
|
|
|
/** Values of bit time register for different baudrates, Sample point = ((1 + uc_prog + uc_phase1) / uc_tq) * 100%. */
|
|
const can_bit_timing_t can_bit_time[] = {
|
|
{8, (2 + 1), (1 + 1), (1 + 1), (2 + 1), 75},
|
|
{9, (1 + 1), (2 + 1), (2 + 1), (1 + 1), 67},
|
|
{10, (2 + 1), (2 + 1), (2 + 1), (2 + 1), 70},
|
|
{11, (3 + 1), (2 + 1), (2 + 1), (3 + 1), 72},
|
|
{12, (2 + 1), (3 + 1), (3 + 1), (3 + 1), 67},
|
|
{13, (3 + 1), (3 + 1), (3 + 1), (3 + 1), 77},
|
|
{14, (3 + 1), (3 + 1), (4 + 1), (3 + 1), 64},
|
|
{15, (3 + 1), (4 + 1), (4 + 1), (3 + 1), 67},
|
|
{16, (4 + 1), (4 + 1), (4 + 1), (3 + 1), 69},
|
|
{17, (5 + 1), (4 + 1), (4 + 1), (3 + 1), 71},
|
|
{18, (4 + 1), (5 + 1), (5 + 1), (3 + 1), 67},
|
|
{19, (5 + 1), (5 + 1), (5 + 1), (3 + 1), 68},
|
|
{20, (6 + 1), (5 + 1), (5 + 1), (3 + 1), 70},
|
|
{21, (7 + 1), (5 + 1), (5 + 1), (3 + 1), 71},
|
|
{22, (6 + 1), (6 + 1), (6 + 1), (3 + 1), 68},
|
|
{23, (7 + 1), (7 + 1), (6 + 1), (3 + 1), 70},
|
|
{24, (6 + 1), (7 + 1), (7 + 1), (3 + 1), 67},
|
|
{25, (7 + 1), (7 + 1), (7 + 1), (3 + 1), 68}
|
|
};
|
|
|
|
/**
|
|
* \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.
|
|
*/
|
|
static uint32_t 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 < 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);
|
|
|
|
/* 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 can_enable_tx_repeat(Can *p_can)
|
|
{
|
|
p_can->CAN_MR &= ~CAN_MR_DRPT;
|
|
}
|
|
|
|
/**
|
|
* \brief Configure CAN Controller reception synchronization stage.
|
|
*
|
|
* \param p_can Pointer to a CAN peripheral instance.
|
|
* \param ul_stage The reception stage to be configured.
|
|
*
|
|
* \note This is just for debug purpose only.
|
|
*/
|
|
void can_set_rx_sync_stage(Can *p_can, uint32_t ul_stage)
|
|
{
|
|
p_can->CAN_MR = (p_can->CAN_MR & ~CAN_MR_RXSYNC_Msk) | ul_stage;
|
|
}
|
|
|
|
/**
|
|
* \brief Enable CAN interrupt.
|
|
*
|
|
* \param p_can Pointer to a CAN peripheral instance.
|
|
* \param dw_mask Interrupt to be enabled.
|
|
*/
|
|
void 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 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 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 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 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 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 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 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 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 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 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 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 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 uc_index Indicate which mailbox is to be configured.
|
|
*/
|
|
void can_mailbox_send_transfer_cmd(Can *p_can, uint8_t uc_index)
|
|
{
|
|
p_can->CAN_MB[uc_index].CAN_MCR |= CAN_MCR_MTCR;
|
|
}
|
|
|
|
/**
|
|
* \brief Send single mailbox abort request.
|
|
*
|
|
* \param p_can Pointer to a CAN peripheral instance.
|
|
* \param uc_index Indicate which mailbox is to be configured.
|
|
*/
|
|
void can_mailbox_send_abort_cmd(Can *p_can, uint8_t uc_index)
|
|
{
|
|
p_can->CAN_MB[uc_index].CAN_MCR |= CAN_MCR_MACR;
|
|
}
|
|
|
|
/**
|
|
* \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 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 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, uc_index);
|
|
|
|
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 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 = (p_can->CAN_MB[uc_index].CAN_MCR &
|
|
~CAN_MCR_MDLC_Msk) | 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 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;
|
|
|
|
/* 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 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);
|
|
}
|
|
}
|
|
|
|
#endif // SAM3XA_SERIES
|
|
|
|
|
|
/// @cond 0
|
|
/**INDENT-OFF**/
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
/**INDENT-ON**/
|
|
/// @endcond
|