外设状态机

MCU 的外设可以被看作是一种状态机. 例如, 简化的 GPIO Pin (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 寄存器来说明一下:

NameBit Number(s)ValueMeaningNotes
enable00disabledDisables the GPIO
1enabledEnables the GPIO
direction10inputSets the direction to Input
1outputSets the direction to Output
input_mode2..300hi-zSets the input as high resistance
01pull-lowInput pin is pulled low
10pull-highInput pin is pulled high
11n/aInvalid state. Do not set
output_mode40set-lowOutput pin is driven low
1set-highOutput pin is driven high
input_status5xin-val0 if input is < 1.5v, 1 if input >= 1.5v

我们可以在 Rust 中公开这个结构体来展示这个寄存器结构:


#![allow(unused)]
fn main() {
/// GPIO interface
struct GpioConfig {
    /// GPIO Configuration structure generated by svd2rust
    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 实际处于输入状态时, 我们将模式设置为输出会发生什么?

通常来说, 使用此结构体可以让我们达到状态机没有定义的状态: 例如, 被拉低的输出, 或者一个被设置为高电平的输入. 对于某些硬件, 这些可能不会起作用. 在其他硬件上, 这可能会导致 exception 或未定义行为.

尽管这个接口很方便, 但并不满足我们的设计.