forked from jappel/VersaMCU
Add end-to-end USB flashing for the app firmware via UF2
Adds uf2conv.py (minimal, dependency-free .bin -> .uf2 converter matching bootloader/inc/uf2format.h's block layout) and upload_uf2.py, a PlatformIO upload hook for env:versapad_usb that finds the mounted VERSABOOT volume and copies the converted firmware onto it. env:versapad_usb previously used upload_protocol=sam-ba, the classic Arduino/Atmel protocol -- the actual bootloader speaks UF2/mass storage, not SAM-BA, so that upload path never worked. Switched to upload_protocol=custom with the new hook, and cleaned the now-unused SAM-BA-specific fields out of boards/versapad.json. Verified end to end on real hardware: pio run -e versapad_usb --target upload builds, converts, copies to the VERSABOOT drive, and the bootloader jumps into the freshly written app on its own. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
f60a29137c
commit
d325297063
7 changed files with 210 additions and 32 deletions
83
uf2conv.py
Normal file
83
uf2conv.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Minimal .bin -> .uf2 converter for the VersaMCU UF2 bootloader.
|
||||
|
||||
Standalone reimplementation of the block format microsoft/uf2-samdx1's
|
||||
uf2conv.py produces (256-byte payload per 512-byte block); see
|
||||
bootloader/inc/uf2format.h for the struct this has to match on the device
|
||||
side. No external dependencies, Python 3 only.
|
||||
|
||||
Usage:
|
||||
python uf2conv.py <input.bin> <output.uf2> [--base 0x2000] [--family 0x68ed2b88]
|
||||
"""
|
||||
import argparse
|
||||
import struct
|
||||
|
||||
UF2_MAGIC_START0 = 0x0A324655 # "UF2\n"
|
||||
UF2_MAGIC_START1 = 0x9E5D5157 # randomly selected, must match the device
|
||||
UF2_MAGIC_END = 0x0AB16F30 # ditto
|
||||
UF2_FLAG_FAMILYID_PRESENT = 0x00002000
|
||||
|
||||
SAMD21_FAMILY_ID = 0x68ED2B88 # bootloader/inc/uf2format.h, #ifdef SAMD21
|
||||
PAYLOAD_SIZE = 256 # bytes per block; matches the upstream uf2conv.py convention
|
||||
|
||||
|
||||
def convert(bin_path: str, uf2_path: str, base_addr: int, family_id: int) -> int:
|
||||
with open(bin_path, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
# Pad to a whole number of blocks; the bootloader writes payloadSize
|
||||
# bytes per block regardless of how much of it is real firmware.
|
||||
if len(data) % PAYLOAD_SIZE != 0:
|
||||
data += b"\x00" * (PAYLOAD_SIZE - len(data) % PAYLOAD_SIZE)
|
||||
num_blocks = len(data) // PAYLOAD_SIZE
|
||||
|
||||
blocks = []
|
||||
for block_no in range(num_blocks):
|
||||
offset = block_no * PAYLOAD_SIZE
|
||||
chunk = data[offset : offset + PAYLOAD_SIZE]
|
||||
header = struct.pack(
|
||||
"<IIIIIIII",
|
||||
UF2_MAGIC_START0,
|
||||
UF2_MAGIC_START1,
|
||||
UF2_FLAG_FAMILYID_PRESENT,
|
||||
base_addr + offset,
|
||||
PAYLOAD_SIZE,
|
||||
block_no,
|
||||
num_blocks,
|
||||
family_id,
|
||||
)
|
||||
padding = b"\x00" * (476 - PAYLOAD_SIZE)
|
||||
footer = struct.pack("<I", UF2_MAGIC_END)
|
||||
blocks.append(header + chunk + padding + footer)
|
||||
|
||||
with open(uf2_path, "wb") as f:
|
||||
f.write(b"".join(blocks))
|
||||
|
||||
return num_blocks
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("input", help="Path to the raw firmware .bin")
|
||||
parser.add_argument("output", help="Path to write the .uf2 to")
|
||||
parser.add_argument(
|
||||
"--base",
|
||||
type=lambda s: int(s, 0),
|
||||
default=0x2000,
|
||||
help="Flash base address the .bin was linked for (default: 0x2000, matches "
|
||||
"variants/versapad/linker_scripts/gcc/flash_with_bootloader.ld)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--family",
|
||||
type=lambda s: int(s, 0),
|
||||
default=SAMD21_FAMILY_ID,
|
||||
help="UF2 family ID (default: SAMD21, 0x68ed2b88)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
num_blocks = convert(args.input, args.output, args.base, args.family)
|
||||
print(f"Wrote {args.output}: {num_blocks} blocks, base 0x{args.base:08x}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue