我被STM32的CAN总线折腾了一下午——结果发现是个低级错误 我被STM32的CAN总线折腾了一下午——结果发现是个低级错误上周搞一个双机通信的项目用了两块STM32F103想着走CAN总线吧毕竟抗干扰强工业场景常用。之前也用过几次CAN自认为轻车熟路结果呢翻车了。先说说场景。两块板子一块发数据一块收。CAN收发器用的TJA1050接线就是CAN_H连CAN_HCAN_L连CAN_L120Ω终端电阻一边一个。代码大概长这样CAN_FilterInitTypeDef filter; CAN_InitTypeDef can; RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE); // CAN1_RX - PB8, CAN1_TX - PB9 GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOB, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_Init(GPIOB, GPIO_InitStructure);看起来没啥问题吧然后我烧进去两块板子都跑起来。发送那边用逻辑分析仪抓CAN_TX脚波形漂亮得很标准CAN帧格式显性隐性交替ID对数据也对。但接收那边就是收不到。stm32的CAN接收标志位FMPFIFO Message Pending始终是0就像信箱里从来没人投过信一样。我调了整整一个半小时把所有能想到的地方都排查了一遍波特率配的500K两边一样过滤器设成了全通模式ID mask全0中断使能了回调函数也挂了CAN1的时钟确认是打开的都不行。// 这是滤波器的配置 filter.FilterActivation ENABLE; filter.FilterMode CAN_FilterMode_IdMask; filter.FilterScale CAN_FilterScale_32bit; filter.FilterIdHigh 0x0000; filter.FilterIdLow 0x0000; filter.FilterMaskIdHigh 0x0000; filter.FilterMaskIdLow 0x0000; filter.FilterFIFOAssignment CAN_FIFO0; filter.FilterNumber 0; CAN_FilterInit(filter);过滤器全通格式也没搞错为什么就是收不到后来我干了一件蠢事——把程序里的CAN初始化部分整个注释掉写了一个最简的轮询版本。就while循环里查CAN_GetFlagStatus别的一概不管。结果你猜怎么着还是收不到。这时候我已经开始怀疑硬件了拿万用表量CAN_H和CAN_L之间的电阻120Ω对的。量对地电压CAN_H大概2.5VCAN_L大概2.5V差分信号静态电平没问题。就在我准备把TJA1050拆了换新的时候我突然注意到一个细节。发送板我用的是PB8(RX)/PB9(TX)接收板也是PB8(RX)/PB9(TX)。两个板子都是Remap1。等等STM32F103的CAN1默认引脚是PA11(RX)/PA12(TX)Remap1才是PB8/PB9。我两个板子都用了Remap1这没问题。问题出在GPIO_Mode上。CAN_RX必须配成GPIO_Mode_IPU上拉输入或者GPIO_Mode_IN_FLOATING这个没错。但CAN_TX必须配成GPIO_Mode_AF_PP复用推挽输出。我送端配对了但是——接收端的CAN_TX我一样配成了GPIO_Mode_AF_PP但是接收板根本不需要CAN_TX初始化啊。如果你只是单收不发CAN_TX不初始化也能工作。但问题是我初始化了而且用错了模式吗没有AF_PP是正确的。那为什么收不到我仔细对照两个板子的配置发现接收板我顺手把CAN_TX的GPIO_Speed也设成了GPIO_Speed_50MHz。但这不是问题。真正的问题是——我接收板把CAN_RX的GPIO配成了GPIO_Mode_Out_PP。你没看错。输出模式。肯定是之前测别的功能的时候改的忘了改回来。CAN_RX作为输入引脚配成推挽输出你说它能收到什么等于接收端的CAN controller根本看不到总线上的电平变化因为RX脚被内部强行拉死了。改回来GPIO_Mode_IPU烧进去。立马收到数据了。就这就这。折腾了一个下午逻辑分析仪、示波器、万用表全上了一遍最后是因为GPIO_Mode写错了三个字母。Out_PP和IPU差的也就这么点。// 正确的接收端配置 GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; // CAN1_RX GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 必须输入!! GPIO_Init(GPIOB, GPIO_InitStructure);说实话我写嵌入式这几年翻车翻得最多的反而不是什么复杂的外设协议、RTOS调度、内存管理全是这种低级问题。GPIO配错、时钟忘开、中断优先级配反了——debug起来一个比一个费时间查到最后都觉得自己像个傻子。不过话说回来CAN协议本身的ack机制倒是值得提一嘴。如果你用CAN分析仪挂在总线上去抓包发现发送端一直重发同一个帧而且帧格式是OK的但就是没有ACK位那多半是总线上没有其他节点在收。CAN的ACK slot在帧的CRC后面接收节点收到正确的帧之后会在ACK slot主动拉低总线显性位表示收到了。如果总线上只有你一个节点在发没人拉ACK发送端的Tx mailbox会一直pending然后不断重发。我当时用逻辑分析仪看发送端的TX脚波形看到一帧一帧重复往外发间隔一样其实就是在重发——因为ACK没回来。所以如果遇到CAN发不出去先别急着怀疑波特率和过滤器。先看看总线上有没有第二个节点。或者像我一样先看看RX脚的GPIO_Mode是不是Input。别问了问就是血的教训。