资讯动态

STM32F103+TJA1050双机CAN通信实战:硬件接线、CubeMX配置与调试避坑指南

发布时间:2026/10/18 1:03:52 来源:尧图企业网站定制
1. 项目缘起与整体设计思路搞嵌入式的人都有一个共识CAN 总线是绕不过去的一道坎。不管是做汽车电子、工业控制还是机器人只要涉及到多节点通信CAN 几乎就是默认选项。但很多朋友第一次接触 CAN 的时候往往卡在几个地方STM32 的 CAN 外设配置太绕、TJA1050 收发器怎么接线搞不清楚、两个板子就是通不上。我自己第一次调 CAN 的时候两块板子对着烧了一整天程序示波器也上了最后发现是终端电阻没加——这种坑文档里不会写只有踩过才知道。这篇内容就是围绕STM32F103 TJA1050 的双机 CAN 通信来展开的。我会从硬件接线、CubeMX 配置、代码实现到实际调试把整个链路完整拆一遍。目标很明确让你看完之后手上有两块 F103 板子和两个 TJA1050 模块就能直接把通信跑起来不用再去翻一堆零散的帖子和手册。适合谁看如果你已经会点 STM32 的基础操作GPIO、串口、中断这些但没碰过 CAN那这篇就是给你写的。如果你之前调过 CAN 但没调通也可以对照着排查。至于完全零基础的朋友建议先把 STM32 的 HAL 库和 CubeMX 基本用法过一遍再来看不然配置部分可能会有点懵。整个方案的核心思路是这样的用 STM32F103 自带的bxCAN 外设作为控制器外接TJA1050收发器芯片完成物理层电平转换两块板子通过 CAN_H 和 CAN_L 两根线连起来两端各加一个120Ω 终端电阻然后跑一套简单的收发程序验证通信。为什么选 TJA1050因为它便宜、好买、电路简单3.3V 和 5V 系统都能用是入门 CAN 最友好的收发器之一。为什么选 F103因为它的 CAN 外设资料最全社区里踩过的坑最多你遇到问题基本都能搜到答案。下面我会按硬件设计 → CubeMX 配置 → 代码实现 → 调试排查这个顺序来拆每一部分都会说清楚“为什么这么做”而不只是“怎么做”。2. 硬件层拆解TJA1050 接线与终端电阻的门道2.1 TJA1050 引脚功能与典型电路TJA1050 是一颗经典的高速 CAN 收发器负责把 STM32 的 CAN_TX / CAN_RX 逻辑信号转换成 CAN 总线上的差分信号。它的引脚不多但每一个都不能接错引脚名称功能说明1TXD发送数据输入接 STM32 的 CAN_TX2GND地3VCC电源5V典型4RXD接收数据输出接 STM32 的 CAN_RX5Vref参考电压输出一般悬空6CANL接 CAN 总线低线7CANH接 CAN 总线高线8S模式选择接地为高速模式实际接线的时候TJA1050 的 TXD 接 STM32 的PA12CAN_TXRXD 接PA11CAN_RX。注意这里有个容易搞反的地方TJA1050 的 TXD 是输入RXD 是输出所以是STM32 的 TX 接 TJA1050 的 TXDSTM32 的 RX 接 TJA1050 的 RXD名字一样直接对接就行不用交叉。VCC 建议用 5V 供电虽然 TJA1050 在 3.3V 下也能工作但 5V 下总线驱动能力更稳。S 引脚接地选择高速模式这个模式下没有斜率控制适合 1Mbps 以下的速率。如果你要做低功耗或者速率很低的场景可以把 S 接高电平进静默模式但入门阶段直接接地就行。注意TJA1050 的 VCC 和 STM32 的 3.3V 是两回事。TJA1050 用 5VSTM32 用 3.3V但它们的逻辑电平是兼容的TJA1050 的 TXD 能识别 3.3V 高电平RXD 输出也是 3.3V 兼容的所以不需要电平转换芯片。2.2 终端电阻为什么必须加这是新手最容易忽略、也最容易导致通信失败的地方。CAN 总线是差分总线它的两端必须各接一个120Ω 终端电阻作用是消除信号反射、保证总线阻抗匹配。你可以这样理解CAN 总线就像一根水管信号在里面传播如果到了末端没有“吸收”掉信号就会反弹回来和后面的信号叠加导致波形畸变、通信出错。120Ω 电阻就是那个“吸收器”让信号能量在末端被消耗掉不产生反射。具体怎么加在两块板子的 CAN_H 和 CAN_L 之间各并联一个 120Ω 电阻。也就是说总线两端各一个总共两个。如果你只有一块板子接了电阻或者两个都接在同一个节点上效果都不对。实测下来如果两个 120Ω 电阻都加上用万用表量 CAN_H 和 CAN_L 之间的电阻应该是60Ω 左右两个 120Ω 并联。如果量出来是 120Ω说明只接了一个如果是无穷大说明一个都没接。这个测量方法非常实用接线完成后先量一下再上电能省掉很多排查时间。实操心得有些 TJA1050 模块出厂就自带 120Ω 电阻有些没有。买的时候一定要看清楚如果模块自带两块板子都用自带电阻的模块那就刚好如果只有一块自带另一块需要自己补一个。我建议不管模块带不带都自己准备几个 120Ω 电阻调试的时候灵活处理。2.3 双机接线图与共地问题两块板子之间的接线其实很简单就三根线CAN_H 接 CAN_HCAN_L 接 CAN_LGND 接 GND。前两根是差分信号线第三根是共地线。共地这件事很多人会忽略觉得 CAN 是差分信号不需要共地。理论上差分信号确实靠两根线的电压差来传输信息但如果两个板子的地电位差太大超出了 TJA1050 的共模输入范围接收就会出错。所以GND 一定要连尤其是两块板子用不同电源供电的时候。接线顺序建议先接 GND再接 CAN_H 和 CAN_L最后上电。这样能避免上电瞬间的电位差损坏收发器。虽然 TJA1050 有一定的保护但养成好习惯没坏处。3. CubeMX 配置CAN 外设参数怎么算、怎么填3.1 时钟树与 CAN 波特率计算CAN 的波特率配置是很多人头疼的地方因为 CubeMX 里要填好几个参数填错了通信就不通。我先说清楚原理再给具体数值。STM32F103 的 CAN 外设挂在APB1 总线上APB1 的时钟频率默认是36MHz系统时钟 72MHz 二分频。CAN 的波特率计算公式是波特率 APB1时钟 / (Prescaler × (BS1 BS2 1))其中 Prescaler 是预分频系数BS1 和 BS2 是位段的时间份额。以500kbps为例这是汽车电子里最常用的速率之一APB1 36MHz目标波特率 500kbps36,000,000 / 500,000 72所以 Prescaler × (BS1 BS2 1) 72常见的组合是 Prescaler 4BS1 15BS2 2这样 4 × (15 2 1) 4 × 18 72刚好。采样点位置 (1 BS1) / (1 BS1 BS2) 16 / 18 ≈ 88.9%这个采样点位置很理想符合 CAN 标准推荐的 87.5% 左右。在 CubeMX 里对应的参数是Prescaler4Time Seg115Time Seg22SJW1如果你要跑250kbps把 Prescaler 改成 8 就行其他不变。要跑1MbpsPrescaler 改成 2。这样算下来同一套 BS1/BS2 参数可以覆盖多个常用速率很方便。3.2 工作模式与中断配置CubeMX 里 CAN 的模式选择有几个选项入门阶段建议这样配Operating ModeNormal正常模式不要选 Loopback 或 Silent那两个是调试用的Auto RetransmissionEnable发送失败自动重传保证可靠性Auto Wake-UpDisable入门用不到Receive FIFO Locked ModeDisable允许溢出时覆盖旧数据Transmit FIFO PriorityEnable按标识符优先级发送中断方面至少要使能RX0 中断接收 FIFO 0 非空中断这样收到数据能及时处理。发送中断可选如果你用轮询方式发送就不需要。我一般建议接收用中断、发送用轮询这样结构简单不容易出问题。NVIC 里把USB low priority or CAN RX0 interrupts勾上优先级设成默认就行。注意 F103 的 CAN RX0 和 USB 中断是共享向量号的如果你同时用 USB 和 CAN要小心处理不过入门项目一般不会同时用。3.3 过滤器配置的关键细节CAN 过滤器是另一个容易踩坑的地方。STM32F103 有14 组过滤器可以配置成掩码模式或列表模式。入门阶段最简单的做法是全部接收。具体配置Filter ModeMaskFilter Scale32-bitFilter ID0Filter Mask0Filter FIFO AssignmentFIFO 0Filter ActivationEnableFilter ID 和 Mask 都设成 0意味着所有位都不关心任何标识符的消息都能通过。这样调试的时候不会因为过滤器把消息挡掉而找不到问题。等通信跑通了再根据实际需求收紧过滤规则。注意CubeMX 生成的过滤器配置代码有时候会有问题特别是 Filter Activation 这一项如果生成的代码里没有正确使能过滤器收不到数据是很常见的。建议生成代码后手动检查一下MX_CAN_Init函数里的过滤器配置部分确认HAL_CAN_ConfigFilter被正确调用。4. 代码实现从初始化到收发全流程4.1 CAN 初始化与启动CubeMX 生成的初始化代码基本可用但启动 CAN 的步骤需要自己加。在main函数里MX_CAN_Init()之后要调用HAL_CAN_Start()启动 CAN 外设然后使能接收中断HAL_CAN_Start(hcan); HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);这两句缺一不可。HAL_CAN_Start让 CAN 进入工作状态HAL_CAN_ActivateNotification打开接收中断。很多人只调了第一个结果中断进不来以为是硬件问题其实是中断没使能。发送部分我封装了一个简单的函数uint8_t CAN_SendMsg(uint32_t id, uint8_t *data, uint8_t len) { CAN_TxHeaderTypeDef txHeader; uint32_t txMailbox; txHeader.StdId id; txHeader.ExtId 0; txHeader.IDE CAN_ID_STD; txHeader.RTR CAN_RTR_DATA; txHeader.DLC len; txHeader.TransmitGlobalTime DISABLE; if (HAL_CAN_AddTxMessage(hcan, txHeader, data, txMailbox) ! HAL_OK) { return 1; } return 0; }这里用的是标准帧StdId11 位标识符。如果你要用扩展帧把 IDE 改成CAN_ID_EXT然后填 ExtId。DLC 是数据长度CAN 最多 8 字节填超过 8 会被截断。4.2 接收中断回调处理接收中断的回调函数是HAL_CAN_RxFifo0MsgPendingCallback在这个函数里读取消息void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { CAN_RxHeaderTypeDef rxHeader; uint8_t rxData[8]; if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, rxHeader, rxData) HAL_OK) { // 处理接收到的数据 // rxHeader.StdId 是标识符 // rxHeader.DLC 是数据长度 // rxData 是数据内容 } }这个回调函数是弱定义的你在main.c里重新实现它就行。注意不要在回调里做太耗时的操作比如打印大量日志否则可能影响后续消息的接收。如果需要处理复杂逻辑可以在回调里置个标志位在主循环里处理。4.3 双机通信测试代码两块板子烧同一套代码通过标识符区分收发。比如板子 A 发标识符 0x100板子 B 发标识符 0x200。每块板子收到对方的消息后可以通过串口打印出来验证。主循环里这样写uint8_t txData[8] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; while (1) { CAN_SendMsg(0x100, txData, 8); HAL_Delay(500); }接收回调里把收到的数据通过串口发出来printf(RX ID:0x%X Data:, rxHeader.StdId); for (int i 0; i rxHeader.DLC; i) { printf( %02X, rxData[i]); } printf(\r\n);这样两块板子都上电后串口应该能看到对方发来的数据。如果只有一边能收到或者都收不到就进入下一节的排查流程。5. 调试排查CAN 通信不通的常见原因5.1 硬件层排查速查表CAN 不通八成是硬件问题。我整理了一个排查顺序按这个走基本能定位排查项正常状态异常处理CAN_H 与 CAN_L 间电阻约 60Ω检查两个 120Ω 终端电阻是否都接TJA1050 VCC 电压5V检查供电3.3V 可能驱动不足STM32 CAN_TX 空闲电平高电平检查 PA12 配置是否被其他功能占用CAN_H 对地电压约 2.5V检查收发器是否正常工作CAN_L 对地电压约 2.5V同上共地线已连接补上 GND 连接其中CAN_H 和 CAN_L 对地电压约 2.5V是一个很好的判断依据。如果上电后量出来是 0V 或 5V说明收发器没工作或者接线错了。正常空闲状态下CAN_H 和 CAN_L 都是 2.5V 左右差分电压为 0。5.2 软件层常见问题硬件没问题的话软件层有几个高频问题第一波特率不匹配。两块板子的 Prescaler、BS1、BS2 必须完全一致差一点都不行。我遇到过一块板子配了 500k、另一块配了 250k怎么都通不上查了半天才发现。第二过滤器没使能。CubeMX 生成的代码有时候过滤器配置不完整收不到数据。检查HAL_CAN_ConfigFilter是否被调用FilterActivation 是否为 ENABLE。第三中断没开。HAL_CAN_ActivateNotification没调用或者 NVIC 里没使能 CAN RX0 中断都会导致收不到数据。第四发送邮箱满。如果发送频率太高邮箱满了HAL_CAN_AddTxMessage会返回 HAL_ERROR。可以加个重试或者检查HAL_CAN_GetTxMailboxesFreeLevel。避坑技巧调试 CAN 的时候建议先用回环模式Loopback验证软件配置。把 CAN 设成 Loopback自己发自己收如果能收到说明软件没问题问题在硬件或接线如果收不到说明软件配置有错。这一步能快速缩小排查范围。5.3 示波器与逻辑分析仪的使用如果有示波器直接看 CAN_H 和 CAN_L 的波形是最直观的。正常通信时波形是一串差分脉冲CAN_H 在 2.5V 到 3.5V 之间跳CAN_L 在 2.5V 到 1.5V 之间跳。如果波形幅度不对或者没有波形说明收发器或接线有问题。逻辑分析仪的话可以接在 STM32 的 CAN_TX 引脚上看有没有数据发出。如果有发出但总线上没有问题在 TJA1050如果 CAN_TX 上就没有问题在 STM32 配置。我个人的习惯是先量电阻再看电压最后看波形。这个顺序从简单到复杂能最快定位问题。6. 进阶扩展与实战经验分享6.1 多节点通信与标识符规划双机通信跑通之后下一步自然是扩展到多节点。CAN 总线理论上可以挂很多节点但实际项目中标识符的规划很重要。建议按功能划分标识符范围比如 0x100-0x1FF 用于电机控制0x200-0x2FF 用于传感器数据这样后期维护清晰。多节点的时候终端电阻仍然只在总线两端各接一个中间节点不要接。如果节点多、距离长还要考虑总线电容和线缆阻抗必要的时候降低波特率。6.2 错误处理与总线恢复实际项目中CAN 总线可能因为干扰进入错误状态。STM32 的 CAN 外设有错误计数器和状态机可以通过HAL_CAN_GetError获取错误码。如果进入 Bus-Off 状态需要调用HAL_CAN_Start重新启动。我一般会在主循环里定期检查 CAN 状态如果发现异常就尝试恢复。这个逻辑虽然简单但在工业现场非常有用能避免因为一次干扰导致整个系统瘫痪。6.3 我踩过的几个坑第一个坑是终端电阻只接了一个。当时两块板子一块模块自带电阻另一块没有我以为自带的那个就够了结果通信时好时坏。后来补上第二个电阻立马稳定。第二个坑是波特率算错。CubeMX 里 Prescaler 填了 5BS1 填了 15BS2 填了 2算下来是 36M / (5 × 18) 400kbps不是标准的 500k。两块板子都这么配倒是能通但和标准设备对接就不行了。所以配完一定要用公式验算一遍。第三个坑是过滤器配置被 CubeMX 覆盖。重新生成代码后手动改的过滤器配置被覆盖了导致收不到数据。后来我养成了习惯生成代码后先检查过滤器部分确认无误再编译。6.4 后续可以怎么扩展这套双机通信跑通后可以往几个方向扩展一是加CAN 报文解析层把原始数据映射成有意义的信号二是做CAN 网关在 CAN 和串口/以太网之间转发数据三是接入CANopen 或 J1939 协议栈做标准化的设备通信。这些都需要在底层通信稳定的基础上做所以先把双机通信调通后面的路就好走了。我个人在实际操作中的体会是CAN 这东西硬件占七分软件占三分。接线对了、电阻加了、波特率匹配了基本就能通。剩下的就是代码细节和调试耐心。希望这篇拆解能帮你少走点弯路把 CAN 通信顺利跑起来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