For public
This commit is contained in:
Regular → Executable
+58
-5
@@ -10,6 +10,7 @@ import serial
|
||||
FRAME_START = 0x55
|
||||
CMD_INFO = ord("I")
|
||||
CMD_READ = ord("R")
|
||||
CMD_WRITE_BYTE = ord("B")
|
||||
CMD_WRITE_PAGE = ord("W")
|
||||
|
||||
REPLY_OK = ord("K")
|
||||
@@ -24,6 +25,27 @@ class ProtocolError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class DeviceError(ProtocolError):
|
||||
def __init__(self, code: int, detail: bytes = b"", address: int | None = None):
|
||||
self.code = code
|
||||
self.detail = detail
|
||||
self.address = address
|
||||
super().__init__(format_device_error(code, detail, address))
|
||||
|
||||
|
||||
def format_device_error(code: int, detail: bytes, address: int | None) -> str:
|
||||
location = f" at 0x{address:04x}" if address is not None else ""
|
||||
if code == 4 and len(detail) >= 4:
|
||||
verify_address = detail[0] | (detail[1] << 8)
|
||||
expected = detail[2]
|
||||
observed = detail[3]
|
||||
return (
|
||||
f"WRITE verify failed{location}: last byte 0x{verify_address:04x}, "
|
||||
f"expected 0x{expected:02x}, observed 0x{observed:02x}"
|
||||
)
|
||||
return f"device error code {code}{location}"
|
||||
|
||||
|
||||
def read_exact(port: serial.Serial, length: int) -> bytes:
|
||||
data = bytearray()
|
||||
while len(data) < length:
|
||||
@@ -47,6 +69,16 @@ def send_command(port: serial.Serial, command: int, payload: bytes = b"") -> int
|
||||
return expect_frame_start(port)
|
||||
|
||||
|
||||
def read_device_error(port: serial.Serial, address: int | None = None) -> DeviceError:
|
||||
code = read_exact(port, 1)[0]
|
||||
try:
|
||||
detail_len = read_exact(port, 1)[0]
|
||||
detail = read_exact(port, detail_len)
|
||||
except ProtocolError:
|
||||
detail = b""
|
||||
return DeviceError(code, detail, address)
|
||||
|
||||
|
||||
def query_info(port: serial.Serial) -> tuple[int, int]:
|
||||
reply = send_command(port, CMD_INFO)
|
||||
if reply != REPLY_DATA:
|
||||
@@ -64,8 +96,7 @@ def read_chunk(port: serial.Serial, address: int, length: int) -> bytes:
|
||||
payload = bytes([address & 0xFF, (address >> 8) & 0xFF, length])
|
||||
reply = send_command(port, CMD_READ, payload)
|
||||
if reply == REPLY_ERROR:
|
||||
code = read_exact(port, 1)[0]
|
||||
raise ProtocolError(f"READ failed with error code {code}")
|
||||
raise read_device_error(port, address)
|
||||
if reply != REPLY_DATA:
|
||||
raise ProtocolError(f"unexpected READ reply: {reply!r}")
|
||||
returned_length = read_exact(port, 1)[0]
|
||||
@@ -80,8 +111,16 @@ def write_page(port: serial.Serial, address: int, data: bytes) -> None:
|
||||
payload = bytes([address & 0xFF, (address >> 8) & 0xFF, len(data)]) + data
|
||||
reply = send_command(port, CMD_WRITE_PAGE, payload)
|
||||
if reply == REPLY_ERROR:
|
||||
code = read_exact(port, 1)[0]
|
||||
raise ProtocolError(f"WRITE failed with error code {code} at 0x{address:04x}")
|
||||
raise read_device_error(port, address)
|
||||
if reply != REPLY_OK:
|
||||
raise ProtocolError(f"unexpected WRITE reply: {reply!r}")
|
||||
|
||||
|
||||
def write_byte(port: serial.Serial, address: int, value: int) -> None:
|
||||
payload = bytes([address & 0xFF, (address >> 8) & 0xFF, value & 0xFF])
|
||||
reply = send_command(port, CMD_WRITE_BYTE, payload)
|
||||
if reply == REPLY_ERROR:
|
||||
raise read_device_error(port, address)
|
||||
if reply != REPLY_OK:
|
||||
raise ProtocolError(f"unexpected WRITE reply: {reply!r}")
|
||||
|
||||
@@ -104,6 +143,16 @@ def write_image(port: serial.Serial, data: bytes, page_size: int) -> None:
|
||||
print(f"wrote and verified page 0x{address:04x}")
|
||||
|
||||
|
||||
def write_image_bytes(port: serial.Serial, data: bytes) -> None:
|
||||
for address, value in enumerate(data):
|
||||
write_byte(port, address, value)
|
||||
verify = read_chunk(port, address, 1)
|
||||
if verify != bytes([value]):
|
||||
raise ProtocolError(f"verify failed at 0x{address:04x}")
|
||||
if (address & 0x3F) == 0:
|
||||
print(f"wrote and verified byte 0x{address:04x}")
|
||||
|
||||
|
||||
def open_port(device: str, baud: int) -> serial.Serial:
|
||||
port = serial.Serial(device, baudrate=baud, timeout=2)
|
||||
time.sleep(2.0)
|
||||
@@ -119,6 +168,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("file", type=Path)
|
||||
parser.add_argument("--baud", type=int, default=115200)
|
||||
parser.add_argument("--size", type=int, default=MAX_SIZE, help="bytes to read in read mode")
|
||||
parser.add_argument("--write-mode", choices=["page", "byte"], default="page")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -146,7 +196,10 @@ def main() -> int:
|
||||
print(f"read {len(image)} bytes to {args.file}")
|
||||
return 0
|
||||
|
||||
write_image(port, data, page_size)
|
||||
if args.write_mode == "byte":
|
||||
write_image_bytes(port, data)
|
||||
else:
|
||||
write_image(port, data, page_size)
|
||||
print(f"programmed {len(data)} bytes from {args.file}")
|
||||
return 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user