Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Периферия как конечные автоматы

Периферию микроконтроллера можно рассматривать как набор конечных автоматов. Например, конфигурацию упрощённого вывода GPIO можно представить следующим деревом состояний:

  • Отключён (Disabled)
  • Включён (Enabled)
    • Настроен как выход (Configured as Output)
      • Выход: высокий уровень (Output: High)
      • Выход: низкий уровень (Output: Low)
    • Настроен как вход (Configured as Input)
      • Вход: высокий импеданс (Input: High Resistance)
      • Вход: подтянут к земле (Input: Pulled Low)
      • Вход: подтянут к питанию (Input: Pulled High)

Если периферия находится в режиме Disabled, то чтобы перейти в состояние Input: High Resistance, нужно выполнить следующие шаги:

  1. Disabled
  2. Enabled
  3. Configured as Input
  4. Input: High Resistance

Если мы хотим перейти из Input: High Resistance в Input: Pulled Low, нужно выполнить такие шаги:

  1. Input: High Resistance
  2. Input: Pulled Low

Аналогично, если мы хотим перевести вывод GPIO из состояния Input: Pulled Low в Output: High, нужно выполнить такие шаги:

  1. Input: Pulled Low
  2. Configured as Input
  3. Configured as Output
  4. Output: High

Представление в аппаратуре

Обычно перечисленные выше состояния задаются записью значений в определённые регистры, отображённые на периферию GPIO. Чтобы проиллюстрировать это, определим воображаемый регистр конфигурации GPIO:

ИмяНомер бита(ов)ЗначениеСмыслПримечания
enable00отключёнОтключает GPIO
1включёнВключает GPIO
direction10входЗадаёт направление: вход
1выходЗадаёт направление: выход
input_mode2..300hi-zЗадаёт вход с высоким сопротивлением
01pull-lowВходной вывод подтянут к земле
10pull-highВходной вывод подтянут к питанию
11н/дНедопустимое состояние. Не устанавливать
output_mode40set-lowВыходной вывод управляется низким уровнем
1set-highВыходной вывод управляется высоким уровнем
input_status5xin-val0, если вход < 1,5 В; 1, если вход >= 1,5 В

Мы могли бы предоставить следующую структуру в Rust для управления этим GPIO:

/// Интерфейс GPIO
struct GpioConfig {
    /// Структура конфигурации GPIO, сгенерированная 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()
    }
}

Однако это позволит изменять некоторые регистры бессмысленным образом. Например, что произойдёт, если мы установим поле output_mode, когда наш GPIO настроен как вход?

В общем случае использование такой структуры позволило бы прийти к состояниям, не определённым нашим конечным автоматом выше: например, выход, подтянутый к земле, или вход, установленный в высокий уровень. Для некоторой аппаратуры это может не иметь значения. На другой аппаратуре это может привести к неожиданному или неопределённому поведению!

Хотя такой интерфейс удобно писать, он не обеспечивает соблюдения проектных контрактов, заданных нашей аппаратной реализацией.