STM32双机CAN通信实战:TJA1050硬件设计到软件调试全解析
1. 为什么双机通信我选了CAN而不是UART——场景选择与总体方案先说一个最常见的场景你在做一个小项目A板和B板之间要互通数据距离可能有个十来米现场还有电机、变频器这类干扰源。多数人第一反应是UART一对TX/RX接起来就能跑确实省事。但UART有几个地方真顶不住一是只能点对点想再加第三个节点就要改硬件二是电平是单端TTL理论上抗干扰能力弱三是UART协议层没有明确的错误处理机制帧丢了就丢了。我早期用UART做双机通信调试时开着电机一启动从机那边就开始乱码后来换CAN之后这些问题基本绝迹了。CAN和UART的根本差异在于它把通信当成一个“总线事件”而不是单纯的“字节流”。CAN支持多主机、多节点任何一个节点都能主动发帧靠11位或29位标识符决定优先级仲裁机制保证同一时刻只有一帧在总线上。而且CAN的电平不是单端而是CANH和CANL两条线之间的差分电压显性位和隐性位由差分电压决定。差分传输天然对共模干扰免疫这就解释了为什么CAN只需要双绞线就能在有变频器的车间里稳定跑到几十米。这个项目用的主控是STM32F103它内置bxCAN外设支持CAN 2.0B标准帧和扩展帧都支持收发有3个发送邮箱和2个接收FIFO还带了硬件过滤功能。外围收发器选了TJA1050这是入门最经典的选择兼容性好、速度可以到1Mbps、出来很久资料也多。整套方案的拓扑是两片STM32F103分别外挂一个TJA1050两个节点之间用双绞线连接两端各放一个120欧终端电阻。这个拓扑结构极其典型CAN规范里最推荐的也就是这种低速短距离的双节点总线。硬件连接说穿了就是一个对应关系。STM32F103的CAN_TX引脚PA12或者PB9接到TJA1050的TXDSTM32的CAN_RXPA11或者PB8接到TJA1050的RXD。TJA1050的CANH和CANL分别接总线双绞线的两条线。注意这些引脚不是随便选的PA11/PA12在F103上是CAN外设的默认映射引脚要复用功能开启后才能当成CAN引脚用否则PA11/PA12默认是USB相关引脚这点容易看资料时忽略。为了把引脚切换到CAN复用功能代码里要写GPIO_PinRemapConfig或者直接用GPIO_PinAFConfig配置AFIO映射具体用哪组配置得先确认你的板上这两个引脚是否有外部器件占用。把这个基础拓扑先讲清楚后面所有软件的配置都围绕这套硬件展开。如果硬件接错了后面无论代码怎么调都不会通这是所有CAN调试里最基础也最关键的一步。2. 硬件电路TJA1050外围电路与关键引脚详解2.1 TJA1050引脚功能与STM32的对应关系TJA1050是SOP8封装的小芯片一共8个引脚真正参与信号通路的只有5个其余是电源和地。引脚功能如下表引脚号名称作用与STM32的连接1TXD发送数据输入接STM32的CAN_TXPA122GND地接AGND3VCC电源5V或3.3V接5V或3.3V4RXD接收数据输出接STM32的CAN_RXPA115VREF参考电压输出1/2 VCC可悬空或接电容到地6CANLCAN低电平线接总线CANL7CANHCAN高电平线接总线CANH8S斜率控制/待机控制通常接GND高速模式引脚接法里有几个细节值得注意。第8脚S在TJA1050的早期版本叫RS是斜率控制引脚直接接地时工作在高速模式收发器的输出级是硬切换适合最高速率1Mbps。如果想控制上升沿斜率以降低电磁辐射可以在S脚和地之间串联一个电阻阻值越大斜率越缓但速度也会受限。对于1Mbps以内的应用绝大多数人直接接地就是省事又稳定的选法。VREF是内部1/2VCC的参考输出一般用来给分压或做比较器参考正常通信时悬空或加一个0.1uF电容到地都可以不需要接入STM32的ADC也别接什么负载。供电方面要特别提一句TJA1050的VCC电压范围一般是4.75V到5.25V不要看它数据手册上标了个宽范围就图省事直接接3.3V。大多数型号在3.3V下工作不可靠总线电平参考电压都是基于5V计算的。如果板子只有3.3V那就上TJA1051T/3这种带3.3V型号的收发器别硬塞5V版。STM32F103的IO虽然是3.3V但它的CAN引脚是5V容忍的前提是配置成开漏内部结构做了保护因此PA11/PA12可以直接接5V供电的TJA1050不需要额外的电平转换电路。这也是为什么F103TJA1050这对组合这么流行接口电平天然兼容省掉了一堆麻烦。2.2 终端电阻为什么两端各需要一个120欧双机通信最容易犯的一个错误就是认为两根线接上了就能通终端电阻可加可不加。CAN协议对总线终端电阻要求是匹配线缆特征阻抗的标准双绞线特征阻抗大约是120欧标准做法是总线的两端各放一个120欧电阻。如果少了终端电阻信号会在总线末端反射表现出来就是发送方能看到波形但接收方采到的位可能是错的尤其在总线较长或者波特率高于250kbps的时候。用万用表量总线电阻是一个很有效的诊断手段。在总线空闲状态从任意一个节点量CANH和CANL之间的电阻如果两头都有终端电阻你会量到大约60欧如果只有一头有量到120欧如果两头都没有量到的是无穷大开路或者收发器内部某些极小的漏电流。很多不熟悉的人第一次量到60欧会以为短路了其实60欧才是两个120欧并联的正常结果。我调试时有一个习惯每接一个节点下来先上电不发数据量一下CANH和CANL之间阻值60欧说明总线结构正常可以直接进入软件调试。注意终端电阻是放在物理总线的两端不是放在每个节点上。双机通信就是两个节点刚好两头各放一个。如果后面加了第三个节点要保证还是只有两个终端电阻分别在最远的两个端点千万不要每个节点都装上120欧那总线负载会太大电平都拉不动。2.3 PCB布线与抗干扰的几句实话硬件上还有一个容易被忽视的点CAN_TX和CAN_RX到TJA1050的走线要短TJA1050的CANH/CANL到接线端子也要短尽量避免用过长的飞线。这种弱信号回路虽然不像高频射频那么严格但CAN总线是在差模干扰环境下工作的收发器引脚附近的寄生电容和走线电感会直接影响信号的跳变沿。在人多问“要不要隔离”这个问题上我的实际态度是短路距离、同电源系统内可以不隔离一旦两个节点之间电源不共地、或者总线要跨设备走很远的线隔离是必须考虑的。所谓隔离就是在STM32的CAN引脚和TJA1050之间加一个带隔离的CAN收发器常见方案是ADM3053或者给TJA1050加一个数字隔离器ISO1050这类模块。不过双机同板供电的实验场景先别上隔离把基本通信调通最重要隔离和地环路问题是下一阶段的事。3. 初始化配置CAN外设时钟、波特率计算与过滤器设置3.1 时钟树与CAN工作时钟来源STM32F103的CAN外设挂在APB1总线上。这里有个容易算错的点F103最高主频72MHzAPB1最大只到36MHzCAN外设的时钟源就是APB1的36MHz。如果你配置系统时钟时没注意APB1分频系数CAN波特率会整体偏移表现就是双机都收不到对方的数据但自己发自己收是好的。我见过不少项目最后查出来是APB1时钟配错波特率实际不是你想的那个值。用标准外设库初始化时CAN外设时钟使能写作RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);注意GPIOA和AFIO时钟是APB2的因为复用功能配置依赖AFIO时钟。如果用的引脚是PB8/PB9部分104脚以上芯片支持时钟配置逻辑一样只是GPIO时钟使能换成GPIOB这个坑常在项目移植时出现原板用PA11/PA12换块板子改PB8/PB9忘了开AFIO时钟结果CAN初始化返回错误。3.2 波特率参数计算实例CAN的波特率不是像UART那样直接写一个数字而是通过位时间Bit Timing寄存器换算。每个位时间分成几个时间量子Time Quantumtq一个位由同步段、传播段、相位缓冲段1、相位缓冲段2组成采样点位于缓冲段1和缓冲段2的交界。STM32的BS1和BS2寄存器字段就是配置传播段相位缓冲段1合起来的时间和相位缓冲段2的时间。波特率公式波特率 时钟频率 / (预分频值 * (1 BS1 BS2))其中BS1和BS2的单位是tq。以36MHz时钟、目标1Mbps为例一个位时间总共需要36个tq。可选组合是预分频1BS18tqBS27tq同步段固定1tq总位时间就是18716tq但这只是36MHz/162.25Mbps显然不对。如果希望精确1Mbps需要总位时间为36tq那可以预分频1、BS117tq、BS217tq合计1171735tq又不对。真正能除尽的组合通常是预分频1、BS113、BS222得到1132236tq。或者用预分频2、BS18、BS28得到18817tq再用18MHz/17近1.058Mbps。实际项目你不需要精确到这种极端程度一般选能让波特率尽量接近目标值、采样点尽量在80%左右的组合即可。最常用的配置是APB136MHz预分频4BS112tqBS25tq总位时间112518tq波特率36M/4/18500kbps。500kbps是CAN总线最常见的工业速率采样点在(112)/1872.2%附近。代码配置如下CAN_InitTypeDef CAN_InitStruct; CAN_InitStruct.CAN_SJW CAN_SJW_1tq; CAN_InitStruct.CAN_BS1 CAN_BS1_12tq; CAN_InitStruct.CAN_BS2 CAN_BS2_5tq; CAN_InitStruct.CAN_Prescaler 4; CAN_InitStruct.CAN_Mode CAN_Mode_Normal; CAN_InitStruct.CAN_SJW CAN_SJW_1tq; CAN_Init(CAN1, CAN_InitStruct);用500kbps还有一个好处双机通信距离几十米内完全没有压力总线负载不高对双绞线要求也低。如果项目里选250kbps波特率配置是预分频8、BS112tq、BS25tq总位时间112518tq36MHz/8/18250kbps方案一脉相承只是预分频翻倍。3.3 过滤器配置掩码模式与列表模式STM32F103的CAN有个硬件过滤器作用是在硬件层面决定接收哪些帧不匹配的直接丢弃CPU根本不被打扰。这个功能被很多人浪费了全部初始化为接收所有帧然后软件里用ID判断。不是说不行但体验过硬件过滤之后你就知道把过滤交给硬件接收中断频率降低CPU负载和代码复杂度都下降一个量级。过滤器有两种工作模式掩码模式和列表模式。掩码模式更像通配符检查ID的某些位其他位任意列表模式则要求ID完全匹配。双机通信最简单的方式是A节点发送ID为0x101的标准帧B节点过滤器设置只接收ID0x101的帧用列表模式最直接。如果后面要做一对多B节点想接收0x101和0x201两个ID列表模式能放两个CAN_FilterInitTypeDef CAN_FilterInitStruct; CAN_FilterInitStruct.CAN_FilterNumber 0; CAN_FilterInitStruct.CAN_FilterMode CAN_FilterMode_IdList; CAN_FilterInitStruct.CAN_FilterScale CAN_FilterScale_32bit; CAN_FilterInitStruct.CAN_FilterIdHigh (0x101 5) 8; CAN_FilterInitStruct.CAN_FilterIdLow (0x101 5) | CAN_ID_STD; CAN_FilterInitStruct.CAN_FilterMaskIdHigh (0x201 5) 8; CAN_FilterInitStruct.CAN_FilterMaskIdLow (0x201 5) | CAN_ID_STD; CAN_FilterInitStruct.CAN_FilterFIFOAssignment CAN_FIFO0; CAN_FilterInitStruct.CAN_FilterActivation ENABLE;这段配置容易出错的地方有两个。第一32位过滤器模式下FilterIdHigh/Low和FilterMaskIdHigh/Low的组合方式不是简单的ID填入标准11位ID要左移5位放到ID的高位部分同时最低位放上CAN_ID_STD表示标准帧。第二如果直接照抄网上的例子但选了IdMask模式掩码的每一位含义是“1必须匹配0忽略”全部填0xFF就等同不过滤。3.4 初始化函数代码示例汇总一个比较完整的初始化流程按这个顺序执行基本不会漏void CAN1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; CAN_InitTypeDef CAN_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); GPIO_InitStruct.GPIO_Pin GPIO_Pin_11; GPIO_InitStruct.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOA, GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin GPIO_Pin_12; GPIO_InitStruct.GPIO_Mode GPIO_Mode_AF_PP; GPIO_Init(GPIOA, GPIO_InitStruct); CAN_DeInit(CAN1); CAN_StructInit(CAN_InitStruct); CAN_InitStruct.CAN_TTCM DISABLE; CAN_InitStruct.CAN_ABOM ENABLE; CAN_InitStruct.CAN_AWUM DISABLE; CAN_InitStruct.CAN_NART DISABLE; CAN_InitStruct.CAN_RFLM DISABLE; CAN_InitStruct.CAN_TXFP DISABLE; CAN_InitStruct.CAN_Mode CAN_Mode_Normal; CAN_InitStruct.CAN_SJW CAN_SJW_1tq; CAN_InitStruct.CAN_BS1 CAN_BS1_12tq; CAN_InitStruct.CAN_BS2 CAN_BS2_5tq; CAN_InitStruct.CAN_Prescaler 4; CAN_Init(CAN1, CAN_InitStruct); }发送和接收中断的优先级、FIFO中断使能可以再细化但核心是这个骨架。ABOM自动总线恢复建议默认开启这个后面排障时能省很多事总线错误进入Bus Off之后外设会自动做恢复扫描不需要应用层介入。4. 数据收发实现发送函数、中断接收与环形缓冲4.1 发送流程拆解邮箱、报文结构、发送模式CAN发送不是把字节丢给外设就完事要组织一个CanTxMsg结构体包括ID、数据长度、数据内容、帧类型。F103有3个发送邮箱每个邮箱可以独立排队一帧数据发送完成后对应邮箱释放。用库函数CAN_Transmit()提交报文返回值是使用的邮箱号如果返回CAN_TxStatus_NoMailBox则说明3个邮箱都满了需要稍后重试。双机通信的一个高频需求是周期性发送状态数据比如A板每10ms上报一次传感器数值。这种场景要一次性把报文填好发出去不要在while循环里频繁调用初始化和过滤器配置那是非常差的做法。标准写法CanTxMsg TxMessage; TxMessage.StdId 0x101; TxMessage.ExtId 0; TxMessage.IDE CAN_Id_Standard; TxMessage.RTR CAN_RTR_Data; TxMessage.DLC 8; TxMessage.Data[0] (uint8_t)(sensor_value 8); TxMessage.Data[1] (uint8_t)(sensor_value 0xFF); // 其他数据字节按需填充 uint8_t mailbox CAN_Transmit(CAN1, TxMessage); if (mailbox CAN_TxStatus_NoMailBox) { // 邮箱满等待或者丢弃 }如果对实时性要求高可以开发送邮箱空中断CAN_IT_TransmitMailboxEmpty在中断里补发待发送队列中的帧但这种双机低频传输场景不需要。一个实用经验是发送前调用一次CAN_TransmitStatus()检查邮箱状态或者直接看返回的mailbox编号比盲目循环重试更靠谱。注意一个细节CAN_Transmit()只是把报文拷贝到邮箱寄存器并请求发送函数本身不会阻塞等待发送完成。如果你紧接着立刻改TxMessage内容再发下一帧只要邮箱还没空内容就不会立即被覆盖但建议还是等发送状态确认后再复用结构体尤其在DLC比较长、波特率比较低的场景。4.2 中断接收与回调设计接收端用中断方式最省CPU。CAN外设接收到一帧有效报文后会把报文放入FIFO0或FIFO1同时触发接收中断。中断服务函数里读CAN_Receive()取出报文然后交给应用层处理。void USB_LP_CAN1_RX0_IRQHandler(void) { CanRxMsg RxMessage; if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) ! RESET) { CAN_Receive(CAN1, CAN_FIFO0, RxMessage); // 这里很关键不要立即做复杂运算先拷贝到缓冲区 CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); ProcessCanFrame(RxMessage); } }有个常见问题CAN接收中断函数为什么会报错因为F103的CAN1接收中断在中断向量表里和USB共用同一个入口USB_LP_CAN1_RX0_IRQHandler如果工程里同时启用了USB就会冲突。双机通信没用USB的话不会撞但如果是从带USB的工程改过来的中断向量表里那个名字要换成自己工程的启动文件版本。很多调试者说“CAN初始化成功但接收中断没触发”排查半天结果是中断向量表没有把IRQHandler函数名和启动文件对上。4.3 环形缓冲区不丢数据的接收方案如果应用层处理比较慢接收中断里只拷贝到临时变量然后马上返回是不够的有可能连续来3帧、4帧时后到的帧覆盖先到的。CAN的FIFO深度是3层对双机低频通信其实够用但为了安全起见我习惯在接收中断里把数据压进一个环形缓冲区主循环再慢慢取。一个简单的环形缓冲区实现如下#define CAN_RX_BUF_SIZE 32 typedef struct { CanRxMsg buf[CAN_RX_BUF_SIZE]; uint8_t head; uint8_t tail; } CanRxRingBuf; void CanRxBuf_Push(CanRxMsg *msg) { uint8_t next (buf-head 1) % CAN_RX_BUF_SIZE; if (next ! buf-tail) { buf-buf[buf-head] *msg; buf-head next; } } uint8_t CanRxBuf_Pop(CanRxMsg *msg) { if (buf-tail buf-head) return 0; *msg buf-buf[buf-tail]; buf-tail (buf-tail 1) % CAN_RX_BUF_SIZE; return 1; }注意环形缓冲区判满和判空都是靠head和tail是否相等区分但这个简单的写法里缓冲区最多存CAN_RX_BUF_SIZE - 1帧不会溢出。接收中断只做Push主循环做Pop两处用volatile修饰head和tail单生产者单消费者模型下不需要加锁。这是个很经典的无锁设计简单可靠双机通信场景下完全够用。主循环里取数据后就可以通过ID区分执行不同的逻辑。比如0x101帧代表A板发来的控制指令0x201帧代表B板回传的状态。ID就是协议层的路由键比UART那种自己加帧头帧尾的解析方式清爽很多。5. 双机通信实测回环测试、主从握手与数据校验5.1 回环测试排除硬件问题的第一步双机连不上时第一步永远是回环测试。把CAN_Init结构体里的CAN_Mode改成CAN_Mode_LoopBackCAN外设会在内部把发送的数据直接回环到接收FIFO不经过外部引脚和收发器。你发一帧0x101马上在接收中断里能收到同样的0x101这说明外设收发的底层通路是好的问题只在硬件连线和收发器。回环模式下发送一帧、接收一帧如果通了基本可以判断STM32F103的CAN控制器配置没问题。然后切回CAN_Mode_Normal如果自发自收都收不到问题集中在TJA1050电路或者总线上。用手摸着TJA1050温度异常、CANH/CANL电压不正常多半是芯片接反、电源不对或者损坏。回环测试建议先只操作一块板子不要两块板同时发数据。这样能把变量控制到最小一块板上电回环发接收确认通过后再接入第二块板。很多双机调不通的案例最后发现是两块板的波特率微妙不一致比如一块是500kbps另一块因为某个初始化顺序问题实际跑成了250kbps回环测试时各自都能收到自己的回环帧但彼此收不到。这时候把两块板的波特率配置打印出来逐字节对比问题立刻暴露。5.2 主从握手与心跳包双机通信不只是互发数据通信双方需要建立“对话关系”。我建议在任何CAN双机项目里都设计一个简单的握手流程上电后A板发一帧0x001握手请求B板收到后在100ms内回一帧0x002应答A板收到应答后进入正常数据交换状态否则进入重试流程重试3次仍无应答就报通信故障。握手成功之后还要有心跳包。所谓心跳包就是A板周期性发一帧包含递增计数器的0x003帧B板只要一段时间没收到心跳就能判定通信链路中断可以触发本地报警或者进入安全状态。这个机制非常朴素但非常有效。在CAN协议栈里没有TCP那种连接状态所有传输本质都是无连接的报文应用层必须自己做“连接监测”。心跳包的周期选择要看应用复杂度。控制类应用可以10ms发一次数据采集类可以100ms发一次。双机调试时我习惯把心跳周期设成100msLED翻转一次肉眼能看到链路活着。这个方法比任何调试器都直观推荐一试。5.3 数据校验策略DLC、CRC、序列号三板斧CAN帧本身带CRC硬件校验只要CAN控制器认为接收通过传输错误基本够低。但应用层仍然建议做一重自己的校验主要防的是A板发送缓冲区里数据还没更新完、被B板读到旧值或者某次发送时数据源自身的逻辑错误。双机通信常用的是这三个层次第一DLC要约定好。不要发1字节接收方却按8字节解析。双机协议里最蠢的bug就是长度不匹配建议所有帧DLC固定为8不够的补0这样解析代码最简单。第二关键数据可以加一个累加和字节。把前7个字节加起来放第8字节接收方同样累加校验不同就丢弃。第三加上一个序列号字节每发一帧加1接收方判断是否连续跳号说明丢帧了可以触发重传。要不要上CRC16或者CRC8看数据重要性。CAN本身硬件有CRC应用层再加CRC是为了端到端的完整性如果是一般状态上报累加和足够。如果是控制帧比如B板收到A板的角度指令这个指令错一位可能导致机械臂乱动那就必须上CRC8校验序列号也必须有。5.4 波特率不匹配时的故障现象这是一个值得单独讲的实测场景。我将A板配成500kbpsB板配成250kbps观察总线现象A板发送时B板的CAN控制器几乎收不到有效帧触发的是错误中断或者总线错误计数器飙升。CAN是一个对位定时非常敏感的协议波特率不匹配时接收方采到的每一位都可能落在错误的采样点几乎会认为总线上全是错误帧。回环模式下两板各自都正常这最容易让人误判。所以双机联调时要先保证两块板子用同一份CAN_InitStruct配置或者打印各自寄存器的实际值CAN_BTR寄存器的预分频、BS1、BS2一对比就知道谁在“挂羊头卖狗肉”。曾经遇到过一块板子初始化里多写了一行CAN_InitStruct.CAN_Prescaler 8覆盖了前面的4波特率直接变250k改掉后瞬间通了。试过用示波器去量CANH-CANL的差分波形来确认波特率——这个方法很可靠一个位时间内的显性/隐性跳变宽度就是波特率的倒数。500kbps的位宽是2μs250kbps是4μs波形上一眼就能分辨。不过没有示波器也别慌正常运行不发数据时总线空闲电压在2.5V左右CANH和CANL各自2.5V差分约0V发数据时差分在2V左右摆动这个也可以用万用表毛估判断收发器是否被激活。6. 调试心得波形观察、总线错误处理与常见坑6.1 波形排查如何判断总线状态是否健康练好一招用示波器看CANH和CANL的差分波形能解决掉双机CAN调试里80%的问题。TJA1050输出的是差分电平直接看单端波形意义不大最好用示波器的A-B数学通道或者差分探头测量CANH-CANL。正常情况下总线空闲是隐性电平CANH和CANL电压重合在2.5V左右。发送显性位时CANH被拉高到3.5V左右CANL被拉低到1.5V左右差分2V。这个“2V跳变”在波形上非常显眼。如果测量时发现CANH和CANL的共模电压整体漂移比如两个都变成1V或者4V那不是差分信号的问题可能是总线对地短路或者TJA1050的5V电源异常。如果显性位差分幅度只有1V不到大概率是终端电阻过多总线负载太重。如果波形上有严重的振铃和过冲说明终端电阻缺失或者双绞线绞距异常信号反射明显。波特率也能直接从波形判断用示波器光标量一个位时间内连续两个跳变沿的总宽度就是位时间。500kbps的位时间2μs250kbps是4μs。这个方法不需要任何软件参与只要一个节点在周期发数据就能看到稳定的波形用来确认“板上实际配置的波特率”非常快。6.2 错误处理机制与恢复策略CAN外设有一整套错误状态机通过错误计数器TEC和REC记录收发错误次数。错误状态分三类主动错误、被动错误、总线关闭。TEC或REC超过127进入被动错误TEC超过255进入总线关闭。进入总线关闭后CAN控制器不再参与总线活动对外表现为“收发都无效”如果没开ABOM控制器会一直卡在这个状态直到软件复位外设。实际项目里我强烈建议开启CAN_ABOM自动总线恢复这样总线关闭后外设会自动等待128个连续隐性位然后重新进入主动错误状态。双机通信场景里如果其中一块板因为临时故障进入Bus Off另一块正常发帧ABOM开启的板子会自动恢复同步不需要人工干预。应用层还需要读取错误状态做可视化。库函数CAN_GetFlagStatus(CAN1, CAN_FLAG_BO)可以检测总线关闭标志。调试时我习惯在串口打印TEC/REC寄存器CAN_GetITStatus里CAN_IT_ERR和CAN_GetErrorCode()配合使用能区分是位填充错误、格式错误、ACK错误还是位显性错误。其中CAN_ErrorCode_ACKErr最常见发给远端节点时如果远端没有正常接收成功就会报ACK错误。这个错误在只有一个节点的总线上发送时一定会出现因为没有其他节点接收。所以“自发自收模式之外单节点发帧报ACK错”别慌那是正常的。6.3 新手最容易踩的几个坑CAN调试里有一个特别容易踩的坑RS脚接法。TJA1050的S/RS脚如果不接有些型号默认是悬空此时内部上拉可能使收发器进入待机模式表现为总线静默、引脚电平对但发不出显性位。标准做法是直接接地这个一定要在硬件设计时定死别留悬空由“用户自行处理”。第二个坑是电源退耦。TJA1050在发送显性位时瞬间电流比较大VCC引脚需要有0.1uF陶瓷电容尽量靠近芯片放置否则长导线的电感会在电流突变时拉低VCC导致TJA1050内部逻辑异常现象是通信时有时无。这种问题非常难查因为万用表量电压都正常只有发送瞬间示波器能看到VCC上有毛刺。第三个坑是终端电阻的“伪短路”现象。有些新手在原理图里把终端电阻画好打板时却忘了贴或者用了排阻把120欧和其他电阻混在一起导致总阻值不是60欧。一定要用万用表实测总线端CANH-CANL的直流电阻不要相信原理图。第四是CAN_TX和CAN_RX引脚死锁。如果PA11/PA12被复用成USB时产生的枚举过程会干扰CAN信号在USB和CAN同时用一块F103时两外设共用同一对引脚严格说CAN也映射在这两个脚代码里先初始化USB后初始化CANUSB驱动的代码可能悄悄重配了GPIO模式把CAN引脚拉回输入模式。双机通信不涉及USB但如果你参考的工程模板里有USB初始化记得删掉。收个尾一点个人体会双机CAN通信这个项目做完我最深刻的体会是CAN看似比UART复杂但它的“复杂”都是规范化的只要你按部就班把硬件拓扑搭对、把位时序算清、把过滤器配准剩下的调试反而比UART简洁因为错误状态在控制器内都有计数器不太会出现“莫名其妙收错字节”这种玄学。回环测试、心跳包、ABOM这一套下来通信的可靠性就有了保障。建议第一次接触CAN的同学不要直接去啃参考手册里的寄存器位定义先从回环跑通开始再切双机自己动手把波形量出来几个月后回头看你会感谢当初那个没有放弃的自己。最后分享一个我实践中挺管用的小技巧在调试阶段把CAN接收到的原始帧ID和数据字节通过串口打印出来串口波特率固定115200两边打印出来人工对比比看调试器的变量窗口直观太多。调通了之后再删掉打印对性能影响微乎其微。这个方案我一直用到所有CAN项目里省了不少事。

