当前位置:首页 > 工业控制 > 工业控制
[导读]产线100台同时烧,最怕“扫错口、进错Boot、烧完不校验、失败不重试”。通用做法是用Python做调度:pyserial扫串口/USB、按VID/PID和白名单过滤;进Bootloader后调用各芯片烧录器(U-Boot/DFU/ST-Link/esptool/OpenOCD均可)写固件;烧完做SHA256+回读校验,线程池限并发,结果落CSV供MES追溯。


产线100台同时烧,最怕“扫错口、进错Boot、烧完不校验、失败不重试”。通用做法是用Python做调度:pyserial扫串口/USB、按VID/PID和白名单过滤;进Bootloader后调用各芯片烧录器(U-Boot/DFU/ST-Link/esptool/OpenOCD均可)写固件;烧完做SHA256+回读校验,线程池限并发,结果落CSV供MES追溯。

一、端口自动识别与白名单

import serial, serial.tools.list_ports, hashlib, subprocess, csv, os

from concurrent.futures import ThreadPoolExecutor


def scan_ports(allow_vids=None, whitelist=None):

   out=[]

   for p in serial.tools.list_ports.comports():

       if allow_vids and p.vid not in allow_vids:

           continue

       if whitelist and p.device not in whitelist:

           continue

       out.append(p.device)

   return out


# ST/Link CP2102/CH340可按实际改VID

ports=scan_ports(allow_vids={0x0483,0x10C4,0x1A86},

                whitelist="/dev/ttyUSB0,/dev/ttyUSB1".split(",") or None)

VID/PID过滤能避开鼠标、4G模块;白名单用于固定治具端口,避免误烧其他设备。

二、按芯片类型进Bootloader并烧录

串口IAP类可发命令进Boot,再分帧传固件;U-Boot类走kgdb/串口命令行或DFU;STM32走ST-Link;ESP走esptool。统一封装:

def enter_boot(port):

   # 示例:自定义串口IAP,发ENTER后等READY

   s=serial.Serial(port,115200,timeout=2)

   s.write(b"ENTER_BOOT\n"); s.flush()

   return b"READY" in s.readline()


def flash_by_tool(kind, port, fw, sn=None):

   if kind=="stlink":

       cmd=["ST-LINK_CLI","-c","SWD","-p",fw,"0x08000000","-v"]

   elif kind=="esp":

       cmd=["esptool.py","--port",port,"--baud","921600",

            "write_flash","--verify","0x10000",fw]

   elif kind=="dfu":

       cmd=["dfu-util","-D",fw]+(["-S",sn] if sn else [])

   else:  # 通用串口IAP由业务脚本实现

       return enter_boot(port)

   r=subprocess.run(cmd,capture_output=True,text=True,timeout=300)

   return r.returncode==0, r.stderr

DFU多设备可用序列号定向烧;ESP可用DTR/RTS模拟复位进下载模式;ST-Link CLI可做擦除、下载、校验。

三、固件哈希与回读校验

烧前算SHA256,烧后让设备上报版本/校验值,或用工具回读比对:

def fw_sha256(path):

   h=hashlib.sha256()

   with open(path,"rb") as f:

       for blk in iter(lambda:f.read(65536),b""):

           h.update(blk)

   return h.hexdigest()


def verify_remote(port,expect_sha):

   s=serial.Serial(port,115200,timeout=2)

   s.write(b"GET_SHA256\n")

   return expect_sha.lower() in s.readline().decode().lower()

OpenOCD类可program xxx verify,重要工位再加CRC32回读,双校验降低虚焊/接触不良漏检。

四、并发调度、重试与日志

100台别全开线程,按USB Hub、调试器、供电分桶,限最大并发:

MAX_WORKERS=8

RETRY=3


def task(dev):

   kind,fw,sn=dev["kind"],dev["fw"],dev["sn"]

   for i in range(1,RETRY+1):

       try:

           ok,err=flash_by_tool(kind,dev["port"],fw,sn)

           if ok and verify_remote(dev["port"],fw_sha256(fw)):

               return (dev["port"],"PASS","")

           err=f"verify fail {i}/{RETRY}"

       except Exception as e:

           err=str(e)

       time.sleep(min(2**i,8))

   return (dev["port"],"FAIL",err)


def run_all(devs,csv_out):

   rows=[]

   with ThreadPoolExecutor(max_workers=MAX_WORKERS) as ex:

       for row in ex.map(task,devs):

           rows.append(row)

   with open(csv_out,"w",newline="") as f:

       csv.writer(f).writerows([("port","result","detail")]+list(rows))


# devs示例:[{"port":"/dev/ttyUSB0","kind":"esp","fw":"app.bin","sn":"S001"}]

偶发超时做指数退避,连续失败标记NG转人工;日志含端口、SN、固件版本、SHA256、起止时间,直接进MES做追溯。

五、100台无人值守落地要点

供电:多路同时烧最忌USB Hub带不动,用工业自带电源Hub,每路测空载/满载电压;

波特率:ESP夹具好可用921600/2M,普通串口IAP量产用115200更稳;

分组:100台按8~16路一桶,多Hub错开UDEV,Linux用udev按SN固定/dev路径,避免重插变口;

写SN/MAC放最后一步,先烧固件并自校验,再写标识并只读回读,防止半写变废片;

异常分级:通信超时重试,签名/分区错误不重试直接NG,bootloader损坏走脱机烧录器恢复;

报表:每班出良率、平均时长、TOP失败原因,连续同Hub失败率高先查Hub/电源而非脚本。

按“自动识口→白名单分组→Bootloader→外部烧录器写固件→SHA/回读双校验→限并发重试→CSV溯源”闭环,100台边缘网关/STM32/ESP类设备可做到无人值守、重烧可复现、问题可回溯。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