Added CAN library (alpha)

This commit is contained in:
Cristian Maglie 2013-02-15 15:51:25 +01:00
parent 0f41b963d4
commit 7314489bea
8 changed files with 974 additions and 143 deletions

View File

@ -0,0 +1,732 @@
/*
Copyright (c) 2013 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "CAN.h"
#include "sn65hvd234.h"
/** 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.
*/
uint32_t CANRaw::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);
p_can->CAN_MR &= ~CAN_MR_CANEN;
/* 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 CANRaw::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 = 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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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 CANRaw::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);
}
}

View File

@ -0,0 +1,131 @@
/*
Copyright (c) 2013 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _CAN_LIBRARY_
#define _CAN_LIBRARY_
#include "sn65hvd234.h"
/** Define the Mailbox mask for eight mailboxes. */
#define GLOBAL_MAILBOX_MASK 0x000000ff
/** Disable all interrupt mask */
#define CAN_DISABLE_ALL_INTERRUPT_MASK 0xffffffff
/** Define the typical baudrate for CAN communication in KHz. */
#define CAN_BPS_1000K 1000
#define CAN_BPS_800K 800
#define CAN_BPS_500K 500
#define CAN_BPS_250K 250
#define CAN_BPS_125K 125
#define CAN_BPS_50K 50
#define CAN_BPS_25K 25
#define CAN_BPS_10K 10
#define CAN_BPS_5K 5
/** Define the mailbox mode. */
#define CAN_MB_DISABLE_MODE 0
#define CAN_MB_RX_MODE 1
#define CAN_MB_RX_OVER_WR_MODE 2
#define CAN_MB_TX_MODE 3
#define CAN_MB_CONSUMER_MODE 4
#define CAN_MB_PRODUCER_MODE 5
/** Define CAN mailbox transfer status code. */
#define CAN_MAILBOX_TRANSFER_OK 0 //! Read from or write into mailbox successfully.
#define CAN_MAILBOX_NOT_READY 0x01 //! Receiver is empty or transmitter is busy.
#define CAN_MAILBOX_RX_OVER 0x02 //! Message overwriting happens or there're messages lost in different receive modes.
#define CAN_MAILBOX_RX_NEED_RD_AGAIN 0x04 //! Application needs to re-read the data register in Receive with Overwrite mode.
class CANRaw
{
protected:
/* CAN peripheral, set by constructor */
//Can* m_pCan ;
/* CAN Transceiver */
SSN65HVD234_Data m_Transceiver ;
/** CAN Transfer */
//can_mb_conf_t m_Mailbox;
private:
public:
// Constructor
//CANRawClass( Can* pCan ) ;
/**
* \defgroup sam_driver_can_group Controller Area Network (CAN) Driver
*
* See \ref sam_can_quickstart.
*
* \par Purpose
*
* The CAN controller provides all the features required to implement
* the serial communication protocol CAN defined by Robert Bosch GmbH,
* the CAN specification. This is a driver for configuration, enabling,
* disabling and use of the CAN peripheral.
*
* @{
*/
uint32_t set_baudrate(Can *p_can, uint32_t ul_mck, uint32_t ul_baudrate);
uint32_t init(Can *p_can, uint32_t ul_mck, uint32_t ul_baudrate);
void enable(Can *p_can);
void disable(Can *p_can);
void disable_low_power_mode(Can *p_can);
void enable_low_power_mode(Can *p_can);
void disable_autobaud_listen_mode(Can *p_can);
void enable_autobaud_listen_mode(Can *p_can);
void disable_overload_frame(Can *p_can);
void enable_overload_frame(Can *p_can);
void set_timestamp_capture_point(Can *p_can, uint32_t ul_flag);
void disable_time_triggered_mode(Can *p_can);
void enable_time_triggered_mode(Can *p_can);
void disable_timer_freeze(Can *p_can);
void enable_timer_freeze(Can *p_can);
void disable_tx_repeat(Can *p_can);
void enable_tx_repeat(Can *p_can);
void set_rx_sync_stage(Can *p_can, uint32_t ul_stage);
void enable_interrupt(Can *p_can, uint32_t dw_mask);
void disable_interrupt(Can *p_can, uint32_t dw_mask);
uint32_t get_interrupt_mask(Can *p_can);
uint32_t get_status(Can *p_can);
uint32_t get_internal_timer_value(Can *p_can);
uint32_t get_timestamp_value(Can *p_can);
uint8_t get_tx_error_cnt(Can *p_can);
uint8_t get_rx_error_cnt(Can *p_can);
void reset_internal_timer(Can *p_can);
void global_send_transfer_cmd(Can *p_can, uint8_t uc_mask);
void global_send_abort_cmd(Can *p_can, uint8_t uc_mask);
void mailbox_set_timemark(Can *p_can, uint8_t uc_index, uint16_t us_cnt);
uint32_t mailbox_get_status(Can *p_can, uint8_t uc_index);
void mailbox_send_transfer_cmd(Can *p_can, uint8_t uc_index);
void mailbox_send_abort_cmd(Can *p_can, uint8_t uc_index);
void mailbox_init(Can *p_can, can_mb_conf_t *p_mailbox);
uint32_t mailbox_read(Can *p_can, can_mb_conf_t *p_mailbox);
uint32_t mailbox_write(Can *p_can, can_mb_conf_t *p_mailbox);
uint32_t mailbox_tx_remote_frame(Can *p_can, can_mb_conf_t *p_mailbox);
void reset_all_mailbox(Can *p_can);
} ;
#endif // _CAN_LIBRARY_

