02-作为状态机的外设

作为状态机的外设(Peripherals as State Machines)

译文 · 基于 The Embedded Rust Book

作为状态机的外设

原文链接: https://doc.rust-lang.org/stable/embedded-book/static-guarantees/state-machines.html

微控制器的外设可以被看作一组状态机。例如,一个简化的 GPIO 引脚 的配置可以表示为如下状态树:

  • Disabled(禁用)
  • Enabled(启用)
    • Configured as Output(配置为输出)
      • Output: High(输出:高)
      • Output: Low(输出:低)
    • Configured as Input(配置为输入)
      • Input: High Resistance(输入:高阻)
      • Input: Pulled Low(输入:下拉)
      • Input: Pulled High(输入:上拉)

若外设从 Disabled 模式开始,要移到 Input: High Resistance 模式,必须执行以下步骤:

  1. 禁用(Disabled)
  2. 启用(Enabled)
  3. 配置为输入(Configured as Input)
  4. 输入:高阻(Input: High Resistance)

若想从 Input: High Resistance 移到 Input: Pulled Low,必须执行以下步骤:

  1. 输入:高阻(Input: High Resistance)
  2. 输入:下拉(Input: Pulled Low)

类似地,若想把 GPIO 引脚从 Input: Pulled Low 配置移到 Output: High,必须执行以下步骤:

  1. 输入:下拉(Input: Pulled Low)
  2. 配置为输入(Configured as Input)
  3. 配置为输出(Configured as Output)
  4. 输出:高(Output: High)

硬件表示

通常,上面列出的状态是通过向映射到 GPIO 外设的给定寄存器写入值来设置的。我们定义一个假想的 GPIO 配置寄存器来说明这一点:

名称位编号值含义说明
enable00disabled禁用 GPIO
1enabled启用 GPIO
direction10input将方向设为输入
1output将方向设为输出
input_mode2..300hi-z将输入设为高阻
01pull-low输入引脚下拉
10pull-high输入引脚上拉
11n/a无效状态,请勿设置
output_mode40set-low输出引脚驱动为低
1set-high输出引脚驱动为高
input_status5xin-val输入 < 1.5v 时为 0,输入 >= 1.5v 时为 1

我们可以在 Rust 中暴露如下结构体来控制这个 GPIO:

/// GPIO 接口
struct GpioConfig {
    /// 由 svd2rust 生成的 GPIO 配置结构
    periph: GPIO_CONFIG,
}

impl GpioConfig {
    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 配置为输入时设置 output_mode 字段会发生什么?

总的来说,使用这个结构体会让我们到达上面状态机未定义的状态:例如被下拉的输出,或被设为高的输入。对某些硬件这可能无关紧要。在其他硬件上,它可能导致意外或未定义行为!

虽然这个接口写起来方便,但它并不强制执行硬件实现所设定的设计契约。

最后修改 August 11, 2026: 更新 (70a5af133)