GPIO 接口的建议
默认情况下,引脚类型为零大小(C-ZST-PIN)
HAL 公开的 GPIO 接口应为每个接口或端口上的每个引脚提供专用的零大小类型,当所有引脚分配都是静态已知时,可实现零成本的 GPIO 抽象。
每个 GPIO 接口或端口应实现一个 split
方法,返回一个包含每个引脚的结构体。
示例
#![allow(unused)] fn main() { pub struct PA0; pub struct PA1; // ... pub struct PortA; impl PortA { pub fn split(self) -> PortAPins { PortAPins { pa0: PA0, pa1: PA1, // ... } } } pub struct PortAPins { pub pa0: PA0, pub pa1: PA1, // ... } }
引脚类型提供擦除引脚和端口的方法 (C-ERASED-PIN)
引脚应提供类型擦除方法,将其属性从编译时移动到运行时,并在应用程序中提供更大的灵活性。
示例
#![allow(unused)] fn main() { /// Port A, pin 0. pub struct PA0; impl PA0 { pub fn erase_pin(self) -> PA { PA { pin: 0 } } } /// A pin on port A. pub struct PA { /// The pin number. pin: u8, } impl PA { pub fn erase_port(self) -> Pin { Pin { port: Port::A, pin: self.pin, } } } pub struct Pin { port: Port, pin: u8, // (these fields can be packed to reduce the memory footprint) } enum Port { A, B, C, D, } }
引脚状态应编码为类型参数 (C-PIN-STATE)
引脚可以配置为输入或输出,其特性取决于芯片或系列。 此状态应在类型系统中编码,以防止在不正确的状态下使用引脚。
其他特定于芯片的状态(例如,驱动强度)也可以使用附加的类型参数以这种方式编码。
用于更改引脚状态的方法应作为 into_input
和 into_output
方法提供。
此外,应提供 with_{input,output}_state
方法,以在不移动引脚的情况下临时以不同的状态重新配置引脚。
应为每个引脚类型提供以下方法(即,擦除和非擦除的引脚类型都应提供相同的 API)
pub fn into_input<N: InputState>(self, input: N) -> Pin<N>
pub fn into_output<N: OutputState>(self, output: N) -> Pin<N>
-
pub fn with_input_state<N: InputState, R>( &mut self, input: N, f: impl FnOnce(&mut PA1<N>) -> R, ) -> R
-
pub fn with_output_state<N: OutputState, R>( &mut self, output: N, f: impl FnOnce(&mut PA1<N>) -> R, ) -> R
引脚状态应受密封 traits 的约束。 HAL 的用户无需添加自己的状态。 Traits 可以提供实现引脚状态 API 所需的 HAL 特定方法。
示例
#![allow(unused)] fn main() { use std::marker::PhantomData; mod sealed { pub trait Sealed {} } pub trait PinState: sealed::Sealed {} pub trait OutputState: sealed::Sealed {} pub trait InputState: sealed::Sealed { // ... } pub struct Output<S: OutputState> { _p: PhantomData<S>, } impl<S: OutputState> PinState for Output<S> {} impl<S: OutputState> sealed::Sealed for Output<S> {} pub struct PushPull; pub struct OpenDrain; impl OutputState for PushPull {} impl OutputState for OpenDrain {} impl sealed::Sealed for PushPull {} impl sealed::Sealed for OpenDrain {} pub struct Input<S: InputState> { _p: PhantomData<S>, } impl<S: InputState> PinState for Input<S> {} impl<S: InputState> sealed::Sealed for Input<S> {} pub struct Floating; pub struct PullUp; pub struct PullDown; impl InputState for Floating {} impl InputState for PullUp {} impl InputState for PullDown {} impl sealed::Sealed for Floating {} impl sealed::Sealed for PullUp {} impl sealed::Sealed for PullDown {} pub struct PA1<S: PinState> { _p: PhantomData<S>, } impl<S: PinState> PA1<S> { pub fn into_input<N: InputState>(self, input: N) -> PA1<Input<N>> { todo!() } pub fn into_output<N: OutputState>(self, output: N) -> PA1<Output<N>> { todo!() } pub fn with_input_state<N: InputState, R>( &mut self, input: N, f: impl FnOnce(&mut PA1<N>) -> R, ) -> R { todo!() } pub fn with_output_state<N: OutputState, R>( &mut self, output: N, f: impl FnOnce(&mut PA1<N>) -> R, ) -> R { todo!() } } // Same for `PA` and `Pin`, and other pin types. }