新闻详情

新闻详情

首页 / 资讯中心 / 详情

STM32CubeMX配置GD32 CAN总线保姆级教程:从环境搭建到异常恢复

发布时间:2026/9/29 19:54:27来源:尧图网络
STM32CubeMX配置GD32 CAN总线保姆级教程:从环境搭建到异常恢复
做嵌入式这些年我见过太多人在GD32上栽跟头了。项目手册明明写着“与主流F103芯片引脚兼容”结果拿STM32CubeMX配好了CAN总线烧到GD32里不是发不出去就是进不了中断最后只能加班排查。其实用STM32CubeMX配置GD32的CAN总线是一套完全可以跑通的流程关键在三个地方选对替代型号、弄清时钟树差异、把异常处理做在前面。这篇保姆级教程我就把这套流程完整拆开讲从软件环境搭建到波特率计算从过滤器配置到总线关闭恢复把那些教程里不会写的坑一个个填平。适合手里有GD32项目、但对HAL库和CubeMX更熟的朋友也适合刚从STM32切换过来想省点入门时间的人。1. 先从整体思路说起1.1 为什么要把STM32CubeMX用在GD32上很多刚接触GD32的人会有一个疑问GD32官方也有自己的图形化配置工具为什么还要绕一圈用STM32CubeMX答案很简单因为STM32CubeMX太成熟了。HAL库的生态、社区资料、各种外设配置案例几乎到了“你踩过的坑别人一定踩过”的程度而GD32 Embedded Builder虽然有官方支持但上手资料相对少遇到问题能搜到的解决方案也少。更重要的是GD32F103系列在引脚定义、内部外设寄存器映射上和STM32F103系列有相当高的兼容性这让“借CubeMX的壳来配GD32”成为可能。具体操作思路是在STM32CubeMX里选择一个与目标GD32芯片对应的STM32型号比如手头是GD32F103C8T6就在CubeMX里选STM32F103C8Tx把CAN外设、GPIO、中断、时钟全部在图形界面里配好生成一份标准HAL库工程然后把CAN相关的初始化参数和逻辑迁移到GD32固件库工程中。这样既享受了CubeMX的可视化配置又能在GD32上落地。这里我必须说清楚一个容易误判的点CubeMX生成的HAL库代码不要指望能“原封不动”编译到GD32上。GD32官方主推的是标准外设库风格的固件库函数名、结构体定义和HAL库差异很大强行移植HAL代码会遇到一堆编译错误。正确的做法是重点关注配置参数本身比如波特率分频值、位时序段长度、过滤器ID和屏蔽位这些底层寄存器值是可以直接换算复用的。把逻辑和参数拿过来用GD32库接口重新实现才是最高效的路径。1.2 CAN总线必须掌握的三个底层概念在动手配置之前我建议你先花10分钟理解三个底层概念否则后面哪怕配置成功了出了异常也是一头雾水。第一显性位和隐性位。CAN总线上的电平分显性和隐性两种状态显性位会覆盖隐性位。这意味着多个节点同时发送时总线上优先级更高的帧ID值更小能继续发送其他节点自动退出发送这是CAN总线仲裁的基础。用生活场景来类比就好比十几个人共用一根麦克风有人先开麦说话其他人听到有人在说就会闭嘴等待优先级最高的人永远不会被抢麦。第二帧结构。标准CAN数据帧由起始位、仲裁段11位标准ID或29位扩展ID、控制段、数据段0到8字节、CRC段、ACK段和帧结束组成。理解帧结构有什么用当你用CAN分析仪抓包时看到的“错在哪一段”直接决定排查方向比如CRC段报错大概率是总线干扰导致数据被篡改ACK段报错说明总线上没有其他节点正确接收到你的帧这可能是没有终端电阻或者总线只有单节点。第三错误处理机制。CAN是自带健康管理的总线每个节点内部都有发送错误计数器和接收错误计数器。当错误计数累积超过一定阈值节点会从主动错误状态降级为被动错误状态最后进入总线关闭状态主动和总线断开。这套机制本意是防止故障节点拖垮整个网络但在实际项目中如果软件没有做好异常恢复节点就会“越错越沉默”直到完全脱离通信你需要知道怎么把它拉回来。这些内容在后面异常处理章节我会展开讲。2. 环境准备与工具链搭建2.1 软件清单怎么选版本别踩坑做GD32开发我的标准软件清单是这五样缺一不可STM32CubeMX建议装6.x以上较新版本用来生成图形化配置和初始化代码。STM32CubeF1固件包在CubeMX里在线安装因为STM32F103是替代型号的基础。Keil MDK建议5.3x以上版本太低的话装不上GD32器件支持包。GD32F10x标准固件库从GD32官网下载这是最终实际编译运行的库。J-Link驱动或DAP-Link驱动用于烧录和调试。如果用的是J-Link刷写GD32时建议选择对应的GD32设备型号而不是直接选STM32以免下载算法不匹配。版本问题是我第一个要提醒的坑。曾经遇到一个朋友用很老版本的CubeMX里面连STM32F103C8Tx都搜不到更别提生成工程了。而Keil如果版本过旧安装GD32的pack时也会报错或者安装成功后器件列表里根本看不到GD32。我的建议是新手直接都装最新稳定版减少环境层面的意外变量。GD32F10x固件库版本建议用官方最新的3.x版本旧版本在CAN外设的函数接口上有变化网上很多教程代码拿来直接编译会报错就是因为固件库版本不一致。2.2 让CubeMX“认识”GD32的两种办法既然CubeMX不直接支持GD32那怎么让它“认识”GD32目前主流有两种办法。第一种替代型号法。CubeMX里选一个和目标GD32引脚、外设高度兼容的STM32型号生成工程后再迁移。这是本教程主推的方法因为CubeMX的界面、参数校验、工程管理都相当成熟对CAN这样的复杂外设来说配置效率非常高。比如GD32F103C8T6就对应STM32F103C8TxGD32F103VCT6就对应STM32F103VCTx。需要注意一点STM32F103C8T6只有一个CAN外设CAN1如果你选了一个双CAN的大容量STM32型号但实际GD32那边可能没有对应的第二个CAN或者引脚完全不对应所以选替代型号时要先核对目标芯片资源表。第二种官方工具法。用GD32 Embedded Builder这是GD官方推出的图形化配置IDE类似CubeMX能直接识别GD32全系列型号生成GCC或者Keil工程。它的优势是不存在“迁移”这一步配置完直接就是GD32固件库代码且对GD32特有资源支持很好比如ITCM、Code Flash和Data Flash的区分。缺点是资料少社区生态远没有CubeMX丰富而且对于从STM32转过来的工程师来说操作习惯上需要重新适应。我的建议是两条路都了解但以替代型号法作为主力。实际项目中我经常把两种工具配合用用GD32 Embedded Builder打开一个官方CAN例程确认GD32库的接口和寄存器差异再用CubeMX生成想要的初始化配置参数两边对照迁移效率最高。3. STM32CubeMX配置CAN的保姆级实操3.1 引脚与时钟树配置打开STM32CubeMX新建工程在芯片搜索框里输入STM32F103C8选中STM32F103C8Tx双击进入配置界面。首先要确认系统时钟来源建议在RCC配置里把HSE设为Crystal/Ceramic Resonator这样后续可以用外部晶振保证CAN的波特率精度。如果直接用内部HSI温漂大波特率容易跑偏高速CAN下偶发错误帧的概率明显增加。时钟树是关键一步。CAN外设的时钟源来自APB1总线。在STM32F103上APB1最高是36MHz而GD32F103的APB1有些型号允许更高但为了通用性和稳定性我建议把APB1配置在36MHz来做参数计算。在CubeMX的Clock Configuration里把APB1 Prescaler调成/2系统时钟如果是72MHzAPB1正好36MHz。如果你用的板子外接晶振不是8MHz时钟树会自动换算你只需要确认APB1那一栏最终显示的是36MHz即可。接下来配置CAN引脚。以默认映射为例CAN1的发送引脚是PA12接收引脚是PA11。在Pinout视图里点击PA11和PA12选择CAN1_RX和CAN1_TX复用功能。如果电路板上把CAN收发器接在PB8/PB9这两个重映射引脚上那就需要另外开启CAN1的重映射功能。这个细节非常容易漏我遇到过不止一次配置完发现数据发不出去最后查到是引脚复用没设置对。在CubeMX里如果选重映射引脚它会自动配置AF功能但到了GD32迁移时就需要你手动设置GPIO的复用功能号不能照搬默认映射的代码。3.2 波特率到底怎么算一文讲透CAN波特率是CAN配置里最容易出错也最核心的部分。计算公式是CAN波特率 外设时钟 / 预分频值 / (1 BS1 BS2)其中1代表同步段固定为1个时间量子tqBS1和BS2分别是两个相位缓冲段。举个例子外设时钟36MHz目标波特率500kbps。先求需要的总tq数36MHz除以500kbps等于72。也就是说一个位时间要分成72个tq才能得到500kbps。如果选择预分频值Prescaler4则tq频率为36MHz/49MHz一个位时间需要的tq数变为9MHz/500kbps18个tq。然后分配位时序段同步段1tqBS113tqBS24tq加起来正好18tq。采样点位置也很关键它决定节点在哪个时刻采样总线电平。采样点 同步段 BS1 /同步段 BS1 BS2也就是113/18约等于77.8%这个值落在高速CAN推荐区间75%到80%内。采样点偏前或偏后会导致总线距离较长时出现误码。125kbps这类低速现场总线采样点通常可以设在80%以上甚至87.5%具体要根据网络拓扑和CAN收发器型号来确定。下面是几个常用波特率对应的参考配置以外设时钟36MHz为例目标波特率PrescalerBS1(Tq)BS2(Tq)采样点1Mbps213477.8%500kbps413477.8%250kbps813477.8%125kbps1613477.8%注意换个外设时钟这些参数就要重新算不要死记硬背。CubeMX的CAN配置界面里可以直接填Prescaler、BS1、BS2它会实时显示计算出的波特率非常方便。我在实际项目里会先根据公式手算一遍再用CubeMX校验确保理解没错。3.3 过滤器与中断配置CAN过滤器的功能是硬件层面的报文筛选它能在数据进入FIFO之前根据ID和屏蔽位决定是否接收某条报文从而减轻MCU的软件负担。理解过滤器核心就是两种模式列表模式和掩码模式。列表模式表示只接收和预期ID完全一致的报文掩码模式则是通过掩码指定哪些ID位必须精确匹配哪些位无关紧要。对初学调试我的建议是先配置成掩码模式掩码设为0也就是不屏蔽任何位所有报文都收进来先把链路跑通确认通信无误之后再逐步收紧过滤器。在CubeMX中CAN配置页面下方有Filter Configuration选项卡可以设置过滤器编号、模式、掩码值等。在STM32F103C8Tx上CAN1的过滤器组数量是有限的但配置一个过滤器就足够初学使用。你还要把FIFO0中断使能也就是CAN接收FIFO0消息挂起中断。在NVIC设置里勾选使能该中断并设置合适的抢占优先级。我习惯让CAN接收中断的优先级比其他普通外设高但不高于系统滴答定时器避免长时间阻塞。需要提醒的是CubeMX里使能CAN错误中断、总线关闭中断的选项不一定在默认页面直接出现有的情况下需要你手动在生成代码里通过HAL_CAN_ActivateNotification开启。这是很多初学者容易卡住的地方生成代码后发现只有接收中断没有错误处理就是因为没有在代码里显式打开这些通知。3.4 生成代码后如何移植到GD32工程CubeMX配置完成后点击GENERATE CODE生成HAL库工程。现在的核心工作是把CAN相关的初始化逻辑迁移到GD32固件库工程这一步我给出一套标准流程。第一步在Keil里创建一个新的GD32标准固件库工程选对目标芯片型号。比如GD32F103C8Tx需要安装GD32的Device Pack并在Device选项里选择GD32F103C8而不是STM32。第二步对照CubeMX生成的can.c文件把初始化参数抄到GD32固件库的初始化结构体里。GD32库的CAN初始化使用can_parameter_struct结构体里面包括工作模式、自动恢复开关、重同步跳转宽度、时间段1、时间段2、预分频值。这些值要和你CubeMX里配置的对应起来。比如CubeMX里BS1填13Tq对应到GD32库就是CAN_BT_BS1_13TQBS2填4Tq对应CAN_BT_BS2_4TQPrescaler填4就填4。第三步GPIO复用配置不能照搬HAL代码。GD32库配置PA11/PA12为CAN复用功能时和STM32的HAL写法不同。GD32F10x系列使用gpio_af_set函数设置复用功能CAN0对应的复用功能号是GPIO_AF_9。这个细节特别重要我见过很多人在这里卡住GPIO只是配成了输出模式没有设置复用功能结果CAN_RX和CAN_TX引脚完全不起作用。第四步把中断服务函数用GD32库的接口重写。GD32的接收中断服务函数名为CAN0_RX0_IRQHandler和STM32不同而且标志位读取、清除的函数也都是GD32风格。比如can_interrupt_flag_get、can_message_receive、can_interrupt_flag_clear。中断使能方面CAN0对应的中断通道可能是CAN0_RX0_IRQn和CAN0_IRQn分别用于FIFO接收和错误处理需要在NVIC配置里分别使能。4. 裸机代码实现与核心逻辑4.1 CAN初始化与发送流程拆解迁移完成后完整的GD32 CAN初始化代码大致是这个样子void CAN_Config(void) { can_parameter_struct can_parameter; can_filter_parameter_struct can_filter; rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_CAN0); gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11 | GPIO_PIN_12); gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11 | GPIO_PIN_12); gpio_af_set(GPIOA, GPIO_AF_9, GPIO_PIN_11 | GPIO_PIN_12); can_deinit(CAN0); can_parameter.time_triggered DISABLE; can_parameter.auto_bus_off_recovery DISABLE; can_parameter.auto_wake_up DISABLE; can_parameter.no_auto_retrans DISABLE; can_parameter.rec_fifo_overwrite DISABLE; can_parameter.trans_fifo_order DISABLE; can_parameter.working_mode CAN_NORMAL_MODE; can_parameter.resync_jump_width CAN_BT_SJW_1TQ; can_parameter.time_segment_1 CAN_BT_BS1_13TQ; can_parameter.time_segment_2 CAN_BT_BS2_4TQ; can_parameter.prescaler 4; can_init(CAN0, can_parameter); can_filter.filter_mode CAN_FILTERMODE_MASK; can_filter.filter_fifo CAN_FIFO0; can_filter.filter_mask 0x00000000; can_filter.filter_bits 0; can_filter.filter_number 0; can_filter.filter_enable ENABLE; can_filter_init(can_filter); can_interrupt_enable(CAN0, CAN_INT_RX_FIFO0); can_interrupt_enable(CAN0, CAN_INT_ERR | CAN_INT_BO); nvic_irq_enable(CAN0_RX0_IRQn, 1, 0); nvic_irq_enable(CAN0_IRQn, 0, 0); }代码并不复杂但每行都很关键。time_triggered一般关闭除非你要做时间触发通信。auto_bus_off_recovery我建议先设为DISABLE后面用软件可控恢复这在异常处理章节会展开。working_mode先设置成CAN_NORMAL_MODE调试时也可以先用CAN_LOOPBACK_MODE做自发自收验证确认硬件链路没有问题后再切回正常模式。发送流程也很直观定义can_message_struct结构体填上ID和数据然后调用can_message_transmit。下面是一个发送标准帧的例子uint8_t CAN_SendMessage(uint32_t id, uint8_t *data, uint8_t len) { can_message_struct tx_message; tx_message.tx_sfid id; tx_message.tx_ff CAN_FF_STANDARD; tx_message.tx_ft CAN_FT_DATA; tx_message.tx_dlen len; memcpy(tx_message.tx_data, data, len); can_message_transmit(CAN0, tx_message); return 1; }发送后可以通过can_transmit_states函数查询邮箱状态判断是否成功也可以直接依赖发送完成中断。但要注意一个隐藏问题如果总线上没有其他节点应答发送方会产生ACK错误发送不成功这是正常现象。单节点调试时要么使用回环模式要么接一个CAN分析仪作为对端。4.2 接收中断与回调处理怎么搭CAN接收中断的写法GD32库和HAL库完全不同。HAL库用HAL_CAN_RxFifo0MsgPendingCallback回调GD32库则直接在中断服务函数里处理void CAN0_RX0_IRQHandler(void) { if (can_interrupt_flag_get(CAN0, CAN_INT_FLAG_RFO0) ! RESET) { can_message_struct rx_message; can_message_receive(CAN0, CAN_FIFO0, rx_message); // 把rx_message塞进环形缓冲区或者直接进行协议解析 CAN_StoreToBuffer(rx_message); can_interrupt_flag_clear(CAN0, CAN_INT_FLAG_RFO0); } }中断里最重要的原则是“快进快出”。CAN波特率哪怕只有125kbps一个8字节标准帧的传输时间也就几百微秒如果总线负载较高FIFO溢出风险很大。我的习惯是在中断里只把报文复制到预先分配好的缓冲区不在这层做复杂的协议解析解析放到主循环里处理。缓冲区用环形队列或者简单的数组加写指针都可以但要当心读写指针的并发访问建议加临界区保护。还有一个容易被忽略的问题接收FIFO溢出标志位。如果CAN接收FIFO满且下一条报文到达硬件会置溢出标志并停止接收后续报文。这个标志位要及时清除否则即使你清除了挂起标志以后也收不到新报文。在实际调试时如果发现“偶尔能收到数据但过一会就完全收不到”优先查这个溢出标志位。4.3 异常处理框架状态机才是真核心很多人写CAN程序只做“初始化收发”认为能把数据发出去就算完成。但在真实的工业现场和车载环境中CAN总线会出现各种异常如果软件没有一个完整的异常处理框架故障节点可能拖垮整个通信网络。我的建议是在工程里维护一个简单的CAN运行状态机至少包含这几个状态CAN_STATE_IDLE正常运行没有异常。CAN_STATE_TX_BUSY正在发送数据。CAN_STATE_ERROR_PASSIVE节点进入被动错误状态说明错误较多。CAN_STATE_BUS_OFF节点进入总线关闭状态已和总线断开。CAN_STATE_RECOVERY正在执行恢复流程。状态机的驱动源就是CAN错误中断和总线关闭中断。在错误中断里读取错误计数器根据计数范围更新状态在总线关闭中断里把状态切到CAN_STATE_BUS_OFF同时累计一次BusOff次数。主循环里检查状态如果发现处于BusOff确实要执行恢复流程而不是直接继续收发。这个框架的好处是所有异常恢复逻辑都收敛在一个地方便于加日志、便于统计。比如出现一次被动错误你可以打印出当前错误计数器数值出现一次总线关闭你可以计数并延时恢复而不是让硬件自动恢复反复打断总线。这比在中断里临时处理要稳得多。5. 异常实战错误帧、总线关闭与排查记录5.1 错误帧是怎么产生的如何定位错误帧是CAN总线上特有的一种“故障通报机制”。当任意节点检测到错误时它会主动发送错误帧通知全网该数据无效。根据错误类型常见的有位错误、填充错误、CRC错误、ACK错误和格式错误。我在实际项目里遇到的错误按频率排序大概是这样的第一ACK错误。发送方发送完一帧数据后在ACK槽位等待接收节点拉低电平结果没等到就会产生ACK错误。最常见原因就是总线上只有它一个节点没有其他节点接收或者收发器有问题导致回读电平不对。遇到这个问题我通常先用回环模式自测确认控制器本身没问题再检查收发器和终端电阻。第二位错误。发送节点发送一个显性位但回读到总线上是隐性位这就叫位错误。常见原因包括总线短路、两个节点波特率不一致、总线干扰太强。定位办法是用示波器同时抓CAN_H和CAN_L看波形是否正常以及用CAN分析仪看错误帧出现的时机和频率。第三填充错误和CRC错误。这两种错误通常是总线信号质量差造成的比如线束过长、没有双绞、屏蔽层接地不良、终端电阻缺失或阻值不对。CAN协议规定连续5个相同电平后必须插入一个反向填充位如果接收方没看到这个填充位就是填充错误CRC错误则是接收方计算出的CRC校验值和发送方不一致说明数据在传输中被干扰了。定位错误帧我的标准动作是先用回环模式排除控制器自身问题再用CAN分析仪监听总线抓错误帧并记录错误类型。如果错误帧频繁出现我就拿示波器看总线波形重点测差分电压幅度、边沿质量、以及总线空闲时的电平。绝大多数硬件层面的CAN问题最终都能通过“分析仪看协议层、示波器看物理层”这两个工具组合定位。5.2 总线关闭后的恢复策略总线关闭是CAN节点最严重的错误状态。当发送错误计数器TEC超过255时节点认为自己是干扰源主动断开与总线的连接不再参与通信。硬件上的恢复条件是检测到128次连续的11个隐性位也就是总线已经空闲一段时间。GD32提供了两种恢复方式。第一种是自动恢复在初始化时把auto_bus_off_recovery设为ENABLE硬件在满足条件后自动重新进入总线。这种方式省事但问题也很明显如果故障原因是外部持续干扰节点恢复后又会立刻再次关闭如此反复反而加重总线不稳定。第二种是手动恢复把auto_bus_off_recovery设为DISABLE在总线关闭中断中做标志记录然后在主循环中延迟一段时间再执行恢复流程。这个“延迟”时间由项目自己决定我通常会延迟几十到几百毫秒让总线稳定一下再恢复。实际工程中我推荐手动恢复。恢复前最好重新初始化CAN外设确保发送和接收路径都处在干净的初始状态。同时我建议记录BusOff次数如果短时间内反复进入BusOff就要在应用层报故障提醒系统进行降级处理而不是无限重试。工业设备上反复故障还可能导致看门狗复位这些都是你需要提前设计的。5.3 中断接收还是DMA接收别再纠结这个问题在社区里经常被问到CAN到底用中断接收还是DMA接收我的答案是绝大多数场合用中断DMA并不适合常规CAN应用。原因有两点。第一CAN报文的长度由DLC字段决定每帧数据从0字节到8字节不等属于变长数据。标准DMA通常需要预设传输长度一个DMA配置一次只能搬运固定字节数如果这次帧长5字节下次帧长8字节DMA要么读多要么读少处理起来很麻烦。第二CAN通信是事件驱动、突发性的报文到达时间不确定中断服务程序只需要把FIFO里的数据读出来耗时很短对CPU的占用率通常很低。在高负载场景担心的是FIFO溢出而不是及时性。那DMA什么时候用呢如果你用某些支持“FIFO联动DMA”的CAN控制器且报文长度固定数据量又特别大比如每秒几千帧那用DMA可以显著降低CPU占用。但GD32标准CAN控制器上这样的场景极少见。与其折腾DMA不如把精力放在“中断接收加环形缓冲区加应用层解包”这套架构上它更通用也更好维护。5.4 负载率计算与常见问题速查表总线负载率是评估CAN网络是否健康的重要指标过高会导致消息延迟增大过低则说明带宽利用率不充分。计算公式很简单负载率等于一段时间内总线上实际传输的位数除以该段时间内可用总位数。要准确估算每帧占用多少位可以这样算标准帧数据帧在未计位填充前一帧的位数大约是44加8乘以数据长度。例如8字节数据就是44加64等于108位。由于CAN协议存在位填充机制数据段和CRC段连续5个相同位时需要插入一个填充位最坏情况下总位数增加约20%实际项目里可以按乘以1.2来做估算也就是大约130位。举个例子总线波特率500kbps每秒可传输500000位。假设每秒实际发送500帧每帧8字节按130位估算每秒消耗65000位负载率约13%。这个数值比较健康。当负载率超过50%时就要考虑降低发送频率或者增大通信波特率否则一旦出现错误帧重发总线很容易进入拥堵状态。最后把常见问题和排查思路整理成一张速查表以我个人的项目经验为准遇到相似现象可以直接按这个顺序查现象可能原因排查手段发送一直超时总线上没有终端电阻、单节点无ACK、收发器损坏先用回环模式自测再检查终端电阻和收发器接线接收中断不触发过滤器配置错误、NVIC未使能、中断标志未清除改成掩码全收模式检查NVIC使能和优先级偶发错误帧波特率偏差、线束干扰、接地不良用分析仪抓错误类型用示波器检查CAN_H/CAN_L波形节点进入BusOff不恢复自动恢复关闭或代码没实现恢复流程检查auto_bus_off_recovery配置主循环实现手动恢复CubeMX生成代码在GD32编译不过HAL库与GD32固件库接口差异、寄存器不兼容只迁移配置参数到GD32库不直接编译HAL代码回环模式正常、外接节点不正常收发器配置问题、总线电平异常、波特率采样点不匹配检查收发器工作模式测量差分电压核对波特率参数最后分享一个我个人的工程习惯。每次拿到一块新的GD32板子我不会上来就写业务逻辑而是先做三类自检回环模式下CAN自发自收是否正常、错误计数器会不会无故增长、裸板状态下总线输出电平是否正确。这三个检查能筛掉九成以上的硬件或配置问题。CAN总线这个东西前期把异常处理框架搭好后期调试会省下成倍的时间。这篇教程里涉及的代码和参数都是我在F103平台和GD32F103平台上实际验证过的你可以放心参考但批量复制到自己的板子上时一定先核对时钟树和引脚映射这样才不会在底层细节上被绊住。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Claude Code CLI 使用的一些技巧:用 TaoToken 统一 Key 打通 MCP 配置 2026/9/29 21:30:51

Claude Code CLI 使用的一些技巧:用 TaoToken 统一 Key 打通 MCP 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
医用吊塔:手术室和 ICU 天花板上那根“臂“是做什么的 2026/9/29 21:30:51

医用吊塔:手术室和 ICU 天花板上那根“臂“是做什么的

进过手术室或 ICU 的人可能会注意到:监护仪、呼吸机、输液泵并不都堆在地面上,而是集中挂在从天花板伸下来的一根可旋转的悬臂上,旁边还整齐排着氧气、负压吸引、压缩空气的接口。这套装置通常被称为医用吊塔,也有吊桥、悬臂等叫法…

阅读更多 →
Spring-AI-Alibaba 初体验:用 Streamable-http 接入 MCP Server 的配置骨架 2026/9/29 21:30:51

Spring-AI-Alibaba 初体验:用 Streamable-http 接入 MCP Server 的配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
vscode配置C/C++环境(超详细保姆级教学):从g++到调试,一次跑通 2026/9/29 21:30:51

vscode配置C/C++环境(超详细保姆级教学):从g++到调试,一次跑通

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
llamap.cpp 和 llama-index连接 2026/9/29 21:30:51

llamap.cpp 和 llama-index连接

1. 启动 llama.cpp 服务器 使用 llama.cpp 二进制文件启动一个兼容 OpenAI API 的服务器。bash./server -m /path/to/your/model.gguf --host 127.0.0.1 --port 8080 -c 40962. 在 LlamaIndex 中连接服务器 实例化 LlamaCPP 时,将 model_url 指向本地服务器地址&…

阅读更多 →
TaoToken 配置实战:用 McEval 多语言代码评测基准验证 40 种编程语言模型能力 2026/9/29 21:30:38

TaoToken 配置实战:用 McEval 多语言代码评测基准验证 40 种编程语言模型能力

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