View File

@ -1,89 +0,0 @@
#include "CANRaw.h"
#include "sn65hvd234.h"
CANRawClass::CANRawClass( Can* pCan )
{
m_pCan=pCan ;
}
uint32_t CANRawClass::begin( uint32_t dwPinEN, uint32_t dwPinRs, uint32_t dwBaudrate )
{
uint32_t dwPins ;
/* Initialize CAN pins */
if ( m_pCan == CAN0 )
{
dwPins=PINS_CAN0 ;
}
else
{
dwPins=PINS_CAN1 ;
}
PIO_Configure(
g_APinDescription[dwPins].pPort,
g_APinDescription[dwPins].ulPinType,
g_APinDescription[dwPins].ulPin,
g_APinDescription[dwPins].ulPinConfiguration);
/* Initialize CAN Transceiver */
SN65HVD234_Init( &m_Transceiver ) ;
SN65HVD234_SetRs( &m_Transceiver, dwPinRs ) ;
SN65HVD234_SetEN( &m_Transceiver, dwPinEN ) ;
/* Enable CAN Transceiver */
SN65HVD234_DisableLowPower( &m_Transceiver ) ;
SN65HVD234_Enable( &m_Transceiver ) ;
if ( can_init( m_pCan, SystemCoreClock, 1000 ) == 1 )
{
can_reset_all_mailbox( m_pCan ) ;
}
else
{
puts( "ERROR CAN initialisation (synchro)" ) ;
return -1 ;
}
return 0 ;
}
uint32_t CANRawClass::end( void )
{
/* Disable CAN Controller */
can_disable( m_pCan ) ;
/* Disable CAN Transceiver */
SN65HVD234_EnableLowPower( &m_Transceiver ) ;
SN65HVD234_Disable( &m_Transceiver ) ;
return 0 ;
}
/*----------------------------------------------------------------------------
* Exported objects
*----------------------------------------------------------------------------*/
CANRawClass CANRaw0( CAN0 ) ;
CANRawClass CANRaw1( CAN1 ) ;
/*----------------------------------------------------------------------------
* Exported functions
*----------------------------------------------------------------------------*/
/**
* \brief Default interrupt handler for CAN 0.
*/
void CAN0_IrqHandler( void )
{
CAN_Handler( CAN0, &gCanTransfer0 ) ;
}
/**
* \brief Default interrupt handler for CAN 1.
*/
void CAN1_IrqHandler( void )
{
CAN_Handler( CAN1, &gCanTransfer1 ) ;
}

View File

@ -1,32 +0,0 @@
#ifndef _CAN_LIBRARY_
#define _CAN_LIBRARY_
#include "sn65hvd234.h"
class CANRawClass
{
protected:
/* CAN peripheral, set by constructor */
Can* m_pCan ;
/* CAN Transceiver */
SSN65HVD234_Data m_Transceiver ;
/** CAN Transfer */
can_mb_conf_t m_Mailbox ;
private:
public:
// Constructor
CANRawClass( Can* pCan ) ;
uint32_t begin( uint32_t dwPinEN, uint32_t dwPinRs, uint32_t dwBaudrate ) ;
uint32_t end( void ) ;
} ;
extern CANRawClass CANRaw0 ;
extern CANRawClass CANRaw1 ;
#endif // _CAN_LIBRARY_

View File

