You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BeagleBone Black与STM32 CAN通信偶发卡顿问题排查

问题描述

通过SSH登录BeagleBone Black,使用Python的python-can库基于SocketCAN向定制PCBA上的STM32发送CAN消息;STM32收到消息后将每个字节加1回传,该逻辑通过main循环调用test_can_bus()执行。

正常运行时脚本能收到回复,但偶发出现脚本在输出Message sent on socketcan channel 'can1'后卡住的情况。经UART确认,无论脚本是否卡住,STM32均成功接收并回传了数据。尝试过用subprocess调用candump/cansend、多线程收消息等方案,问题依旧。

代码片段

Python发送/接收脚本

import can

# Specify the CAN interface and interface type
can_interface = "can1"

with can.interface.Bus(channel=can_interface, bustype="socketcan") as bus:
    msg = can.Message(
        arbitration_id = 0x5A1,
        data = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07],
        is_extended_id = True
    )
    try:
        bus.send(msg)
        print(f"Message sent on {bus.channel_info}")
    except can.CanError:
        print("Message NOT sent")

    response = bus.recv()
    print ("Response = ", response)

STM32 CAN处理逻辑

void test_can_bus()
{
    /* Receive data */
    if (HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0))
    {
        HAL_UART_Transmit(&hlpuart1, "Received:", 9, HAL_MAX_DELAY);
        HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &RxHeader, RxData);
        HAL_Delay(500);
        sprintf((char*) debug_str, "%02x %02x %02x %02x %02x %02x %02x %02x\r\n", RxData[0], RxData[1], RxData[2], RxData[3], RxData[4], RxData[5], RxData[6], RxData[7]);

        /* Prepare received data to be sent back */
        for (int i = 0; i<8; i++)
        {
            /* AUX 1 increase by 1 */
            TxData[i] = RxData[i] + 1;
            /* AUX 2 increase by 2 */
            //TxData[i] = RxData[i] + 2;
        }
        //HAL_Delay(1000);
        HAL_UART_Transmit(&hlpuart1, (uint8_t*)debug_str, strlen((const char*)(debug_str)), HAL_MAX_DELAY);
        HAL_UART_Transmit(&hlpuart1, "Sent:    ", 9, HAL_MAX_DELAY);
        if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData) != HAL_OK)
        {
            // Transmission request Error
              Error_Handler();
        }
        HAL_Delay(500);
        sprintf((char*) debug_str, "%02x %02x %02x %02x %02x %02x %02x %02x\r\n", TxData[0],
                      TxData[1], TxData[2], TxData[3], TxData[4], TxData[5], TxData[6], TxData[7]);
        HAL_UART_Transmit(&hlpuart1, (uint8_t*)debug_str, strlen((const char*)debug_str), HAL_MAX_DELAY);
        HAL_UART_Transmit(&hlpuart1, NL, sizeof(NL), HAL_MAX_DELAY);
    }
}

正常输出示例

Message sent on socketcan channel 'can1'
Response =  Timestamp: 1699543213.502878    ID: 00000023    X Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: can1
可能的原因及解决办法

1. 无限阻塞接收导致假死

原脚本中bus.recv()未设置超时参数,默认会无限等待。即便STM32已发送回复,若因总线或配置问题导致帧未被接收,脚本会一直卡住。

解决办法:给recv()添加超时限制,同时增加超时判断逻辑:

# 替换原recv代码
response = bus.recv(timeout=2.0)  # 超时2秒
if response:
    print("Response = ", response)
else:
    print("No response received within timeout")

2. CAN帧参数不匹配(ID类型/掩码)

从正常输出看,STM32回复的帧ID为0x23且标记为X(扩展帧),但需确认STM32发送时的帧类型配置是否与BeagleBone的接收过滤器匹配。若STM32发送的是标准帧,而Python未配置对应过滤器,可能导致帧被丢弃。

解决办法:

  • 检查STM32的TxHeader配置,确认IdType是FDCAN_STANDARD_ID还是FDCAN_EXTENDED_ID,确保与回复帧的ID类型一致。
  • 在Python创建Bus时添加精准过滤器,只接收目标ID的帧:
    # 假设回复是扩展ID 0x23
    can_filters = [{"can_id": 0x23, "can_mask": 0x1FFFFFFF, "extended": True}]
    with can.interface.Bus(channel=can_interface, bustype="socketcan", can_filters=can_filters) as bus:
    

3. SocketCAN接收缓冲区溢出

BeagleBone默认的SocketCAN接收缓冲区较小,若STM32回复较快或总线存在其他帧,可能导致缓冲区溢出,丢失回复帧。

解决办法:

  • 临时调整缓冲区大小:
    ip link set can1 type can restart-ms 100
    ip link set can1 rxqueuelen 1000
    ip link set can1 txqueuelen 1000
    
  • 持久化配置:在/etc/network/interfaces中对应CAN接口的配置块添加:
    up ip link set $IFACE rxqueuelen 1000
    up ip link set $IFACE txqueuelen 1000
    

4. CAN总线物理层问题

总线存在干扰、终端电阻缺失/不匹配、波特率不一致等问题,会导致帧传输丢包。

解决办法:

  • 检查CAN总线两端是否都接有120Ω终端电阻。
  • 验证波特率配置:确保BeagleBone和STM32的CAN波特率完全一致。
  • 查看总线错误统计:
    ip -details link show can1
    
    若rx_errors或tx_errors数值较高,需排查布线干扰或硬件故障。

5. 改用非阻塞/监听模式接收

避免单线程阻塞接收的局限性,使用BufferedReader或监听模式异步处理接收:

import can
from can import BufferedReader
import time

can_interface = "can1"

with can.interface.Bus(channel=can_interface, bustype="socketcan") as bus:
    # 设置监听器
    listener = BufferedReader()
    notifier = can.Notifier(bus, [listener])

    # 发送消息
    msg = can.Message(
        arbitration_id = 0x5A1,
        data = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07],
        is_extended_id = True
    )
    try:
        bus.send(msg)
        print(f"Message sent on {bus.channel_info}")
    except can.CanError:
        print("Message NOT sent")

    # 超时等待回复
    start_time = time.time()
    response = None
    while time.time() - start_time < 2:
        response = listener.get_message(timeout=0.1)
        if response:
            break
    notifier.stop()

    if response:
        print("Response = ", response)
    else:
        print("No response received")

内容的提问来源于stack exchange,提问作者whatevername

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 18:20:11