前言
本篇开始调试GD32H7的CAN,目标为跑通回环、自发收、以及与上位机通讯。需要额外2条杜邦线、一块candlelight USB-CAN模块。本篇为边开发边记载,所以阅读顺序可能不那么愉快。
一、驱动代码编写与kconfig修改
1.1 为何要自己编写can驱动
首先rt-thread v5.2.2自带的gd32驱动(~/gd32/rt-thread/bsp/gd32/arm/libraries/gd32_drivers/drv_can.c)并不支持gd32h7的flexCAN IP。它只支持 bxCAN ( basic extended CAN,gd32F4/F5/F1都是这个类型的CAN IP)。以下对比来自于互联网:
| bxCAN | flexCAN | |
| 收发缓冲 | 3 个发送邮箱 + 2 个三级接收 FIFO | 32 个 mailbox + 外设内部 message RAM |
| 滤波 | 28 组滤波器(标识符/掩码模式) | RX FIFO ID 滤波表 + 单 mailbox 精确匹配 |
| CAN-FD | 不支持 | 支持 |
1.2 、驱动代码及kconfig修改
1.2.1、一些有用的用户手册信息+官方例程参考
- 引脚:CAN1=tx:PB12、rx:PB5(AF9); CAN2=tx:PF7、PF6(AF2)
- 时钟:
rcu_can_clock_config(IDX_CANx, RCU_CANSRC_APB2)→300MHz - 分频器只有 10bit(最大 1024),300MHz 时钟下10kbps 物理达不到
- 官方固件库代码里RCU 宏只有
RCU_CAN1 / RCU_CAN2——没有 CAN0,因此我们从CAN1开始编号 - message RAM 在外设地址空间(CAN_BASE+0x80),不经过 D-cache,无需考虑一致性问题
- 发送完成判定:发送邮箱 code 从
CAN_MB_TX_STATUS_DATA(12)自动变回INACTIVE(8)
1.2.2、驱动代码
花了少许时间看了一会rt-thread的CAN框架代(components/drivers/can/dev_can.c),可知,can框架对can驱动要实现为以下5个接口以及我总结的5点理解:
struct rt_can_ops { rt_err_t (*configure)(struct rt_can_device *can, struct can_configure *cfg); rt_err_t (*control)(struct rt_can_device *can, int cmd, void *arg); int (*sendmsg)(struct rt_can_device *can, const void *buf, rt_uint32_t boxno); int (*recvmsg)(struct rt_can_device *can, void *buf, rt_uint32_t fifo); int (*sendmsg_nonblocking)(struct rt_can_device *can, const void *buf); };- sendmsg返回 RT_EOK只表示硬件已经受理,写完成依靠中断上报。
rt_hw_can_isr(can, RT_CAN_EVENT_TX_DONE | (boxno << 8)); - 接收对称:RX 中断里清标志、上报
RT_CAN_EVENT_RX_IND | (fifo << 8);框架回调应用的 rx_indicate,应用再rt_device_read→ 驱动recvmsg取帧。 config.sndboxnumber必须如实填硬件发送邮箱数——框架据此分配 box 号。- 设备 open 时框架会依次
control(SET_INT)使能 RX/TX 中断,并总是附带RT_DEVICE_CAN_INT_ERR - 注册入口:
rt_hw_can_register(&dev->can_dev, "can1", &ops, &priv),建议INIT_BOARD_EXPORT自动初始化
现在贴出代码(bsp/gd32/arm/libraries/gd32_drivers/drv_can_h7.c)
/* * File : drv_can_h7.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006 - 2025, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * GD32H7xx 的 CAN 控制器是 FlexCAN 风格架构(邮箱 + 片内消息 RAM,原生支持 * CAN-FD),与 GD32F1/F4/F5 的 bxCAN 完全不同,无法复用 drv_can.c,故独立实现。 * * v1 范围:经典 CAN 8 字节帧;标准/扩展帧;中断收发; * RX 固定邮箱 MB0,TX 固定邮箱 MB1; * 支持 NORMAL / LISTEN(监听) / LOOPBACK(回环) 模式。 * * 已知限制(v2 计划): * - 未接 CANx_Error_IRQn/Busoff_IRQn,总线异常时 blocking 写会等不到 TX_DONE; * - 过滤器为单邮箱粗粒度(id + public mask),未实现 RT_CAN_USING_HDR 多过滤器; * - 未启用 CAN-FD。 */ #include <board.h> #include <rtdevice.h> #if defined(BSP_USING_CAN) && defined(SOC_SERIES_GD32H7xx) #define H7_CAN_RX_MB 0U #define H7_CAN_TX_MB 1U /* 硬件实例配置 */ struct h7_can_hw { rt_uint32_t can_x; const char *name; rcu_periph_enum can_clk; can_idx_enum rcu_idx; IRQn_Type msg_irqn; rt_uint32_t af; rt_uint32_t tx_port; rt_uint32_t tx_pin; rcu_periph_enum tx_clk; rt_uint32_t rx_port; rt_uint32_t rx_pin; rcu_periph_enum rx_clk; }; struct h7_can_device { struct rt_can_device can_dev; const struct h7_can_hw *hw; can_mailbox_descriptor_struct rx_desc; rt_uint8_t rx_buf[8]; rt_uint32_t filter_id; /* 0 + mask 0 = 接收全部 */ rt_uint32_t filter_mask; /* bit=1 表示该位参与匹配 */ }; /* 波特率表:CAN 时钟 = APB2 = 300MHz * TQ = 1 + prop + tseg1 + tseg2,baud = 300MHz / presc / TQ * 分频器 10bit(最大 1024),10kbps 在 300MHz 下不可达,故不提供 */ struct h7_can_baud { rt_uint32_t baud; rt_uint8_t sjw, prop, tseg1, tseg2; rt_uint16_t presc; }; static const struct h7_can_baud h7_can_baud_tbl[] = { /* baud sjw prop tseg1 tseg2 presc TQ=15 采样点80%,1M 为官方例程值 */ { CAN1MBaud, 1, 2, 5, 2, 30 },/* TQ=10,采样点 80% */ { CAN800kBaud, 1, 2, 9, 3, 25 }, { CAN500kBaud, 1, 2, 9, 3, 40 }, { CAN250kBaud, 1, 2, 9, 3, 80 }, { CAN125kBaud, 1, 2, 9, 3, 160 }, { CAN100kBaud, 1, 2, 9, 3, 200 }, { CAN50kBaud, 1, 2, 9, 3, 400 }, { CAN20kBaud, 1, 2, 9, 3, 1000 }, }; static const struct h7_can_hw h7_can_hw_tbl[] = { #ifdef BSP_USING_CAN1 { .can_x = CAN1, .name = "can1", .can_clk = RCU_CAN1, .rcu_idx = IDX_CAN1, .msg_irqn = CAN1_Message_IRQn, .af = GPIO_AF_9, #if defined(BSP_CAN1_TX_PB13) .tx_port = GPIOB, .tx_pin = GPIO_PIN_13, .tx_clk = RCU_GPIOB, #else #error "Select CAN1 tx pin" #endif #if defined(BSP_CAN1_RX_PB5) .rx_port = GPIOB, .rx_pin = GPIO_PIN_5, .rx_clk = RCU_GPIOB, #else #error "Select CAN1 rx pin" #endif }, #endif #ifdef BSP_USING_CAN2 { .can_x = CAN2, .name = "can2", .can_clk = RCU_CAN2, .rcu_idx = IDX_CAN2, .msg_irqn = CAN2_Message_IRQn, .af = GPIO_AF_2, #if defined(BSP_CAN2_TX_PF7) .tx_port = GPIOF, .tx_pin = GPIO_PIN_7, .tx_clk = RCU_GPIOF, #else #error "Select CAN2 tx pin" #endif #if defined(BSP_CAN2_RX_PF6) .rx_port = GPIOF, .rx_pin = GPIO_PIN_6, .rx_clk = RCU_GPIOF, #else #error "Select CAN2 rx pin" #endif }, #endif }; #ifdef BSP_USING_CAN1 static struct h7_can_device h7_can1; #endif #ifdef BSP_USING_CAN2 static struct h7_can_device h7_can2; #endif static void h7_can_gpio_init(const struct h7_can_hw *hw) { /* CAN 时钟源选 APB2(300MHz),官方例程做法 */ rcu_can_clock_config(hw->rcu_idx, RCU_CANSRC_APB2); rcu_periph_clock_enable(hw->can_clk); rcu_periph_clock_enable(hw->tx_clk); rcu_periph_clock_enable(hw->rx_clk); gpio_output_options_set(hw->tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, hw->tx_pin); gpio_mode_set(hw->tx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, hw->tx_pin); gpio_af_set(hw->tx_port, hw->af, hw->tx_pin); gpio_output_options_set(hw->rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, hw->rx_pin); gpio_mode_set(hw->rx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, hw->rx_pin); gpio_af_set(hw->rx_port, hw->af, hw->rx_pin); } static rt_err_t h7_can_config(struct rt_can_device *can, struct can_configure *cfg) { struct h7_can_device *dev = (struct h7_can_device *)can->parent.user_data; rt_uint32_t can_x = dev->hw->can_x; can_parameter_struct param; can_operation_modes_enum mode = CAN_NORMAL_MODE; const struct h7_can_baud *bt = &h7_can_baud_tbl[0]; ErrStatus st; rt_kprintf("[drv] cfg enter: mode=%lu baud=%lu\n", cfg->mode, cfg->baud_rate); for (rt_uint32_t i = 0; i < sizeof(h7_can_baud_tbl) / sizeof(h7_can_baud_tbl[0]); i++) { if (h7_can_baud_tbl[i].baud == cfg->baud_rate) { bt = &h7_can_baud_tbl[i]; break; } } can_deinit(can_x); can_struct_para_init(CAN_INIT_STRUCT, ¶m); param.internal_counter_source = CAN_TIMER_SOURCE_BIT_CLOCK; param.mb_tx_order = CAN_TX_HIGH_PRIORITY_MB_FIRST; param.mb_tx_abort_enable = ENABLE; param.local_priority_enable = DISABLE; param.mb_rx_ide_rtr_type = CAN_IDE_RTR_FILTERED; param.mb_remote_frame = CAN_STORE_REMOTE_REQUEST_FRAME; param.rx_private_filter_queue_enable = DISABLE; param.edge_filter_enable = DISABLE; param.protocol_exception_enable = DISABLE; param.rx_filter_order = CAN_RX_FILTER_ORDER_MAILBOX_FIRST; param.memory_size = CAN_MEMSIZE_32_UNIT; param.mb_public_filter = dev->filter_mask; param.self_reception = (cfg->mode == RT_CAN_MODE_LOOPBACK || cfg->mode == RT_CAN_MODE_LOOPBACKANLISTEN) ? ENABLE : DISABLE; param.resync_jump_width = bt->sjw; param.prop_time_segment = bt->prop; param.time_segment_1 = bt->tseg1; param.time_segment_2 = bt->tseg2; param.prescaler = bt->presc; st = can_init(can_x, ¶m); rt_kprintf("[drv] can_init -> %d\n", st); if (st != SUCCESS) return -RT_ERROR; can_struct_para_init(CAN_MDSC_STRUCT, &dev->rx_desc); dev->rx_desc.code = CAN_MB_RX_STATUS_EMPTY; dev->rx_desc.id = dev->filter_id; dev->rx_desc.data = (rt_uint32_t *)dev->rx_buf; can_mailbox_config(can_x, H7_CAN_RX_MB, &dev->rx_desc); rt_kprintf("[drv] rx mailbox ok\n"); switch (cfg->mode) { case RT_CAN_MODE_NORMAL: mode = CAN_NORMAL_MODE; break; case RT_CAN_MODE_LISTEN: mode = CAN_MONITOR_MODE; break; case RT_CAN_MODE_LOOPBACK: case RT_CAN_MODE_LOOPBACKANLISTEN: mode = CAN_LOOPBACK_SILENT_MODE; break; default: rt_kprintf("[drv] bad mode\n"); return -RT_ERROR; } st = can_operation_mode_enter(can_x, mode); rt_kprintf("[drv] mode_enter(%d) -> %d\n", mode, st); if (st != SUCCESS) return -RT_ERROR; if (can->parent.open_flag & RT_DEVICE_FLAG_INT_RX) { can_interrupt_enable(can_x, CAN_INT_MB0); nvic_irq_enable(dev->hw->msg_irqn, 1, 0); } if (can->parent.open_flag & RT_DEVICE_FLAG_INT_TX) { can_interrupt_enable(can_x, CAN_INT_MB1); nvic_irq_enable(dev->hw->msg_irqn, 1, 0); } rt_kprintf("[drv] cfg done\n"); return RT_EOK; } static rt_err_t h7_can_control(struct rt_can_device *can, int cmd, void *arg) { struct h7_can_device *dev = (struct h7_can_device *)can->parent.user_data; rt_uint32_t can_x = dev->hw->can_x; rt_uint32_t argval; switch (cmd) { case RT_DEVICE_CTRL_SET_INT: argval = (rt_uint32_t)(rt_ubase_t)arg; if (argval == RT_DEVICE_FLAG_INT_RX) { can_interrupt_enable(can_x, CAN_INT_MB0); nvic_irq_enable(dev->hw->msg_irqn, 1, 0); } else if (argval == RT_DEVICE_FLAG_INT_TX) { can_interrupt_enable(can_x, CAN_INT_MB1); nvic_irq_enable(dev->hw->msg_irqn, 1, 0); } /* RT_DEVICE_CAN_INT_ERR:v1 未接错误中断,直接忽略 */ break; case RT_DEVICE_CTRL_CLR_INT: argval = (rt_uint32_t)(rt_ubase_t)arg; if (argval == RT_DEVICE_FLAG_INT_RX) { can_interrupt_disable(can_x, CAN_INT_MB0); nvic_irq_disable(dev->hw->msg_irqn); } else if (argval == RT_DEVICE_FLAG_INT_TX) { can_interrupt_disable(can_x, CAN_INT_MB1); nvic_irq_disable(dev->hw->msg_irqn); } break; case RT_CAN_CMD_SET_MODE: argval = (rt_uint32_t)(rt_ubase_t)arg; if (argval != RT_CAN_MODE_NORMAL && argval != RT_CAN_MODE_LISTEN && argval != RT_CAN_MODE_LOOPBACK && argval != RT_CAN_MODE_LOOPBACKANLISTEN) { return -RT_ERROR; } if (argval != can->config.mode) { can->config.mode = argval; return h7_can_config(can, &can->config); } break; case RT_CAN_CMD_SET_BAUD: argval = (rt_uint32_t)(rt_ubase_t)arg; if (argval != CAN1MBaud && argval != CAN800kBaud && argval != CAN500kBaud && argval != CAN250kBaud && argval != CAN125kBaud && argval != CAN100kBaud && argval != CAN50kBaud && argval != CAN20kBaud) { return -RT_ERROR; /* 10k 在 300MHz 时钟下不可达 */ } if (argval != can->config.baud_rate) { can->config.baud_rate = argval; return h7_can_config(can, &can->config); } break; case RT_CAN_CMD_SET_PRIV: argval = (rt_uint32_t)(rt_ubase_t)arg; if (argval != RT_CAN_MODE_PRIV && argval != RT_CAN_MODE_NOPRIV) { return -RT_ERROR; } can->config.privmode = argval; /* 由框架层处理,无需重配硬件 */ break; case RT_CAN_CMD_SET_FILTER: if (arg == RT_NULL) { dev->filter_id = 0; dev->filter_mask = 0; } else { struct rt_can_filter_config *fcfg = (struct rt_can_filter_config *)arg; if (fcfg->count > 0) { dev->filter_id = fcfg->items[0].id; dev->filter_mask = fcfg->items[0].mask; } } return h7_can_config(can, &can->config); case RT_CAN_CMD_GET_STATUS: { can_error_counter_struct errcnt; can_error_counter_get(can_x, &errcnt); can->status.rcverrcnt = errcnt.rx_errcnt; can->status.snderrcnt = errcnt.tx_errcnt; can->status.errcode = (rt_uint32_t)can_error_state_get(can_x); rt_memcpy(arg, &can->status, sizeof(can->status)); break; } default: break; } return RT_EOK; } static rt_ssize_t h7_can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num) { struct h7_can_device *dev = (struct h7_can_device *)can->parent.user_data; struct rt_can_msg *pmsg = (struct rt_can_msg *)buf; rt_uint32_t can_x = dev->hw->can_x; can_mailbox_descriptor_struct tx_msg; /* 上一帧还没发出去 */ if (can_mailbox_code_get(can_x, H7_CAN_TX_MB) == CAN_MB_TX_STATUS_DATA) { return -RT_EBUSY; } can_struct_para_init(CAN_MDSC_STRUCT, &tx_msg); tx_msg.code = CAN_MB_TX_STATUS_DATA; tx_msg.ide = (pmsg->ide == RT_CAN_EXTID) ? 1U : 0U; tx_msg.rtr = (pmsg->rtr == RT_CAN_RTR) ? 1U : 0U; tx_msg.id = pmsg->id; tx_msg.data_bytes = (pmsg->len > 8U) ? 8U : pmsg->len; tx_msg.data = (rt_uint32_t *)pmsg->data; can_mailbox_config(can_x, H7_CAN_TX_MB, &tx_msg); return RT_EOK; } static rt_ssize_t h7_can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo) { struct h7_can_device *dev = (struct h7_can_device *)can->parent.user_data; struct rt_can_msg *pmsg = (struct rt_can_msg *)buf; if (can_mailbox_receive_data_read(dev->hw->can_x, H7_CAN_RX_MB, &dev->rx_desc) != SUCCESS) { return -RT_ERROR; } pmsg->ide = dev->rx_desc.ide ? RT_CAN_EXTID : RT_CAN_STDID; pmsg->id = dev->rx_desc.id; pmsg->rtr = dev->rx_desc.rtr ? RT_CAN_RTR : RT_CAN_DTR; pmsg->len = (dev->rx_desc.data_bytes > 8U) ? 8U : (rt_uint8_t)dev->rx_desc.data_bytes; rt_memcpy(pmsg->data, dev->rx_buf, pmsg->len); pmsg->hdr_index = 0; return RT_EOK; } static rt_ssize_t h7_can_sendmsg_nonblocking(struct rt_can_device *can, const void *buf) { return h7_can_sendmsg(can, buf, 0); } static const struct rt_can_ops h7_can_ops = { h7_can_config, h7_can_control, h7_can_sendmsg, h7_can_recvmsg, h7_can_sendmsg_nonblocking, }; static void h7_can_msg_isr(struct h7_can_device *dev) { rt_uint32_t can_x = dev->hw->can_x; if (can_interrupt_flag_get(can_x, CAN_INT_FLAG_MB0)) { can_interrupt_flag_clear(can_x, CAN_INT_FLAG_MB0); rt_hw_can_isr(&dev->can_dev, RT_CAN_EVENT_RX_IND | (0 << 8)); } if (can_interrupt_flag_get(can_x, CAN_INT_FLAG_MB1)) { can_interrupt_flag_clear(can_x, CAN_INT_FLAG_MB1); rt_hw_can_isr(&dev->can_dev, RT_CAN_EVENT_TX_DONE | (0 << 8)); } } #ifdef BSP_USING_CAN1 void CAN1_Message_IRQHandler(void) { rt_interrupt_enter(); h7_can_msg_isr(&h7_can1); rt_interrupt_leave(); } #endif #ifdef BSP_USING_CAN2 void CAN2_Message_IRQHandler(void) { rt_interrupt_enter(); h7_can_msg_isr(&h7_can2); rt_interrupt_leave(); } #endif int rt_hw_can_init(void) { struct can_configure config = CANDEFAULTCONFIG; rt_uint32_t i = 0; config.privmode = RT_CAN_MODE_NOPRIV; config.ticks = 50; config.sndboxnumber = 1; /* 单 TX 邮箱,框架只会分配 box 0 */ #ifdef BSP_USING_CAN1 h7_can1.hw = &h7_can_hw_tbl[i++]; h7_can1.can_dev.config = config; h7_can_gpio_init(h7_can1.hw); rt_hw_can_register(&h7_can1.can_dev, h7_can1.hw->name, &h7_can_ops, &h7_can1); #endif #ifdef BSP_USING_CAN2 h7_can2.hw = &h7_can_hw_tbl[i++]; h7_can2.can_dev.config = config; h7_can_gpio_init(h7_can2.hw); rt_hw_can_register(&h7_can2.can_dev, h7_can2.hw->name, &h7_can_ops, &h7_can2); #endif return 0; } INIT_BOARD_EXPORT(rt_hw_can_init); #endif /* BSP_USING_CAN && SOC_SERIES_GD32H7xx */代码说明:驱动并没实现对接rt-thread框架下的hdr滤波表,也仅使用了32 个 mailbox 中的两个:MB0 收、MB1 发。后续或许会花些时间把它写完整 :D
1.2.3、修改kconfig文件以及sconscript编译脚本
gd32_drivers/SConscript修改如下:
if GetDepend('RT_USING_CAN'): if GetDepend('SOC_SERIES_GD32H7xx'): src += ['drv_can_h7.c'] else: src += ['drv_can.c']board/Kconfig:
menuconfig BSP_USING_CAN bool "Enable CAN" select RT_USING_CAN default n if BSP_USING_CAN config BSP_USING_CAN1 bool "Enable CAN1 (TX:PB13 RX:PB5)" default n config BSP_USING_CAN2 bool "Enable CAN2 (TX:PF7 RX:PF6)" default n endif二、测试代码
2.1、代码编写
~/gd32/rt-thread/bsp/gd32/arm/gd32h759i-eval/applications/can_test.c
/* * can_test.c — RT-Thread 标准 CAN 设备 API 验证 * can_test loop : can1 回环自测(免接线) * can_test net : can2 发 -> can1 收(需 JP12<->JP14 H-H L-L) */ #include <rtthread.h> #include <rtdevice.h> static rt_err_t can_rx_ind(rt_device_t dev, rt_size_t size) { struct rt_can_msg msg; while (rt_device_read(dev, 0, &msg, sizeof(msg)) == sizeof(msg)) { rt_kprintf("[CAN] rx id=0x%lX len=%d data=", msg.id, msg.len); for (int i = 0; i < msg.len; i++) rt_kprintf("%02X ", msg.data[i]); rt_kprintf("\n"); } return RT_EOK; } static void can_test(int argc, char **argv) { rt_bool_t loop = (argc > 1 && !rt_strcmp(argv[1], "loop")); const char *txname = loop ? "can1" : "can2"; rt_device_t rxdev = rt_device_find("can1"); rt_device_t txdev = rt_device_find(txname); if (!rxdev || !txdev) { rt_kprintf("can device not found, check menuconfig\n"); return; } rt_device_control(rxdev, RT_CAN_CMD_SET_BAUD, (void *)CAN1MBaud); rt_device_control(txdev, RT_CAN_CMD_SET_BAUD, (void *)CAN1MBaud); if (loop) { rt_device_control(rxdev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_LOOPBACK); } else { rt_device_control(rxdev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_NORMAL); rt_device_control(txdev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_NORMAL); } rt_device_open(rxdev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX); if (txdev != rxdev) rt_device_open(txdev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_TX); rt_device_set_rx_indicate(rxdev, can_rx_ind); struct rt_can_msg msg = {0}; msg.id = 0x55; msg.ide = RT_CAN_STDID; msg.rtr = RT_CAN_DTR; msg.len = 8; for (int i = 0; i < 8; i++) msg.data[i] = 0xA1 + i; rt_kprintf("[CAN] %s tx id=0x55, A1..A8\n", txname); if (rt_device_write(txdev, 0, &msg, sizeof(msg)) != sizeof(msg)) rt_kprintf("[CAN] tx failed\n"); } MSH_CMD_EXPORT(can_test, can test: can_test loop | net);2026年9月11日追加编译过程:
- scons --menuconfig , (Top) → Hardware Drivers Config → On-chip Peripheral Drivers → Enable CAN ,选中 CAN1和CAN2
- scons -j16
2.2、跳线设置
JP65(1,2) 、 JP53(2,3) 、 JP67(2,3),插跳线时可以观察板子上的丝印,印刷得很清楚
2.3、自发自收回环测试
在串口中msh>控制台中输入
msh />can_test loop看到如下信息就代表回环自发自发测试成功:
[drv] cfg enter: mode=2 baud=1000000 [drv] can_init -> 1 [drv] rx mailbox ok [drv] mode_enter(2) -> 1 [drv] cfg done [drv] cfg enter: mode=2 baud=1000000 [drv] can_init -> 1 [drv] rx mailbox ok [drv] mode_enter(2) -> 1 [drv] cfg done [CAN] can1 tx id=0x55, A1..A8 [CAN] rx id=0x55 len=8 data=A1 A2 A3 A4 A5 A6 A7 A82.4、双节点通讯(net模式)
板上自带两路 CAN 收发器,杜邦线对接两个排针(JP12、JP14)即可组网:
JP12 的 H ── JP14 的 H; JP12 的 L ── JP14 的 L
串口输入:
msh />can_test net看到如下信息就代表双节点通讯成功:
[drv] cfg enter: mode=0 baud=1000000 [drv] can_init -> 1 [drv] rx mailbox ok [drv] mode_enter(0) -> 1 [drv] cfg done [drv] cfg enter: mode=0 baud=1000000 [drv] can_init -> 1 [drv] rx mailbox ok [drv] mode_enter(0) -> 1 [drv] cfg done [CAN] can2 tx id=0x55, A1..A8 [CAN] rx id=0x55 len=8 data=A1 A2 A3 A4 A5 A6 A7 A82.5、上位机通讯测试
2026年9月11日追加:
我购买的模块是 candlelight USB-CAN类型的。上位机测试软件使用了 cangaroo windows版(感谢社区有人编译了它:https://github.com/yltzdhbc/cangaroo_win/tree/master/)
接插线:板子的JP12 H/L 对应链接模块的 H/L
上位机cangaroo设置如图:
板子上电,使用双节点通讯模式一样输入:
msh /> can_test net然后可以在板子串口里看到:
msh />can_test net [CAN] can2 tx id=0x55, A1..A8 msh />上位机上可以看到(截图中可以看到我发了很多次):