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 can1rx_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
相关产品推荐
相关产品推荐

