Skip to content

Instantly share code, notes, and snippets.

@matthewjberger
Created August 6, 2024 01:34
Show Gist options
  • Save matthewjberger/f27e87346b5c2d78b21bd57a6822fc55 to your computer and use it in GitHub Desktop.
Save matthewjberger/f27e87346b5c2d78b21bd57a6822fc55 to your computer and use it in GitHub Desktop.
Analysis of how embassy uses rust types for pins and peripherals
use std::sync::atomic::{AtomicBool, Ordering};
use std::marker::PhantomData;
// Simulate hardware-specific types
mod hal {
pub mod peripherals {
pub struct TIMER0;
pub struct UART0;
pub struct GPIO;
impl TIMER0 {
pub(crate) unsafe fn steal() -> Self { TIMER0 }
}
impl UART0 {
pub(crate) unsafe fn steal() -> Self { UART0 }
}
impl GPIO {
pub(crate) unsafe fn steal() -> Self { GPIO }
}
}
}
// Type-state for GPIO pins
pub struct Input;
pub struct Output;
pub struct Analog;
// GPIO pin abstraction using const generics and type-state
pub struct Pin<const N: u8, STATE> {
_state: PhantomData<STATE>,
}
impl<const N: u8> Pin<N, Input> {
pub fn new() -> Self {
Pin { _state: PhantomData }
}
pub fn into_output(self) -> Pin<N, Output> {
println!("Converting GPIO {} to output", N);
Pin { _state: PhantomData }
}
}
impl<const N: u8> Pin<N, Output> {
pub fn new() -> Self {
Pin { _state: PhantomData }
}
pub fn set_high(&mut self) {
println!("Setting GPIO {} high", N);
}
pub fn set_low(&mut self) {
println!("Setting GPIO {} low", N);
}
}
// UART abstraction with configurable baud rate
pub struct Uart<const BAUD: u32> {
_baud: PhantomData<u32>,
}
impl<const BAUD: u32> Uart<BAUD> {
pub fn new() -> Self {
Uart { _baud: PhantomData }
}
pub fn send(&self, data: &str) {
println!("UART sending at {} baud: {}", BAUD, data);
}
}
// Timer abstraction
pub struct Timer {
counter: u32,
}
impl Timer {
pub fn new() -> Self {
Timer { counter: 0 }
}
pub fn start(&mut self) {
println!("Timer started");
}
pub fn tick(&mut self) {
self.counter += 1;
println!("Timer ticked. Count: {}", self.counter);
}
}
// Trait for peripherals
pub trait Peripheral {
fn init(&mut self);
}
// Macro to define peripherals
macro_rules! define_peripherals {
($($name:ident: $type:ty),*) => {
pub struct Peripherals {
$(pub $name: $type,)*
}
impl Peripherals {
pub fn take() -> Option<Self> {
static TAKEN: AtomicBool = AtomicBool::new(false);
if TAKEN.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire).is_ok() {
Some(unsafe { Self::steal() })
} else {
None
}
}
pub unsafe fn steal() -> Self {
Self {
$($name: <$type>::new(),)*
}
}
}
$(
impl Peripheral for $type {
fn init(&mut self) {
println!("{} initialized", stringify!($name));
}
}
)*
}
}
// Use the macro to define peripherals for our simulated chip
define_peripherals! {
TIMER0: Timer,
UART0: Uart<115200>,
GPIO0: Pin<0, Input>,
GPIO1: Pin<1, Output>
}
fn main() {
let mut peripherals = Peripherals::take().expect("Peripherals already taken");
// Initialize all peripherals
peripherals.TIMER0.init();
peripherals.UART0.init();
peripherals.GPIO0.init();
peripherals.GPIO1.init();
// Use the peripherals
peripherals.TIMER0.start();
peripherals.TIMER0.tick();
peripherals.UART0.send("Hello, Embassy!");
// Type-state transitions
let mut output_pin = peripherals.GPIO0.into_output();
output_pin.set_high();
peripherals.GPIO1.set_low();
// Attempting to take peripherals again will fail
assert!(Peripherals::take().is_none(), "Should not be able to take peripherals twice");
println!("All operations completed successfully!");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment