Peripheriegeräte als Zustandsautomaten
Die Peripherie eines Mikrocontrollers lässt sich als eine Menge von Zustandsautomaten auffassen. So ließe sich beispielsweise die Konfiguration eines vereinfachten GPIO-Pins als der folgende Zustandsbaum darstellen:
- Deaktiviert
Aktiviert
Als Ausgang konfiguriert
- Ausgang: High
- Ausgang: Low
Als Eingang konfiguriert
- Eingang: Hoher Widerstand
- Eingang: Auf Low-Pegel gezogen
- Eingang: Auf High-Pegel gezogen
Wenn die Peripherieeinheit im Modus Deaktiviert startet, müssen wir die folgenden Schritte ausführen, um in den Modus Eingang: Hoher Widerstand zu wechseln:
- Deaktiviert
- Aktiviert
- Als Eingang konfiguriert
- Eingang: Hoher Widerstand
Wenn wir von „Eingang: Hoher Widerstand“ zu „Eingang: Auf Low-Pegel gezogen” wechseln möchten, müssen wir die folgenden Schritte ausführen:
- Eingang: Hoher Widerstand 2. Eingang: Auf Low-Pegel gezogen
Wenn wir einen GPIO-Pin von der Konfiguration „Eingang: Auf Low-Pegel gezogen“ auf „Ausgang: High” umstellen wollen, müssen wir ebenfalls die folgenden Schritte ausführen:
- Eingang: Auf Low-Pegel gezogen
- Als Eingang konfiguriert
- Als Ausgang konfiguriert
- Ausgang: High
Hardware-Darstellung
Typischerweise werden die oben aufgeführten Zustände eingestellt, indem Werte in bestimmte Register geschrieben werden, die einer GPIO-Peripherieeinheit zugeordnet sind. Definieren wir zur Veranschaulichung ein fiktives GPIO-Konfigurationsregister:
| Name | Bit-Nummer | Wert | Bedeutung | Hinweise |
|---|---|---|---|---|
| enable | 0 | 0 | disabled | Deaktiviert den GPIO |
| 1 | enabled | Aktiviert den GPIO | ||
| direction | 1 | 0 | input | Legt die Richtung auf „Eingang“ fest. |
| 1 | output | Legt die Richtung auf „Ausgang” fest. | ||
| input_mode | 2..3 | 00 | hi-z | Setzt den Eingang auf hochohmig. |
| 01 | pull-low | Der Eingangspin wird auf Low-Pegel gezogen. | ||
| 10 | pull-high | Der Eingangspin wird auf High-Pegel gezogen | ||
| 11 | n/a | Ungültiger Zustand. Nicht setzen. | ||
| output_mode | 4 | 0 | set-low | Der Ausgangspin wird auf Low-Pegel gesteuert. |
| 1 | set-high | Der Ausgangspin wird auf High-Pegel gesteuert. | ||
| input_status | 5 | x | in-val | 0, wenn der Eingang < 1,5 V ist; 1, wenn der Eingang ≥ 1,5 V ist. |
Wir könnten die folgende Struktur in Rust bereitstellen, um diesen GPIO zu steuern:
/// GPIO interface
struct GpioConfig {
/// Von svd2rust generierte GPIO-Konfigurationsstruktur
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()
}
}
Dies würde uns jedoch erlauben, bestimmte Register zu verändern, was keinen Sinn ergibt. Was passiert beispielsweise, wenn wir das Feld output_mode setzen, während unser GPIO als Eingang konfiguriert ist?
Im Allgemeinen würde die Verwendung dieser Struktur es uns ermöglichen, Zustände zu erreichen, die nicht durch unsere obige Zustandsmaschine definiert sind: z. B. einen Ausgang, der auf LOW gesetzt wird, oder einen Eingang, der auf HIGH gesetzt wird. Bei mancher Hardware mag dies keine Rolle spielen. Bei anderer Hardware könnte es jedoch zu unerwartetem oder undefiniertem Verhalten führen!
Obwohl diese Schnittstelle bequem zu implementieren ist, erzwingt sie nicht die in unserer Hardware-Implementierung festgelegten Designvorgaben.