Skip to content

Loading code onto the device

Ian edited this page Nov 20, 2023 · 1 revision

Firmware can be loaded in to the microcontroller using a variety of methods, broadly speaking these can be separated in to two categories: using the SWD debug interface, or over USB via a bootloader.

Using SWD

cargo-flash

This is the preferred pure rust ecosystem method for flashing with debugger.

cargo flash replaces the cargo build command to include flashing over debugger using probe-rs and libusb.

$ cargo install cargo-flash

We need to know the specific id of your device's chip. Luckily adafruit lists ATSAMD21G18 for metro_m0

$ cargo flash --list-chips | grep ATSAMD21G18
        ATSAMD21G18A
        ATSAMD21G18AU

You can stash this chip in the cargo toml so you never have to pass it as an argument, which we recommend.

# for cargo flash
[package.metadata]
chip = "ATSAMD21G18A"

And cargo flash simply replaces your cargo build command!

$ cargo flash --example blinky_basic --features unproven --release

Or you can provide it via the chip command line argument

$ cargo flash --example blinky_basic --features unproven --release --chip ATSAMD21G18A

JLink

If you have a board with a SWD debug header, such as the [Metro M0][metro_m0], or if you attached the header yourself, you can use your JLink programmer to either flash the device or debug it (together with gdb).

You can use JFlashLiteExe to just flash the .bin file (note that for some boards, such as [Feather M0][feather_m0], you need to specify the program memory starting address offset or you'll just get a nondescript flashing error).

To debug your board you can run JLinkGDBServer instead of JFlashLiteExe. @wez prefers using the JLinkGDBServer, but you can also use OpenOCD. Note that if you have a load command within your .gdbinit then starting a debug session triggers a flashing, so you don't need a separate flashing step before this.

In one window, run JLinkGDBServer -if SWD -device ATSAMD21G18, then in another, run these commands from the root of this repo so that you pick up its .gdbinit file:

$ cargo build --manifest-path metro_m0/Cargo.toml --example blinky_basic
$ arm-none-eabi-gdb metro_m0/target/thumbv6m-none-eabi/debug/examples/blinky_basic

If you prefer or otherwise need to use OpenOCD, then you'd run it in place of the JLinkGDBServer and then modify the .gdbinit file to comment out the JLink section and uncomment the OpenOCD section.

Using a USB bootloader

Adafruit M0/M4 board (such as Gemma M0 & Feather M0)

If you want to flash the device using the tools that come with the Adafruit arduino support package:

$ cd gemma_m0
$ cargo build --example blinky_basic
$ arm-none-eabi-objcopy -O binary \
    target/thumbv6m-none-eabi/debug/examples/blinky_basic \
    target/thumbv6m-none-eabi/debug/examples/blinky_basic.bin
# if using cargo-binutils, you can `rust-objcopy` with the same flags, or combine the previous 2 steps with `cargo objcopy`
$ stty -F /dev/ttyACM1 ospeed 1200
$ ~/.arduino15/packages/arduino/tools/bossac/1.7.0/bossac -i -d \
    --port=ttyACM1 -U -e -w -v \
    target/thumbv6m-none-eabi/debug/examples/blinky_basic.bin -R

This same technique should work for all of the Adafruit M0/M4 boards, as they all ship with a bossac compatible bootloader. Note that M0 devices may need -o 0x2000 and M4 devices may need -o 0x4000 added to the bossac parameter lists.

hf2-rs

This is the preferred pure rust ecosystem method for interacting with bootloaders.

hf2-rs implements Microsofts HID Flashing Format (HF2) to upload firmware to UF2 bootloaders. UF2 is factory programmed extensively by Microsoft MakeCode and Adafruit hardware.

The cargo-hf2 crate replaces the cargo build command to include flashing over USB to connected UF2 devices, using hf2 flashing over HID protocol.

$ cargo install cargo-hf2

and from a bsp directory

$ cargo hf2 --example blinky_basic --features unproven --release

If you are on Linux and hf2 fails to flash your board even if it is connected and in bootloader mode, you might need to add some udev rules if you have not done that yet.

You might want to have all the hf2 related rules in a single file, i.e. /etc/udev/rules.d/99-hf2-boards.rules, or have a different rules file for each vendor.

The rules for Seeeduino and Adafruit boards look like this:

#adafruit rules
ATTRS{idVendor}=="239a", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEM=="usb", ATTRS{idVendor}=="239a", MODE="0666"
SUBSYSTEM=="tty", ATTRS{idVendor}=="239a", MODE="0666"

#seeeduino rules
ATTRS{idVendor}=="2886", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEM=="usb", ATTRS{idVendor}=="2886", MODE="0666"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2886", MODE="0666"

If you want to add boards from another vendor, you can get the vendor id with the lsusb command, for example:

$ lsusb
Bus 001 Device 005: ID 2886:002f Seeed Technology Co., Ltd. Seeeduino XIAO
...

Here 2886 is the vendor id and 002f the product id.

After adding the rules remember to reboot or run:

sudo udevadm control --reload-rules
sudo udevadm trigger

For more information, refer to the README files for each crate:

uf2conv-rs

uf2conv adds a uf2 header Microsofts HID Flashing Format (UF2) for copying over to UF2 bootloader mass storage devices. UF2 is factory programmed extensively by Microsoft MakeCode and Adafruit hardware. cargo-binutils replaces the cargo build command to find and convert elf files into binary.

Install the dependencies

$ rustup component add llvm-tools-preview
$ cargo install uf2conv cargo-binutils

Then for say, metro_m0 examples

$ cargo objcopy --example blinky_basic --features unproven --release -- -O binary blinky_basic.bin
$ uf2conv blinky_basic.bin --base 0x2000 --output blinky_basic.uf2
$ cp blinky_basic.uf2 /Volumes/PYGAMERBOOT/

For more information, refer to the README files for each crate:

bossac

If you want to flash the device using the tools that come with the Adafruit arduino support package:

$ cd gemma_m0
$ cargo build --example blinky_basic
$ arm-none-eabi-objcopy -O binary \
    target/thumbv6m-none-eabi/debug/examples/blinky_basic \
    target/thumbv6m-none-eabi/debug/examples/blinky_basic.bin
# if using cargo-binutils, you can `rust-objcopy` with the same flags, or combine the previous 2 steps with `cargo objcopy`
$ stty -F /dev/ttyACM1 ospeed 1200
$ ~/.arduino15/packages/arduino/tools/bossac/1.7.0/bossac -i -d \
    --port=ttyACM1 -U -e -w -v \
    target/thumbv6m-none-eabi/debug/examples/blinky_basic.bin -R

This same technique should work for all of the Adafruit M0/M4 boards, as they all ship with a bossac compatible bootloader. Note that M0 devices may need -o 0x2000 and M4 devices may need -o 0x4000 added to the bossac parameter lists.