相关新闻

数据库技术选型决策记录(ADR)实战:关系型、文档型与事件型数据库的完整评估与决策

数据库技术选型决策记录(ADR)实战:关系型、文档型与事件型数据库的完整评估与决策

【免费下载链接】architecture-decision-record Architecture decision record (ADR) examples for software planning, IT leadership, and template documentation 项目地址: https://gitcode.com/gh_mirrors/ar/architecture-decision-record 点击查看 免费下载 …

2026/10/13 12:40:44 阅读更多 →
8款硬核MCP服务器实战:让AI告别复制粘贴

8款硬核MCP服务器实战:让AI告别复制粘贴

我先从一个特别没面子但特别真实的场景说起。 上周我要做一份跨项目的数据汇总报告,具体流程是这样的:先从A系统的后台导出两个月的订单记录,再从B系统拉出成本表,打开电子表格软件把两边的格式统一,然后把关键数字一…

2026/10/13 12:40:44 阅读更多 →
Natron 中 G‘MIC Layers to Tiles 节点完全指南:多图层图像拆分为网格瓦片

Natron 中 G‘MIC Layers to Tiles 节点完全指南:多图层图像拆分为网格瓦片

音视频视频处理图形学桌面应用 【免费下载链接】Natron Open-source video compositing software. Node-graph based. Similar in functionalities to Adobe After Effects and Nuke by The Foundry. 项目地址: https://gitcode.com/gh_mirrors/na/Natron 点击查看 …

