# 外设作为状态机

微控制器的外围设备可以认为是一组状态机。例如，简化的[GPIO引脚]的配置可以表示为以下状态树:

[GPIO引脚]:https://en.wikipedia.org/wiki/General-purpose_input/output

* 禁用
* 已启用
    * 配置为输出
        * 输出:高
        * 输出:低
    * 配置为输入
        * 输入:高电阻
        * 输入:拉低
        * 输入:拉高

如果外围设备以“禁用”模式启动，想要转移至“输入:高电阻”模式，我们必须执行以下步骤:

1. 禁用模式
2. 启用
3. 配置为输入
4. 输入:高电阻

如果要从“输入:高电阻”转移至“输入:拉低”，则必须执行以下步骤:

1. 输入:高阻
2. 输入:拉低

同样，如果要将GPIO引脚从“输入:拉低”转移到“输出:高”，则必须执行以下步骤:

1. 输入:拉低
2. 配置为输入
3. 配置为输出
4. 输出:高

## 硬件表示

通常，上面列出的状态是通过将修改映射到GPIO外设的寄存器来设置的。让我们虚拟一个的PIO配置寄存器来说明这一点:

|名字     | 位号         | 值    |含义    |注意事项 |
| :------  |------------:| ----:|------: | :---- |
|启用       | 0          | 0     | 禁用    | 禁用GPIO |
|        |           | 1     |启用    |启用GPIO |
|方向     | 1         | 0    |输入    |将方向设置为输入|
|         |          | 1     |输出    |将方向设置为输出|
| 输入模式  | 2..3       | 00    | 高电阻 |将输入设置为高阻|
|         |           | 01   |拉低     |输入引脚被拉低|
|         |           | 10   |拉高     |输入引脚被拉高|
|         |           | 11    | 状态无效  |不设|
| 输出模式 | 4         | 0      |低     |引脚被驱动为低电平|
|         |           | 1      |高     |输出引脚被驱动为高电平
| 输入状态   | 5          | x      |输入值  |如果输入<1.5v，则为0；如果输入> = 1.5v，则为1 |

我们可以在Rust中定义以下结构来控制此GPIO:

```rust , ignore
/// 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配置为输入时,修改输出模式(表中位4)会发生什么？

一般来说,这个结构体的定义虽然可以工作,但是让我们很容易进入没有定义的状态,比如输出模式时,输入引脚被配置为拉低;输入模式时,输出引脚被配置为高。对于某些硬件，这可能无所谓,但是在其他硬件上，这可能会导致意外或未定义的行为！

总的来说,尽管编写该接口很方便，但是它并没有严格遵循硬件的设计约定(design contracts)。