最近在搞一个项目,用 STM32 控制器配合 TJA1050 CAN 收发器做 CAN 总线通信,结果死活连不上。波特率确认没问题,代码逻辑也反复检查,但就是收不到数据。更绝的是,调试过程中用到了 USB 扩展坞,结果问题变得更加诡异。这次就把整个排查过程和解决方案记录下来,希望能帮到大家。
问题场景重现:CAN分析仪无情显示无数据
硬件环境:
- STM32F103C8T6 最小系统板
- TJA1050 CAN 收发器模块
- CAN 分析仪
- USB 扩展坞
软件环境:
- Keil MDK
- 自定义的 CAN 通信协议
现象:
- STM32 通过 TJA1050 发送 CAN 数据,CAN 分析仪显示无数据接收。
- 使用 USB 扩展坞给 STM32 供电后,问题更加不稳定,有时能收到零星数据,有时完全没反应。
底层原理深度剖析:波特率、时序、供电噪声一个都不能少
波特率与时序:
CAN 总线通信对波特率精度要求很高。STM32 的 CAN 控制器需要配置正确的时钟源和分频系数,才能保证实际波特率与目标波特率一致。否则,接收端可能无法正确采样数据。如果用了 RTOS,需要确认时钟初始化有没有被其他任务干扰。此外,CAN 控制器的采样点位置也需要根据总线长度和节点数量进行调整,一般在 75% 左右。
TJA1050 工作模式:

TJA1050 有高速模式和低速模式,需要根据实际应用场景选择合适的工作模式。同时,需要注意 TJA1050 的使能引脚(通常是 Standby 引脚),确保其处于正常工作状态。
供电问题:
这是这次排查中最坑的地方。USB 扩展坞的供电质量参差不齐,一些劣质扩展坞可能会引入大量的噪声,影响 STM32 和 TJA1050 的正常工作。特别是在进行高速 CAN 通信时,电源噪声的影响更加明显。此外,USB 扩展坞的供电能力也可能不足,导致 STM32 工作不稳定。类似的情况也可能出现在使用面包板搭建电路时,面包板的接触电阻和分布电容可能会引入干扰。

代码/配置解决方案:逐一击破
波特率配置
// STM32 CAN 初始化 void CAN_Config(void) { CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能 CAN 时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置 CAN 引脚,PA11(CAN_RX) PA12(CAN_TX) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); // CAN 单元初始化 CAN_InitStructure.CAN_TTCM = DISABLE; // 时间触发通信模式关闭 CAN_InitStructure.CAN_ABOM = DISABLE; // 自动离线管理关闭 CAN_InitStructure.CAN_AWUM = DISABLE; // 自动唤醒管理关闭 CAN_InitStructure.CAN_NART = DISABLE; // 禁止报文自动重传 CAN_InitStructure.CAN_RFLM = DISABLE; // 接收 FIFO 锁定模式 CAN_InitStructure.CAN_TXFP = DISABLE; // 发送 FIFO 优先级模式 // 波特率设置。1M波特率时,AP=0,TS1=12,TS2=2,BS=1 CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; // 正常模式 CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; // 重新同步跳跃宽度1个时间单位 CAN_InitStructure.CAN_BS1 = CAN_BS1_12tq; // 时间段1为12个时间单位 CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq; // 时间段2为2个时间单位 CAN_InitStructure.CAN_Prescaler = 1; // 分频系数(Fdiv)为1,最终波特率=Fpclk1/(Prescaler*(TS1+TS2+1))=36M/(1*(12+2+1))=1M CAN_Init(CAN1, &CAN_InitStructure); // CAN 过滤器初始化 CAN_FilterInitStructure.CAN_FilterNumber = 0; // 过滤器组0 CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // 标识符屏蔽模式 CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // 32位宽 CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; // 32位ID高位 CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; // 32位ID低位 CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; // 32位MASK高位 CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; // 32位MASK低位 CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 过滤器0关联到FIFO0 CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // 使能过滤器 CAN_FilterInit(&CAN_FilterInitStructure); // 使能 CAN 中断 CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); // FIFO0 消息挂号中断使能 NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn; // CAN1 RX0 中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级 0 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断 NVIC_Init(&NVIC_InitStructure); }TJA1050 使能引脚
确保 TJA1050 的 Standby 引脚拉低,使其处于正常工作模式。

// 初始化 TJA1050 使能引脚 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能 GPIOB 时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 假设 PB0 连接 TJA1050 的 Standby 引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 拉低 Standby 引脚,使能 TJA1050更换供电方式:
尝试使用质量更好的 USB 电源适配器,或者直接使用开发板自带的电源接口供电。如果条件允许,可以使用线性稳压电源,提供更纯净的电源。
增加滤波电容:
在 STM32 和 TJA1050 的电源引脚附近增加滤波电容,可以有效抑制电源噪声。建议使用 100nF 和 10uF 的电容并联。
// 在电源引脚附近增加滤波电容 // C1: 100nF, C2: 10uF
实战避坑经验总结
- 优先排除硬件问题:使用万用表检查 STM32、TJA1050 和 CAN 总线的连接是否正确,电源电压是否正常。
- 波特率校准至关重要:使用 CAN 分析仪测量实际波特率,并与目标波特率进行对比,确保误差在允许范围内。必要时,调整 STM32 的时钟配置。
- 供电是隐藏的雷:尽量避免使用劣质 USB 扩展坞供电。如果必须使用,尝试增加滤波电容,或者更换质量更好的电源适配器。
- 示波器是好帮手:使用示波器观察 CAN 总线上的信号波形,可以帮助判断是否存在噪声干扰或其他问题。重点关注信号的上升沿和下降沿是否平滑,是否存在毛刺。
- CAN分析仪的配置:有些CAN分析仪需要配置正确的波特率,才能正常接收数据,不要忽略这个配置。
最后,遇到类似问题,不要急于否定自己的代码。仔细分析问题现象,逐一排除可能的原因,才能最终找到解决方案。希望这篇文章能帮助大家少走弯路。
冠军资讯
键盘上的咸鱼