2026/10/13 12:39:44 阅读更多 →

最新新闻

个人主页置顶帖与投稿汇总实操指南:打造内容导航体系

个人主页置顶帖与投稿汇总实操指南:打造内容导航体系

1. 先搞清楚:置顶帖和投稿汇总到底在解决什么问题1.1 置顶帖是主页的第一屏,也是你的数字名片我见过太多创作者陷入同一个困境:内容发了几百条,主页却像一个乱糟糟的仓库,新访客点进来根本不知道你擅长什么、写过哪些代…

2026/10/13 13:14:25 阅读更多 →
RDV4GUI230228.rar 解压即用实战:依赖配置与排错指南

RDV4GUI230228.rar 解压即用实战:依赖配置与排错指南

简介:RDV4GUI230228.rar 是一份面向 Proxmark3(PM3)设备使用者的图形界面工具整合包,适合需要刷写固件、管理驱动与执行自动化脚本的 RFID 研究及安全测试人员。压缩包共 643 个文件,约 149.27MB,以 bz2 固…

2026/10/13 13:14:25 阅读更多 →
微信小程序图书管理系统源码解析:从跑通到二次开发

微信小程序图书管理系统源码解析:从跑通到二次开发

简介:这份资源是面向计算机相关专业学生与Java初学者的微信小程序图书管理系统完整项目包,可作为毕业设计、课程设计或项目立项的参考方案。项目采用SSM框架(SpringMVCSpringMyBatis)搭建服务端接口,客户端基于微信开发…

