ステートマシンとしてのペリフェラル

マイクロコントローラのペリフェラルは、一連のステートマシンとして考えることができます。 例えば、簡略化されたGPIOピンの設定は、次の状態ツリーとして表すことができます。

  • 無効
  • 有効
    • 出力として設定
      • 出力:ハイ
      • 出力:ロー
    • 入力として設定
      • 入力:高抵抗(訳注:ハイインピーダンス)
      • 入力:プルダウン
      • 入力:プルアップ

このペリフェラルが無効モードから始まるとすると、入力:高抵抗モードに移行するには、次のステップを踏みます。

  1. 無効
  2. 有効
  3. 入力として設定
  4. 入力:高抵抗

入力:高抵抗から入力:プルダウンへと移る場合、次のステップを実行しなければなりません。

  1. 入力:高抵抗
  2. 入力:プルダウン

同じように、入力:プルダウンと設定されているGPIOピンを、出力:ハイにするには、次のステップを実行しなければなりません。

  1. 入力:プルダウン
  2. 入力として設定
  3. 出力として設定
  4. 出力:ハイ

ハードウェアの表現

通常、上記の状態は、GPIOペリフェラルに割り当てられたレジスタに値を書き込むことで設定できます。 これを説明するために、架空のGPIO設定レジスタを定義しましょう。

名前ビットフィールド意味説明
有効00無効GPIOを無効にする
1有効GPIOを有効にする
方向10入力方向を入力に設定する
1出力方向を出力に設定する
入力モード2..300hi-z入力を高抵抗に設定する
01プルダウン入力ピンはプルダウンになる
10プルアップ入力ピンはプルアップになる
11n/a無効な状態。設定しないこと。
出力モード40ロー出力ピンをローにする
1ハイ出力ピンをハイにする
入力状態5x入力値入力が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を入力に設定している時に、出力モードを設定すると、どうなるのでしょう?

通常、この構造体を利用すると、上のステートマシンで定義されていない状態に到達できてしまいます。 例えば、プルダウンの出力や、ハイに設定された入力、です。

このインタフェースは書き込みには便利ですが、ハードウェア実装によって定められた設計の契約を強制しません。