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-пина] может быть представлена следующим деревом состояний:

  • Отключен
  • Включен
    • Настроен как выход
      • Выход: Высокий
      • Выход: Низкий
    • Настроен как вход
      • Вход: Высокое сопротивление
      • Вход: Подтяжка вниз
      • Вход: Подтяжка вверх

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

  1. Отключен
  2. Включен
  3. Настроен как вход
  4. Вход: Высокое сопротивление

Если мы хотим перейти из Вход: Высокое сопротивление в Вход: Подтяжка вниз, необходимо выполнить следующие шаги:

  1. Вход: Высокое сопротивление
  2. Вход: Подтяжка вниз

Аналогично, если мы хотим перевести GPIO-пин из режима Вход: Подтяжка вниз в Выход: Высокий, необходимо выполнить следующие шаги:

  1. Вход: Подтяжка вниз
  2. Настроен как вход
  3. Настроен как выход
  4. Выход: Высокий

Аппаратное представление

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

ИмяБит(ы)ЗначениеЗначениеПримечания
enable00отключеноОтключает GPIO
1включеноВключает GPIO
direction10входУстанавливает направление на вход
1выходУстанавливает направление на выход
input_mode2..300высокое сопротивлениеУстанавливает вход как высокое сопротивление
01подтяжка внизВходной пин подтянут вниз
10подтяжка вверхВходной пин подтянут вверх
11н/дНедопустимое состояние. Не устанавливать
output_mode40установить низкийВыходной пин притянут к низкому уровню
1установить высокийВыходной пин притянут к высокому уровню
input_status5xвходное значение0, если вход < 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 настроен как вход?

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

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