@ -0,0 +1,37 @@
Controller Area network (CAN) API for Arduino Due
This is a beta release of the CAN API for Arduino Due. It contains the necessary classes and functions to configure, enable, disable and use of the CAN peripherals controllers embedded in the SAM3X8E core inside Arduino Due, and the two external SN65HVD234 transceivers.
This CAN API for Arduino Due is released together with a CAN sample1 sketch for the Arduino IDE 1.5.2 and it shows how to configure the CAN controllers and how to manage CAN message transfers. The two CAN controllers (CAN0 and CAN1) and two mailboxes (mailbox 0 and mailbox 1) are used: CAN0 mailbox 0 is configured as transmitter, and CAN1 mailbox 0 is configured as receiver. The communication baudrate is 1Mbit/s. The CAN0 controller tries to send on the bus messages through the mailbox 0 and waits for messages from mailbox 1 on CAN1 controller.
It is required to use the two CAN pins in Arduino Due, connected to the two external SN65HVD234transceivers in loop mode via a pair cable. The CAN message transaction can be monitored by a serial UART connection (115.2 Kbps, 8 data bits, no parity, 1 stop bit, no flux control) or serial monitor of the Arduino IDE 1.5.2 with autoscroll and newline modes activated.
Source files:
- CAN.cpp
- CAN.h
- sn65hvd234.c
- sn65hvd234.h
- variant.cpp
- variant.h
CAN files (.cpp .h) contain the CANRaw class with 38 functions.
sn65hvd234 files (.c .h) contain 7 driver functions.
The variant files (.cpp .h) are updates to the IDE 1.5.2 ones. Added initialization of the CAN pins in variant.cpp
and CAN pins definition in variant.h.
Hardware requirements:
- Arduino Due board
- Dual CAN transceiver shield
- Twisted shielded pair cable
Software requirements:
- Arduino IDE 1.5.2
- CAN API library
To perform the CAN sample 1 test, the pair cable needs to be connected between the two CAN ports as follows:
CANRX0 <-> CANRX0
CANRX1 <-> CANRX1
Once the CAN sample 1 and the CAN library are loaded in Arduino IDE 1.5.2, after serial monitor open or after a reset of the Arduino board, the following message should be displayed in the monitor terminal: Type CAN message to send. Then, an 8 digit message can be typed and after a return stroke of the keyboard, the following message should be displayed: Sent value=XXXXXXXX, where XXXXXXXX is the typed message. If the message was sent/received successfully, the following message should be displayed: CAN message received=XXXXXXX and End of test. Otherwise, there is a CAN communication error.

View File

