Rust
Embedded
Program microcontrollers with embedded-hal.
Embedded
Hardware access in embedded Rust is layered: vendor register definitions (PAC), a chip HAL, and on top of those the embedded-hal traits. Drivers (sensors, displays) are written against these traits, so the same driver runs on different microcontrollers:
// embedded-hal
pub trait OutputPin {
type Error;
fn set_high(&mut self) -> Result<(), Self::Error>;
fn set_low(&mut self) -> Result<(), Self::Error>;
}
fn yak_sondur<P: OutputPin>(led: &mut P) -> Result<(), P::Error> {
led.set_high()?;
led.set_low()
}
For testing on a desktop, a mock pin implementing the same trait is written.
A mock pin
A mock pin recording state changes verifies driver logic without real hardware:
struct SahtePin {
kayit: String,
}
impl OutputPin for SahtePin {
type Error = core::convert::Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
self.kayit.push('H');
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
self.kayit.push('L');
Ok(())
}
}
Infallible is an empty error type saying "this operation never fails".
Exercise Blink an LED
Write the OutputPin trait above and the SahtePin type. Blink the mock pin three times with fn yak_sondur<P: OutputPin>(led: &mut P) and print the record.
Show example solution
pub trait OutputPin {
type Error;
fn set_high(&mut self) -> Result<(), Self::Error>;
fn set_low(&mut self) -> Result<(), Self::Error>;
}
struct SahtePin {
kayit: String,
}
impl OutputPin for SahtePin {
type Error = core::convert::Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
self.kayit.push('H');
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
self.kayit.push('L');
Ok(())
}
}
fn yak_sondur<P: OutputPin>(led: &mut P) -> Result<(), P::Error> {
led.set_high()?;
led.set_low()
}
fn main() {
let mut pin = SahtePin { kayit: String::new() };
for _ in 0..3 {
yak_sondur(&mut pin).unwrap();
}
println!("{}", pin.kayit);
}
Test yourself
-
What is the benefit of writing a driver against a trait instead of a concrete chip type?
- The same driver works on any compatible hardware and in desktop tests
- It's faster
- Less memory
- No benefit
Show answer
The same driver works on any compatible hardware and in desktop tests — Since it's generic there's no runtime cost; the compiler generates specialized code for each chip.
-
Never fails
impl OutputPin for SahtePin {
type Error = core::convert::____;
..
}
Show answer
impl OutputPin for SahtePin {
type Error = core::convert::Infallible;
..
}
-
Layer and role
- PAC
- Register definitions
- HAL
- Chip abstraction
- embedded-hal
- Common traits
- Driver
- Written against traits
Next lesson: WebAssembly