外设作为状态机
微控制器的外设可以被认为是状态机的集合。例如,简化的 GPIO 引脚的配置可以表示为以下状态树
- 禁用
- 启用
- 配置为输出
- 输出:高电平
- 输出:低电平
- 配置为输入
- 输入:高阻抗
- 输入:下拉
- 输入:上拉
- 配置为输出
如果外设从 禁用
模式开始,要移动到 输入:高阻抗
模式,我们必须执行以下步骤
- 禁用
- 启用
- 配置为输入
- 输入:高阻抗
如果我们想从 输入:高阻抗
移动到 输入:下拉
,我们必须执行以下步骤
- 输入:高阻抗
- 输入:下拉
类似地,如果我们想将 GPIO 引脚从配置为 输入:下拉
移动到 输出:高电平
,我们必须执行以下步骤
- 输入:下拉
- 配置为输入
- 配置为输出
- 输出:高电平
硬件表示
通常,上面列出的状态是通过向映射到 GPIO 外设的给定寄存器写入值来设置的。让我们定义一个虚构的 GPIO 配置寄存器来说明这一点
名称 | 位号 | 值 | 含义 | 备注 |
---|---|---|---|---|
使能 | 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
/// 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
字段会发生什么?
通常,使用此结构将允许我们达到上面状态机未定义的状态:例如,一个被下拉的输出,或一个被设置为高电平的输入。对于某些硬件,这可能无关紧要。在其他硬件上,它可能会导致意外或未定义的行为!
虽然此接口编写起来很方便,但它并未强制执行我们的硬件实现设置的设计契约。