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

Raspberry Pico对接RS485 Board时随运行时长增加出现数据包重叠故障

问题背景
  • 基于Raspberry Pico搭建RS485总线数据记录仪,功能为嗅探总线数据并存储到SD卡,基础功能可正常运行,但存在随运行时长增加,错误数据读取频率逐步升高的故障。
  • 标准数据包固定长度80字节,固定以0x02为帧头、0x03为帧尾,正常数据包示例:
[2, 140, 130, 128, 137, 128, 0, 0, 0, 128, 130, 135, 130, 173, 178, 128, 178, 128, 178, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 194, 188, 128, 230, 129, 184, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 56, 3]
  • 故障表现为数据包重叠:异常包中会在未收满前序包时出现新的合法0x02帧头,新包以0x03正常结尾,截断前序未完成接收的数据包,异常数据包示例:
[2, 140, 128, 128, 137, 128, 0, 0, 0, 160, 136, 157, 136, 173, 167, 129, 181, 129, 181, 129, 128, 133, 128, 129, 128, 128, 128, 175, 155, 147, 160, 128, 128, 128, 128, 221, 255, 249, 255, 133, 188, 128, 230, 129, 192, 128, 192, 128, 128, 128, 152, 0, 2, 140, 128, 128, 137, 128, 0, 0, 0, 168, 136, 165, 136, 173, 167, 129, 181, 129, 181, 129, 128, 133, 128, 129, 128, 128, 128, 153, 156, 253, 159, 128, 128, 249, 255, 242, 255, 149, 128, 133, 188, 128, 230, 129, 192, 128, 192, 128, 128, 128, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 67, 3]
  • 当前使用MicroPython编写UART读取逻辑,核心逻辑为逐字节读取,等待0x02帧头后持续缓存字节,直到检测到0x03帧尾即判定单包接收完成,现有实现代码:
uart1 = UART(0, baudrate=38400, tx=Pin(0), rx=Pin(1))
led = Pin(25, Pin.OUT)

while True:
    while uart1.any() > 0:
        data_raw = uart1.read(1)
        if data_raw == b'\x02':
            line = bytearray()
            led.toggle()
            line += data_raw
            R_EOF = False
            while not R_EOF:
                data_raw = uart1.read(1)
                if data_raw == b'\x03':
                    line += data_raw
                    R_EOF = True
                    led.toggle()
                elif data_raw == None:
                    pass
                else:
                    line += data_raw
  • 初步怀疑硬件焊接不良,但该假设无法解释故障频率随运行时长增加逐步升高的特征,需排查故障根因并给出解决方案。
故障根因分析
  • 现有UART读取逻辑采用阻塞设计是核心诱因:内层收包的while not R_EOF循环无退出条件兜底,一旦进入收包流程,未读到0x03就会持续循环,未做长度校验、超时控制,也未处理接收过程中出现新帧头的异常场景。
  • Raspberry Pico UART硬件接收缓冲区默认仅128字节,当收包逻辑阻塞、SD卡写入等操作占用CPU时,缓冲区中未及时读取的字节会被新到的数据覆盖,产生丢字节问题。一旦帧尾0x03丢失,程序会持续卡在收包状态,将后续多个数据包的内容全部读入同一缓存,直到碰到下一个0x03才退出,直接表现为观测到的数据包重叠现象。
  • 故障频率随运行时长升高的特征与SD卡写入特性直接相关:随运行时间增加,SD卡存储的文件体积不断增大,每次写入的寻址、写入耗时逐步上升,UART缓冲区溢出丢字节的概率同步升高,故障出现频率随之上涨。硬件焊接不良导致的故障通常表现为随机、与运行时长无相关性的丢包,与该特征不符,可基本排除。
修复方案
  • 去掉阻塞式的内层收包循环,改用状态机逐字节处理,所有读取逻辑都在最外层非阻塞循环中运行,避免收包阶段卡死
  • 增加固定长度校验:已知标准包固定80字节,收到帧头后计数接收字节数,达到80字节时校验最后一个字节是否为0x03,符合则判定为有效包,否则直接丢弃当前缓存重置状态
  • 增加接收超时机制:收到帧头后启动计时器,超过单包最大传输时间(38400波特率下80字节传输耗时约20ms,超时阈值可设为50ms)还没收到合法帧尾,直接重置接收状态
  • 收包过程中如果检测到新的0x02帧头,直接丢弃之前未接收完成的残包,从新帧头开始重新缓存
  • 扩大UART接收缓冲区,初始化UART时增加rxbuf=256参数将软件缓冲区扩到256字节,降低溢出概率
  • 解耦UART读取与SD卡写入逻辑:单独开辟内存队列,收到有效包先存入队列,主循环优先处理UART读取,空闲时再批量把队列里的数据写入SD卡,避免SD写入操作阻塞UART读取导致丢包

修复后参考实现代码:

from machine import UART, Pin
import time

# 初始化UART,扩大接收缓冲区
uart1 = UART(0, baudrate=38400, tx=Pin(0), rx=Pin(1), rxbuf=256)
led = Pin(25, Pin.OUT)

# 接收参数配置
PACKET_LEN = 80
RX_TIMEOUT = 50  # 单位:ms
recv_buff = bytearray()
recv_start_tick = 0
save_queue = []

while True:
    # 非阻塞读取所有可用字节
    while uart1.any() > 0:
        data_raw = uart1.read(1)
        if not data_raw:
            continue
        byte = data_raw[0]

        # 检测到帧头,重置接收状态
        if byte == 0x02:
            recv_buff = bytearray()
            recv_buff.append(byte)
            recv_start_tick = time.ticks_ms()
            led.toggle()
            continue

        # 处于收包状态时处理后续字节
        if len(recv_buff) > 0:
            recv_buff.append(byte)
            # 达到固定长度校验帧尾
            if len(recv_buff) == PACKET_LEN:
                if byte == 0x03:
                    # 有效包入队,等待写入SD卡
                    save_queue.append(bytes(recv_buff))
                    led.toggle()
                # 无论包是否有效,重置接收状态
                recv_buff = bytearray()
            # 超时重置,避免卡死
            elif time.ticks_diff(time.ticks_ms(), recv_start_tick) > RX_TIMEOUT:
                recv_buff = bytearray()
    
    # 空闲时批量写入SD卡,单次写入不超过队列中10个包,避免长时间阻塞
    if len(save_queue) > 0 and uart1.any() == 0:
        # 此处补充SD卡写入逻辑,将save_queue中前N个包写入文件后移除对应元素
        pass

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 16:30:45