外设作为状态机
微控制器的外设可以被认为是一组状态机。例如,简化的 GPIO 引脚 的配置可以用以下状态树表示
- 禁用
- 启用
- 配置为输出
- 输出:高
- 输出:低
- 配置为输入
- 输入:高阻抗
- 输入:下拉
- 输入:上拉
- 配置为输出
如果外设从 禁用
模式开始,要移动到 输入:高阻抗
模式,我们必须执行以下步骤
- 禁用
- 启用
- 配置为输入
- 输入:高阻抗
如果我们想从 输入:高阻抗
移动到 输入:下拉
,我们必须执行以下步骤
- 输入:高阻抗
- 输入:下拉
类似地,如果我们想将 GPIO 引脚从配置为 输入:下拉
移动到 输出:高
,我们必须执行以下步骤
- 输入:下拉
- 配置为输入
- 配置为输出
- 输出:高
硬件表示
通常,上面列出的状态是通过写入映射到 GPIO 外设的给定寄存器来设置的。让我们定义一个虚构的 GPIO 配置寄存器来说明这一点
名称 | 位号 | 值 | 含义 | 备注 |
---|---|---|---|---|
enable | 0 | 0 | disabled | 禁用 GPIO |
1 | enabled | 启用 GPIO | ||
direction | 1 | 0 | input | 将方向设置为输入 |
1 | output | 将方向设置为输出 | ||
input_mode | 2..3 | 00 | hi-z | 将输入设置为高阻抗 |
01 | pull-low | 输入引脚下拉 | ||
10 | pull-high | 输入引脚上拉 | ||
11 | n/a | 无效状态。不要设置 | ||
output_mode | 4 | 0 | set-low | 输出引脚驱动为低 |
1 | set-high | 输出引脚驱动为高 | ||
input_status | 5 | x | in-val | 如果输入 < 1.5v,则为 0;如果输入 >= 1.5v,则为 1 |
我们可以在 Rust 中公开以下结构来控制此 GPIO
/// 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 配置为输入时设置 output_mode
字段会发生什么?
通常,使用此结构将允许我们到达上面状态机中未定义的状态:例如,下拉的输出或设置为高的输入。对于某些硬件,这可能无关紧要。在其他硬件上,这可能会导致意外或未定义的行为!
尽管此接口便于编写,但它没有强制执行硬件实现设定的设计契约。