Created
August 6, 2024 01:34
-
-
Save matthewjberger/f27e87346b5c2d78b21bd57a6822fc55 to your computer and use it in GitHub Desktop.
Analysis of how embassy uses rust types for pins and peripherals
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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