最近在搞一个项目,使用 STM32 进行 CAN 总线通信,采用 TJA1050 作为 CAN 收发器。理论上应该非常简单的东西,结果卡了很久,各种波特率、滤波器设置、STM32 芯片配置都检查了无数遍,数据就是发不出去。后来发现,问题竟然出在一个小小的 USB 扩展坞上,简直是绝了!
问题场景重现:CAN分析仪的迷惑行为
我用的是周立功的 CAN 分析仪,能正常收发数据。但是 STM32 这边,用示波器看 CAN_H 和 CAN_L 的波形,波形是有的,但就是没法和设备建立通信。用 CAN 分析仪监控 STM32 发送的数据,完全看不到。一开始怀疑是 STM32 程序的问题,各种调试,甚至重新写了 CAN 的初始化代码,依然无效。波特率、仲裁场、数据场等等都用 CAN 分析仪确认过,和程序里设置的完全一致。
底层原理深度剖析:TJA1050与CAN总线的握手协议
CAN 通信需要 CAN 控制器和 CAN 收发器协同工作。STM32 内部集成了 CAN 控制器,而 TJA1050 负责物理层的信号收发。CAN 控制器负责处理 CAN 协议的各种帧类型(数据帧、遥控帧、错误帧、过载帧),并进行位时序控制(同步段、传播段、相位缓冲段 1、相位缓冲段 2)。
TJA1050 作为一个物理层器件,负责将 CAN 控制器发出的逻辑信号转换为差分信号,并通过 CAN_H 和 CAN_L 线发送到 CAN 总线上。同时,它也负责接收 CAN 总线上的差分信号,并将其转换为逻辑信号传递给 CAN 控制器。如果 STM32 和 TJA1050 之间的连接有问题,或者 TJA1050 的供电有问题,都会导致通信失败。
代码/配置解决方案:基础配置检查与优化
首先,必须确保 STM32 的 CAN 初始化配置正确。这里贴一段示例代码:
// 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 | RCC_APB2Periph_AFIO, ENABLE);
// 配置 CAN 引脚:PA11 (CAN_RX) and PA12 (CAN_TX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; // CAN_RX
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; // CAN_TX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap_CAN1, ENABLE); // CAN 端口重映射
// 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 优先级
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; // 正常模式
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; // 同步跳转宽度 1 个时间单元
CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq; // 时间段 1 为 8 个时间单元
CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq; // 时间段 2 为 7 个时间单元
CAN_InitStructure.CAN_Prescaler = 3; // 分频系数 (设置波特率,这里 36MHz/ (1+8+7)/3 = 500kbps)
CAN_Init(CAN1, &CAN_InitStructure);
// CAN 过滤器配置
CAN_FilterInitStructure.CAN_FilterNumber = 0; // 过滤器编号
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // 屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // 32 位
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; // 过滤器 ID 高 16 位
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; // 过滤器 ID 低 16 位
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; // 屏蔽 ID 高 16 位
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; // 屏蔽 ID 低 16 位
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0; // 过滤器关联到 FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; // 使能过滤器
CAN_FilterInit(&CAN_FilterInitStructure);
// 使能 CAN RX 中断
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
// 配置 NVIC 中断控制器
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; // CAN RX0 中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_Init(&NVIC_InitStructure);
}
// 中断处理函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
// 处理接收到的 CAN 数据
// ...
}
代码中,CAN_Prescaler 参数非常重要,它决定了 CAN 总线的波特率。务必根据实际需求进行调整。另外,CAN 过滤器的配置也需要仔细检查,确保能够接收到目标 ID 的数据。
实战避坑经验总结:USB 扩展坞的罪与罚
经过一系列的排查,我发现问题竟然出在 USB 扩展坞上!我把 STM32 的 ST-Link 下载器插在了 USB 扩展坞上,然后 ST-Link 再连接到 STM32 开发板。换句话说,STM32 的供电和调试都通过 USB 扩展坞。但是,这个 USB 扩展坞的供电能力不足,导致 STM32 工作不稳定,CAN 收发器 TJA1050 的工作电压也跟着波动,最终导致 CAN 通信失败。将 ST-Link 直接插到电脑的 USB 接口上,问题立刻解决!
总结一下几个关键点:
- 供电问题: 确保 STM32 和 TJA1050 的供电稳定可靠。尽量避免使用劣质的 USB 扩展坞,特别是那些供电能力不足的扩展坞。
- 接地问题: 确保 STM32、TJA1050 和 CAN 总线的地线连接良好,避免出现地线环路。
- 波特率匹配: 确保 STM32、CAN 分析仪和其他 CAN 设备使用相同的波特率。
- 终端电阻: CAN 总线的两端需要连接 120 欧姆的终端电阻,以防止信号反射。
- 示波器调试: 使用示波器观察 CAN_H 和 CAN_L 的波形,可以帮助你判断 CAN 总线上是否存在问题。
这个坑踩得真是印象深刻,以后再遇到类似问题,一定要先排除供电问题!
冠军资讯
代码一只喵