blob: b957ed48b0cb8b5d1a51cf23054ffd48d708a7bf [file] [log] [blame]
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2015, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: 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
* 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.
* ----------------------------------------------------------------------------
*/
/** \file
* Implements functions for Controller Area Network with Flexible Data-rate,
* relying on the MCAN peripheral.
*/
/** \addtogroup can_module
*@{*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "board.h"
#include "chip.h"
#include "mcan.h"
#include "pmc.h"
#include <assert.h>
#include <string.h>
/*---------------------------------------------------------------------------
* Local definitions
*---------------------------------------------------------------------------*/
enum mcan_dlc
{
CAN_DLC_0 = 0,
CAN_DLC_1 = 1,
CAN_DLC_2 = 2,
CAN_DLC_3 = 3,
CAN_DLC_4 = 4,
CAN_DLC_5 = 5,
CAN_DLC_6 = 6,
CAN_DLC_7 = 7,
CAN_DLC_8 = 8,
CAN_DLC_12 = 9,
CAN_DLC_16 = 10,
CAN_DLC_20 = 11,
CAN_DLC_24 = 12,
CAN_DLC_32 = 13,
CAN_DLC_48 = 14,
CAN_DLC_64 = 15
};
/*---------------------------------------------------------------------------
* Local functions
*---------------------------------------------------------------------------*/
/**
* \brief Convert data length to Data Length Code.
* \param len length, in bytes
* \param dlc address where the matching Data Length Code will be written
* \return true if a code matched the provided length, false if this exact
* length is not supported.
*/
static bool get_length_code(uint8_t len, enum mcan_dlc *dlc)
{
assert(dlc);
if (len <= 8) {
*dlc = (enum mcan_dlc)len;
return true;
}
if (len % 4)
return false;
len /= 4;
if (len <= 6) {
*dlc = (enum mcan_dlc)(len + 6);
return true;
}
if (len % 4)
return false;
len /= 4;
if (len > 4)
return false;
*dlc = (enum mcan_dlc)(len + 11);
return true;
}
/**
* \brief Convert Data Length Code to actual data length.
* \param dlc CAN_DLC_xx enum value
* \return Data length, expressed in bytes.
*/
static uint8_t get_data_length(enum mcan_dlc dlc)
{
assert((dlc == CAN_DLC_0 || dlc > CAN_DLC_0) && dlc <= CAN_DLC_64);
if (dlc <= CAN_DLC_8)
return (uint8_t)dlc;
if (dlc <= CAN_DLC_24)
return ((uint8_t)dlc - 6) * 4;
return ((uint8_t)dlc - 11) * 16;
}
/**
* \brief Compute the size of the Message RAM, depending on the application.
* \param set Pointer to a MCAN instance that will be setup accordingly.
* \param cfg MCAN configuration to be considered. Only integer size parameters
* need to be configured. The other parameters can be left blank at this stage.
* \param size address where the required size of the Message RAM will be
* written, expressed in (32-bit) words.
* \return true if successful, false if a parameter is set to an unsupported
* value.
*/
static bool configure_ram(struct mcan_set *set,
const struct mcan_config *cfg, uint32_t *size)
{
if (cfg->array_size_filt_std > 128 || cfg->array_size_filt_ext > 64
|| cfg->fifo_size_rx0 > 64 || cfg->fifo_size_rx1 > 64
|| cfg->array_size_rx > 64 || cfg->fifo_size_tx_evt > 32
|| cfg->array_size_tx > 32 || cfg->fifo_size_tx > 32
|| cfg->array_size_tx + cfg->fifo_size_tx > 32
|| cfg->buf_size_rx_fifo0 > 64 || cfg->buf_size_rx_fifo1 > 64
|| cfg->buf_size_rx > 64 || cfg->buf_size_tx > 64)
return false;
set->ram_filt_std = cfg->msg_ram;
*size = (uint32_t)cfg->array_size_filt_std * MCAN_RAM_FILT_STD_SIZE;
set->ram_filt_ext = cfg->msg_ram + *size;
*size += (uint32_t)cfg->array_size_filt_ext * MCAN_RAM_FILT_EXT_SIZE;
set->ram_fifo_rx0 = cfg->msg_ram + *size;
*size += (uint32_t)cfg->fifo_size_rx0 * (MCAN_RAM_BUF_HDR_SIZE
+ cfg->buf_size_rx_fifo0 / 4);
set->ram_fifo_rx1 = cfg->msg_ram + *size;
*size += (uint32_t)cfg->fifo_size_rx1 * (MCAN_RAM_BUF_HDR_SIZE
+ cfg->buf_size_rx_fifo1 / 4);
set->ram_array_rx = cfg->msg_ram + *size;
*size += (uint32_t)cfg->array_size_rx * (MCAN_RAM_BUF_HDR_SIZE
+ cfg->buf_size_rx / 4);
set->ram_fifo_tx_evt = cfg->msg_ram + *size;
*size += (uint32_t)cfg->fifo_size_tx_evt * MCAN_RAM_TX_EVT_SIZE;
set->ram_array_tx = cfg->msg_ram + *size;
*size += (uint32_t)cfg->array_size_tx * (MCAN_RAM_BUF_HDR_SIZE
+ cfg->buf_size_tx / 4);
*size += (uint32_t)cfg->fifo_size_tx * (MCAN_RAM_BUF_HDR_SIZE
+ cfg->buf_size_tx / 4);
return true;
}
/*---------------------------------------------------------------------------
* Exported Functions
*---------------------------------------------------------------------------*/
bool mcan_configure_msg_ram(const struct mcan_config *cfg, uint32_t *size)
{
assert(cfg);
assert(size);
struct mcan_set tmp_set = { .cfg = { 0 } };
return configure_ram(&tmp_set, cfg, size);
}
bool mcan_initialize(struct mcan_set *set, const struct mcan_config *cfg)
{
assert(set);
assert(cfg);
assert(cfg->regs);
assert(cfg->msg_ram);
Mcan *mcan = cfg->regs;
uint32_t *element = NULL, *elem_end = NULL;
uint32_t freq, regVal32;
enum mcan_dlc dlc;
memset(set, 0, sizeof(*set));
if (!configure_ram(set, cfg, &regVal32))
return false;
set->cfg = *cfg;
/* Configure the MSB of the Message RAM Base Address */
regVal32 = (uint32_t)cfg->msg_ram >> 16;
if (cfg->id == ID_CAN0_INT0 || cfg->id == ID_CAN0_INT1)
regVal32 = (SFR->SFR_CAN & ~SFR_CAN_EXT_MEM_CAN0_ADDR_Msk)
| SFR_CAN_EXT_MEM_CAN0_ADDR(regVal32);
else
regVal32 = (SFR->SFR_CAN & ~SFR_CAN_EXT_MEM_CAN1_ADDR_Msk)
| SFR_CAN_EXT_MEM_CAN1_ADDR(regVal32);
SFR->SFR_CAN = regVal32;
/* Reset the CC Control Register */
mcan->MCAN_CCCR = 0 | MCAN_CCCR_INIT_ENABLED;
mcan_disable(set);
mcan_reconfigure(set);
/* Global Filter Configuration: Reject remote frames, reject non-matching frames */
mcan->MCAN_GFC = MCAN_GFC_RRFE_REJECT | MCAN_GFC_RRFS_REJECT
| MCAN_GFC_ANFE(2) | MCAN_GFC_ANFS(2);
/* Extended ID Filter AND mask */
mcan->MCAN_XIDAM = 0x1FFFFFFF;
/* Interrupt configuration - leave initialization with all interrupts off
* Disable all interrupts */
mcan->MCAN_IE = 0;
mcan->MCAN_TXBTIE = 0x00000000;
/* All interrupts directed to Line 0 */
mcan->MCAN_ILS = 0x00000000;
/* Disable both interrupt LINE 0 & LINE 1 */
mcan->MCAN_ILE = 0x00;
/* Clear all interrupt flags */
mcan->MCAN_IR = 0xFFCFFFFF;
/* Configure CAN bit timing */
if (cfg->bit_rate == 0
|| cfg->quanta_before_sp < 3 || cfg->quanta_before_sp > 257
|| cfg->quanta_after_sp < 1 || cfg->quanta_after_sp > 128
|| cfg->quanta_sync_jump < 1 || cfg->quanta_sync_jump > 128)
return false;
/* Retrieve the frequency of the CAN core clock i.e. the Generated Clock */
freq = pmc_get_gck_clock(cfg->id);
/* Compute the Nominal Baud Rate Prescaler */
regVal32 = ROUND_INT_DIV(freq, cfg->bit_rate
* (cfg->quanta_before_sp + cfg->quanta_after_sp));
if (regVal32 < 1 || regVal32 > 512)
return false;
/* Apply bit timing configuration */
mcan->MCAN_NBTP = MCAN_NBTP_NBRP(regVal32 - 1)
| MCAN_NBTP_NTSEG1(cfg->quanta_before_sp - 1 - 1)
| MCAN_NBTP_NTSEG2(cfg->quanta_after_sp - 1)
| MCAN_NBTP_NSJW(cfg->quanta_sync_jump - 1);
/* Configure fast CAN FD bit timing */
if (cfg->bit_rate_fd < cfg->bit_rate
|| cfg->quanta_before_sp_fd < 3 || cfg->quanta_before_sp_fd > 33
|| cfg->quanta_after_sp_fd < 1 || cfg->quanta_after_sp_fd > 16
|| cfg->quanta_sync_jump_fd < 1 || cfg->quanta_sync_jump_fd > 8)
return false;
/* Compute the Fast Baud Rate Prescaler */
regVal32 = ROUND_INT_DIV(freq, cfg->bit_rate_fd
* (cfg->quanta_before_sp_fd + cfg->quanta_after_sp_fd));
if (regVal32 < 1 || regVal32 > 32)
return false;
/* Apply bit timing configuration */
mcan->MCAN_DBTP = MCAN_DBTP_FBRP(regVal32 - 1)
| MCAN_DBTP_DTSEG1(cfg->quanta_before_sp_fd - 1 - 1)
| MCAN_DBTP_DTSEG2(cfg->quanta_after_sp_fd - 1)
| MCAN_DBTP_DSJW(cfg->quanta_sync_jump_fd - 1);
/* Configure Message RAM starting addresses and element count */
/* 11-bit Message ID Rx Filters */
mcan->MCAN_SIDFC =
MCAN_SIDFC_FLSSA((uint32_t)set->ram_filt_std >> 2)
| MCAN_SIDFC_LSS(cfg->array_size_filt_std);
/* 29-bit Message ID Rx Filters */
mcan->MCAN_XIDFC =
MCAN_XIDFC_FLESA((uint32_t)set->ram_filt_ext >> 2)
| MCAN_XIDFC_LSE(cfg->array_size_filt_ext);
/* Rx FIFO 0 */
mcan->MCAN_RXF0C =
MCAN_RXF0C_F0SA((uint32_t)set->ram_fifo_rx0 >> 2)
| MCAN_RXF0C_F0S(cfg->fifo_size_rx0)
| MCAN_RXF0C_F0WM(0)
| 0; /* clear MCAN_RXF0C_F0OM */
/* Rx FIFO 1 */
mcan->MCAN_RXF1C =
MCAN_RXF1C_F1SA((uint32_t)set->ram_fifo_rx1 >> 2)
| MCAN_RXF1C_F1S(cfg->fifo_size_rx1)
| MCAN_RXF1C_F1WM(0)
| 0; /* clear MCAN_RXF1C_F1OM */
/* Dedicated Rx Buffers
* Note: the HW does not know (and does not care about) how many
* dedicated Rx Buffers are used by the application. */
mcan->MCAN_RXBC =
MCAN_RXBC_RBSA((uint32_t)set->ram_array_rx >> 2);
/* Tx Event FIFO */
mcan->MCAN_TXEFC =
MCAN_TXEFC_EFSA((uint32_t)set->ram_fifo_tx_evt >> 2)
| MCAN_TXEFC_EFS(cfg->fifo_size_tx_evt)
| MCAN_TXEFC_EFWM(0);
/* Tx Buffers */
mcan->MCAN_TXBC =
MCAN_TXBC_TBSA((uint32_t)set->ram_array_tx >> 2)
| MCAN_TXBC_NDTB(cfg->array_size_tx)
| MCAN_TXBC_TFQS(cfg->fifo_size_tx)
| 0; /* clear MCAN_TXBC_TFQM */
/* Configure the size of data fields in Rx and Tx Buffer Elements */
if (!get_length_code(cfg->buf_size_rx_fifo0, &dlc))
return false;
regVal32 = MCAN_RXESC_F0DS(dlc < CAN_DLC_8 ? 0 : dlc - CAN_DLC_8);
if (!get_length_code(cfg->buf_size_rx_fifo1, &dlc))
return false;
regVal32 |= MCAN_RXESC_F1DS(dlc < CAN_DLC_8 ? 0 : dlc - CAN_DLC_8);
if (!get_length_code(cfg->buf_size_rx, &dlc))
return false;
regVal32 |= MCAN_RXESC_RBDS(dlc < CAN_DLC_8 ? 0 : dlc - CAN_DLC_8);
mcan->MCAN_RXESC = regVal32;
if (!get_length_code(cfg->buf_size_tx, &dlc))
return false;
mcan->MCAN_TXESC =
MCAN_TXESC_TBDS(dlc < CAN_DLC_8 ? 0 : dlc - CAN_DLC_8);
/* Configure Message ID Filters
* ...Disable all standard filters */
for (element = set->ram_filt_std, elem_end = set->ram_filt_std
+ (uint32_t)cfg->array_size_filt_std * MCAN_RAM_FILT_STD_SIZE;
element < elem_end;
element += MCAN_RAM_FILT_STD_SIZE)
element[0] = MCAN_RAM_FILT_SFEC_DIS;
/* ...Disable all extended filters */
for (element = set->ram_filt_ext, elem_end = set->ram_filt_ext
+ (uint32_t)cfg->array_size_filt_ext * MCAN_RAM_FILT_EXT_SIZE;
element < elem_end;
element += MCAN_RAM_FILT_EXT_SIZE)
element[0] = MCAN_RAM_FILT_EFEC_DIS;
mcan->MCAN_NDAT1 = 0xFFFFFFFF; /* clear new (rx) data flags */
mcan->MCAN_NDAT2 = 0xFFFFFFFF; /* clear new (rx) data flags */
regVal32 = mcan->MCAN_CCCR & ~(MCAN_CCCR_BRSE | MCAN_CCCR_FDOE);
mcan->MCAN_CCCR = regVal32 | MCAN_CCCR_PXHD | MCAN_CCCR_BRSE_DISABLED
| MCAN_CCCR_FDOE_DISABLED;
DSB();
ISB();
return true;
}
void mcan_reconfigure(struct mcan_set *set)
{
Mcan *mcan = set->cfg.regs;
uint32_t regVal32;
regVal32 = mcan->MCAN_CCCR & ~MCAN_CCCR_CCE;
assert((regVal32 & MCAN_CCCR_INIT) == MCAN_CCCR_INIT_ENABLED);
/* Enable writing to configuration registers */
mcan->MCAN_CCCR = regVal32 | MCAN_CCCR_CCE_CONFIGURABLE;
}
void mcan_set_mode(struct mcan_set *set, enum mcan_can_mode mode)
{
Mcan *mcan = set->cfg.regs;
uint32_t regVal32;
regVal32 = mcan->MCAN_CCCR & ~(MCAN_CCCR_BRSE | MCAN_CCCR_FDOE);
switch (mode) {
case MCAN_MODE_CAN:
regVal32 |= MCAN_CCCR_BRSE_DISABLED | MCAN_CCCR_FDOE_DISABLED;
break;
case MCAN_MODE_EXT_LEN_CONST_RATE:
regVal32 |= MCAN_CCCR_BRSE_DISABLED | MCAN_CCCR_FDOE_ENABLED;
break;
case MCAN_MODE_EXT_LEN_DUAL_RATE:
regVal32 |= MCAN_CCCR_BRSE_ENABLED | MCAN_CCCR_FDOE_ENABLED;
break;
default:
return;
}
mcan->MCAN_CCCR = regVal32;
}
enum mcan_can_mode mcan_get_mode(const struct mcan_set *set)
{
const uint32_t cccr = set->cfg.regs->MCAN_CCCR;
if ((cccr & MCAN_CCCR_FDOE) == MCAN_CCCR_FDOE_DISABLED)
return MCAN_MODE_CAN;
if ((cccr & MCAN_CCCR_BRSE) == MCAN_CCCR_BRSE_DISABLED)
return MCAN_MODE_EXT_LEN_CONST_RATE;
return MCAN_MODE_EXT_LEN_DUAL_RATE;
}
void mcan_init_loopback(struct mcan_set *set)
{
Mcan *mcan = set->cfg.regs;
mcan->MCAN_CCCR |= MCAN_CCCR_TEST_ENABLED;
#if 0
mcan->MCAN_CCCR |= MCAN_CCCR_MON_ENABLED; /* for internal loop back */
#endif
mcan->MCAN_TEST |= MCAN_TEST_LBCK_ENABLED;
}
void mcan_set_tx_queue_mode(struct mcan_set *set)
{
Mcan *mcan = set->cfg.regs;
mcan->MCAN_TXBC |= MCAN_TXBC_TFQM;
}
void mcan_enable(struct mcan_set *set)
{
uint32_t index, val;
/* Depending on bus condition, the HW may switch back to the
* Initialization state, by itself. Therefore, upon timeout, return.
* [Using an arbitrary timeout criterion.] */
for (index = 0; index < 1024; index++) {
val = set->cfg.regs->MCAN_CCCR;
if ((val & MCAN_CCCR_INIT) == MCAN_CCCR_INIT_DISABLED)
break;
if (index == 0)
set->cfg.regs->MCAN_CCCR = (val & ~MCAN_CCCR_INIT)
| MCAN_CCCR_INIT_DISABLED;
}
}
void mcan_disable(struct mcan_set *set)
{
uint32_t val;
bool initial;
for (initial = true; true; initial = false) {
val = set->cfg.regs->MCAN_CCCR;
if ((val & MCAN_CCCR_INIT) == MCAN_CCCR_INIT_ENABLED)
break;
if (initial)
set->cfg.regs->MCAN_CCCR = (val & ~MCAN_CCCR_INIT)
| MCAN_CCCR_INIT_ENABLED;
}
}
void mcan_loopback_on(struct mcan_set *set)
{
Mcan *mcan = set->cfg.regs;
mcan->MCAN_TEST |= MCAN_TEST_LBCK_ENABLED;
}
void mcan_loopback_off(struct mcan_set *set)
{
Mcan *mcan = set->cfg.regs;
mcan->MCAN_TEST &= ~MCAN_TEST_LBCK_ENABLED;
}
void mcan_enable_rx_array_flag(struct mcan_set *set, uint8_t line)
{
assert(line == 0 || line == 1);
Mcan *mcan = set->cfg.regs;
if (line) {
mcan->MCAN_ILS |= MCAN_ILS_DRXL;
mcan->MCAN_ILE |= MCAN_ILE_EINT1;
} else {
mcan->MCAN_ILS &= ~MCAN_ILS_DRXL;
mcan->MCAN_ILE |= MCAN_ILE_EINT0;
}
mcan->MCAN_IR = MCAN_IR_DRX; /* clear previous flag */
mcan->MCAN_IE |= MCAN_IE_DRXE; /* enable it */
}
uint8_t * mcan_prepare_tx_buffer(struct mcan_set *set, uint8_t buf_idx,
uint32_t id, uint8_t len)
{
assert(buf_idx < set->cfg.array_size_tx);
assert(len <= set->cfg.buf_size_tx);
Mcan *mcan = set->cfg.regs;
uint32_t *pThisTxBuf = 0;
uint32_t val;
const enum mcan_can_mode mode = mcan_get_mode(set);
enum mcan_dlc dlc;
if (buf_idx >= set->cfg.array_size_tx)
return NULL;
if (!get_length_code(len, &dlc))
dlc = CAN_DLC_0;
pThisTxBuf = set->ram_array_tx + buf_idx
* (MCAN_RAM_BUF_HDR_SIZE + set->cfg.buf_size_tx / 4);
if (mcan_is_extended_id(id))
*pThisTxBuf++ = MCAN_RAM_BUF_XTD | MCAN_RAM_BUF_ID_XTD(id);
else
*pThisTxBuf++ = MCAN_RAM_BUF_ID_STD(id);
val = MCAN_RAM_BUF_MM(0) | MCAN_RAM_BUF_DLC((uint32_t)dlc);
if (mode == MCAN_MODE_EXT_LEN_CONST_RATE)
val |= MCAN_RAM_BUF_FDF;
else if (mode == MCAN_MODE_EXT_LEN_DUAL_RATE)
val |= MCAN_RAM_BUF_FDF | MCAN_RAM_BUF_BRS;
*pThisTxBuf++ = val;
/* enable transmit from buffer to set TC interrupt bit in IR,
* but interrupt will not happen unless TC interrupt is enabled */
mcan->MCAN_TXBTIE = (1 << buf_idx);
return (uint8_t *)pThisTxBuf; /* now it points to the data field */
}
void mcan_send_tx_buffer(struct mcan_set *set, uint8_t buf_idx)
{
Mcan *mcan = set->cfg.regs;
if (buf_idx < set->cfg.array_size_tx)
mcan->MCAN_TXBAR = (1 << buf_idx);
}
uint8_t mcan_enqueue_outgoing_msg(struct mcan_set *set, uint32_t id,
uint8_t len, const uint8_t *data)
{
assert(len <= set->cfg.buf_size_tx);
Mcan *mcan = set->cfg.regs;
uint32_t val;
uint32_t *pThisTxBuf = 0;
const enum mcan_can_mode mode = mcan_get_mode(set);
enum mcan_dlc dlc;
uint8_t putIdx = 255;
if (!get_length_code(len, &dlc))
dlc = CAN_DLC_0;
/* Configured for FifoQ and FifoQ not full? */
if (set->cfg.fifo_size_tx == 0 || (mcan->MCAN_TXFQS & MCAN_TXFQS_TFQF))
return putIdx;
putIdx = (uint8_t)((mcan->MCAN_TXFQS & MCAN_TXFQS_TFQPI_Msk)
>> MCAN_TXFQS_TFQPI_Pos);
pThisTxBuf = set->ram_array_tx + (uint32_t)
putIdx * (MCAN_RAM_BUF_HDR_SIZE + set->cfg.buf_size_tx / 4);
if (mcan_is_extended_id(id))
*pThisTxBuf++ = MCAN_RAM_BUF_XTD | MCAN_RAM_BUF_ID_XTD(id);
else
*pThisTxBuf++ = MCAN_RAM_BUF_ID_STD(id);
val = MCAN_RAM_BUF_MM(0) | MCAN_RAM_BUF_DLC((uint32_t)dlc);
if (mode == MCAN_MODE_EXT_LEN_CONST_RATE)
val |= MCAN_RAM_BUF_FDF;
else if (mode == MCAN_MODE_EXT_LEN_DUAL_RATE)
val |= MCAN_RAM_BUF_FDF | MCAN_RAM_BUF_BRS;
*pThisTxBuf++ = val;
memcpy(pThisTxBuf, data, len);
/* enable transmit from buffer to set TC interrupt bit in IR,
* but interrupt will not happen unless TC interrupt is enabled
*/
mcan->MCAN_TXBTIE = (1 << putIdx);
/* request to send */
mcan->MCAN_TXBAR = (1 << putIdx);
return putIdx;
}
bool mcan_is_buffer_sent(const struct mcan_set *set, uint8_t buf_idx)
{
Mcan *mcan = set->cfg.regs;
return mcan->MCAN_TXBTO & (1 << buf_idx) ? true : false;
}
void mcan_filter_single_id(struct mcan_set *set,
uint8_t buf_idx, uint8_t filter, uint32_t id)
{
assert(buf_idx < set->cfg.array_size_rx);
assert(id & CAN_EXT_MSG_ID ? filter < set->cfg.array_size_filt_ext
: filter < set->cfg.array_size_filt_std);
assert(id & CAN_EXT_MSG_ID ? (id & ~CAN_EXT_MSG_ID) <= 0x1fffffff :
id <= 0x7ff);
uint32_t *pThisRxFilt = 0;
if (buf_idx >= set->cfg.array_size_rx)
return;
if (mcan_is_extended_id(id)) {
pThisRxFilt = set->ram_filt_ext + filter
* MCAN_RAM_FILT_EXT_SIZE;
*pThisRxFilt++ = MCAN_RAM_FILT_EFEC_BUF
| MCAN_RAM_FILT_EFID1(id);
*pThisRxFilt = MCAN_RAM_FILT_EFID2_BUF
| MCAN_RAM_FILT_EFID2_BUF_IDX(buf_idx);
} else {
pThisRxFilt = set->ram_filt_std + filter
* MCAN_RAM_FILT_STD_SIZE;
*pThisRxFilt = MCAN_RAM_FILT_SFEC_BUF
| MCAN_RAM_FILT_SFID1(id)
| MCAN_RAM_FILT_SFID2_BUF
| MCAN_RAM_FILT_SFID2_BUF_IDX(buf_idx);
}
}
void mcan_filter_id_mask(struct mcan_set *set, uint8_t fifo, uint8_t filter,
uint32_t id, uint32_t mask)
{
assert(fifo == 0 || fifo == 1);
assert(id & CAN_EXT_MSG_ID ? filter < set->cfg.array_size_filt_ext
: filter < set->cfg.array_size_filt_std);
assert(id & CAN_EXT_MSG_ID ? (id & ~CAN_EXT_MSG_ID) <= 0x1fffffff :
id <= 0x7ff);
assert(id & CAN_EXT_MSG_ID ? mask <= 0x1fffffff : mask <= 0x7ff);
uint32_t *pThisRxFilt = 0;
uint32_t val;
if (mcan_is_extended_id(id)) {
pThisRxFilt = set->ram_filt_ext + filter
* MCAN_RAM_FILT_EXT_SIZE;
*pThisRxFilt++ = (fifo ? MCAN_RAM_FILT_EFEC_FIFO1
: MCAN_RAM_FILT_EFEC_FIFO0) | MCAN_RAM_FILT_EFID1(id);
*pThisRxFilt = MCAN_RAM_FILT_EFT_CLASSIC
| MCAN_RAM_FILT_EFID2(mask);
} else {
pThisRxFilt = set->ram_filt_std + filter
* MCAN_RAM_FILT_STD_SIZE;
val = MCAN_RAM_FILT_SFT_CLASSIC
| MCAN_RAM_FILT_SFID1(id)
| MCAN_RAM_FILT_SFID2(mask);
*pThisRxFilt = (fifo ? MCAN_RAM_FILT_SFEC_FIFO1
: MCAN_RAM_FILT_SFEC_FIFO0) | val;
}
}
bool mcan_rx_buffer_data(const struct mcan_set *set, uint8_t buf_idx)
{
Mcan *mcan = set->cfg.regs;
if (buf_idx < 32)
return mcan->MCAN_NDAT1 & (1 << buf_idx) ? true : false;
else if (buf_idx < 64)
return mcan->MCAN_NDAT2 & (1 << (buf_idx - 32)) ? true : false;
else
return false;
}
void mcan_read_rx_buffer(struct mcan_set *set, uint8_t buf_idx,
struct mcan_msg_info *msg)
{
assert(buf_idx < set->cfg.array_size_rx);
Mcan *mcan = set->cfg.regs;
const uint32_t *pThisRxBuf = 0;
uint32_t tempRy; /* temp copy of RX buffer word */
uint8_t len;
if (buf_idx >= set->cfg.array_size_rx) {
msg->id = 0;
msg->timestamp = 0;
msg->full_len = 0;
msg->data_len = 0;
return;
}
pThisRxBuf = set->ram_array_rx + (buf_idx
* (MCAN_RAM_BUF_HDR_SIZE + set->cfg.buf_size_rx / 4));
tempRy = *pThisRxBuf++; /* word R0 contains ID */
if (tempRy & MCAN_RAM_BUF_XTD)
msg->id = CAN_EXT_MSG_ID | (tempRy & MCAN_RAM_BUF_ID_XTD_Msk)
>> MCAN_RAM_BUF_ID_XTD_Pos;
else
msg->id = (tempRy & MCAN_RAM_BUF_ID_STD_Msk)
>> MCAN_RAM_BUF_ID_STD_Pos;
tempRy = *pThisRxBuf++; /* word R1 contains DLC & time stamp */
msg->full_len = len = get_data_length((enum mcan_dlc)
((tempRy & MCAN_RAM_BUF_DLC_Msk) >> MCAN_RAM_BUF_DLC_Pos));
msg->timestamp = (tempRy & MCAN_RAM_BUF_RXTS_Msk)
>> MCAN_RAM_BUF_RXTS_Pos;
if (msg->data) {
/* copy the data from the Rx Buffer Element to the
* application-owned buffer */
if (len > set->cfg.buf_size_rx)
len = set->cfg.buf_size_rx;
if (len > msg->data_len)
len = msg->data_len;
memcpy(msg->data, pThisRxBuf, len);
msg->data_len = len;
}
else
msg->data_len = 0;
/* clear the new data flag for the buffer */
if (buf_idx < 32)
mcan->MCAN_NDAT1 = (1 << buf_idx);
else
mcan->MCAN_NDAT2 = (1 << (buf_idx - 32));
}
uint8_t mcan_dequeue_received_msg(struct mcan_set *set, uint8_t fifo,
struct mcan_msg_info *msg)
{
assert(fifo == 0 || fifo == 1);
Mcan *mcan = set->cfg.regs;
uint32_t *pThisRxBuf = 0;
uint32_t tempRy; /* temp copy of RX buffer word */
uint8_t buf_elem_data_size, len;
uint32_t *fifo_ack_reg;
uint32_t get_index;
uint8_t fill_level = 0; /* default: fifo empty */
if (fifo) {
get_index = (mcan->MCAN_RXF1S & MCAN_RXF1S_F1GI_Msk) >>
MCAN_RXF1S_F1GI_Pos;
fill_level = (uint8_t)((mcan->MCAN_RXF1S & MCAN_RXF1S_F1FL_Msk)
>> MCAN_RXF1S_F1FL_Pos);
pThisRxBuf = set->ram_fifo_rx1;
buf_elem_data_size = set->cfg.buf_size_rx_fifo1;
fifo_ack_reg = (uint32_t *) & mcan->MCAN_RXF1A;
} else {
get_index = (mcan->MCAN_RXF0S & MCAN_RXF0S_F0GI_Msk)
>> MCAN_RXF0S_F0GI_Pos;
fill_level = (uint8_t)((mcan->MCAN_RXF0S & MCAN_RXF0S_F0FL_Msk)
>> MCAN_RXF0S_F0FL_Pos);
pThisRxBuf = set->ram_fifo_rx0;
buf_elem_data_size = set->cfg.buf_size_rx_fifo0;
fifo_ack_reg = (uint32_t *) & mcan->MCAN_RXF0A;
}
if (fill_level == 0)
return 0;
pThisRxBuf += get_index * (MCAN_RAM_BUF_HDR_SIZE + buf_elem_data_size
/ 4);
tempRy = *pThisRxBuf++; /* word R0 contains ID */
if (tempRy & MCAN_RAM_BUF_XTD)
msg->id = CAN_EXT_MSG_ID | (tempRy & MCAN_RAM_BUF_ID_XTD_Msk)
>> MCAN_RAM_BUF_ID_XTD_Pos;
else
msg->id = (tempRy & MCAN_RAM_BUF_ID_STD_Msk)
>> MCAN_RAM_BUF_ID_STD_Pos;
tempRy = *pThisRxBuf++; /* word R1 contains DLC & timestamps */
msg->full_len = len = get_data_length((enum mcan_dlc)
((tempRy & MCAN_RAM_BUF_DLC_Msk) >> MCAN_RAM_BUF_DLC_Pos));
msg->timestamp = (tempRy & MCAN_RAM_BUF_RXTS_Msk)
>> MCAN_RAM_BUF_RXTS_Pos;
if (msg->data) {
/* copy the data from the Rx Buffer Element to the
* application-owned buffer */
if (len > buf_elem_data_size)
len = buf_elem_data_size;
if (len > msg->data_len)
len = msg->data_len;
memcpy(msg->data, pThisRxBuf, len);
msg->data_len = len;
}
else
msg->data_len = 0;
/* acknowledge reading the fifo entry */
*fifo_ack_reg = get_index;
/* return entries remaining in FIFO */
return (fill_level);
}
/**@}*/