From a1a3cf5f32567a6c22c75a2ccc2722f3f8e279fa Mon Sep 17 00:00:00 2001 From: misonyo Date: Fri, 28 Jun 2019 14:02:20 +0800 Subject: [PATCH] [bsp/imxrt]add can driver --- bsp/imxrt/Libraries/MIMXRT1050/SConscript | 2 +- bsp/imxrt/Libraries/drivers/SConscript | 3 + bsp/imxrt/Libraries/drivers/drv_can.c | 461 ++++++++++++++++++ bsp/imxrt/Libraries/drivers/drv_can.h | 16 + bsp/imxrt/imxrt1052-fire-pro/README.md | 1 + bsp/imxrt/imxrt1052-fire-pro/board/Kconfig | 11 + .../board/MCUX_Config/clock_config.c | 5 +- .../board/MCUX_Config/pin_mux.c | 26 + bsp/imxrt/imxrt1052-fire-pro/rtconfig.h | 4 + 9 files changed, 526 insertions(+), 3 deletions(-) create mode 100644 bsp/imxrt/Libraries/drivers/drv_can.c create mode 100644 bsp/imxrt/Libraries/drivers/drv_can.h diff --git a/bsp/imxrt/Libraries/MIMXRT1050/SConscript b/bsp/imxrt/Libraries/MIMXRT1050/SConscript index 65ce9d4044..a689cd8cbe 100644 --- a/bsp/imxrt/Libraries/MIMXRT1050/SConscript +++ b/bsp/imxrt/Libraries/MIMXRT1050/SConscript @@ -55,7 +55,7 @@ if GetDepend(['BSP_USING_LCD']): if GetDepend(['RT_USING_USB_HOST']) or GetDepend(['RT_USING_USB_DEVICE']): src += ['MIMXRT1052/drivers/fsl_usdhc.c'] -if GetDepend(['RT_USING_CAN']): +if GetDepend(['BSP_USING_CAN']): src += ['MIMXRT1052/drivers/fsl_flexcan.c'] if GetDepend(['BSP_USING_ETH']): diff --git a/bsp/imxrt/Libraries/drivers/SConscript b/bsp/imxrt/Libraries/drivers/SConscript index 3509785bcb..8f6ec9dcfc 100644 --- a/bsp/imxrt/Libraries/drivers/SConscript +++ b/bsp/imxrt/Libraries/drivers/SConscript @@ -31,6 +31,9 @@ if GetDepend('BSP_USING_PWM'): if GetDepend('BSP_USING_ADC'): src += ['drv_adc.c'] +if GetDepend('BSP_USING_CAN'): + src += ['drv_can.c'] + if GetDepend('BSP_USING_SDRAM'): src += ['drv_sdram.c'] diff --git a/bsp/imxrt/Libraries/drivers/drv_can.c b/bsp/imxrt/Libraries/drivers/drv_can.c new file mode 100644 index 0000000000..829060e2d7 --- /dev/null +++ b/bsp/imxrt/Libraries/drivers/drv_can.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-06-27 misonyo the first version. + */ + +#include +#ifdef BSP_USING_CAN + +#include +#include "drv_can.h" +#include "fsl_common.h" +#include "fsl_iomuxc.h" +#include "fsl_flexcan.h" + +#define LOG_TAG "drv.can" +#include + +#if defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL + #error "Please don't define 'FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL'!" +#endif + +#define RX_MB_COUNT 32 +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_CAN1 + CAN1_INDEX, +#endif +#ifdef BSP_USING_CAN2 + CAN2_INDEX, +#endif +}; + +struct imxrt_can +{ + char *name; + CAN_Type *base; + IRQn_Type irqn; + flexcan_handle_t handle; + struct rt_can_device can_dev; +}; + +struct imxrt_can flexcans[] = +{ +#ifdef BSP_USING_CAN1 + { + .name = "can1", + .base = CAN1, + .irqn = CAN1_IRQn, + }, +#endif +#ifdef BSP_USING_CAN2 + { + .name = "can2", + .base = CAN2, + .irqn = CAN2_IRQn, + }, +#endif +}; + +uint32_t GetCanSrcFreq(void) +{ + uint32_t freq; + + freq = (CLOCK_GetFreq(kCLOCK_Usb1PllClk) / 6) / (CLOCK_GetDiv(kCLOCK_CanDiv) + 1U); + + return freq; +} + +static void flexcan_callback(CAN_Type *base, flexcan_handle_t *handle, status_t status, uint32_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 | (63 - result) << 8); + break; + + case kStatus_FLEXCAN_WakeUp: + + case kStatus_FLEXCAN_ErrorStatus: + if ((result >= 47) && (result <= 63)) + { + rt_hw_can_isr(&can->can_dev, RT_CAN_EVENT_TX_FAIL | (63 - result) << 8); + } + break; + + case kStatus_FLEXCAN_TxSwitchToRx: + break; + + default: + break; + } +} + +static rt_err_t can_cfg(struct rt_can_device *can_dev, struct can_configure *cfg) +{ + struct imxrt_can *can; + 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; + + RT_ASSERT(can_dev != RT_NULL); + RT_ASSERT(cfg != RT_NULL); + + can = (struct imxrt_can *)can_dev->parent.user_data; + RT_ASSERT(can != RT_NULL); + + FLEXCAN_GetDefaultConfig(&config); + config.baudRate = cfg->baud_rate; + config.maxMbNum = 64; /* all series have 64 MB */ + config.enableIndividMask = true; /* one filter per MB */ + switch (cfg->mode) + { + case RT_CAN_MODE_NORMAL: + /* default mode */ + break; + case RT_CAN_MODE_LISEN: + break; + case RT_CAN_MODE_LOOPBACK: + config.enableLoopBack = true; + break; + case RT_CAN_MODE_LOOPBACKANLISEN: + break; + } + FLEXCAN_Init(can->base, &config, GetCanSrcFreq()); + 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); + NVIC_SetPriority(can->irqn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + 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) + { + LOG_E("%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 from RX MB 1 */ + if (item->hdr == -1) + { + + for (i = 0; i < 32; i++) + { + if (!(filter_mask & (1 << i))) + { + index = i; + break; + } + } + } + else /* use user specified hdr */ + { + if (filter_mask & (1 << item->hdr)) + { + res = RT_ERROR; + LOG_E("%s hdr%d filter already set!\n", can->name, item->hdr); + break; + } + else + { + index = item->hdr; + } + } + + /* 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: + 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; + default: + res = RT_ERROR; + break; + } + + return res; +} + +static int can_send(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_uint8_t sendMB; + + 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); + + /* use the last 16 MB to send msg */ + sendMB = 63 - boxno; + FLEXCAN_SetTxMbConfig(can->base, sendMB, 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 = sendMB; + 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; + case kStatus_FLEXCAN_TxBusy: + ret = RT_EBUSY; + break; + } + + return ret; +} + +static int can_recv(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; /* 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 struct rt_can_ops imxrt_can_ops = +{ + .configure = can_cfg, + .control = can_control, + .sendmsg = can_send, + .recvmsg = can_recv, +}; + +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 = 16; /* send Mailbox count */ + config.msgboxsz = RX_MB_COUNT; /* RX msg buffer 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; + 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 /* BSP_USING_CAN */ diff --git a/bsp/imxrt/Libraries/drivers/drv_can.h b/bsp/imxrt/Libraries/drivers/drv_can.h new file mode 100644 index 0000000000..55cca16200 --- /dev/null +++ b/bsp/imxrt/Libraries/drivers/drv_can.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-06-28 misonyo the first version. + */ + +#ifndef DRV_SPI_H__ +#define DRV_SPI_H__ + +int rt_hw_can_init(void); + +#endif /* DRV_SPI_H__ */ diff --git a/bsp/imxrt/imxrt1052-fire-pro/README.md b/bsp/imxrt/imxrt1052-fire-pro/README.md index 868f8f9fb6..f0d19cbb57 100644 --- a/bsp/imxrt/imxrt1052-fire-pro/README.md +++ b/bsp/imxrt/imxrt1052-fire-pro/README.md @@ -54,6 +54,7 @@ i.MX RT1052 EVK Pro 是野火推出的一款基于 ARM Cortex-M7 内核的开发 | WDT | 支持 | | | PWM | 支持 | | | GPT | 支持 | | +| CAN | 支持 | CAN1 | ## 使用说明 diff --git a/bsp/imxrt/imxrt1052-fire-pro/board/Kconfig b/bsp/imxrt/imxrt1052-fire-pro/board/Kconfig index 45d2a55ee8..9280cbfb7f 100644 --- a/bsp/imxrt/imxrt1052-fire-pro/board/Kconfig +++ b/bsp/imxrt/imxrt1052-fire-pro/board/Kconfig @@ -16,6 +16,17 @@ menu "On-chip Peripheral Drivers" select RT_USING_PIN default y + menuconfig BSP_USING_CAN + bool "Enable CAN" + select RT_USING_CAN + default n + + if BSP_USING_CAN + config BSP_USING_CAN1 + bool "Enable CAN1" + default y + endif + menuconfig BSP_USING_LPUART bool "Enable UART" select RT_USING_SERIAL diff --git a/bsp/imxrt/imxrt1052-fire-pro/board/MCUX_Config/clock_config.c b/bsp/imxrt/imxrt1052-fire-pro/board/MCUX_Config/clock_config.c index 87b20a1e25..2df780010a 100644 --- a/bsp/imxrt/imxrt1052-fire-pro/board/MCUX_Config/clock_config.c +++ b/bsp/imxrt/imxrt1052-fire-pro/board/MCUX_Config/clock_config.c @@ -53,7 +53,7 @@ name: BOARD_BootClockRUN called_from_default_init: true outputs: - {id: AHB_CLK_ROOT.outFreq, value: 600 MHz} -- {id: CAN_CLK_ROOT.outFreq, value: 40 MHz} +- {id: CAN_CLK_ROOT.outFreq, value: 20 MHz} - {id: CKIL_SYNC_CLK_ROOT.outFreq, value: 32.768 kHz} - {id: CLK_1M.outFreq, value: 1 MHz} - {id: CLK_24M.outFreq, value: 24 MHz} @@ -93,6 +93,7 @@ outputs: settings: - {id: CCM.AHB_PODF.scale, value: '1', locked: true} - {id: CCM.ARM_PODF.scale, value: '2', locked: true} +- {id: CCM.CAN_CLK_PODF.scale, value: '4', locked: true} - {id: CCM.FLEXSPI_PODF.scale, value: '1', locked: true} - {id: CCM.FLEXSPI_SEL.sel, value: CCM_ANALOG.PLL3_PFD0_CLK} - {id: CCM.LCDIF_PODF.scale, value: '8', locked: true} @@ -297,7 +298,7 @@ void BOARD_BootClockRUN(void) CLOCK_DisableClock(kCLOCK_Can1S); CLOCK_DisableClock(kCLOCK_Can2S); /* Set CAN_CLK_PODF. */ - CLOCK_SetDiv(kCLOCK_CanDiv, 1); + CLOCK_SetDiv(kCLOCK_CanDiv, 3); /* Set Can clock source. */ CLOCK_SetMux(kCLOCK_CanMux, 2); /* Disable UART clock gate. */ diff --git a/bsp/imxrt/imxrt1052-fire-pro/board/MCUX_Config/pin_mux.c b/bsp/imxrt/imxrt1052-fire-pro/board/MCUX_Config/pin_mux.c index b3adfd7fec..a837d6f11e 100644 --- a/bsp/imxrt/imxrt1052-fire-pro/board/MCUX_Config/pin_mux.c +++ b/bsp/imxrt/imxrt1052-fire-pro/board/MCUX_Config/pin_mux.c @@ -110,6 +110,32 @@ void BOARD_InitPins(void) { IOMUXC_SetPinMux( IOMUXC_GPIO_B1_13_LPUART5_RX, /* GPIO_B1_13 is configured as LPUART5_RX */ 0U); /* Software Input On Field: Input Path is determined by functionality */ + IOMUXC_SetPinMux( + IOMUXC_GPIO_AD_B0_14_FLEXCAN2_TX, /* GPIO_AD_B0_14 is configured as FLEXCAN2_TX */ + 1U); /* Software Input On Field: Force input path of pad GPIO_AD_B0_14 */ + IOMUXC_SetPinMux( + IOMUXC_GPIO_AD_B0_15_FLEXCAN2_RX, /* GPIO_AD_B0_15 is configured as FLEXCAN2_RX */ + 1U); /* Software Input On Field: Force input path of pad GPIO_AD_B0_15 */ + IOMUXC_SetPinConfig( + IOMUXC_GPIO_AD_B0_14_FLEXCAN2_TX, /* GPIO_AD_B0_14 PAD functional properties : */ + 0x10B0u); /* Slew Rate Field: Slow Slew Rate + Drive Strength Field: R0/6 + Speed Field: medium(100MHz) + Open Drain Enable Field: Open Drain Disabled + Pull / Keep Enable Field: Pull/Keeper Enabled + Pull / Keep Select Field: Keeper + Pull Up / Down Config. Field: 100K Ohm Pull Down + Hyst. Enable Field: Hysteresis Disabled */ + IOMUXC_SetPinConfig( + IOMUXC_GPIO_AD_B0_15_FLEXCAN2_RX, /* GPIO_AD_B0_15 PAD functional properties : */ + 0x10B0u); /* Slew Rate Field: Slow Slew Rate + Drive Strength Field: R0/6 + Speed Field: medium(100MHz) + Open Drain Enable Field: Open Drain Disabled + Pull / Keep Enable Field: Pull/Keeper Enabled + Pull / Keep Select Field: Keeper + Pull Up / Down Config. Field: 100K Ohm Pull Down + Hyst. Enable Field: Hysteresis Disabled */ } /*********************************************************************************************************************** diff --git a/bsp/imxrt/imxrt1052-fire-pro/rtconfig.h b/bsp/imxrt/imxrt1052-fire-pro/rtconfig.h index 9faf831e05..3602ac8144 100644 --- a/bsp/imxrt/imxrt1052-fire-pro/rtconfig.h +++ b/bsp/imxrt/imxrt1052-fire-pro/rtconfig.h @@ -78,6 +78,8 @@ #define RT_USING_SERIAL #define RT_SERIAL_USING_DMA #define RT_SERIAL_RB_BUFSZ 64 +#define RT_USING_CAN +#define RT_CAN_USING_HDR #define RT_USING_CPUTIME #define RT_USING_PIN @@ -158,6 +160,8 @@ /* On-chip Peripheral Drivers */ #define BSP_USING_GPIO +#define BSP_USING_CAN +#define BSP_USING_CAN2 #define BSP_USING_LPUART #define BSP_USING_LPUART1 -- GitLab