2026/10/13 13:14:25 阅读更多 →
VS2010绿色精简版部署指南:Win10/11上快速编译老MFC工程

VS2010绿色精简版部署指南:Win10/11上快速编译老MFC工程

简介:VS2010绿色精简版是一份面向初学者、临时项目开发或系统资源有限电脑用户的轻量级开发环境。它移除了大量非核心组件,安装与运行时无需手动配置环境变量,解压后即可启动Visual Studio 2010,适合日常编写调试C、C#等基础程序&…

2026/10/13 13:14:25 阅读更多 →
ABAQUS在卡簧非线性有限元分析与工程优化中的应用

ABAQUS在卡簧非线性有限元分析与工程优化中的应用

1. 项目背景与工程意义卡簧(又称弹性挡圈)作为机械连接中的关键部件,广泛应用于轴承固定、轴端定位等场景。其工作原理看似简单——通过弹性变形产生的径向力实现轴向固定,但在实际工程中,卡簧的装配质量直接影响设备运…

2026/10/13 13:14:25 阅读更多 →
RabbitMQ从0到1:核心模型、生产实践与高频问题排查指南

RabbitMQ从0到1:核心模型、生产实践与高频问题排查指南

如果你手头有个项目需要在系统里传递消息,十有八九会想到RabbitMQ。但很多人装完、跑个Hello World就算“会用”了,等遇到连接失败、消息丢失、谁该消费谁不该消费、甚至admin账号没法创建virtual host时,才发现自己其实并不理解它。这篇就从…

2026/10/13 13:13:23 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/12 18:11:17 阅读更多 →