Portability

(This section is optional. Feel free to skip to the next section, where we clean our code up a bit and call it a day.)

You may wonder whether all this fancy ecosystem is worth its weight. The setup for our blinky is pretty fancy, and uses a lot of Rust crates and features for such a simple job.

One cool advantage, though, is that our code becomes really portable. On a different board, the setup may be different, but the actual blinky loop is identical!

Let's take a look at a blinky for the Sipeed Longan Nano. This is a little $5 board that, like the MB2, is an embedded board with an MCU. Otherwise, it is completely different: different processor (the GD32VF103, with a RISC-V instruction set entirely unlike the ARM instruction set we're using), different peripherals, different board. But it has an LED attached to a GPIO pin, so we can blinky it.

#![no_std] #![no_main] use panic_halt as _; use riscv_rt::entry; use gd32vf103xx_hal::{pac, prelude::*, delay::McycleDelay}; use embedded_hal::{blocking::delay::DelayMs, digital::v2::OutputPin}; #[entry] fn main() -> ! { let dp = pac::Peripherals::take().unwrap(); let mut rcu = dp.RCU.configure().ext_hf_clock(8.mhz()).sysclk(108.mhz()).freeze(); let gpioc = dp.GPIOC.split(&mut rcu); let mut led = gpioc.pc13.into_push_pull_output(); let mut delay = McycleDelay::new(&rcu.clocks); loop { delay.delay_ms(500); led.set_high().unwrap(); delay.delay_ms(500); led.set_low().unwrap(); } }

The differences in setup here are partly because different hardware, and partly because this code uses an older HAL crate that hasn't yet been updated for embedded-hal 1.0. Yet the main loop is identical as advertised, and the rest of the code is pretty recognizable. Because of the portability provided by Rust's easy cross-compilation and the embedded Rust ecosystem, blinky is just blinky.

You can find a complete working nanoblinky example on GitHub, if you want to see all the details or even get your own board and try it yourself.