I realize I’ve dived so deep into this sound rathole that I want to talk about sound on the device before I’ve really talked about the device itself. To correct that, this post is all about the Feather M4 Express and how to write software for it in Rust.

image of Feather M4 Express

It’s what you’ve been waiting for, the Feather M4 Express featuring ATSAMD51. This Feather is fast like a swift, smart like an owl, strong like a ox-bird (it’s half ox, half bird, OK?) This feather is powered by our new favorite chip, the ATSAMD51J19A - with its 120MHz Cortex M4 with floating point support and 512KB Flash and 192KB RAM. Your code will zig and zag and zoom, and with a bunch of extra peripherals for support, this will for sure be your favorite new chipset.

I picked the Feather M4 to experiment with using embedded Rust for reasons I no longer recall. It was a good choice, though, as the community of developers who provide the Rust support for the ATSAMD family of processors could not be more friendly and helpful.

The first bit of excellent advice I got was to get a debug probe. With such a device one can easily download new software to an attached device and easily see printing from it. Programming these little boards without it is possible, but it would be quite a slog.

I ended up picking the ST-Link STM8/STM32 v2 Compatible Programmer & Emulator, another lucky choice as I’ve gotten a few others over the years but have never been able to make them work. I also ended having to do some fairly hairy-to-me soldering to connect the SWDIO and SWCKL signals from pads on the bottom of the board to something I could connect to the probe. I was amazed that I got it to work. These days, though, I use this excellent adapter.

With a probe one can use cargo run to run a binary on a device, through the magic of cargo config and probe-run and with the following settings in .cargo/config.

[build]
target = "thumbv7em-none-eabihf"
rustflags = [
    "-C", "linker=flip-link",
    "-C", "link-arg=--nmagic",
    "-C", "link-arg=-Tlink.x",
]

[target.thumbv7em-none-eabihf]
runner = "probe-run --chip ATSAMD51J20A"

and a memory.x file.

The probe-run tool takes advantage of the runner feature of cargo.

With all that, I present to you the “Hello, World” of embedded programming; blinking an LED.

#![no_std]
#![no_main]

use feather_m4 as bsp;

use bsp::{
    ehal::blocking::delay::DelayMs,
    entry,
    hal::{clock::GenericClockController, delay::Delay, prelude::*},
    pac::{CorePeripherals, Peripherals},
};
use panic_semihosting as _;
use rtt_target::{rprintln, rtt_init_print};

#[entry]
fn main() -> ! {
    rtt_init_print!();
    let mut peripherals = Peripherals::take().unwrap();
    let core = CorePeripherals::take().unwrap();
    let mut clocks = GenericClockController::with_external_32kosc(
        peripherals.GCLK,
        &mut peripherals.MCLK,
        &mut peripherals.OSC32KCTRL,
        &mut peripherals.OSCCTRL,
        &mut peripherals.NVMCTRL,
    );
    let pins = bsp::Pins::new(peripherals.PORT);
    let mut red_led = pins.d13.into_push_pull_output();
    let mut delay = Delay::new(core.SYST, &mut clocks);
    loop {
        delay.delay_ms(250u16);
        red_led.set_high().unwrap();
        delay.delay_ms(250u16);
        red_led.set_low().unwrap();
    }
}