ステートマシンとしてのペリフェラル
マイクロコントローラのペリフェラルは、一連のステートマシンとして考えることができます。 例えば、簡略化されたGPIOピンの設定は、次の状態ツリーとして表すことができます。
- 無効
- 有効
- 出力として設定
- 出力:ハイ
- 出力:ロー
- 入力として設定
- 入力:高抵抗(訳注:ハイインピーダンス)
- 入力:プルダウン
- 入力:プルアップ
- 出力として設定
このペリフェラルが無効
モードから始まるとすると、入力:高抵抗
モードに移行するには、次のステップを踏みます。
- 無効
- 有効
- 入力として設定
- 入力:高抵抗
入力:高抵抗
から入力:プルダウン
へと移る場合、次のステップを実行しなければなりません。
- 入力:高抵抗
- 入力:プルダウン
同じように、入力:プルダウン
と設定されているGPIOピンを、出力:ハイ
にするには、次のステップを実行しなければなりません。
- 入力:プルダウン
- 入力として設定
- 出力として設定
- 出力:ハイ
ハードウェアの表現
通常、上記の状態は、GPIOペリフェラルに割り当てられたレジスタに値を書き込むことで設定できます。 これを説明するために、架空のGPIO設定レジスタを定義しましょう。
名前 | ビットフィールド | 値 | 意味 | 説明 |
---|---|---|---|---|
有効 | 0 | 0 | 無効 | GPIOを無効にする |
1 | 有効 | GPIOを有効にする | ||
方向 | 1 | 0 | 入力 | 方向を入力に設定する |
1 | 出力 | 方向を出力に設定する | ||
入力モード | 2..3 | 00 | hi-z | 入力を高抵抗に設定する |
01 | プルダウン | 入力ピンはプルダウンになる | ||
10 | プルアップ | 入力ピンはプルアップになる | ||
11 | n/a | 無効な状態。設定しないこと。 | ||
出力モード | 4 | 0 | ロー | 出力ピンをローにする |
1 | ハイ | 出力ピンをハイにする | ||
入力状態 | 5 | x | 入力値 | 入力が1.5Vより低ければ0、1.5V以上であれば1 |
このGPIOを制御するために、次のようなRustの構造体を公開することが できます。
/// GPIO interface
/// GPIOインタフェース
struct GpioConfig {
/// GPIO Configuration structure generated by svd2rust
/// svd2rustで生成されたGPIO設定構造体
periph: GPIO_CONFIG,
}
impl Gpio {
pub fn set_enable(&mut self, is_enabled: bool) {
self.periph.modify(|_r, w| {
w.enable().set_bit(is_enabled)
});
}
pub fn set_direction(&mut self, is_output: bool) {
self.periph.modify(|r, w| {
w.direction().set_bit(is_output)
});
}
pub fn set_input_mode(&mut self, variant: InputMode) {
self.periph.modify(|_r, w| {
w.input_mode().variant(variant)
});
}
pub fn set_output_mode(&mut self, is_high: bool) {
self.periph.modify(|_r, w| {
w.output_mode.set_bit(is_high)
});
}
pub fn get_input_status(&self) -> bool {
self.periph.read().input_status().bit_is_set()
}
}
しかし、この実装では、筋が通らないレジスタの修正が可能になってしまいます。例えば、GPIOを入力に設定している時に、出力モード
を設定すると、どうなるのでしょう?
通常、この構造体を利用すると、上のステートマシンで定義されていない状態に到達できてしまいます。 例えば、プルダウンの出力や、ハイに設定された入力、です。
このインタフェースは書き込みには便利ですが、ハードウェア実装によって定められた設計の契約を強制しません。