@ -1,23 +1,32 @@
// Arduino Due - CAN Sample 1
// Brief CAN example for Arduino Due
// Test the transmission from CAN0 Mailbox 0 to CAN1 Mailbox 0
// Modified from Atmel ASF SAM3X-EK CAN sample by Wilfredo Molina 2012
// By Thibaut Viard/Wilfredo Molina 2012
// Required libraries
#include "variant.h"
#include "CAN.h"
#include <CAN.h>
#define TEST1_CAN_COMM_MB_IDX 0
#define TEST1_CAN_TRANSFER_ID 0x07
#define TEST1_CAN0_TX_PRIO 15
#define CAN_MSG_DUMMY_DATA 0x55AAAA55
/** CAN frame max data length */
// CAN frame max data length
#define MAX_CAN_FRAME_DATA_LEN 8
//Message variable to be send
// CAN class
CANRaw CAN;
// Message variable to be send
uint32_t CAN_MSG_1 = 0;
// CAN0 Transceiver
SSN65HVD234_Data can0_transceiver;
// CAN1 Transceiver
SSN65HVD234_Data can1_transceiver;
// Define the struct for CAN message mailboxes needed
can_mb_conf_t can0_mailbox;
can_mb_conf_t can1_mailbox;
@ -29,60 +38,91 @@ void setup()
Serial.println("Type CAN message to send");
while (Serial.available() == 0);
}
void loop(){
while (Serial.available() > 0) {
CAN_MSG_1 = Serial.parseInt();
if (Serial.read() == '\n') {
while (Serial.available() > 0) {
CAN_MSG_1 = Serial.parseInt();
if (Serial.read() == '\n') {
Serial.print("Sent value= ");
Serial.println(CAN_MSG_1);
delay(1000);
}
}
// Initialize CAN0 Transceiver
SN65HVD234_Init(&can0_transceiver);
SN65HVD234_SetRs(&can0_transceiver, 61);
SN65HVD234_SetEN(&can0_transceiver, 62);
// Enable CAN0 Transceiver
SN65HVD234_DisableLowPower(&can0_transceiver);
SN65HVD234_Enable(&can0_transceiver);
// Initialize CAN1 Transceiver
SN65HVD234_Init(&can1_transceiver);
SN65HVD234_SetRs(&can1_transceiver, 63);
SN65HVD234_SetEN(&can1_transceiver, 64);
// Enable CAN1 Transceiver
SN65HVD234_DisableLowPower(&can1_transceiver);
SN65HVD234_Enable(&can1_transceiver);
// Enable CAN0 & CAN1 clock
pmc_enable_periph_clk(ID_CAN0);
pmc_enable_periph_clk(ID_CAN1);
// Initialize CAN0 and CAN1, baudrate is 1Mb/s
can_init(CAN0, SystemCoreClock, CAN_BPS_1000K);
can_init(CAN1, SystemCoreClock, CAN_BPS_1000K);
CAN.init(CAN0, SystemCoreClock, CAN_BPS_1000K);
CAN.init(CAN1, SystemCoreClock, CAN_BPS_1000K);
// Initialize CAN1 mailbox 0 as receiver, frame ID is 0x07
can_reset_mailbox_data(&can1_mailbox);
// can_reset_mailbox_data(&can1_mailbox);
can1_mailbox.ul_mb_idx = TEST1_CAN_COMM_MB_IDX;
can1_mailbox.uc_obj_type = CAN_MB_RX_MODE;
can1_mailbox.ul_id_msk = CAN_MAM_MIDvA_Msk | CAN_MAM_MIDvB_Msk;
can1_mailbox.ul_id = CAN_MID_MIDvA(TEST1_CAN_TRANSFER_ID);
can_mailbox_init(CAN1, &can1_mailbox);
CAN.mailbox_init(CAN1, &can1_mailbox);
// Initialize CAN0 mailbox 0 as transmitter, transmit priority is 15
can_reset_mailbox_data(&can0_mailbox);
// can_reset_mailbox_data(&can0_mailbox);
can0_mailbox.ul_mb_idx = TEST1_CAN_COMM_MB_IDX;
can0_mailbox.uc_obj_type = CAN_MB_TX_MODE;
can0_mailbox.uc_tx_prio = TEST1_CAN0_TX_PRIO;
can0_mailbox.uc_id_ver = 0;
can0_mailbox.ul_id_msk = 0;
can_mailbox_init(CAN0, &can0_mailbox);
CAN.mailbox_init(CAN0, &can0_mailbox);
// Prepare transmit ID, data and data length in CAN0 mailbox 0
can0_mailbox.ul_id = CAN_MID_MIDvA(TEST1_CAN_TRANSFER_ID);
can0_mailbox.ul_datal = CAN_MSG_1;
can0_mailbox.ul_datah = CAN_MSG_DUMMY_DATA;
can0_mailbox.uc_length = MAX_CAN_FRAME_DATA_LEN;
can_mailbox_write(CAN0, &can0_mailbox);
CAN.mailbox_write(CAN0, &can0_mailbox);
// Send out the information in the mailbox
can_global_send_transfer_cmd(CAN0, CAN_TCR_MB0);
CAN.global_send_transfer_cmd(CAN0, CAN_TCR_MB0);
// Wait for CAN1 mailbox 0 to receive the data
while (!(can_mailbox_get_status(CAN1, 0) & CAN_MSR_MRDY)) {
}
while (!(CAN.mailbox_get_status(CAN1, 0) & CAN_MSR_MRDY)) {
}
// Read the received data from CAN1 mailbox 0
can_mailbox_read(CAN1, &can1_mailbox);
CAN.mailbox_read(CAN1, &can1_mailbox);
Serial.print("CAN message received= ");
Serial.println(can1_mailbox.ul_datal);
while (1) {}
// Disable CAN0 Controller
CAN.disable(CAN0);
// Disable CAN0 Transceiver
SN65HVD234_EnableLowPower(&can0_transceiver);
SN65HVD234_Disable(&can0_transceiver);
// Disable CAN1 Controller
CAN.disable(CAN1);
// Disable CAN1 Transceiver
SN65HVD234_EnableLowPower(&can1_transceiver);
SN65HVD234_Disable(&can1_transceiver);
Serial.print("End of test");
while (1) {
}
}

View File

@ -401,6 +401,18 @@ void init( void )
g_APinDescription[PINS_USB].ulPin,
g_APinDescription[PINS_USB].ulPinConfiguration);
// Initialize CAN pins
PIO_Configure(
g_APinDescription[PINS_CAN0].pPort,
g_APinDescription[PINS_CAN0].ulPinType,
g_APinDescription[PINS_CAN0].ulPin,
g_APinDescription[PINS_CAN0].ulPinConfiguration);
PIO_Configure(
g_APinDescription[PINS_CAN1].pPort,
g_APinDescription[PINS_CAN1].ulPinType,
g_APinDescription[PINS_CAN1].ulPin,
g_APinDescription[PINS_CAN1].ulPinConfiguration);
// Initialize Analog Controller
pmc_enable_periph_clk(ID_ADC);
adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);