mbed-os/targets/TARGET_NUVOTON/TARGET_M460/can_api.c

1025 lines
32 KiB
C

/*
* Copyright (c) 2023, Nuvoton Technology Corporation
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "can_api.h"
#if DEVICE_CAN
#include "cmsis.h"
#include "mbed_error.h"
#include "mbed_assert.h"
#include "mbed_toolchain.h"
#include "PeripheralPins.h"
#include "gpio_api.h"
#include "nu_modutil.h"
#include "nu_bitutil.h"
#include <string.h>
#include <stdbool.h>
#include <assert.h>
/* Notes of implementation
*
* 1. Each CANFD instance supports two IRQ lines. Use only line 0. Line 1 is not used.
* 2. For Rx disabling multiple filter handles,
* (1) Map all filter handles to filter handle 0
* (2) Use Rx FIFO 0 for filter handle 0
* (3) Support mask
* 3. For Rx enabling multiple filter handles,
* (1) Map filter handles to SID/XID filter elements directly: 0 to 0/0, 1 to 1/1, etc.
* (2) Use Rx FIFO 0 for filter handle 0
* (3) Use Rx FIFO 1 for filter handle through first invoking can_filter()
* (4) Use dedicated Rx Buffer for other filter handles
* The front half for SID filter element and back half for XID filter element
* (5) Support mask only for filter handle 0 and the one through first invoking can_filter()
* H/W supports mask on Rx FIFO 0/1 but not on dedicated Rx Buffer.
* 4. Continuing above, the thread below discusses on CAN filter more precisely:
* https://os.mbed.com/questions/85183/How-to-use-CAN-filter-function/
* It has some points to note:
* (1) No message will get accepted without filter configured.
* (2) Received message is compared following filter handle 0, 1, 2, 3.
* If no match, the message is discarded.
* On match, the message can be fetched through can_read() with the match filter.
* (3) It is required filter handle 0 be configured in the constructor and accept any message by default.
* If not reconfigured, no message will reach filter handle other than 0.
* 5. For Tx, use only dedicated Tx Buffer. BSP CANFD driver doesn't support Tx FIFO/Queue.
* 6. Support no CAN FD.
* 7. CAN HAL doesn't define modes clearly. Following other chip porting, map them to H/W as below:
* MODE_NORMAL --> Normal operation
* MODE_SILENT --> Bus Monitor mode
* MODE_TEST_GLOBAL/LOCAL --> Test/External Loop Back mode
* MODE_TEST_SILENT --> Test/Internal Loop Back mode
*/
/* Enable or not multiple filter handles
*
* Reasons for disabling the feature:
* (1) Per-handle implementation doesn't support mask on all filter handles.
* SID/XID filter elements directing to Rx FIFO 0/1 can support mask.
* SID/XID filter elements directing to dedicated Rx Buffer cannot support mask.
* (2) Mbed OS CAN HAL API allows ignoring 'handle' parameter.
* (3) The default filter handle 0 will accept all messages,
* so by default, all messages cannot reach other user defined ones,
* unless filter handle 0 reconfigured.
* However, most samples are unaware of this.
*/
#define NU_CAN_EN_MULT_HNDL 0
/* Max number of message ID filter handle */
#define NU_CAN_MAXNUM_HNDL 8
/* Max number of Standard message ID filter elements configured */
#define NU_CAN_MAXNUM_SIDFLTR NU_CAN_MAXNUM_HNDL
/* Max number of Extended message ID filter elements configured */
#define NU_CAN_MAXNUM_XIDFLTR NU_CAN_MAXNUM_HNDL
/* Max number of dedicated Rx Buffer elements configured */
#define NU_CAN_MAXNUM_RXBUF (NU_CAN_MAXNUM_SIDFLTR + NU_CAN_MAXNUM_XIDFLTR)
/* Configured number of SID filter elements must be less than H/W limit */
static_assert(NU_CAN_MAXNUM_SIDFLTR <= CANFD_MAX_11_BIT_FTR_ELEMS,
"Configured number of SID filter elements must be less than H/W limit");
/* Configured number of SID filter elements must be equal to filter handles */
static_assert(NU_CAN_MAXNUM_SIDFLTR == NU_CAN_MAXNUM_HNDL,
"Configured number of SID filter elements must be equal to filter handles");
/* Configured number of XID filter elements must be less than H/W limit */
static_assert(NU_CAN_MAXNUM_XIDFLTR <= CANFD_MAX_29_BIT_FTR_ELEMS,
"Configured number of XID filter elements must be less than H/W limit");
/* Configured number of XID filter elements must be equal to filter handles */
static_assert(NU_CAN_MAXNUM_XIDFLTR == NU_CAN_MAXNUM_HNDL,
"Configured number of XID filter elements must be equal to filter handles");
/* Configured number of dedicated Rx Buffer elements must be less than H/W limit */
static_assert(NU_CAN_MAXNUM_RXBUF <= CANFD_MAX_RX_BUF_ELEMS,
"Configured number of dedicated Rx Buffer elements must be less than H/W limit");
/* Convert to string literal */
#define NU_STR_(X) #X
#define NU_STR(X) NU_STR_(X)
/* Rx buffer type for filter */
enum {
NU_CAN_RXBUF_TYPE_NONE = 0,
NU_CAN_RXBUF_TYPE_FIFO_0,
NU_CAN_RXBUF_TYPE_FIFO_1,
NU_CAN_RXBUF_TYPE_DEDICATED,
};
struct nu_can_filter {
uint32_t id;
uint32_t mask;
CANFormat format;
int32_t handle;
uint32_t rxbuf_type;
};
struct nu_can_var {
CANFD_FD_T canfd_config;
bool rx_fifo_0_used;
bool rx_fifo_1_used;
struct nu_can_filter filters[NU_CAN_MAXNUM_HNDL];
CANFD_FD_MSG_T msg_staging;
can_irq_handler irq_handler;
uintptr_t irq_context;
/* Mark the following area is reserved for not being cleared */
uint32_t reserved;
/* Following fields are static-initialized */
void (*vec)(void);
IRQn_Type irq_line1;
};
/* IRQ handler for CAN IRQ line 0. CAN IRQ line 1 is not used. */
void CANFD00_IRQHandler(void);
void CANFD10_IRQHandler(void);
void CANFD20_IRQHandler(void);
void CANFD30_IRQHandler(void);
static void can_reconfig(can_t *obj, CANFD_FD_T *canfd_config);
static void can_irq(CANName can);
static void can_filters_reconfig(can_t *obj);
static int can_filter_bind_rxbuf_type(can_t *obj, struct nu_can_filter *filter);
static int can_filter_config(can_t *obj, const struct nu_can_filter *filter);
/* Get SID/XID filter element index by filter handle
*
* One filter handle maps to one SID filter and one XID filter.
* These three are numbered the same.
*/
static inline uint32_t can_filter_sidfltr_index(const struct nu_can_filter *filter)
{
return ((uint32_t) filter->handle);
}
static inline uint32_t can_filter_xidfltr_index(const struct nu_can_filter *filter)
{
return ((uint32_t) filter->handle);
}
/* Get dedicated Rx Buffer element index for SID/XID filter element by filter handle
*
* Front half of dedicated Rx Buffer elements for SID filter, back half for XID filter.
*/
static inline uint32_t can_filter_sidfltr_rxbuf_index(const struct nu_can_filter *filter)
{
return ((uint32_t) filter->handle);
}
static inline uint32_t can_filter_xidfltr_rxbuf_index(const struct nu_can_filter *filter)
{
return (((uint32_t) filter->handle) + NU_CAN_MAXNUM_SIDFLTR);
}
/* Is message matched? */
static inline bool can_filter_message_matched(const struct nu_can_filter *filter, const CANFD_FD_MSG_T *msg_staging)
{
if (filter->format == CANStandard && msg_staging->eIdType != eCANFD_SID) {
return false;
}
if (filter->format == CANExtended && msg_staging->eIdType != eCANFD_XID) {
return false;
}
if ((filter->id & filter->mask) != (msg_staging->u32Id & filter->mask)) {
return false;
}
return true;
}
static struct nu_can_var can0_var = {
.vec = CANFD00_IRQHandler,
.irq_line1 = CANFD01_IRQn,
};
static struct nu_can_var can1_var = {
.vec = CANFD10_IRQHandler,
.irq_line1 = CANFD11_IRQn,
};
static struct nu_can_var can2_var = {
.vec = CANFD20_IRQHandler,
.irq_line1 = CANFD21_IRQn,
};
static struct nu_can_var can3_var = {
.vec = CANFD30_IRQHandler,
.irq_line1 = CANFD31_IRQn,
};
static const struct nu_modinit_s can_modinit_tab[] = {
{CAN_0, CANFD0_MODULE, CLK_CLKSEL0_CANFD0SEL_HCLK, CLK_CLKDIV5_CANFD0(1), CANFD0_RST, CANFD00_IRQn, &can0_var},
{CAN_1, CANFD1_MODULE, CLK_CLKSEL0_CANFD1SEL_HCLK, CLK_CLKDIV5_CANFD1(1), CANFD1_RST, CANFD10_IRQn, &can1_var},
{CAN_2, CANFD2_MODULE, CLK_CLKSEL0_CANFD2SEL_HCLK, CLK_CLKDIV5_CANFD2(1), CANFD2_RST, CANFD20_IRQn, &can2_var},
{CAN_3, CANFD3_MODULE, CLK_CLKSEL0_CANFD3SEL_HCLK, CLK_CLKDIV5_CANFD3(1), CANFD3_RST, CANFD30_IRQn, &can3_var},
{NC, 0, 0, 0, 0, (IRQn_Type) 0, NULL}
};
void can_init_freq(can_t *obj, PinName rd, PinName td, int hz)
{
uint32_t can_rd = (CANName)pinmap_peripheral(rd, PinMap_CAN_RD);
uint32_t can_td = (CANName)pinmap_peripheral(td, PinMap_CAN_TD);
obj->can = (CANName)pinmap_merge(can_rd, can_td);
MBED_ASSERT((int)obj->can != NC);
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
/* Clear var to zero except reserved area */
memset(var, 0x00, offsetof(struct nu_can_var, reserved));
obj->pin_rd = rd;
obj->pin_td = td;
pinmap_pinout(rd, PinMap_CAN_RD);
pinmap_pinout(td, PinMap_CAN_TD);
// Enable IP clock
CLK_EnableModuleClock(modinit->clkidx);
// Reset IP
SYS_ResetModule(modinit->rsetidx);
CANFD_FD_T *canfd_config = &var->canfd_config;
/* Based on BSP CAN driver default configuration, no CAN FD */
CANFD_GetDefaultConfig(canfd_config, CANFD_OP_CAN_MODE);
/* Change default configuration here */
{
/* Change normal bit rate to specified. CAN FD is not supported,
* so data bit rate will be the same as above. */
if (hz > 0) {
canfd_config->sBtConfig.sNormBitRate.u32BitRate = hz;
}
/* Change max number of SID filter elements */
canfd_config->sElemSize.u32SIDFC = NU_CAN_MAXNUM_SIDFLTR;
/* Change max number of SID filter elements */
canfd_config->sElemSize.u32XIDFC = NU_CAN_MAXNUM_XIDFLTR;
/* Change max number of dedicated Rx Buffer elements */
canfd_config->sElemSize.u32RxBuf = NU_CAN_MAXNUM_RXBUF;
can_reconfig(obj, canfd_config);
}
/* As required, filter handle 0 defaults to accept-any */
struct nu_can_filter *filter_0 = &var->filters[0];
filter_0->id = 0;
filter_0->mask = 0;
filter_0->format = CANAny;
filter_0->handle = 0;
/* Bind filter handle 0 to Rx Buffer type */
can_filter_bind_rxbuf_type(obj, filter_0);
MBED_ASSERT(filter_0->rxbuf_type != NU_CAN_RXBUF_TYPE_NONE);
/* Configure filter handle 0 */
can_filter_config(obj, filter_0);
}
void can_init(can_t *obj, PinName rd, PinName td)
{
can_init_freq(obj, rd, td, -1);
}
void can_free(can_t *obj)
{
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
CANFD_Close(can_base);
// Reset this module
SYS_ResetModule(modinit->rsetidx);
// Disable interrupts
CANFD_DisableInt(can_base,
0xFFFFFFFF, // interrupt line 0
0, // interrupt line 1 unused
0xFFFFFFFF, // Tx Buffer Transmission 0-31 Interrupts
0xFFFFFFFF); // Tx Buffer Cancellation Finished 0-31 Interrupts
NVIC_DisableIRQ(modinit->irq_n);
// Disable IP clock
CLK_DisableModuleClock(modinit->clkidx);
/* Free up pins */
gpio_set(obj->pin_rd);
gpio_set(obj->pin_td);
obj->pin_rd = NC;
obj->pin_td = NC;
}
int can_frequency(can_t *obj, int hz)
{
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
/* Based on previous configuration */
CANFD_FD_T *canfd_config = &var->canfd_config;
canfd_config->sBtConfig.sNormBitRate.u32BitRate = hz;
can_reconfig(obj, canfd_config);
/* With BSP CAN FD driver, all filters configured before will get cleared by above call.
* Reconfigure them back. */
can_filters_reconfig(obj);
/* 1 on success, or 0 on failure */
return 1;
}
void CANFD00_IRQHandler(void)
{
can_irq(CAN_0);
}
void CANFD10_IRQHandler(void)
{
can_irq(CAN_1);
}
void CANFD20_IRQHandler(void)
{
can_irq(CAN_2);
}
void CANFD30_IRQHandler(void)
{
can_irq(CAN_3);
}
void can_irq_init(can_t *obj, can_irq_handler handler, uintptr_t context)
{
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
var->irq_handler = handler;
var->irq_context = context;
}
void can_irq_free(can_t *obj)
{
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
var->irq_handler = NULL;
var->irq_context = 0;
}
void can_irq_set(can_t *obj, CanIrqType irq, uint32_t enable)
{
if (enable) {
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
NVIC_SetVector(modinit->irq_n, (uint32_t) var->vec);
NVIC_EnableIRQ(modinit->irq_n);
}
/* We use only interrupt line 0. */
uint32_t line0_interrupts = 0;
uint32_t line1_interrupts = 0;
/* Interrupt mapping from HAL CAN to MCU CANFD */
switch (irq) {
case IRQ_RX:
line0_interrupts = CANFD_IE_RF0NE_Msk | CANFD_IE_RF1NE_Msk | CANFD_IE_DRXE_Msk;
break;
case IRQ_TX:
line0_interrupts = CANFD_IE_TCE_Msk;
break;
case IRQ_ERROR:
line0_interrupts = CANFD_IE_EWE_Msk;
break;
case IRQ_OVERRUN:
break;
case IRQ_WAKEUP:
break;
case IRQ_PASSIVE:
line0_interrupts = CANFD_IE_EPE_Msk;
break;
case IRQ_ARB:
break;
case IRQ_BUS:
line0_interrupts = CANFD_IE_BOE_Msk;
break;
case IRQ_READY:
break;
default:
break;
}
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
uint32_t ie = CANFD_ReadReg(&can_base->IE);
/* Tx Buffer Transmission/Cancellation Finished Interrupt Enable
*
* Each Tx Buffer has its own Transmission/Cancellation Finished Interrupt Enable bit.
* Dependent on their overall switch IE.TCE/IE.TCFE, these bits are set altogether or not.
*/
uint32_t txbtie = (CANFD_IE_TCE_Msk & (ie | line0_interrupts)) ? 0xFFFFFFFF : 0;
uint32_t txbcie = (CANFD_IE_TCFE_Msk & (ie | line0_interrupts)) ? 0xFFFFFFFF : 0;
if (enable) {
CANFD_EnableInt(can_base, line0_interrupts, line1_interrupts, txbtie, txbcie);
} else {
CANFD_DisableInt(can_base, line0_interrupts, line1_interrupts, txbtie, txbcie);
}
}
int can_write(can_t *obj, CAN_Message msg, int cc)
{
/* Unused */
(void) cc;
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
/* Populate the message */
CANFD_FD_MSG_T *msg_staging = &var->msg_staging;
memset(msg_staging, 0x00, sizeof(CANFD_FD_MSG_T));
/* Message ID */
msg_staging->u32Id = msg.id;
/* CANFD_FD_MSG_T.u32DLC, not suitable naming, is data length in bytes, not data length code. */
msg_staging->u32DLC = (msg.len <= CANFD_MAX_MESSAGE_BYTES) ? msg.len : CANFD_MAX_MESSAGE_BYTES;
memcpy(msg_staging->au8Data, msg.data, msg_staging->u32DLC);
/* Standard/extended message */
msg_staging->eIdType = (msg.format == CANStandard) ? eCANFD_SID : eCANFD_XID;
/* Data/remote message */
msg_staging->eFrmType = (msg.type == CANData) ? eCANFD_DATA_FRM : eCANFD_REMOTE_FRM;
/* No FD format */
msg_staging->bFDFormat = 0;
/* No bit rate switch */
msg_staging->bBitRateSwitch = 0;
int success;
/* BSP CAN driver supports Tx Dedicated Buffer */
uint32_t i;
for (i = 0; i < var->canfd_config.sElemSize.u32TxBuf; i ++) {
success = CANFD_TransmitTxMsg(can_base, i, msg_staging);
if (success) {
return 1;
}
}
/* BSP CAN driver supports no Tx FIFO/Queue */
return 0;
}
int can_read(can_t *obj, CAN_Message *msg, int handle)
{
#if NU_CAN_EN_MULT_HNDL
/* Check validity of filter handle */
if (handle < 0 || handle >= NU_CAN_MAXNUM_HNDL) {
error("Support max " NU_STR(NU_CAN_MAXNUM_HNDL) " CAN filters");
return 0;
}
#else
/* Single filter handle 0 */
int32_t handle_orig = handle;
(void) handle_orig;
handle = 0;
#endif
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
const struct nu_can_filter *filter = &var->filters[handle];
CANFD_FD_MSG_T *msg_staging = &var->msg_staging;
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
if (filter->rxbuf_type == NU_CAN_RXBUF_TYPE_FIFO_0 ||
filter->rxbuf_type == NU_CAN_RXBUF_TYPE_FIFO_1) {
uint32_t fifo_idx = (filter->rxbuf_type == NU_CAN_RXBUF_TYPE_FIFO_0) ? 0: 1;
while (1) {
if (!CANFD_ReadRxFifoMsg(can_base, fifo_idx, msg_staging)) {
/* Rx FIFO 0/1 empty */
goto message_no_accepted;
}
if (!can_filter_message_matched(filter, msg_staging)) {
/* Not matched, go next one */
continue;
}
/* Find one matched */
goto message_matched;
}
} else {
/* Get dedicated Rx Buffer element index for SID/XID filter element by filter handle */
uint32_t sidfltr_rxdbf_idx = can_filter_sidfltr_rxbuf_index(filter);
uint32_t xidfltr_rxdbf_idx = can_filter_xidfltr_rxbuf_index(filter);
/* Receive from dedicated Rx Buffer for SID filter element */
if (filter->format == CANStandard || filter->format == CANAny) {
if (!CANFD_ReadRxBufMsg(can_base, sidfltr_rxdbf_idx, msg_staging)) {
/* Dedicated Rx Buffer empty */
goto check_xid_filter;
}
if (!can_filter_message_matched(filter, msg_staging)) {
/* Not matched */
goto check_xid_filter;
}
/* Find one matched */
goto message_matched;
}
check_xid_filter:
/* Receive from dedicated Rx Buffer for XID filter element */
if (filter->format == CANExtended || filter->format == CANAny) {
if (!CANFD_ReadRxBufMsg(can_base, xidfltr_rxdbf_idx, msg_staging)) {
/* Dedicated Rx Buffer empty */
goto message_no_accepted;
}
if (!can_filter_message_matched(filter, msg_staging)) {
/* Not matched */
goto message_no_matched;
}
/* Find one matched */
goto message_matched;
}
}
message_no_accepted:
message_no_matched:
return 0;
message_matched:
/* ID match, populate the message */
memset(msg, 0x00, sizeof(CAN_Message));
/* Message ID */
msg->id = msg_staging->u32Id;
/* CANFD_FD_MSG_T.u32DLC, not suitable naming, is data length in bytes, not data length code. */
msg->len = (msg_staging->u32DLC <= sizeof(msg->data)) ? msg_staging->u32DLC : sizeof(msg->data);
memcpy(&msg->data[0], &msg_staging->au8Data[0], msg->len);
/* Standard/Extended message */
msg->format = (msg_staging->eIdType == eCANFD_SID) ? CANStandard : CANExtended;
/* Data/Remote message */
msg->type = (msg_staging->eFrmType == eCANFD_DATA_FRM) ? CANData : CANRemote;
return 1;
}
int can_mode(can_t *obj, CanMode mode)
{
bool monitor_enabled;
bool test_enabled;
bool loopback_enabled;
switch (mode) {
case MODE_RESET:
MBED_FALLTHROUGH;
case MODE_NORMAL:
monitor_enabled = false;
test_enabled = false;
loopback_enabled = false;
break;
case MODE_SILENT:
monitor_enabled = true;
test_enabled = false;
loopback_enabled = false;
break;
case MODE_TEST_GLOBAL:
MBED_FALLTHROUGH;
case MODE_TEST_LOCAL:
monitor_enabled = false;
test_enabled = true;
loopback_enabled = true;
break;
case MODE_TEST_SILENT:
monitor_enabled = true;
test_enabled = true;
loopback_enabled = true;
break;
default:
/* 1 if success, or 0 if failure */
return 0;
}
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
/* Enter INIT mode for configuration */
CANFD_RunToNormal(can_base, false);
/* Enable write-protect configuration change */
can_base->CCCR = CANFD_ReadReg(&can_base->CCCR) | CANFD_CCCR_CCE_Msk;
/* Enable monitor or not */
if (monitor_enabled) {
can_base->CCCR = CANFD_ReadReg(&can_base->CCCR) | CANFD_CCCR_MON_Msk;
} else {
can_base->CCCR = CANFD_ReadReg(&can_base->CCCR) & ~CANFD_CCCR_MON_Msk;
}
/* Enable Test mode or not */
if (test_enabled) {
can_base->CCCR = CANFD_ReadReg(&can_base->CCCR) | CANFD_CCCR_TEST_Msk;
} else {
can_base->CCCR = CANFD_ReadReg(&can_base->CCCR) & ~CANFD_CCCR_TEST_Msk;
}
/* Enable loopback or not */
if (loopback_enabled) {
can_base->TEST = CANFD_ReadReg(&can_base->TEST) | CANFD_TEST_LBCK_Msk;
} else {
can_base->TEST = CANFD_ReadReg(&can_base->TEST) & ~CANFD_TEST_LBCK_Msk;
}
/* Leave INIT mode for normal operation */
CANFD_RunToNormal(can_base, true);
/* 1 if success, or 0 if failure */
return 1;
}
int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t handle)
{
#if NU_CAN_EN_MULT_HNDL
/* Check validity of filter handle */
if (handle < 0 || handle >= NU_CAN_MAXNUM_HNDL) {
error("Support max " NU_STR(NU_CAN_MAXNUM_HNDL) " CAN filters");
return 0;
}
#else
/* Single filter handle 0 */
int32_t handle_orig = handle;
(void) handle_orig;
handle = 0;
#endif
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
struct nu_can_filter *filter = &var->filters[handle];
/* Keep user-defined filter configuration */
filter->id = id;
filter->mask = mask;
filter->format = format;
filter->handle = handle;
/* Bind filter to Rx Buffer type */
can_filter_bind_rxbuf_type(obj, filter);
MBED_ASSERT(filter->rxbuf_type != NU_CAN_RXBUF_TYPE_NONE);
if (can_filter_config(obj, filter)) {
#if NU_CAN_EN_MULT_HNDL
return handle;
#else
/* NOTE: 0 is ambiguous, error or filter handle 0. */
return handle_orig;
#endif
} else {
return 0;
}
}
void can_reset(can_t *obj)
{
can_mode(obj, MODE_RESET);
}
unsigned char can_rderror(can_t *obj)
{
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
uint32_t ecr = CANFD_ReadReg(&can_base->ECR);
return (uint8_t) ((ecr >> CANFD_ECR_REC_Pos) & CANFD_ECR_REC_Msk);
}
unsigned char can_tderror(can_t *obj)
{
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
uint32_t ecr = CANFD_ReadReg(&can_base->ECR);
return (uint8_t) ((ecr >> CANFD_ECR_TEC_Pos) & CANFD_ECR_TEC_Msk);
}
void can_monitor(can_t *obj, int silent)
{
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
bool test_enabled = CANFD_ReadReg(&can_base->CCCR) & CANFD_CCCR_TEST_Msk;
CanMode mode;
if (silent) {
if (test_enabled) {
mode = MODE_TEST_SILENT;
} else {
mode = MODE_SILENT;
}
} else {
if (test_enabled) {
mode = MODE_TEST_GLOBAL;
} else {
mode = MODE_NORMAL;
}
}
can_mode(obj, mode);
}
const PinMap *can_rd_pinmap()
{
return PinMap_CAN_TD;
}
const PinMap *can_td_pinmap()
{
return PinMap_CAN_RD;
}
void can_reconfig(can_t *obj, CANFD_FD_T *canfd_config)
{
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
/* Enter INIT mode for configuration */
CANFD_RunToNormal(can_base, false);
/* Enable write-protect configuration change */
can_base->CCCR = CANFD_ReadReg(&can_base->CCCR) | CANFD_CCCR_CCE_Msk;
/* Keep IRQ enabled or not */
bool irq_enabled = NVIC_GetEnableIRQ(modinit->irq_n);
CANFD_Open(can_base, canfd_config);
/* Cover side effect of CANFD_Open() */
if (!irq_enabled) {
NVIC_DisableIRQ(modinit->irq_n); // Disable IRQ line 0
NVIC_DisableIRQ(var->irq_line1); // Disable IRQ line 1
}
/* Leave INIT mode for normal operation */
CANFD_RunToNormal(can_base, true);
}
/**
* \brief Reconfigure filters configured before
*/
static void can_filters_reconfig(can_t *obj)
{
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
struct nu_can_filter *filter = var->filters;
struct nu_can_filter *filter_end = var->filters + sizeof(var->filters) / sizeof(var->filters[0]);
for (; filter != filter_end; filter ++) {
/* A filter not binding to Rx Buffer type is not configured before. */
if (filter->rxbuf_type == NU_CAN_RXBUF_TYPE_NONE) {
continue;
}
can_filter_config(obj, filter);
}
}
/**
* \brief BInd filter to Rx Buffer type
*
* \return 0 if failure, or 1 if success
*/
static int can_filter_bind_rxbuf_type(can_t *obj, struct nu_can_filter *filter)
{
const struct nu_modinit_s *modinit = get_modinit(obj->can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) obj->can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
/* Bind to Rx FIFO 0/1 first if free, or dedicated Rx Buffer */
if (filter->rxbuf_type == NU_CAN_RXBUF_TYPE_NONE) {
if (!var->rx_fifo_0_used) {
var->rx_fifo_0_used = true;
filter->rxbuf_type = NU_CAN_RXBUF_TYPE_FIFO_0;
} else if (!var->rx_fifo_1_used) {
var->rx_fifo_1_used = true;
filter->rxbuf_type = NU_CAN_RXBUF_TYPE_FIFO_1;
} else {
/* H/W doesn't support mask on directing to dedicated Rx Buffer. */
if (filter->mask != 0xFFFFFFFF) {
error("CAN HAL supports mask only on first two configured filters (including handle 0). Try disabling mask by setting mask to 0xFFFFFFFF.");
return 0;
}
filter->rxbuf_type = NU_CAN_RXBUF_TYPE_DEDICATED;
}
}
return 1;
}
/**
* \brief Set up message ID filter
*
* \return 0 if failure, or 1 if success
*/
static int can_filter_config(can_t *obj, const struct nu_can_filter *filter)
{
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(obj->can);
/* Enter INIT mode for configuration */
CANFD_RunToNormal(can_base, false);
/* Enable write-protect configuration change */
can_base->CCCR = CANFD_ReadReg(&can_base->CCCR) | CANFD_CCCR_CCE_Msk;
/* Global filter configuration
*
* 1. Reject unmatched standard/extended message ID
* 2. Accept remote message
*/
CANFD_SetGFC(CANFD0, eCANFD_REJ_NON_MATCH_FRM, eCANFD_REJ_NON_MATCH_FRM, 0, 0);
/* Get SID/XID filter element index by filter handle */
uint32_t sidfltr_idx = can_filter_sidfltr_index(filter);
uint32_t xidfltr_idx = can_filter_xidfltr_index(filter);
/* Get dedicated Rx Buffer element index for SID/XID filter element by filter handle */
uint32_t sidfltr_rxdbf_idx = can_filter_sidfltr_rxbuf_index(filter);
uint32_t xidfltr_rxdbf_idx = can_filter_xidfltr_rxbuf_index(filter);
/* Configure filter for Standard message ID
*
* Direct accepted message to Rx FIFO 0, Rx FIFO 1, or dedicated Rx Buffer
*
* NOTE: H/W doesn't support mask on directing to dedicated Rx Buffer.
*/
if (filter->format == CANStandard || filter->format == CANAny) {
switch (filter->rxbuf_type) {
case NU_CAN_RXBUF_TYPE_FIFO_0:
CANFD_SetSIDFltr(can_base, sidfltr_idx, CANFD_RX_FIFO0_STD_MASK(filter->id, filter->mask));
break;
case NU_CAN_RXBUF_TYPE_FIFO_1:
CANFD_SetSIDFltr(can_base, sidfltr_idx, CANFD_RX_FIFO1_STD_MASK(filter->id, filter->mask));
break;
default:
CANFD_SetSIDFltr(can_base, sidfltr_idx, CANFD_RX_BUFFER_STD(filter->id, sidfltr_rxdbf_idx));
}
}
/* Configure filter for Extended message ID
*
* Direct accepted message to Rx FIFO 0, Rx FIFO 1, or dedicated Rx Buffer
*
* NOTE: H/W doesn't support mask on directing to dedicated Rx Buffer.
* NOTE: CANFD.XIDAM applies to all XID filters and is unsuitable for the
* per-XID filter requirement.
*/
if (filter->format == CANExtended || filter->format == CANAny) {
switch (filter->rxbuf_type) {
case NU_CAN_RXBUF_TYPE_FIFO_0:
CANFD_SetXIDFltr(can_base, xidfltr_idx, CANFD_RX_FIFO0_EXT_MASK_LOW(filter->id), CANFD_RX_FIFO0_EXT_MASK_HIGH(filter->mask));
break;
case NU_CAN_RXBUF_TYPE_FIFO_1:
CANFD_SetXIDFltr(can_base, xidfltr_idx, CANFD_RX_FIFO1_EXT_MASK_LOW(filter->id), CANFD_RX_FIFO1_EXT_MASK_HIGH(filter->mask));
break;
default:
CANFD_SetXIDFltr(can_base, xidfltr_idx, CANFD_RX_BUFFER_EXT_LOW(filter->id, xidfltr_rxdbf_idx), CANFD_RX_BUFFER_EXT_HIGH(filter->id, xidfltr_rxdbf_idx));
}
}
/* Leave INIT mode for normal operation */
CANFD_RunToNormal(can_base, true);
return 1;
}
static void can_irq(CANName can)
{
const struct nu_modinit_s *modinit = get_modinit(can, can_modinit_tab);
MBED_ASSERT(modinit != NULL);
MBED_ASSERT(modinit->modname == (int) can);
struct nu_can_var *var = (struct nu_can_var *) modinit->var;
CANFD_T *can_base = (CANFD_T *) NU_MODBASE(can);
uint32_t ir = CANFD_ReadReg(&can_base->IR);
uint32_t ie = CANFD_ReadReg(&can_base->IE);
/* Clear all interrupt status flags */
CANFD_ClearStatusFlag(can_base, ir);
if (ir & CANFD_IR_TC_Msk) {
if (var->irq_handler && (ie & CANFD_IE_TCE_Msk)) {
var->irq_handler(var->irq_context, IRQ_TX);
}
}
if (ir & (CANFD_IR_RF0N_Msk | CANFD_IR_RF1N_Msk | CANFD_IR_DRX_Msk)) {
if (var->irq_handler && (ie & (CANFD_IE_RF0NE_Msk | CANFD_IE_RF1NE_Msk | CANFD_IE_DRXE_Msk))) {
var->irq_handler(var->irq_context, IRQ_RX);
}
}
if (ir & CANFD_IR_EW_Msk) {
if (var->irq_handler && (ie & CANFD_IE_EWE_Msk)) {
var->irq_handler(var->irq_context, IRQ_ERROR);
}
}
if (ir & CANFD_IR_EP_Msk) {
if (var->irq_handler && (ie & CANFD_IE_EPE_Msk)) {
var->irq_handler(var->irq_context, IRQ_PASSIVE);
}
}
if (ir & CANFD_IR_BO_Msk) {
if (var->irq_handler && (ie & CANFD_IE_BOE_Msk)) {
var->irq_handler(var->irq_context, IRQ_BUS);
}
}
}
#endif // DEVICE_CAN