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() {
/// ポートAのピン0。
pub struct PA0;
impl PA0 {
pub fn erase_pin(self) -> PA {
PA { pin: 0 }
}
}
/// ポートA上のピン。
pub struct PA {
/// ピン番号。
pin: u8,
}
impl PA {
pub fn erase_port(self) -> Pin {
Pin {
port: Port::A,
pin: self.pin,
}
}
}
pub struct Pin {
port: Port,
pin: u8,
// (これらのフィールドはメモリフットプリントを削減するためにパックできる)
}
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
ピン状態は sealed トレイトによって境界付けするべきです。HAL の利用者が独自の状態を追加する必要はないはずです。これらのトレイトは、ピン状態 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!()
}
}
// `PA`、`Pin`、および他のピン型についても同様。
}