作为状态机的外设

一个微控制器的外设可以被想成是一组状态机。比如,一个简化的GPIO管脚的配置可以被表达成下列的状态树:

  • 关闭
  • 使能
    • 配置成输出
      • 输出: 高
      • 输出: 低
    • 配置成输入
      • 输入: 高阻抗
      • 输入: 下拉
      • 输入: 上拉

如果外设开始于关闭模式,切换到输入: 高阻抗模式,我们必须执行下面的步骤:

  1. 关闭
  2. 使能
  3. 配置成输入
  4. 输入: 高阻抗

如果我们想要从输入: 高阻抗切换到输入: 下拉,我们必须执行下列的步骤:

  1. 输入: 高阻抗
  2. 输入: 下拉

同样地,如果我们想要把一个GPIO管脚从输入: 下拉切换到输出: 高,我们必须执行下列的步骤:

  1. 输入: 下拉
  2. 配置成输入
  3. 配置成输出
  4. 输出: 高

硬件表征(Hardware Representation)

通常,上面列的状态通过向指定的映射到一个GPIO外设的寄存器中写入值来配置。让我们定义一个假想的GPIO配置寄存器来解释下它:

名字位数(s)含义注释
使能00关闭关闭GPIO
1使能使能GPIO
方向10输入方向设置成输入
1输出方向设置成输出
输入模式2..300hi-z输入设置为高阻抗
01下拉下拉输入管脚
10上拉上拉输入管脚
11n/a无效模式。不要设置
输出模式40拉低拉低输出管脚
1拉高拉高输出管脚
输入状态5xin-val如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1

我们 在Rust中暴露下列的结构体来控制这个GPIO:

  1. /// GPIO接口
  2. struct GpioConfig {
  3. /// 由svd2rust生成的GPIO配置结构体
  4. periph: GPIO_CONFIG,
  5. }
  6. impl GpioConfig {
  7. pub fn set_enable(&mut self, is_enabled: bool) {
  8. self.periph.modify(|_r, w| {
  9. w.enable().set_bit(is_enabled)
  10. });
  11. }
  12. pub fn set_direction(&mut self, is_output: bool) {
  13. self.periph.modify(|_r, w| {
  14. w.direction().set_bit(is_output)
  15. });
  16. }
  17. pub fn set_input_mode(&mut self, variant: InputMode) {
  18. self.periph.modify(|_r, w| {
  19. w.input_mode().variant(variant)
  20. });
  21. }
  22. pub fn set_output_mode(&mut self, is_high: bool) {
  23. self.periph.modify(|_r, w| {
  24. w.output_mode.set_bit(is_high)
  25. });
  26. }
  27. pub fn get_input_status(&self) -> bool {
  28. self.periph.read().input_status().bit_is_set()
  29. }
  30. }

然而,这将会允许我们修改某些没有意义的寄存器。比如,如果当我们的GPIO被配置为输入时我们设置output_mode字段,将会发生什么?

正常使用这个结构体将会允许我们访问我们上面的状态机没有定义的状态: e.g. 一个被上拉的输出,或者一个被拉高的输入。对于一些硬件,这并没有关系。对另外一些硬件来说,这将会导致不可预期或者没有定义的行为!

虽然这个接口很方便写入,但是它没有强制我们遵守为硬件的实现所规定的设计协约。