diff --git a/bsp/nxp/mcx/mcxa/Libraries/drivers/drv_can.c b/bsp/nxp/mcx/mcxa/Libraries/drivers/drv_can.c index 1445bc6bbb6..aeddab88c36 100644 --- a/bsp/nxp/mcx/mcxa/Libraries/drivers/drv_can.c +++ b/bsp/nxp/mcx/mcxa/Libraries/drivers/drv_can.c @@ -1,551 +1,1027 @@ -/* - * Copyright (c) 2006-2025, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2025-12-23 CYFS the first version. - */ - -#include - -#ifdef RT_USING_CAN - -#include "fsl_common.h" -#include "fsl_flexcan.h" - -#define TX_MB_IDX (6) -#define RX_MB_COUNT (1) -static flexcan_frame_t frame[RX_MB_COUNT]; /* one frame buffer per RX MB */ -static rt_uint32_t filter_mask = 0; - -enum -{ -#ifdef BSP_USING_CAN0 - CAN0_INDEX, -#endif -#ifdef BSP_USING_CAN1 - CAN1_INDEX, -#endif -}; - -struct imxrt_can -{ - char *name; - CAN_Type *base; - IRQn_Type irqn; - uint32_t instance; - clock_div_name_t clock_div_name; - clock_attach_id_t clock_attach_id; - flexcan_handle_t handle; - struct rt_can_device can_dev; -}; - -struct imxrt_can flexcans[] = -{ -#ifdef BSP_USING_CAN0 - { - .name = "can0", - .base = CAN0, - .instance = 0, - .irqn = CAN0_IRQn, - .clock_div_name = kCLOCK_DivFLEXCAN0, - .clock_attach_id = kFRO_HF_DIV_to_FLEXCAN0, - }, -#endif -#ifdef BSP_USING_CAN1 - { - .name = "can1", - .base = CAN1, - .instance = 1, - .irqn = CAN1_IRQn, - .clock_div_name = kCLOCK_DivFLEXCAN1, - .clock_attach_id = kFRO_HF_DIV_to_FLEXCAN1, - }, -#endif -}; - -static void flexcan_callback(CAN_Type *base, flexcan_handle_t *handle, status_t status, uint64_t result, void *userData) -{ - struct imxrt_can *can; - flexcan_mb_transfer_t rxXfer; - - can = (struct imxrt_can *)userData; - - switch (status) - { - case kStatus_FLEXCAN_RxIdle: - rt_hw_can_isr(&can->can_dev, RT_CAN_EVENT_RX_IND | result << 8); - rxXfer.frame = &frame[result - 1]; - rxXfer.mbIdx = result; - FLEXCAN_TransferReceiveNonBlocking(can->base, &can->handle, &rxXfer); - break; - - case kStatus_FLEXCAN_TxIdle: - rt_hw_can_isr(&can->can_dev, RT_CAN_EVENT_TX_DONE | result << 8); - break; - default: - break; - } -} - -static rt_err_t _can_config(struct rt_can_device *can_dev, struct can_configure *cfg) -{ - struct imxrt_can *can = (struct imxrt_can *)can_dev->parent.user_data; - flexcan_config_t config; - rt_uint32_t res = RT_EOK; - flexcan_rx_mb_config_t mbConfig; - flexcan_mb_transfer_t rxXfer; - rt_uint8_t i, mailbox; - - FLEXCAN_GetDefaultConfig(&config); - config.baudRate = cfg->baud_rate; - config.enableIndividMask = true; /* one filter per MB */ - config.disableSelfReception = true; - - switch (cfg->mode) - { - case RT_CAN_MODE_NORMAL: - /* default mode */ - break; - case RT_CAN_MODE_LISTEN: - break; - case RT_CAN_MODE_LOOPBACK: - config.enableLoopBack = true; - break; - case RT_CAN_MODE_LOOPBACKANLISTEN: - break; - } - - flexcan_timing_config_t timing_config; - rt_memset(&timing_config, 0, sizeof(flexcan_timing_config_t)); - - if(FLEXCAN_CalculateImprovedTimingValues(can->base, config.baudRate, CLOCK_GetFlexcanClkFreq(can->instance), &timing_config)) - { - /* Update the improved timing configuration*/ - rt_memcpy(&(config.timingConfig), &timing_config, sizeof(flexcan_timing_config_t)); - } - else - { - //rt_kprintf("No found Improved Timing Configuration. Just used default configuration\n"); - } - - FLEXCAN_Init(can->base, &config, CLOCK_GetFlexcanClkFreq(can->instance)); - FLEXCAN_TransferCreateHandle(can->base, &can->handle, flexcan_callback, can); - - /* init RX_MB_COUNT RX MB to default status */ - mbConfig.format = kFLEXCAN_FrameFormatStandard; /* standard ID */ - mbConfig.type = kFLEXCAN_FrameTypeData; /* data frame */ - mbConfig.id = FLEXCAN_ID_STD(0); /* default ID is 0 */ - for (i = 0; i < RX_MB_COUNT; i++) - { - /* the used MB index from 1 to RX_MB_COUNT */ - mailbox = i + 1; - - /* all ID bit in the filter is "don't care" */ - FLEXCAN_SetRxIndividualMask(can->base, mailbox, FLEXCAN_RX_MB_STD_MASK(0, 0, 0)); - FLEXCAN_SetRxMbConfig(can->base, mailbox, &mbConfig, true); - /* one frame buffer per MB */ - rxXfer.frame = &frame[i]; - rxXfer.mbIdx = mailbox; - FLEXCAN_TransferReceiveNonBlocking(can->base, &can->handle, &rxXfer); - } - - return res; -} - -static rt_err_t _can_control(struct rt_can_device *can_dev, int cmd, void *arg) -{ - struct imxrt_can *can; - rt_uint32_t argval, mask; - rt_uint32_t res = RT_EOK; - flexcan_rx_mb_config_t mbConfig; - struct rt_can_filter_config *cfg; - struct rt_can_filter_item *item; - rt_uint8_t i, count, index; - - RT_ASSERT(can_dev != RT_NULL); - - can = (struct imxrt_can *)can_dev->parent.user_data; - RT_ASSERT(can != RT_NULL); - - switch (cmd) - { - case RT_DEVICE_CTRL_SET_INT: - argval = (rt_uint32_t) arg; - if (argval == RT_DEVICE_FLAG_INT_RX) - { - mask = kFLEXCAN_RxWarningInterruptEnable; - } - else if (argval == RT_DEVICE_FLAG_INT_TX) - { - mask = kFLEXCAN_TxWarningInterruptEnable; - } - else if (argval == RT_DEVICE_CAN_INT_ERR) - { - mask = kFLEXCAN_ErrorInterruptEnable; - } - FLEXCAN_EnableInterrupts(can->base, mask); - EnableIRQ(can->irqn); - break; - case RT_DEVICE_CTRL_CLR_INT: - /* each CAN device have one IRQ number. */ - DisableIRQ(can->irqn); - break; - case RT_CAN_CMD_SET_FILTER: - cfg = (struct rt_can_filter_config *)arg; - item = cfg->items; - count = cfg->count; - - if (filter_mask == 0xffffffff) - { - rt_kprintf("%s filter is full!\n", can->name); - res = -RT_ERROR; - break; - } - else if (filter_mask == 0) - { - /* deinit all init RX MB */ - for (i = 0; i < RX_MB_COUNT; i++) - { - FLEXCAN_SetRxMbConfig(can->base, i + 1, RT_NULL, false); - } - } - - while (count) - { - if (item->ide) - { - mbConfig.format = kFLEXCAN_FrameFormatExtend; - mbConfig.id = FLEXCAN_ID_EXT(item->id); - mask = FLEXCAN_RX_MB_EXT_MASK(item->mask, 0, 0); - } - else - { - mbConfig.format = kFLEXCAN_FrameFormatStandard; - mbConfig.id = FLEXCAN_ID_STD(item->id); - mask = FLEXCAN_RX_MB_STD_MASK(item->mask, 0, 0); - } - - if (item->rtr) - { - mbConfig.type = kFLEXCAN_FrameTypeRemote; - } - else - { - mbConfig.type = kFLEXCAN_FrameTypeData; - } - - /* user does not specify hdr index,set hdr_bank from RX MB 1 */ - if (item->hdr_bank == -1) - { - - for (i = 0; i < 32; i++) - { - if (!(filter_mask & (1 << i))) - { - index = i; - break; - } - } - } - else /* use user specified hdr_bank */ - { - if (filter_mask & (1 << item->hdr_bank)) - { - res = -RT_ERROR; - rt_kprintf("%s hdr%d filter already set!\n", can->name, item->hdr_bank); - break; - } - else - { - index = item->hdr_bank; - } - } - - /* RX MB index from 1 to 32,hdr index 0~31 map RX MB index 1~32. */ - FLEXCAN_SetRxIndividualMask(can->base, index + 1, mask); - FLEXCAN_SetRxMbConfig(can->base, index + 1, &mbConfig, true); - filter_mask |= 1 << index; - - item++; - count--; - } - - break; - - case RT_CAN_CMD_SET_BAUD: - { - struct can_configure *cfg = (struct can_configure *)arg; - if (cfg != RT_NULL) - { - can->can_dev.config = *cfg; - _can_config(can_dev, cfg); - res = RT_EOK; - } - else - { - res = -RT_ERROR; - } - break; - } - case RT_CAN_CMD_SET_MODE: - res = -RT_ERROR; - break; - - case RT_CAN_CMD_SET_PRIV: - res = -RT_ERROR; - break; - case RT_CAN_CMD_GET_STATUS: - FLEXCAN_GetBusErrCount(can->base, (rt_uint8_t *)(&can->can_dev.status.snderrcnt), (rt_uint8_t *)(&can->can_dev.status.rcverrcnt)); - rt_memcpy(arg, &can->can_dev.status, sizeof(can->can_dev.status)); - break; - case RT_CAN_CMD_START: - /* already started in can_cfg */ - break; - default: - res = -RT_ERROR; - break; - } - - return res; -} - -static rt_ssize_t _can_sendmsg(struct rt_can_device *can_dev, const void *buf, rt_uint32_t boxno) -{ - struct imxrt_can *can; - struct rt_can_msg *msg; - status_t ret; - flexcan_frame_t frame; - flexcan_mb_transfer_t txXfer; - - RT_ASSERT(can_dev != RT_NULL); - RT_ASSERT(buf != RT_NULL); - - can = (struct imxrt_can *)can_dev->parent.user_data; - msg = (struct rt_can_msg *) buf; - - RT_ASSERT(can != RT_NULL); - RT_ASSERT(msg != RT_NULL); - - FLEXCAN_SetTxMbConfig(can->base, boxno, true); - - if (RT_CAN_STDID == msg->ide) - { - frame.id = FLEXCAN_ID_STD(msg->id); - frame.format = kFLEXCAN_FrameFormatStandard; - } - else if (RT_CAN_EXTID == msg->ide) - { - frame.id = FLEXCAN_ID_EXT(msg->id); - frame.format = kFLEXCAN_FrameFormatExtend; - } - - if (RT_CAN_DTR == msg->rtr) - { - frame.type = kFLEXCAN_FrameTypeData; - } - else if (RT_CAN_RTR == msg->rtr) - { - frame.type = kFLEXCAN_FrameTypeRemote; - } - - frame.length = msg->len; - frame.dataByte0 = msg->data[0]; - frame.dataByte1 = msg->data[1]; - frame.dataByte2 = msg->data[2]; - frame.dataByte3 = msg->data[3]; - frame.dataByte4 = msg->data[4]; - frame.dataByte5 = msg->data[5]; - frame.dataByte6 = msg->data[6]; - frame.dataByte7 = msg->data[7]; - - txXfer.mbIdx = boxno; - txXfer.frame = &frame; - ret = FLEXCAN_TransferSendBlocking(can->base, boxno, txXfer.frame); - switch (ret) - { - case kStatus_Success: - ret = RT_EOK; - rt_hw_can_isr(&can->can_dev, RT_CAN_EVENT_TX_DONE | boxno << 8); - break; - case kStatus_Fail: - ret = -RT_ERROR; - break; - } - - return (rt_ssize_t)ret; -} - -static rt_ssize_t _can_recvmsg(struct rt_can_device *can_dev, void *buf, rt_uint32_t boxno) -{ - struct imxrt_can *can; - struct rt_can_msg *pmsg; - rt_uint8_t index; - - RT_ASSERT(can_dev != RT_NULL); - - can = (struct imxrt_can *)can_dev->parent.user_data; - pmsg = (struct rt_can_msg *) buf; - RT_ASSERT(can != RT_NULL); - - index = boxno - 1; - - if (frame[index].format == kFLEXCAN_FrameFormatStandard) - { - pmsg->ide = RT_CAN_STDID; - pmsg->id = frame[index].id >> CAN_ID_STD_SHIFT; - } - else - { - pmsg->ide = RT_CAN_EXTID; - pmsg->id = frame[index].id >> CAN_ID_EXT_SHIFT; - } - - if (frame[index].type == kFLEXCAN_FrameTypeData) - { - pmsg->rtr = RT_CAN_DTR; - } - else if (frame[index].type == kFLEXCAN_FrameTypeRemote) - { - pmsg->rtr = RT_CAN_RTR; - } - pmsg->hdr_index = index; /* one hdr filter per MB */ - pmsg->len = frame[index].length; - pmsg->data[0] = frame[index].dataByte0; - pmsg->data[1] = frame[index].dataByte1; - pmsg->data[2] = frame[index].dataByte2; - pmsg->data[3] = frame[index].dataByte3; - pmsg->data[4] = frame[index].dataByte4; - pmsg->data[5] = frame[index].dataByte5; - pmsg->data[6] = frame[index].dataByte6; - pmsg->data[7] = frame[index].dataByte7; - - return 0; -} - -static uint8_t FLEXCAN_GetFirstValidMb(CAN_Type *base) -{ - uint8_t firstValidMbNum; - - if (0U != (base->MCR & CAN_MCR_RFEN_MASK)) - { - firstValidMbNum = (uint8_t)((base->CTRL2 & CAN_CTRL2_RFFN_MASK) >> CAN_CTRL2_RFFN_SHIFT); - firstValidMbNum = ((firstValidMbNum + 1U) * 2U) + 6U; - } - else - { - firstValidMbNum = 0U; - } - - return firstValidMbNum; -} - -rt_ssize_t _can_sendmsg_nonblocking(struct rt_can_device *can_dev, const void *buf) -{ - struct imxrt_can *can; - struct rt_can_msg *msg; - status_t ret; - flexcan_frame_t frame; - flexcan_mb_transfer_t txXfer; - rt_uint32_t boxno; - RT_ASSERT(can_dev != RT_NULL); - RT_ASSERT(buf != RT_NULL); - - can = (struct imxrt_can *)can_dev->parent.user_data; - msg = (struct rt_can_msg *) buf; - - RT_ASSERT(can != RT_NULL); - RT_ASSERT(msg != RT_NULL); - - boxno = FLEXCAN_GetFirstValidMb(can->base); - - if (boxno == 0xFF) - { - return -RT_EBUSY; - } - - if (RT_CAN_STDID == msg->ide) - { - frame.id = FLEXCAN_ID_STD(msg->id); - frame.format = kFLEXCAN_FrameFormatStandard; - } - else if (RT_CAN_EXTID == msg->ide) - { - frame.id = FLEXCAN_ID_EXT(msg->id); - frame.format = kFLEXCAN_FrameFormatExtend; - } - - if (RT_CAN_DTR == msg->rtr) - { - frame.type = kFLEXCAN_FrameTypeData; - } - else if (RT_CAN_RTR == msg->rtr) - { - frame.type = kFLEXCAN_FrameTypeRemote; - } - - frame.length = msg->len; - frame.dataByte0 = msg->data[0]; - frame.dataByte1 = msg->data[1]; - frame.dataByte2 = msg->data[2]; - frame.dataByte3 = msg->data[3]; - frame.dataByte4 = msg->data[4]; - frame.dataByte5 = msg->data[5]; - frame.dataByte6 = msg->data[6]; - frame.dataByte7 = msg->data[7]; - - txXfer.mbIdx = boxno; - txXfer.frame = &frame; - ret = FLEXCAN_TransferSendNonBlocking(can->base, &can->handle, &txXfer); - switch (ret) - { - case kStatus_Success: - ret = RT_EOK; - break; - case kStatus_Fail: - ret = -RT_ERROR; - break; - } - - return (rt_ssize_t)ret; -} - -static struct rt_can_ops imxrt_can_ops = -{ - .configure = _can_config, - .control = _can_control, - .sendmsg = _can_sendmsg, - .recvmsg = _can_recvmsg, - .sendmsg_nonblocking = _can_sendmsg_nonblocking -}; - -int rt_hw_can_init(void) -{ - int i; - rt_err_t ret = RT_EOK; - struct can_configure config = CANDEFAULTCONFIG; - - config.privmode = 0; - config.ticks = 50; - config.sndboxnumber = 1; - config.msgboxsz = RX_MB_COUNT; -#ifdef RT_CAN_USING_HDR - config.maxhdr = RX_MB_COUNT; /* filter count,one filter per MB */ -#endif - - for (i = 0; i < sizeof(flexcans) / sizeof(flexcans[0]); i++) - { - flexcans[i].can_dev.config = config; - CLOCK_SetClockDiv(flexcans[i].clock_div_name, 1u); - CLOCK_AttachClk(flexcans[i].clock_attach_id); - - ret = rt_hw_can_register(&flexcans[i].can_dev, flexcans[i].name, &imxrt_can_ops, &flexcans[i]); - } - - return ret; -} -INIT_BOARD_EXPORT(rt_hw_can_init); - -#endif /*RT_USING_CAN */ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-12-23 CYFS the first version. + * 2026-07-05 WANGYU add CAN-FD and loopback support. + */ + +#include + +#ifdef RT_USING_CAN + +#include "fsl_common.h" +#include "fsl_clock.h" +#include "fsl_flexcan.h" + +#define TX_MB_IDX (0) +#define RX_MB_IDX (1) +#define RX_MB_COUNT (1) +#define CAN_TX_WAIT_MS (2000U) + +enum +{ +#ifdef BSP_USING_CAN0 + CAN0_INDEX, +#endif +#ifdef BSP_USING_CAN1 + CAN1_INDEX, +#endif +}; + +struct imxrt_can +{ + char *name; + CAN_Type *base; + IRQn_Type irqn; + uint32_t instance; + clock_div_name_t clock_div_name; + clock_attach_id_t clock_attach_id; + flexcan_handle_t handle; + struct rt_can_device can_dev; + flexcan_frame_t frame[RX_MB_COUNT]; +#ifdef RT_CAN_USING_CANFD + flexcan_fd_frame_t framefd[RX_MB_COUNT]; + rt_bool_t fd_enabled; +#endif + rt_uint32_t filter_mask; +}; + +static void _can_dump_err(struct imxrt_can *can, const char *stage) +{ + uint32_t esr1 = can->base->ESR1; + uint32_t fltconf = (esr1 & CAN_ESR1_FLTCONF_MASK) >> CAN_ESR1_FLTCONF_SHIFT; + const char *flt = "active"; + + if (fltconf == 1U) + { + flt = "passive"; + } + else if (fltconf >= 2U) + { + flt = "busoff"; + } + + rt_kprintf("%s: %s, MCR=0x%08x ESR1=0x%08x IFLAG1=0x%08x flt=%s", + can->name, stage, can->base->MCR, esr1, can->base->IFLAG1, flt); + if (0U != (esr1 & CAN_ESR1_ACKERR_MASK)) + { + rt_kprintf(" ACKERR"); + } + if (0U != (esr1 & CAN_ESR1_BIT1ERR_MASK)) + { + rt_kprintf(" BIT1ERR"); + } + if (0U != (esr1 & CAN_ESR1_BIT0ERR_MASK)) + { + rt_kprintf(" BIT0ERR"); + } + rt_kprintf("\n"); +} + +static void _can_recover_bus(struct imxrt_can *can) +{ + CAN_Type *base = can->base; + uint32_t fltconf = (base->ESR1 & CAN_ESR1_FLTCONF_MASK) >> CAN_ESR1_FLTCONF_SHIFT; + + if (fltconf < 2U) + { + return; + } + + (void)FLEXCAN_EnterFreezeMode(base); + base->MCR &= ~CAN_MCR_HALT_MASK; + (void)FLEXCAN_ExitFreezeMode(base); + FLEXCAN_ClearStatusFlags(base, (uint32_t)kFLEXCAN_BusOffIntFlag); +} + +static void _can_setup_loopback_mb(struct imxrt_can *can) +{ + flexcan_rx_mb_config_t mb_config; + + mb_config.format = kFLEXCAN_FrameFormatStandard; + mb_config.type = kFLEXCAN_FrameTypeData; + mb_config.id = FLEXCAN_ID_STD(0); + + FLEXCAN_SetRxMbGlobalMask(can->base, 0U); + FLEXCAN_SetRxIndividualMask(can->base, RX_MB_IDX, 0U); + +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled) + { + FLEXCAN_SetFDRxMbConfig(can->base, RX_MB_IDX, &mb_config, true); + } + else +#endif + { + FLEXCAN_SetRxMbConfig(can->base, RX_MB_IDX, &mb_config, true); + } +} + +#ifdef RT_CAN_USING_CANFD +static rt_uint8_t _can_dlc2len(rt_uint8_t dlc) +{ + return (rt_uint8_t)DLC_LENGTH_DECODE(dlc); +} + +static rt_uint8_t _can_len2dlc(rt_uint8_t len) +{ + static const rt_uint8_t table[] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 9, 9, 9, + 10, 10, 10, 10, + 11, 11, 11, 11, + 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + }; + + if (len <= 64) + { + return table[len]; + } + + return 15; +} +#endif + +static void _can_fill_classic_frame(flexcan_frame_t *frame, const struct rt_can_msg *msg) +{ + if (RT_CAN_STDID == msg->ide) + { + frame->id = FLEXCAN_ID_STD(msg->id); + frame->format = kFLEXCAN_FrameFormatStandard; + } + else + { + frame->id = FLEXCAN_ID_EXT(msg->id); + frame->format = kFLEXCAN_FrameFormatExtend; + } + + frame->type = (RT_CAN_DTR == msg->rtr) ? kFLEXCAN_FrameTypeData : kFLEXCAN_FrameTypeRemote; + frame->length = msg->len; + frame->dataByte0 = msg->data[0]; + frame->dataByte1 = msg->data[1]; + frame->dataByte2 = msg->data[2]; + frame->dataByte3 = msg->data[3]; + frame->dataByte4 = msg->data[4]; + frame->dataByte5 = msg->data[5]; + frame->dataByte6 = msg->data[6]; + frame->dataByte7 = msg->data[7]; +} + +static void _can_parse_classic_frame(struct rt_can_msg *msg, const flexcan_frame_t *frame) +{ + if (frame->format == kFLEXCAN_FrameFormatStandard) + { + msg->ide = RT_CAN_STDID; + msg->id = frame->id >> CAN_ID_STD_SHIFT; + } + else + { + msg->ide = RT_CAN_EXTID; + msg->id = frame->id >> CAN_ID_EXT_SHIFT; + } + + msg->rtr = (frame->type == kFLEXCAN_FrameTypeData) ? RT_CAN_DTR : RT_CAN_RTR; + msg->len = frame->length; + msg->data[0] = frame->dataByte0; + msg->data[1] = frame->dataByte1; + msg->data[2] = frame->dataByte2; + msg->data[3] = frame->dataByte3; + msg->data[4] = frame->dataByte4; + msg->data[5] = frame->dataByte5; + msg->data[6] = frame->dataByte6; + msg->data[7] = frame->dataByte7; +} + +struct imxrt_can flexcans[] = +{ +#ifdef BSP_USING_CAN0 + { + .name = "can0", + .base = CAN0, + .instance = 0, + .irqn = CAN0_IRQn, + .clock_div_name = kCLOCK_DivFLEXCAN0, + .clock_attach_id = kFRO_HF_DIV_to_FLEXCAN0, + }, +#endif +#ifdef BSP_USING_CAN1 + { + .name = "can1", + .base = CAN1, + .instance = 1, + .irqn = CAN1_IRQn, + .clock_div_name = kCLOCK_DivFLEXCAN1, + .clock_attach_id = kFRO_HF_DIV_to_FLEXCAN1, + }, +#endif +}; + +static rt_bool_t _can_is_loopback(const struct can_configure *cfg) +{ + return (cfg->mode == RT_CAN_MODE_LOOPBACK) || + (cfg->mode == RT_CAN_MODE_LOOPBACKANLISTEN); +} + +static void _can_abort_rx(struct imxrt_can *can) +{ +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled) + { + FLEXCAN_TransferFDAbortReceive(can->base, &can->handle, RX_MB_IDX); + } + else +#endif + { + FLEXCAN_TransferAbortReceive(can->base, &can->handle, RX_MB_IDX); + } +} + +static void _can_enable_periph_clock(struct imxrt_can *can) +{ + clock_ip_name_t can_clocks[] = FLEXCAN_CLOCKS; + + CLOCK_EnableClock(can_clocks[can->instance]); + CLOCK_SetClockDiv(can->clock_div_name, 2u); + CLOCK_AttachClk(can->clock_attach_id); +} + +static void _can_safe_deinit(struct imxrt_can *can) +{ + CAN_Type *base = can->base; + + _can_enable_periph_clock(can); + + /* FLEXCAN_Reset() inside FLEXCAN_Deinit() requires MDIS cleared. */ + if (0U != (base->MCR & CAN_MCR_MDIS_MASK)) + { + (void)FLEXCAN_Enable(base, true); + } + + FLEXCAN_Deinit(base); +} + +static status_t _can_wait_mb_tx_done(struct imxrt_can *can, uint8_t mb_idx) +{ + CAN_Type *base = can->base; + rt_tick_t start = rt_tick_get(); + rt_tick_t timeout = rt_tick_from_millisecond(CAN_TX_WAIT_MS); + + while (0U == FLEXCAN_GetMbStatusFlags(base, (uint64_t)1U << mb_idx)) + { + if ((rt_tick_get() - start) >= timeout) + { + _can_dump_err(can, "tx timeout"); + return kStatus_Timeout; + } + rt_thread_mdelay(1); + } + + FLEXCAN_ClearMbStatusFlags(base, (uint64_t)1U << mb_idx); + return kStatus_Success; +} + +static status_t _can_wait_mb_rx_done(CAN_Type *base, uint8_t mb_idx) +{ + rt_tick_t start = rt_tick_get(); + rt_tick_t timeout = rt_tick_from_millisecond(CAN_TX_WAIT_MS); + + while (0U == FLEXCAN_GetMbStatusFlags(base, (uint64_t)1U << mb_idx)) + { + if ((rt_tick_get() - start) >= timeout) + { + return kStatus_Timeout; + } + rt_thread_mdelay(1); + } + + FLEXCAN_ClearMbStatusFlags(base, (uint64_t)1U << mb_idx); + return kStatus_Success; +} + +static void _can_loopback_post_rx(struct imxrt_can *can) +{ + rt_hw_can_isr(&can->can_dev, RT_CAN_EVENT_RX_IND | RX_MB_IDX << 8); +} + +static void _can_setup_mode(flexcan_config_t *config, const struct can_configure *cfg) +{ + switch (cfg->mode) + { + case RT_CAN_MODE_NORMAL: + break; + case RT_CAN_MODE_LISTEN: + config->enableListenOnlyMode = true; + break; + case RT_CAN_MODE_LOOPBACK: + config->enableLoopBack = true; + config->disableSelfReception = false; + config->enableIndividMask = false; + break; + case RT_CAN_MODE_LOOPBACKANLISTEN: + config->enableLoopBack = true; + config->enableListenOnlyMode = true; + config->disableSelfReception = false; + config->enableIndividMask = false; + break; + default: + break; + } +} + +static void _can_start_rx(struct imxrt_can *can) +{ + flexcan_rx_mb_config_t mbConfig; + flexcan_mb_transfer_t rxXfer; + rt_uint8_t i, mailbox; + + mbConfig.format = kFLEXCAN_FrameFormatStandard; + mbConfig.type = kFLEXCAN_FrameTypeData; + mbConfig.id = FLEXCAN_ID_STD(0); + + FLEXCAN_SetRxMbGlobalMask(can->base, 0U); + + for (i = 0; i < RX_MB_COUNT; i++) + { + mailbox = RX_MB_IDX + i; + FLEXCAN_SetRxIndividualMask(can->base, mailbox, 0U); + +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled) + { + FLEXCAN_SetFDRxMbConfig(can->base, mailbox, &mbConfig, true); + rxXfer.framefd = &can->framefd[i]; + rxXfer.frame = RT_NULL; + rxXfer.mbIdx = mailbox; + FLEXCAN_TransferFDReceiveNonBlocking(can->base, &can->handle, &rxXfer); + } + else +#endif + { + FLEXCAN_SetRxMbConfig(can->base, mailbox, &mbConfig, true); + rxXfer.frame = &can->frame[i]; +#ifdef RT_CAN_USING_CANFD + rxXfer.framefd = RT_NULL; +#endif + rxXfer.mbIdx = mailbox; + FLEXCAN_TransferReceiveNonBlocking(can->base, &can->handle, &rxXfer); + } + } +} + +static void _can_enable_controller_irq(struct imxrt_can *can) +{ + FLEXCAN_EnableInterrupts(can->base, (uint32_t)kFLEXCAN_BusOffInterruptEnable | + (uint32_t)kFLEXCAN_ErrorInterruptEnable | + (uint32_t)kFLEXCAN_RxWarningInterruptEnable | + (uint32_t)kFLEXCAN_TxWarningInterruptEnable); + EnableIRQ(can->irqn); +} + +#ifdef BSP_USING_CAN0 +void CAN0_IRQHandler(void) +{ + rt_interrupt_enter(); + FLEXCAN_TransferHandleIRQ(CAN0, &flexcans[CAN0_INDEX].handle); + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_CAN1 +void CAN1_IRQHandler(void) +{ + rt_interrupt_enter(); + FLEXCAN_TransferHandleIRQ(CAN1, &flexcans[CAN1_INDEX].handle); + rt_interrupt_leave(); +} +#endif + +static void flexcan_callback(CAN_Type *base, flexcan_handle_t *handle, status_t status, uint64_t result, void *userData) +{ + struct imxrt_can *can; + flexcan_mb_transfer_t rxXfer; + rt_uint8_t mb_idx; + + can = (struct imxrt_can *)userData; + + switch (status) + { + case kStatus_FLEXCAN_RxIdle: + mb_idx = (rt_uint8_t)result; + rt_hw_can_isr(&can->can_dev, RT_CAN_EVENT_RX_IND | mb_idx << 8); + +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled) + { + rxXfer.framefd = &can->framefd[mb_idx - RX_MB_IDX]; + rxXfer.frame = RT_NULL; + rxXfer.mbIdx = mb_idx; + FLEXCAN_TransferFDReceiveNonBlocking(can->base, &can->handle, &rxXfer); + } + else +#endif + { + rxXfer.frame = &can->frame[mb_idx - RX_MB_IDX]; +#ifdef RT_CAN_USING_CANFD + rxXfer.framefd = RT_NULL; +#endif + rxXfer.mbIdx = mb_idx; + FLEXCAN_TransferReceiveNonBlocking(can->base, &can->handle, &rxXfer); + } + break; + + case kStatus_FLEXCAN_TxIdle: + rt_hw_can_isr(&can->can_dev, RT_CAN_EVENT_TX_DONE | result << 8); + break; + default: + break; + } +} + +static rt_err_t _can_config(struct rt_can_device *can_dev, struct can_configure *cfg) +{ + struct imxrt_can *can = (struct imxrt_can *)can_dev->parent.user_data; + flexcan_config_t config; + flexcan_timing_config_t timing_config; + rt_uint32_t clk_freq; + rt_err_t res = RT_EOK; + + can_dev->config = *cfg; + _can_enable_periph_clock(can); + clk_freq = CLOCK_GetFlexcanClkFreq(can->instance); + if (clk_freq == 0U) + { + rt_kprintf("%s: flexcan clock is 0\n", can->name); + return -RT_ERROR; + } + + rt_kprintf("%s: flexcan clk=%u Hz, baud=%u\n", can->name, clk_freq, cfg->baud_rate); + + _can_safe_deinit(can); + + FLEXCAN_GetDefaultConfig(&config); + config.baudRate = cfg->baud_rate; + config.maxMbNum = 8; + config.enableIndividMask = false; + config.enableSupervisorMode = false; + config.disableSelfReception = !_can_is_loopback(cfg); + _can_setup_mode(&config, cfg); + + if (_can_is_loopback(cfg) +#ifdef RT_CAN_USING_CANFD + || (cfg->enable_canfd == 0) +#endif + ) + { + config.enableTransceiverDelayMeasure = false; + } + else + { + config.enableTransceiverDelayMeasure = true; + } + + rt_memset(&timing_config, 0, sizeof(flexcan_timing_config_t)); + +#ifdef RT_CAN_USING_CANFD + can->fd_enabled = (cfg->enable_canfd != 0) ? RT_TRUE : RT_FALSE; + + if (can->fd_enabled) + { + bool brs_enable = _can_is_loopback(cfg) ? false : + (cfg->baud_rate_fd > cfg->baud_rate); + bool timing_ok; + + config.bitRateFD = _can_is_loopback(cfg) ? config.baudRate : + (cfg->baud_rate_fd ? cfg->baud_rate_fd : 2000000U); + + timing_ok = FLEXCAN_FDCalculateImprovedTimingValues(can->base, config.baudRate, + config.bitRateFD, clk_freq, &timing_config); + if (timing_ok) + { + rt_memcpy(&(config.timingConfig), &timing_config, sizeof(flexcan_timing_config_t)); + } + + FLEXCAN_FDInit(can->base, &config, clk_freq, kFLEXCAN_16BperMB, brs_enable); + if (!timing_ok) + { + if (FLEXCAN_SetFDBitRate(can->base, clk_freq, config.baudRate, config.bitRateFD) != kStatus_Success) + { + rt_kprintf("%s: CAN FD timing setup failed, clk=%u\n", can->name, clk_freq); + return -RT_ERROR; + } + } + } + else +#endif + { + if (FLEXCAN_CalculateImprovedTimingValues(can->base, config.baudRate, clk_freq, &timing_config)) + { + rt_memcpy(&(config.timingConfig), &timing_config, sizeof(flexcan_timing_config_t)); + } + + FLEXCAN_Init(can->base, &config, clk_freq); + } + + can->filter_mask = 0; + FLEXCAN_TransferCreateHandle(can->base, &can->handle, flexcan_callback, can); + if (_can_is_loopback(cfg)) + { + _can_setup_loopback_mb(can); + } + else + { + _can_start_rx(can); + } + _can_enable_controller_irq(can); + + return res; +} + +static rt_err_t _can_control(struct rt_can_device *can_dev, int cmd, void *arg) +{ + struct imxrt_can *can; + rt_uint32_t argval, mask; + rt_err_t res = RT_EOK; + flexcan_rx_mb_config_t mbConfig; + struct rt_can_filter_config *cfg; + struct rt_can_filter_item *item; + rt_uint8_t i, count, index; + + RT_ASSERT(can_dev != RT_NULL); + + can = (struct imxrt_can *)can_dev->parent.user_data; + RT_ASSERT(can != RT_NULL); + + switch (cmd) + { + case RT_DEVICE_CTRL_SET_INT: + argval = (rt_uint32_t)arg; + if (argval == RT_DEVICE_FLAG_INT_RX || argval == RT_DEVICE_FLAG_INT_TX || + argval == RT_DEVICE_CAN_INT_ERR) + { + _can_enable_controller_irq(can); + } + else + { + res = -RT_ERROR; + } + break; + case RT_DEVICE_CTRL_CLR_INT: + DisableIRQ(can->irqn); + break; + case RT_CAN_CMD_SET_FILTER: + cfg = (struct rt_can_filter_config *)arg; + item = cfg->items; + count = cfg->count; + + if (can->filter_mask == 0xffffffff) + { + rt_kprintf("%s filter is full!\n", can->name); + res = -RT_ERROR; + break; + } + else if (can->filter_mask == 0) + { + for (i = 0; i < RX_MB_COUNT; i++) + { +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled) + { + FLEXCAN_SetFDRxMbConfig(can->base, i + 1, RT_NULL, false); + } + else +#endif + { + FLEXCAN_SetRxMbConfig(can->base, i + 1, RT_NULL, false); + } + } + } + + while (count) + { + if (item->ide) + { + mbConfig.format = kFLEXCAN_FrameFormatExtend; + mbConfig.id = FLEXCAN_ID_EXT(item->id); + mask = FLEXCAN_RX_MB_EXT_MASK(item->mask, 0, 0); + } + else + { + mbConfig.format = kFLEXCAN_FrameFormatStandard; + mbConfig.id = FLEXCAN_ID_STD(item->id); + mask = FLEXCAN_RX_MB_STD_MASK(item->mask, 0, 0); + } + + mbConfig.type = item->rtr ? kFLEXCAN_FrameTypeRemote : kFLEXCAN_FrameTypeData; + + if (item->hdr_bank == -1) + { + for (i = 0; i < 32; i++) + { + if (!(can->filter_mask & (1 << i))) + { + index = i; + break; + } + } + } + else + { + if (can->filter_mask & (1 << item->hdr_bank)) + { + res = -RT_ERROR; + rt_kprintf("%s hdr%d filter already set!\n", can->name, item->hdr_bank); + break; + } + index = item->hdr_bank; + } + + FLEXCAN_SetRxIndividualMask(can->base, index + 1, mask); +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled) + { + FLEXCAN_SetFDRxMbConfig(can->base, index + 1, &mbConfig, true); + } + else +#endif + { + FLEXCAN_SetRxMbConfig(can->base, index + 1, &mbConfig, true); + } + can->filter_mask |= 1 << index; + + item++; + count--; + } + break; + + case RT_CAN_CMD_SET_BAUD: + if (arg != RT_NULL) + { + can->can_dev.config.baud_rate = *(rt_uint32_t *)arg; + res = _can_config(can_dev, &can->can_dev.config); + } + else + { + res = -RT_ERROR; + } + break; + case RT_CAN_CMD_SET_MODE: + argval = (rt_uint32_t)arg; + if (argval > RT_CAN_MODE_LOOPBACKANLISTEN) + { + res = -RT_ERROR; + break; + } + can->can_dev.config.mode = argval; + res = _can_config(can_dev, &can->can_dev.config); + break; +#ifdef RT_CAN_USING_CANFD + case RT_CAN_CMD_SET_CANFD: + argval = (rt_uint32_t)arg; + can->can_dev.config.enable_canfd = argval ? 1 : 0; + res = _can_config(can_dev, &can->can_dev.config); + break; + case RT_CAN_CMD_SET_BAUD_FD: + if (arg != RT_NULL) + { + can->can_dev.config.baud_rate_fd = *(rt_uint32_t *)arg; + res = _can_config(can_dev, &can->can_dev.config); + } + else + { + res = -RT_ERROR; + } + break; +#endif + case RT_CAN_CMD_SET_PRIV: + res = -RT_ERROR; + break; + case RT_CAN_CMD_GET_STATUS: + FLEXCAN_GetBusErrCount(can->base, (rt_uint8_t *)(&can->can_dev.status.snderrcnt), + (rt_uint8_t *)(&can->can_dev.status.rcverrcnt)); + rt_memcpy(arg, &can->can_dev.status, sizeof(can->can_dev.status)); + break; + case RT_CAN_CMD_START: + break; + default: + res = -RT_ERROR; + break; + } + + return res; +} + +#ifdef RT_CAN_USING_CANFD +static void _can_fill_fd_frame(flexcan_fd_frame_t *frame, const struct rt_can_msg *msg) +{ + rt_uint8_t dlc; + rt_uint8_t data_len; + + rt_memset(frame, 0, sizeof(*frame)); + + if (RT_CAN_STDID == msg->ide) + { + frame->id = FLEXCAN_ID_STD(msg->id); + frame->format = kFLEXCAN_FrameFormatStandard; + } + else + { + frame->id = FLEXCAN_ID_EXT(msg->id); + frame->format = kFLEXCAN_FrameFormatExtend; + } + + frame->type = (RT_CAN_DTR == msg->rtr) ? kFLEXCAN_FrameTypeData : kFLEXCAN_FrameTypeRemote; + frame->edl = 1; + frame->brs = msg->brs ? 1 : 0; + + dlc = (msg->len <= 15) ? msg->len : _can_len2dlc((rt_uint8_t)msg->len); + frame->length = dlc; + data_len = _can_dlc2len(dlc); + rt_memcpy(frame->dataWord, msg->data, data_len); +} + +static void _can_parse_fd_frame(struct rt_can_msg *msg, const flexcan_fd_frame_t *frame) +{ + rt_uint8_t data_len; + + if (frame->format == kFLEXCAN_FrameFormatStandard) + { + msg->ide = RT_CAN_STDID; + msg->id = frame->id >> CAN_ID_STD_SHIFT; + } + else + { + msg->ide = RT_CAN_EXTID; + msg->id = frame->id >> CAN_ID_EXT_SHIFT; + } + + msg->rtr = (frame->type == kFLEXCAN_FrameTypeData) ? RT_CAN_DTR : RT_CAN_RTR; + msg->fd_frame = frame->edl ? 1 : 0; + msg->brs = frame->brs ? 1 : 0; + msg->len = frame->length; + data_len = _can_dlc2len(frame->length); + rt_memcpy(msg->data, frame->dataWord, data_len); +} +#endif + +static rt_ssize_t _can_sendmsg(struct rt_can_device *can_dev, const void *buf, rt_uint32_t boxno) +{ + struct imxrt_can *can; + struct rt_can_msg *msg; + status_t ret = kStatus_Fail; + rt_uint8_t hw_mb = TX_MB_IDX; + + RT_ASSERT(can_dev != RT_NULL); + RT_ASSERT(buf != RT_NULL); + + can = (struct imxrt_can *)can_dev->parent.user_data; + msg = (struct rt_can_msg *)buf; + RT_UNUSED(boxno); + + if (!_can_is_loopback(&can_dev->config)) + { + _can_recover_bus(can); + } + + if (!_can_is_loopback(&can_dev->config)) + { + _can_abort_rx(can); + } + else + { + _can_setup_loopback_mb(can); + } + +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled && msg->fd_frame) + { + flexcan_fd_frame_t tx_frame; + + FLEXCAN_ClearMbStatusFlags(can->base, ((uint64_t)1U << hw_mb) | ((uint64_t)1U << RX_MB_IDX)); + FLEXCAN_SetFDTxMbConfig(can->base, hw_mb, true); + _can_fill_fd_frame(&tx_frame, msg); + + if (FLEXCAN_WriteFDTxMb(can->base, hw_mb, &tx_frame) != kStatus_Success) + { + ret = kStatus_Fail; + } + else + { + ret = _can_wait_mb_tx_done(can, hw_mb); + } + + if (ret == kStatus_Success && _can_is_loopback(&can_dev->config)) + { + ret = _can_wait_mb_rx_done(can->base, RX_MB_IDX); + if (ret == kStatus_Success) + { + ret = FLEXCAN_ReadFDRxMb(can->base, RX_MB_IDX, &can->framefd[0]); + if (ret == kStatus_Success) + { + _can_loopback_post_rx(can); + } + } + } + } + else +#endif + { + flexcan_frame_t tx_frame; + + FLEXCAN_ClearMbStatusFlags(can->base, ((uint64_t)1U << hw_mb) | ((uint64_t)1U << RX_MB_IDX)); + FLEXCAN_SetTxMbConfig(can->base, hw_mb, true); + _can_fill_classic_frame(&tx_frame, msg); + + if (FLEXCAN_WriteTxMb(can->base, hw_mb, &tx_frame) != kStatus_Success) + { + ret = kStatus_Fail; + } + else + { + ret = _can_wait_mb_tx_done(can, hw_mb); + } + + if (ret == kStatus_Success && _can_is_loopback(&can_dev->config)) + { + ret = _can_wait_mb_rx_done(can->base, RX_MB_IDX); + if (ret == kStatus_Success) + { + ret = FLEXCAN_ReadRxMb(can->base, RX_MB_IDX, &can->frame[0]); + if (ret == kStatus_Success) + { + _can_loopback_post_rx(can); + } + } + } + } + + if (ret == kStatus_Success) + { + if (!_can_is_loopback(&can_dev->config)) + { + _can_start_rx(can); + } + + rt_hw_can_isr(&can->can_dev, RT_CAN_EVENT_TX_DONE | boxno << 8); + return RT_EOK; + } + + if (ret == kStatus_Timeout) + { + _can_dump_err(can, "tx fail"); + } + + return -RT_ERROR; +} + +static rt_ssize_t _can_recvmsg(struct rt_can_device *can_dev, void *buf, rt_uint32_t boxno) +{ + struct imxrt_can *can; + struct rt_can_msg *pmsg; + rt_uint8_t index; + + RT_ASSERT(can_dev != RT_NULL); + + can = (struct imxrt_can *)can_dev->parent.user_data; + pmsg = (struct rt_can_msg *)buf; + index = (boxno >= RX_MB_IDX) ? (boxno - RX_MB_IDX) : 0; + +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled && can->framefd[index].edl) + { + _can_parse_fd_frame(pmsg, &can->framefd[index]); + } + else if (can->fd_enabled) + { + _can_parse_classic_frame(pmsg, (const flexcan_frame_t *)&can->framefd[index]); + pmsg->fd_frame = 0; + pmsg->brs = 0; + } + else +#endif + { + _can_parse_classic_frame(pmsg, &can->frame[index]); + } + + pmsg->hdr_index = index; + + return 0; +} + +static uint8_t FLEXCAN_GetFirstValidMb(CAN_Type *base) +{ + uint8_t firstValidMbNum; + + if (0U != (base->MCR & CAN_MCR_RFEN_MASK)) + { + firstValidMbNum = (uint8_t)((base->CTRL2 & CAN_CTRL2_RFFN_MASK) >> CAN_CTRL2_RFFN_SHIFT); + firstValidMbNum = ((firstValidMbNum + 1U) * 2U) + 6U; + } + else + { + firstValidMbNum = 0U; + } + + return firstValidMbNum; +} + +static rt_ssize_t _can_sendmsg_nonblocking(struct rt_can_device *can_dev, const void *buf) +{ + struct imxrt_can *can; + struct rt_can_msg *msg; + status_t ret; + rt_uint32_t boxno; + + RT_ASSERT(can_dev != RT_NULL); + RT_ASSERT(buf != RT_NULL); + + can = (struct imxrt_can *)can_dev->parent.user_data; + msg = (struct rt_can_msg *)buf; + boxno = FLEXCAN_GetFirstValidMb(can->base); + + if (boxno == 0xFF) + { + return -RT_EBUSY; + } + +#ifdef RT_CAN_USING_CANFD + if (can->fd_enabled && msg->fd_frame) + { + flexcan_mb_transfer_t txXfer; + flexcan_fd_frame_t tx_frame; + + FLEXCAN_SetFDTxMbConfig(can->base, boxno, true); + _can_fill_fd_frame(&tx_frame, msg); + txXfer.mbIdx = boxno; + txXfer.framefd = &tx_frame; + txXfer.frame = RT_NULL; + ret = FLEXCAN_TransferFDSendNonBlocking(can->base, &can->handle, &txXfer); + } + else +#endif + { + flexcan_mb_transfer_t txXfer; + flexcan_frame_t tx_frame; + + _can_fill_classic_frame(&tx_frame, msg); + txXfer.mbIdx = boxno; + txXfer.frame = &tx_frame; +#ifdef RT_CAN_USING_CANFD + txXfer.framefd = RT_NULL; +#endif + ret = FLEXCAN_TransferSendNonBlocking(can->base, &can->handle, &txXfer); + } + + return (ret == kStatus_Success) ? RT_EOK : -RT_ERROR; +} + +static struct rt_can_ops imxrt_can_ops = +{ + .configure = _can_config, + .control = _can_control, + .sendmsg = _can_sendmsg, + .recvmsg = _can_recvmsg, + .sendmsg_nonblocking = _can_sendmsg_nonblocking +}; + +int rt_hw_can_init(void) +{ + int i; + rt_err_t ret = RT_EOK; + struct can_configure config = CANDEFAULTCONFIG; + + config.privmode = 0; + config.ticks = 50; + config.sndboxnumber = 1; + config.msgboxsz = RX_MB_COUNT; +#ifdef RT_CAN_USING_HDR + config.maxhdr = RX_MB_COUNT; +#endif +#ifdef RT_CAN_USING_CANFD + config.enable_canfd = 1; + config.baud_rate_fd = 2000000; +#endif + + for (i = 0; i < sizeof(flexcans) / sizeof(flexcans[0]); i++) + { + flexcans[i].can_dev.config = config; + flexcans[i].filter_mask = 0; + _can_enable_periph_clock(&flexcans[i]); + + ret = rt_hw_can_register(&flexcans[i].can_dev, flexcans[i].name, &imxrt_can_ops, &flexcans[i]); + } + + return ret; +} +INIT_BOARD_EXPORT(rt_hw_can_init); + +#endif /* RT_USING_CAN */ diff --git a/bsp/nxp/mcx/mcxa/frdm-mcxa366/applications/can_fd_dual_test.c b/bsp/nxp/mcx/mcxa/frdm-mcxa366/applications/can_fd_dual_test.c new file mode 100644 index 00000000000..6e1fe8fb6dd --- /dev/null +++ b/bsp/nxp/mcx/mcxa/frdm-mcxa366/applications/can_fd_dual_test.c @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Dual CAN FD bus test: CAN0 <-> CAN1 via external transceivers. + * Hardware: connect J16 (CAN0) and J17 (CAN1) CAN_H/CAN_L/GND together. + */ + +#include + +#if defined(BSP_USING_CAN) && defined(BSP_USING_CAN0) && defined(BSP_USING_CAN1) + +#define CAN0_DEV_NAME "can0" +#define CAN1_DEV_NAME "can1" +#define CAN0_TX_ID 0x100U +#define CAN1_TX_ID 0x200U +#define CAN_RX_TIMEOUT 3000 + +/* FRDM-MCXA366: dual TJA1057, CAN0->J16, CAN1->J17 (UM12438). */ +#define CAN_BOARD_NOTE "FRDM-MCXA366: CAN0 J16 (P1_2/P1_11), CAN1 J17 (P1_17/P1_12)" +#define CAN_JUMPER_NOTE "J16-1<->J17-1 CAN_H, J16-2<->J17-2 CAN_L, J16-4<->J17-4 GND" +#define CAN_JP7_NOTE "Short JP7(USB2CAN_DIS) - disables MCU-Link USB-CAN bridge on bus" + +struct can_fd_bus_ctx +{ + rt_device_t dev; + struct rt_semaphore rx_sem; + struct rt_can_msg rx_msg; + rt_bool_t sem_inited; +}; + +static struct can_fd_bus_ctx s_can0; +static struct can_fd_bus_ctx s_can1; + +static rt_err_t can0_rx_indicate(rt_device_t dev, rt_size_t size) +{ + RT_UNUSED(dev); + RT_UNUSED(size); + rt_sem_release(&s_can0.rx_sem); + return RT_EOK; +} + +static rt_err_t can1_rx_indicate(rt_device_t dev, rt_size_t size) +{ + RT_UNUSED(dev); + RT_UNUSED(size); + rt_sem_release(&s_can1.rx_sem); + return RT_EOK; +} + +static void can_dump_status(const char *name, rt_device_t dev) +{ + struct rt_can_status status; + + rt_memset(&status, 0, sizeof(status)); + if (rt_device_control(dev, RT_CAN_CMD_GET_STATUS, &status) == RT_EOK) + { + rt_kprintf("%s status: txerr=%u rxerr=%u ackerr=%u snd=%u rcv=%u\n", + name, status.snderrcnt, status.rcverrcnt, status.ackerrcnt, + status.sndpkg, status.rcvpkg); + } +} + +static rt_bool_t can_msg_equal(const struct rt_can_msg *tx, const struct rt_can_msg *rx, rt_uint8_t payload_len) +{ + rt_uint8_t i; + + if (tx->id != rx->id || tx->ide != rx->ide || tx->rtr != rx->rtr) + { + return RT_FALSE; + } + +#ifdef RT_CAN_USING_CANFD + if (tx->fd_frame != rx->fd_frame || tx->brs != rx->brs) + { + return RT_FALSE; + } +#endif + + if (tx->len != rx->len) + { + return RT_FALSE; + } + + for (i = 0; i < payload_len; i++) + { + if (tx->data[i] != rx->data[i]) + { + return RT_FALSE; + } + } + + return RT_TRUE; +} + +static rt_err_t can_bus_open(struct can_fd_bus_ctx *ctx, + const char *name, + rt_bool_t enable_fd, + rt_err_t (*rx_ind)(rt_device_t, rt_size_t)) +{ + struct rt_can_device *can_dev; + struct can_configure cfg; + rt_err_t res; + + rt_kprintf("open %s ...\n", name); + + ctx->dev = rt_device_find(name); + if (ctx->dev == RT_NULL) + { + rt_kprintf("find %s failed\n", name); + return -RT_ERROR; + } + + can_dev = (struct rt_can_device *)ctx->dev; + cfg = can_dev->config; + + res = rt_sem_init(&ctx->rx_sem, name, 0, RT_IPC_FLAG_FIFO); + if (res != RT_EOK) + { + rt_kprintf("%s sem init failed, res=%d\n", name, res); + return res; + } + ctx->sem_inited = RT_TRUE; + + cfg.baud_rate = CAN500kBaud; + cfg.mode = RT_CAN_MODE_NORMAL; +#ifdef RT_CAN_USING_CANFD + cfg.enable_canfd = enable_fd ? 1 : 0; + cfg.baud_rate_fd = CAN500kBaud; +#endif + + res = rt_device_control(ctx->dev, RT_DEVICE_CTRL_CONFIG, &cfg); + if (res != RT_EOK) + { + rt_kprintf("%s config failed, res=%d\n", name, res); + goto err_sem; + } + + res = rt_device_open(ctx->dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX); + if (res != RT_EOK) + { + rt_kprintf("%s open failed, res=%d\n", name, res); + goto err_sem; + } + + rt_device_set_rx_indicate(ctx->dev, rx_ind); + can_dump_status(name, ctx->dev); + rt_kprintf("%s ready\n", name); + return RT_EOK; + +err_sem: + if (ctx->sem_inited) + { + rt_sem_detach(&ctx->rx_sem); + ctx->sem_inited = RT_FALSE; + } + ctx->dev = RT_NULL; + return res; +} + +static void can_bus_close(struct can_fd_bus_ctx *ctx) +{ + if (ctx->dev != RT_NULL) + { + rt_device_set_rx_indicate(ctx->dev, RT_NULL); + rt_device_close(ctx->dev); + ctx->dev = RT_NULL; + } + + if (ctx->sem_inited) + { + rt_sem_detach(&ctx->rx_sem); + ctx->sem_inited = RT_FALSE; + } +} + +static rt_err_t can_bus_transfer_once(struct can_fd_bus_ctx *tx_ctx, + struct can_fd_bus_ctx *rx_ctx, + struct rt_can_msg *tx, + rt_uint8_t payload_len, + const char *desc) +{ + rt_err_t res; + + rt_memset(&rx_ctx->rx_msg, 0, sizeof(rx_ctx->rx_msg)); + while (rt_sem_trytake(&rx_ctx->rx_sem) == RT_EOK) + { + } + + rt_kprintf("%s: sending ...\n", desc); + res = rt_device_write(tx_ctx->dev, 0, tx, sizeof(*tx)); + if (res != sizeof(*tx)) + { + rt_kprintf("%s TX failed, res=%d\n", desc, res); + can_dump_status(desc, tx_ctx->dev); + return -RT_ERROR; + } + + rt_kprintf("%s: waiting RX ...\n", desc); + res = rt_sem_take(&rx_ctx->rx_sem, rt_tick_from_millisecond(CAN_RX_TIMEOUT)); + if (res != RT_EOK) + { + rt_kprintf("%s RX timeout\n", desc); + can_dump_status("tx", tx_ctx->dev); + can_dump_status("rx", rx_ctx->dev); + return -RT_ETIMEOUT; + } + + rx_ctx->rx_msg.hdr_index = -1; + res = rt_device_read(rx_ctx->dev, 0, &rx_ctx->rx_msg, sizeof(rx_ctx->rx_msg)); + if (res != sizeof(rx_ctx->rx_msg)) + { + rt_kprintf("%s RX read failed, res=%d\n", desc, res); + return -RT_ERROR; + } + + if (!can_msg_equal(tx, &rx_ctx->rx_msg, payload_len)) + { + rt_kprintf("%s compare failed, tx id=0x%x len=%u, rx id=0x%x len=%u\n", + desc, tx->id, tx->len, rx_ctx->rx_msg.id, rx_ctx->rx_msg.len); + return -RT_ERROR; + } + + rt_kprintf("%s %u bytes ok\n", desc, payload_len); + return RT_EOK; +} + +static rt_err_t can_dual_phase(const char *phase_name, + rt_bool_t enable_fd, + rt_uint8_t payload_len, + rt_uint8_t dlc) +{ + struct rt_can_msg tx = {0}; + rt_err_t res; + rt_uint8_t j; + + rt_kprintf("\n=== %s (%u bytes, fd=%d) ===\n", phase_name, payload_len, enable_fd); + + can_bus_close(&s_can1); + can_bus_close(&s_can0); + + res = can_bus_open(&s_can0, CAN0_DEV_NAME, enable_fd, can0_rx_indicate); + if (res != RT_EOK) + { + return res; + } + + res = can_bus_open(&s_can1, CAN1_DEV_NAME, enable_fd, can1_rx_indicate); + if (res != RT_EOK) + { + can_bus_close(&s_can0); + return res; + } + + tx.ide = RT_CAN_STDID; + tx.rtr = RT_CAN_DTR; + tx.len = dlc; +#ifdef RT_CAN_USING_CANFD + tx.fd_frame = enable_fd ? 1 : 0; + tx.brs = 0; +#endif + + for (j = 0; j < payload_len; j++) + { + tx.data[j] = (rt_uint8_t)(0xC0 + j); + } + tx.id = CAN0_TX_ID; + res = can_bus_transfer_once(&s_can0, &s_can1, &tx, payload_len, "CAN0->CAN1"); + if (res != RT_EOK) + { + return res; + } + + for (j = 0; j < payload_len; j++) + { + tx.data[j] = (rt_uint8_t)(0xD0 + j); + } + tx.id = CAN1_TX_ID; + res = can_bus_transfer_once(&s_can1, &s_can0, &tx, payload_len, "CAN1->CAN0"); + return res; +} + +static rt_err_t can_fd_dual_run(void) +{ + rt_err_t res; + + rt_kprintf("\nCAN FD dual bus test start\n"); + rt_kprintf("%s\n", CAN_BOARD_NOTE); + rt_kprintf("Jumper: %s\n", CAN_JUMPER_NOTE); + rt_kprintf("%s\n", CAN_JP7_NOTE); + + if (rt_device_find(CAN0_DEV_NAME) == RT_NULL || rt_device_find(CAN1_DEV_NAME) == RT_NULL) + { + rt_kprintf("can0/can1 device missing, check BSP_USING_CAN0/CAN1\n"); + return -RT_ERROR; + } + + res = can_dual_phase("Classic CAN", RT_FALSE, 8, 8); + if (res != RT_EOK) + { + rt_kprintf("Classic CAN failed, stop here\n"); + goto exit; + } + + res = can_dual_phase("CAN FD 8B", RT_TRUE, 8, 8); + if (res != RT_EOK) + { + goto exit; + } + + res = can_dual_phase("CAN FD 16B", RT_TRUE, 16, CAN_MSG_16BYTES); + if (res != RT_EOK) + { + goto exit; + } + + rt_kprintf("\nCAN FD dual bus test success\n"); + +exit: + can_bus_close(&s_can1); + can_bus_close(&s_can0); + return res; +} + +static int canfd_dual_test(int argc, char **argv) +{ + RT_UNUSED(argc); + RT_UNUSED(argv); + + rt_kprintf("canfd_dual_test enter\n"); + return (can_fd_dual_run() == RT_EOK) ? 0 : -1; +} +MSH_CMD_EXPORT(canfd_dual_test, CAN FD dual bus test CAN0<->CAN1); + +static int canfd_dual_diag(int argc, char **argv) +{ + RT_UNUSED(argc); + RT_UNUSED(argv); + + rt_kprintf("can0: %s\n", rt_device_find(CAN0_DEV_NAME) ? "found" : "missing"); + rt_kprintf("can1: %s\n", rt_device_find(CAN1_DEV_NAME) ? "found" : "missing"); + rt_kprintf("%s\n", CAN_BOARD_NOTE); + rt_kprintf("Jumper: %s\n", CAN_JUMPER_NOTE); + rt_kprintf("%s\n", CAN_JP7_NOTE); + rt_kprintf("Do NOT short Pin3 (5V). Run canfd_dual_probe, canfd_dual_sniff, canfd_dual_test\n"); + return 0; +} +MSH_CMD_EXPORT(canfd_dual_diag, Check CAN0/CAN1 device registration); + +static rt_err_t can_sniff_direction(struct can_fd_bus_ctx *tx_ctx, + struct can_fd_bus_ctx *rx_ctx, + rt_uint32_t tx_id, + const char *desc) +{ + struct rt_can_msg tx = {0}; + rt_err_t res; + rt_uint8_t i; + + tx.id = tx_id; + tx.ide = RT_CAN_STDID; + tx.rtr = RT_CAN_DTR; + tx.len = 8; + for (i = 0; i < 8; i++) + { + tx.data[i] = 0x55; + } + + while (rt_sem_trytake(&rx_ctx->rx_sem) == RT_EOK) + { + } + + rt_kprintf("%s: sending ...\n", desc); + res = rt_device_write(tx_ctx->dev, 0, &tx, sizeof(tx)); + rt_kprintf("%s: write res=%d\n", desc, res); + + if (rt_sem_take(&rx_ctx->rx_sem, rt_tick_from_millisecond(500)) == RT_EOK) + { + rx_ctx->rx_msg.hdr_index = -1; + rt_device_read(rx_ctx->dev, 0, &rx_ctx->rx_msg, sizeof(rx_ctx->rx_msg)); + rt_kprintf("%s: RX ok id=0x%x\n", desc, rx_ctx->rx_msg.id); + return RT_EOK; + } + + rt_kprintf("%s: RX timeout\n", desc); + return -RT_ETIMEOUT; +} + +static int canfd_dual_sniff(int argc, char **argv) +{ + rt_err_t res; + + RT_UNUSED(argc); + RT_UNUSED(argv); + + rt_kprintf("CAN bus sniff (classic 8B, 500k)\n"); + rt_kprintf("%s\n", CAN_BOARD_NOTE); + rt_kprintf("Jumper: %s\n", CAN_JUMPER_NOTE); + rt_kprintf("%s\n", CAN_JP7_NOTE); + rt_kprintf("NXP SDK uses 2 boards J16-to-J16; same-board needs J17 transceiver\n"); + + res = can_bus_open(&s_can1, CAN1_DEV_NAME, RT_FALSE, can1_rx_indicate); + if (res != RT_EOK) + { + return -1; + } + + res = can_bus_open(&s_can0, CAN0_DEV_NAME, RT_FALSE, can0_rx_indicate); + if (res != RT_EOK) + { + can_bus_close(&s_can1); + return -1; + } + + can_sniff_direction(&s_can0, &s_can1, CAN0_TX_ID, "CAN0->CAN1"); + can_sniff_direction(&s_can1, &s_can0, CAN1_TX_ID, "CAN1->CAN0"); + + can_dump_status("can0", s_can0.dev); + can_dump_status("can1", s_can1.dev); + + can_bus_close(&s_can1); + can_bus_close(&s_can0); + return 0; +} +MSH_CMD_EXPORT(canfd_dual_sniff, Sniff CAN0 TX on CAN1 RX); + +static rt_err_t can_bus_set_mode(rt_device_t dev, rt_uint32_t mode) +{ + return rt_device_control(dev, RT_CAN_CMD_SET_MODE, (void *)mode); +} + +static int canfd_dual_probe(int argc, char **argv) +{ + struct rt_can_msg tx = {0}; + rt_err_t res; + + RT_UNUSED(argc); + RT_UNUSED(argv); + + rt_kprintf("CAN bus probe (classic 8B, 500k)\n"); + rt_kprintf("%s\n", CAN_BOARD_NOTE); + rt_kprintf("Jumper: %s\n", CAN_JUMPER_NOTE); + rt_kprintf("%s\n", CAN_JP7_NOTE); + rt_kprintf("Step1: CAN1 listen-only, CAN0 TX -> check CAN1 RX (no ACK needed)\n"); + + res = can_bus_open(&s_can1, CAN1_DEV_NAME, RT_FALSE, can1_rx_indicate); + if (res != RT_EOK) + { + return -1; + } + + res = can_bus_open(&s_can0, CAN0_DEV_NAME, RT_FALSE, can0_rx_indicate); + if (res != RT_EOK) + { + can_bus_close(&s_can1); + return -1; + } + + res = can_bus_set_mode(s_can1.dev, RT_CAN_MODE_LISTEN); + if (res != RT_EOK) + { + rt_kprintf("can1 listen mode failed, res=%d\n", res); + goto out; + } + + tx.id = CAN0_TX_ID; + tx.ide = RT_CAN_STDID; + tx.rtr = RT_CAN_DTR; + tx.len = 8; + tx.data[0] = 0xA5; + + while (rt_sem_trytake(&s_can1.rx_sem) == RT_EOK) + { + } + + rt_kprintf("CAN0 TX (expect ACKERR on can0 if bus one-way OK)...\n"); + res = rt_device_write(s_can0.dev, 0, &tx, sizeof(tx)); + rt_kprintf("can0 write res=%d\n", res); + can_dump_status("can0", s_can0.dev); + + if (rt_sem_take(&s_can1.rx_sem, rt_tick_from_millisecond(500)) == RT_EOK) + { + s_can1.rx_msg.hdr_index = -1; + rt_device_read(s_can1.dev, 0, &s_can1.rx_msg, sizeof(s_can1.rx_msg)); + rt_kprintf("PASS step1: CAN1 heard CAN0 id=0x%x\n", s_can1.rx_msg.id); + rt_kprintf("Bus RX path OK. Run canfd_dual_sniff with both in normal mode.\n"); + } + else + { + rt_kprintf("FAIL step1: CAN1 did not hear CAN0 (rcv still 0)\n"); + can_dump_status("can1", s_can1.dev); + + rt_kprintf("Step2: CAN0 listen-only, CAN1 TX -> check CAN0 RX\n"); + if (can_bus_set_mode(s_can0.dev, RT_CAN_MODE_LISTEN) != RT_EOK || + can_bus_set_mode(s_can1.dev, RT_CAN_MODE_NORMAL) != RT_EOK) + { + rt_kprintf("mode switch failed\n"); + goto out; + } + + tx.id = CAN1_TX_ID; + tx.data[0] = 0x5A; + while (rt_sem_trytake(&s_can0.rx_sem) == RT_EOK) + { + } + + rt_kprintf("CAN1 TX ...\n"); + res = rt_device_write(s_can1.dev, 0, &tx, sizeof(tx)); + rt_kprintf("can1 write res=%d\n", res); + can_dump_status("can1", s_can1.dev); + + if (rt_sem_take(&s_can0.rx_sem, rt_tick_from_millisecond(500)) == RT_EOK) + { + s_can0.rx_msg.hdr_index = -1; + rt_device_read(s_can0.dev, 0, &s_can0.rx_msg, sizeof(s_can0.rx_msg)); + rt_kprintf("PASS step2: CAN0 heard CAN1 id=0x%x (one-way bus OK)\n", s_can0.rx_msg.id); + } + else + { + rt_kprintf("FAIL step2: CAN0 did not hear CAN1 either\n"); + can_dump_status("can0", s_can0.dev); + rt_kprintf("=> No electrical bus between J16 and J17, or transceiver/path dead.\n"); + rt_kprintf("Hardware checklist:\n"); + rt_kprintf(" 1) JP7 MUST be shorted (disable MCU-Link USB-CAN bridge)\n"); + rt_kprintf(" 2) Verify J16/J17 silk: Pin1=CAN_H Pin2=CAN_L Pin4=GND\n"); + rt_kprintf(" 3) DMM: J16-1<->J17-1 and J16-2<->J17-2 continuity\n"); + rt_kprintf(" 4) Confirm U6 TJA1057 populated near J17 (board may be CAN0-only)\n"); + rt_kprintf(" 5) NXP official test uses 2 boards J16-to-J16, not same-board J17\n"); + } + } + +out: + can_bus_close(&s_can1); + can_bus_close(&s_can0); + return 0; +} +MSH_CMD_EXPORT(canfd_dual_probe, Probe CAN bus RX path CAN0->CAN1); + +#endif /* BSP_USING_CAN && BSP_USING_CAN0 && BSP_USING_CAN1 */ diff --git a/bsp/nxp/mcx/mcxa/frdm-mcxa366/applications/can_fd_loopback_sample.c b/bsp/nxp/mcx/mcxa/frdm-mcxa366/applications/can_fd_loopback_sample.c new file mode 100644 index 00000000000..cc8f68613f6 --- /dev/null +++ b/bsp/nxp/mcx/mcxa/frdm-mcxa366/applications/can_fd_loopback_sample.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#ifdef BSP_USING_CAN + +#define CAN_DEV_NAME "can0" + +static rt_device_t s_can_dev; +static struct rt_semaphore s_rx_sem; +static struct rt_can_msg s_rx_msg; + +static rt_err_t can_rx_indicate(rt_device_t dev, rt_size_t size) +{ + RT_UNUSED(dev); + RT_UNUSED(size); + rt_sem_release(&s_rx_sem); + return RT_EOK; +} + +static rt_bool_t can_msg_equal(const struct rt_can_msg *tx, const struct rt_can_msg *rx, rt_uint8_t payload_len) +{ + rt_uint8_t i; + + if (tx->id != rx->id || tx->ide != rx->ide || tx->rtr != rx->rtr) + { + return RT_FALSE; + } + +#ifdef RT_CAN_USING_CANFD + if (tx->fd_frame != rx->fd_frame || tx->brs != rx->brs) + { + return RT_FALSE; + } +#endif + + if (tx->len != rx->len) + { + return RT_FALSE; + } + + for (i = 0; i < payload_len; i++) + { + if (tx->data[i] != rx->data[i]) + { + return RT_FALSE; + } + } + + return RT_TRUE; +} + +static rt_uint8_t can_payload_len(rt_uint8_t dlc) +{ +#ifdef RT_CAN_USING_CANFD + static const rt_uint8_t dlc_table[] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64 + }; + + if (dlc <= 15) + { + return dlc_table[dlc]; + } +#endif + + return (dlc <= 8) ? dlc : 8; +} + +static rt_err_t can_loopback_once(struct rt_can_msg *tx, rt_uint8_t payload_len) +{ + rt_err_t res; + + rt_memset(&s_rx_msg, 0, sizeof(s_rx_msg)); + res = rt_device_write(s_can_dev, 0, tx, sizeof(*tx)); + if (res != sizeof(*tx)) + { + rt_kprintf("CAN TX failed, res=%d\n", res); + return -RT_ERROR; + } + + res = rt_sem_take(&s_rx_sem, rt_tick_from_millisecond(1000)); + if (res != RT_EOK) + { + rt_kprintf("CAN RX timeout\n"); + return -RT_ETIMEOUT; + } + + s_rx_msg.hdr_index = -1; + res = rt_device_read(s_can_dev, 0, &s_rx_msg, sizeof(s_rx_msg)); + if (res != sizeof(s_rx_msg)) + { + rt_kprintf("CAN RX read failed, res=%d\n", res); + return -RT_ERROR; + } + + if (!can_msg_equal(tx, &s_rx_msg, payload_len)) + { + rt_kprintf("CAN compare failed, tx len=%u, rx len=%u\n", tx->len, s_rx_msg.len); + return -RT_ERROR; + } + + return RT_EOK; +} + +static rt_err_t can_fd_loopback_run(void) +{ + struct rt_can_device *can_dev; + struct can_configure cfg; + struct rt_can_msg tx = {0}; + rt_uint8_t payload_sizes[] = {8, 16}; + rt_uint8_t dlc_values[] = {8, CAN_MSG_16BYTES}; + rt_err_t res = RT_EOK; + rt_uint8_t i, j; + + s_can_dev = rt_device_find(CAN_DEV_NAME); + if (s_can_dev == RT_NULL) + { + rt_kprintf("find %s failed\n", CAN_DEV_NAME); + return -RT_ERROR; + } + + can_dev = (struct rt_can_device *)s_can_dev; + cfg = can_dev->config; + + res = rt_sem_init(&s_rx_sem, "canfd", 0, RT_IPC_FLAG_FIFO); + if (res != RT_EOK) + { + return res; + } + + cfg.baud_rate = CAN500kBaud; + cfg.mode = RT_CAN_MODE_LOOPBACK; +#ifdef RT_CAN_USING_CANFD + cfg.enable_canfd = 1; + cfg.baud_rate_fd = CAN500kBaud; +#endif + + res = rt_device_control(s_can_dev, RT_DEVICE_CTRL_CONFIG, &cfg); + if (res != RT_EOK) + { + rt_kprintf("CAN config failed, res=%d\n", res); + goto exit; + } + + res = rt_device_open(s_can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX); + if (res != RT_EOK) + { + rt_kprintf("CAN open failed, res=%d\n", res); + goto exit; + } + + rt_device_set_rx_indicate(s_can_dev, can_rx_indicate); + + tx.id = 0x123; + tx.ide = RT_CAN_STDID; + tx.rtr = RT_CAN_DTR; + + for (i = 0; i < sizeof(payload_sizes); i++) + { + tx.len = dlc_values[i]; +#ifdef RT_CAN_USING_CANFD + tx.fd_frame = 1; + tx.brs = 0; +#endif + + for (j = 0; j < payload_sizes[i]; j++) + { + tx.data[j] = (rt_uint8_t)(0xA0 + j); + } + + rt_kprintf("CAN FD loopback test: %u bytes ...\n", payload_sizes[i]); + res = can_loopback_once(&tx, payload_sizes[i]); + if (res != RT_EOK) + { + rt_kprintf("CAN FD loopback failed at %u bytes, res=%d\n", payload_sizes[i], res); + goto exit_close; + } + + rt_kprintf("CAN FD loopback %u bytes ok\n", payload_sizes[i]); + } + + rt_kprintf("CAN FD loopback test success\n"); + +exit_close: + rt_device_set_rx_indicate(s_can_dev, RT_NULL); + rt_device_close(s_can_dev); +exit: + rt_sem_detach(&s_rx_sem); + return res; +} + +static int canfd_loopback_test(int argc, char **argv) +{ + RT_UNUSED(argc); + RT_UNUSED(argv); + return (can_fd_loopback_run() == RT_EOK) ? 0 : -1; +} +MSH_CMD_EXPORT(canfd_loopback_test, CAN FD internal loopback test); + +#endif /* BSP_USING_CAN */ diff --git a/bsp/nxp/mcx/mcxa/frdm-mcxa366/board/MCUX_Config/board/pin_mux.c b/bsp/nxp/mcx/mcxa/frdm-mcxa366/board/MCUX_Config/board/pin_mux.c index 57004a9c156..4721c43d594 100644 --- a/bsp/nxp/mcx/mcxa/frdm-mcxa366/board/MCUX_Config/board/pin_mux.c +++ b/bsp/nxp/mcx/mcxa/frdm-mcxa366/board/MCUX_Config/board/pin_mux.c @@ -435,4 +435,38 @@ void BOARD_InitPins(void) .lockRegister = kPORT_UnlockRegister}; /* PORT1_2 (pin 137) is configured as CAN0_TXD */ PORT_SetPinConfig(PORT1, 2U, &port1_2_pin137_config); + +#ifdef BSP_USING_CAN1 + const port_pin_config_t port1_12_pin5_config = {/* Internal pull-up/down resistor is disabled */ + .pullSelect = kPORT_PullDisable, + .pullValueSelect = kPORT_LowPullResistor, + .slewRate = kPORT_FastSlewRate, + .passiveFilterEnable = kPORT_PassiveFilterDisable, + .openDrainEnable = kPORT_OpenDrainDisable, + .driveStrength = kPORT_LowDriveStrength, + .driveStrength1 = kPORT_NormalDriveStrength, + /* Pin is configured as CAN1_RXD */ + .mux = kPORT_MuxAlt11, + .inputBuffer = kPORT_InputBufferEnable, + .invertInput = kPORT_InputNormal, + .lockRegister = kPORT_UnlockRegister}; + /* PORT1_12 (pin 5) is configured as CAN1_RXD */ + PORT_SetPinConfig(PORT1, 12U, &port1_12_pin5_config); + + const port_pin_config_t port1_17_pin10_config = {/* Internal pull-up/down resistor is disabled */ + .pullSelect = kPORT_PullDisable, + .pullValueSelect = kPORT_LowPullResistor, + .slewRate = kPORT_FastSlewRate, + .passiveFilterEnable = kPORT_PassiveFilterDisable, + .openDrainEnable = kPORT_OpenDrainDisable, + .driveStrength = kPORT_LowDriveStrength, + .driveStrength1 = kPORT_NormalDriveStrength, + /* Pin is configured as CAN1_TXD */ + .mux = kPORT_MuxAlt11, + .inputBuffer = kPORT_InputBufferEnable, + .invertInput = kPORT_InputNormal, + .lockRegister = kPORT_UnlockRegister}; + /* PORT1_17 (pin 10) is configured as CAN1_TXD */ + PORT_SetPinConfig(PORT1, 17U, &port1_17_pin10_config); +#endif /* BSP_USING_CAN1 */ } diff --git a/bsp/nxp/mcx/mcxa/frdm-mcxa366/board/linker_scripts/MCXA366_flash.scf b/bsp/nxp/mcx/mcxa/frdm-mcxa366/board/linker_scripts/MCXA366_flash.scf index ca20ca60346..b1cdaa8bd45 100644 --- a/bsp/nxp/mcx/mcxa/frdm-mcxa366/board/linker_scripts/MCXA366_flash.scf +++ b/bsp/nxp/mcx/mcxa/frdm-mcxa366/board/linker_scripts/MCXA366_flash.scf @@ -35,7 +35,7 @@ #if (defined(__heap_size__)) #define Heap_Size __heap_size__ #else - #define Heap_Size 0x0400 + #define Heap_Size 0x8000 #endif #define m_interrupts_start 0x00000000 diff --git a/bsp/nxp/mcx/mcxa/frdm-mcxa366/project.uvprojx b/bsp/nxp/mcx/mcxa/frdm-mcxa366/project.uvprojx index 0ca901958bc..a0d14aee652 100644 --- a/bsp/nxp/mcx/mcxa/frdm-mcxa366/project.uvprojx +++ b/bsp/nxp/mcx/mcxa/frdm-mcxa366/project.uvprojx @@ -16,7 +16,7 @@ MCXA366VLQ NXP - NXP.MCXA366_DFP.25.12.00 + NXP.MCXA366_DFP.26.06.00 https://mcuxpresso.nxp.com/cmsis_pack/repo/ IRAM(0x20000000,0x03c000) IRAM2(0x04000000,0x2000) IROM(0x03000000,0x2000) IROM2(0x00000000,0x0fe000) XRAM(0x04002000,0x2000) CPUTYPE("Cortex-M33") FPU3(SFPU) DSP CLOCK(12000000) ELITTLE @@ -338,9 +338,9 @@ 0 --target=arm-arm-none-eabi - __RTTHREAD__, CPU_MCXA366VLQ, __STDC_LIMIT_MACROS, RT_USING_ARMLIBC, RT_USING_LIBC, __CLK_TCK=RT_TICK_PER_SECOND, DEBUG + CPU_MCXA366VLQ, __CLK_TCK=RT_TICK_PER_SECOND, DEBUG, __RTTHREAD__, RT_USING_ARMLIBC, __STDC_LIMIT_MACROS, RT_USING_LIBC - ..\..\..\..\..\include;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\drivers\spi;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\libc\posix\io\eventfd;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\libc\posix\ipc;..\..\..\..\..\libcpu\arm\cortex-m33;packages\nxp-mcx-series-latest\MCXA366\components\codec;packages\nxp-mcx-series-latest\MCXA366\periph2;..\..\..\..\..\components\finsh;board;..\..\..\..\..\components\libc\posix\io\poll;..\..\..\..\..\components\drivers\phy;packages\nxp-mcx-cmsis-latest\Core\Include;.;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\libc\compilers\common\extension\fcntl\octal;..\..\..\..\..\components\drivers\smp_call;..\..\..\..\..\components\drivers\include;packages\nxp-mcx-series-latest\MCXA366;..\..\..\..\..\components\libc\compilers\common\extension;packages\nxp-mcx-series-latest\MCXA366\drivers;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\libc\compilers\common\include;applications;..\Libraries\drivers;..\..\..\..\..\libcpu\arm\common;..\..\..\..\..\components\libc\posix\io\epoll;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\net\utest;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\drivers\include;board\MCUX_Config\board;..\Libraries\drivers\config + ..\..\..\..\..\components\drivers\smp_call;packages\nxp-mcx-series-latest\MCXA366\periph2;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\finsh;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\drivers\spi;..\..\..\..\..\components\drivers\phy;..\..\..\..\..\components\drivers\clock_time;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\libc\compilers\common\extension\fcntl\octal;..\..\..\..\..\components\drivers\include;..\Libraries\drivers\config;board\MCUX_Config\board;..\Libraries\drivers;..\..\..\..\..\components\libc\posix\io\poll;..\..\..\..\..\components\libc\compilers\common\include;board;packages\nxp-mcx-series-latest\MCXA366\components\codec;..\..\..\..\..\components\net\utest;..\..\..\..\..\components\drivers\include;applications;..\..\..\..\..\include;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\drivers\include;.;..\..\..\..\..\components\libc\compilers\common\extension;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\libc\posix\ipc;packages\nxp-mcx-series-latest\MCXA366;board\ports;..\..\..\..\..\components\drivers\include;..\..\..\..\..\components\drivers\include;..\..\..\..\..\libcpu\arm\common;..\..\..\..\..\components\drivers\include;packages\nxp-mcx-cmsis-latest\Core\Include;..\..\..\..\..\components\libc\posix\io\eventfd;..\..\..\..\..\components\libc\posix\io\epoll;packages\nxp-mcx-series-latest\MCXA366\drivers;..\..\..\..\..\libcpu\arm\cortex-m33;..\..\..\..\..\components\drivers\include @@ -384,6 +384,16 @@ Applications + + can_fd_dual_test.c + 1 + applications\can_fd_dual_test.c + + + can_fd_loopback_sample.c + 1 + applications\can_fd_loopback_sample.c + drv_spi_sample_rw007.c 1 @@ -440,9 +450,9 @@ DeviceDrivers - device.c + adc.c 1 - ..\..\..\..\..\components\drivers\core\device.c + ..\..\..\..\..\components\drivers\adc\adc.c 2 @@ -486,7 +496,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -496,9 +506,9 @@ - dev_i2c_bit_ops.c + dev_can.c 1 - ..\..\..\..\..\components\drivers\i2c\dev_i2c_bit_ops.c + ..\..\..\..\..\components\drivers\can\dev_can.c 2 @@ -542,7 +552,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -552,9 +562,9 @@ - dev_i2c_core.c + clock_boottime.c 1 - ..\..\..\..\..\components\drivers\i2c\dev_i2c_core.c + ..\..\..\..\..\components\drivers\clock_time\clock_boottime.c 2 @@ -598,7 +608,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -608,9 +618,9 @@ - dev_i2c_dev.c + clock_hrtimer.c 1 - ..\..\..\..\..\components\drivers\i2c\dev_i2c_dev.c + ..\..\..\..\..\components\drivers\clock_time\clock_hrtimer.c 2 @@ -654,7 +664,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -664,9 +674,9 @@ - completion_comm.c + clock_time_core.c 1 - ..\..\..\..\..\components\drivers\ipc\completion_comm.c + ..\..\..\..\..\components\drivers\clock_time\clock_time_core.c 2 @@ -710,7 +720,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -720,9 +730,9 @@ - completion_up.c + clock_timer.c 1 - ..\..\..\..\..\components\drivers\ipc\completion_up.c + ..\..\..\..\..\components\drivers\clock_time\clock_timer.c 2 @@ -766,7 +776,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -776,9 +786,9 @@ - condvar.c + device.c 1 - ..\..\..\..\..\components\drivers\ipc\condvar.c + ..\..\..\..\..\components\drivers\core\device.c 2 @@ -822,7 +832,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -832,9 +842,9 @@ - dataqueue.c + dev_i2c_bit_ops.c 1 - ..\..\..\..\..\components\drivers\ipc\dataqueue.c + ..\..\..\..\..\components\drivers\i2c\dev_i2c_bit_ops.c 2 @@ -878,7 +888,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -888,9 +898,9 @@ - pipe.c + dev_i2c_core.c 1 - ..\..\..\..\..\components\drivers\ipc\pipe.c + ..\..\..\..\..\components\drivers\i2c\dev_i2c_core.c 2 @@ -934,7 +944,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -944,9 +954,9 @@ - ringblk_buf.c + dev_i2c_dev.c 1 - ..\..\..\..\..\components\drivers\ipc\ringblk_buf.c + ..\..\..\..\..\components\drivers\i2c\dev_i2c_dev.c 2 @@ -990,7 +1000,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1000,9 +1010,9 @@ - ringbuffer.c + completion_comm.c 1 - ..\..\..\..\..\components\drivers\ipc\ringbuffer.c + ..\..\..\..\..\components\drivers\ipc\completion_comm.c 2 @@ -1046,7 +1056,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1056,9 +1066,9 @@ - waitqueue.c + completion_up.c 1 - ..\..\..\..\..\components\drivers\ipc\waitqueue.c + ..\..\..\..\..\components\drivers\ipc\completion_up.c 2 @@ -1102,7 +1112,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1112,9 +1122,9 @@ - workqueue.c + condvar.c 1 - ..\..\..\..\..\components\drivers\ipc\workqueue.c + ..\..\..\..\..\components\drivers\ipc\condvar.c 2 @@ -1158,7 +1168,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1168,9 +1178,9 @@ - adc.c + dataqueue.c 1 - ..\..\..\..\..\components\drivers\misc\adc.c + ..\..\..\..\..\components\drivers\ipc\dataqueue.c 2 @@ -1214,7 +1224,287 @@ 2 2 - + -std=gnu99 + __RT_IPC_SOURCE__ + + + + + + + + + pipe.c + 1 + ..\..\..\..\..\components\drivers\ipc\pipe.c + + + 2 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 0 + 0 + 2 + 2 + 2 + 2 + 2 + + -std=gnu99 + __RT_IPC_SOURCE__ + + + + + + + + + ringblk_buf.c + 1 + ..\..\..\..\..\components\drivers\ipc\ringblk_buf.c + + + 2 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 0 + 0 + 2 + 2 + 2 + 2 + 2 + + -std=gnu99 + __RT_IPC_SOURCE__ + + + + + + + + + ringbuffer.c + 1 + ..\..\..\..\..\components\drivers\ipc\ringbuffer.c + + + 2 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 0 + 0 + 2 + 2 + 2 + 2 + 2 + + -std=gnu99 + __RT_IPC_SOURCE__ + + + + + + + + + waitqueue.c + 1 + ..\..\..\..\..\components\drivers\ipc\waitqueue.c + + + 2 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 0 + 0 + 2 + 2 + 2 + 2 + 2 + + -std=gnu99 + __RT_IPC_SOURCE__ + + + + + + + + + workqueue.c + 1 + ..\..\..\..\..\components\drivers\ipc\workqueue.c + + + 2 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + 0 + 0 + 3 + + + 1 + + + + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 0 + 0 + 2 + 2 + 2 + 2 + 2 + + -std=gnu99 __RT_IPC_SOURCE__ @@ -1270,7 +1560,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1326,7 +1616,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1382,7 +1672,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1438,7 +1728,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1494,7 +1784,7 @@ 2 2 - + -std=gnu99 __RT_IPC_SOURCE__ @@ -1523,11 +1813,21 @@ 1 board\board.c + + drv_can.c + 1 + ..\Libraries\drivers\drv_can.c + drv_pin.c 1 ..\Libraries\drivers\drv_pin.c + + drv_timer.c + 1 + ..\Libraries\drivers\drv_timer.c + drv_uart.c 1