#!/usr/bin/env python3 """Чтение SPI-флеша рации через CPS-протокол (USART6) из WSL по powershell.exe. Рация — в ОБЫЧНОМ режиме (не мост, не прошивка), кабель в COM. CPS-протокол: notify : 34 00 00 10 44 -> ACK 06 read : 52 -> 1028 Б (3 hdr + 1024 data + 1 cksum), offset в КБ cksum : (sum) & 0xFF, seed 0 python3 tools/spi_read.py 0 16 --out cur_cal.bin # блоки 0..15 (первые 16 КБ) python3 tools/spi_read.py 0 4096 --out full.bin # весь 4 МБ (долго) """ import argparse, subprocess, sys, os WIN_TMP_WSL = "/mnt/c/Users/vikto/AppData/Local/Temp" WIN_TMP_WIN = r"C:\Users\vikto\AppData\Local\Temp" PS = r''' $ErrorActionPreference='Stop' $p = New-Object System.IO.Ports.SerialPort "{port}",115200,"None",8,"one" $p.ReadTimeout=1500; $p.WriteTimeout=3000 try {{ $p.Open() }} catch {{ Write-Output "ERR:openfail"; exit 1 }} function ck([byte[]]$a,$n){{ $s=0; for($i=0;$i -lt $n;$i++){{ $s=($s+$a[$i]) -band 0xFF }}; return [byte]$s }} # notify $p.DiscardInBuffer() $nf=[byte[]](0x34,0x00,0x00,0x10,0x44) $p.Write($nf,0,5) $ok=$false; $sw=[Diagnostics.Stopwatch]::StartNew() while($sw.ElapsedMilliseconds -lt 3000){{ try {{ if($p.ReadByte() -eq 6){{ $ok=$true; break }} }} catch {{}} }} if(-not $ok){{ Write-Output "ERR:notify"; $p.Close(); exit 1 }} $fs=[System.IO.File]::Open("{outwin}",[System.IO.FileMode]::Create) for($off={first}; $off -lt {last}; $off++){{ $cmd=[byte[]](0x52,(($off -shr 8) -band 0xFF),($off -band 0xFF),0) $cmd[3]=ck $cmd 3 $p.DiscardInBuffer(); $p.Write($cmd,0,4) $buf=New-Object byte[] 1028; $got=0; $sw2=[Diagnostics.Stopwatch]::StartNew() while($got -lt 1028 -and $sw2.ElapsedMilliseconds -lt 2500){{ try {{ $b=$p.ReadByte(); $buf[$got]=[byte]$b; $got++ }} catch {{}} }} if($got -lt 1028){{ Write-Output ("ERR:read@"+$off); $fs.Close(); $p.Close(); exit 1 }} if($buf[0] -eq 0xFF){{ Write-Output ("ERR:noblk@"+$off); $fs.Close(); $p.Close(); exit 1 }} $fs.Write($buf,3,1024) if(($off % 64) -eq 0){{ Write-Output ("PROG:"+$off) }} }} $fs.Close() $cl=[byte[]](0x34,0x52,0x05,0xEE,0x79); $p.Write($cl,0,5) $p.Close() Write-Output "OK" ''' def find_port(): out = subprocess.run(["powershell.exe","-NoProfile","-Command", "[System.IO.Ports.SerialPort]::GetPortNames() -join ','"], capture_output=True, text=True).stdout ports=[p for p in out.strip().replace("\r","").split(",") if p] if not ports: sys.exit("нет COM-порта") return ports[0] def main(): ap=argparse.ArgumentParser() ap.add_argument("first", type=lambda x:int(x,0)) ap.add_argument("last", type=lambda x:int(x,0)) ap.add_argument("--out", default="spi_read.bin") ap.add_argument("--port") a=ap.parse_args() port=a.port or find_port() name="rt4d_spiread.bin" outwin=os.path.join(WIN_TMP_WIN,name) script=PS.format(port=port,outwin=outwin.replace("\\","\\\\"),first=a.first,last=a.last) print(f"[чтение SPI блоков {a.first}..{a.last-1} ({(a.last-a.first)} КБ), порт {port}]") proc=subprocess.Popen(["powershell.exe","-NoProfile","-Command",script], stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True,encoding="utf-8",errors="replace") for line in proc.stdout: line=line.strip() if line.startswith("PROG:"): print(f"\r блок {line[5:]}",end="",flush=True) elif line.startswith("ERR:"): print("\n"+line); elif line=="OK": print("\n чтение завершено") proc.wait() # забрать файл из Windows temp src=os.path.join(WIN_TMP_WSL,name) if os.path.exists(src): import shutil; shutil.copy(src,a.out) print(f"сохранено: {a.out} ({os.path.getsize(a.out)} Б)") else: print("файл не создан — чтение не удалось") if __name__=="__main__": main()