mirror of https://github.com/ARMmbed/mbed-os.git
1025 lines
32 KiB
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
|