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

Портируемость

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

Общий способ устранения таких различий — использование слоя, называемого уровнем абстракции оборудования или HAL.

Абстракции оборудования — это наборы программных процедур, которые эмулируют некоторые специфические для платформы детали, предоставляя программам прямой доступ к аппаратным ресурсам.

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

Википедия: Уровень абстракции оборудования

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

Как это делается в Rust? Встречайте embedded-hal...

Что такое embedded-hal?

Вкратце, это набор трейтов, которые определяют контракты реализации между реализациями HAL, драйверами и приложениями (или прошивками). Эти контракты включают как возможности (т.е. если трейт реализован для определенного типа, реализация HAL предоставляет определенную функциональность), так и методы (т.е. если вы можете создать тип, реализующий трейт, гарантируется наличие методов, указанных в этом трейте).

Типичная структура уровней может выглядеть следующим образом:

Некоторые из определенных трейтов в embedded-hal включают:

  • GPIO (пины ввода и вывода)
  • Последовательная связь
  • I2C
  • SPI
  • Таймеры/обратные отсчеты
  • Аналогово-цифровое преобразование

Основная причина использования трейтов embedded-hal и крейтов, их реализующих и использующих, — это контроль сложности. Если учесть, что приложение должно реализовать использование периферийного устройства в оборудовании, а также само приложение и, возможно, драйверы для дополнительных аппаратных компонентов, становится понятно, что возможности повторного использования весьма ограничены. Математически, если M — это количество реализаций HAL для периферийных устройств, а N — количество драйверов, то без использования embedded-hal сложность реализации может достигать M×N. Использование трейтов embedded-hal снижает сложность реализации до уровня, близкого к M+N. Конечно, есть и дополнительные преимущества, такие как меньшее количество проб и ошибок благодаря хорошо определенным и готовым к использованию API.

Пользователи embedded-hal

Как упомянуто выше, есть три основных пользователя HAL:

Реализация HAL

Реализация HAL обеспечивает взаимодействие между оборудованием и пользователями трейтов HAL. Типичные реализации состоят из трех частей:

  • Один или несколько типов, специфичных для оборудования
  • Функции для создания и инициализации таких типов, часто предоставляющие различные параметры конфигурации (скорость, режим работы, используемые пины и т.д.)
  • Одна или несколько реализаций (impl) трейтов embedded-hal для этого типа

Такая реализация HAL может быть представлена в различных вариантах:

  • Через низкоуровневый доступ к оборудованию, например, через регистры
  • Через операционную систему, например, с использованием sysfs в Linux
  • Через адаптер, например, заглушки типов для модульного тестирования
  • Через драйвер для аппаратных адаптеров, например, мультиплексор I2C или расширитель GPIO

Драйвер

Драйвер реализует набор пользовательских функций для внутреннего или внешнего компонента, подключенного к периферийному устройству, реализующему трейты embedded-hal. Типичные примеры таких драйверов включают различные датчики (температуры, магнитометр, акселерометр, освещенности), устройства отображения (светодиодные матрицы, ЖК-дисплеи) и исполнительные механизмы (двигатели, передатчики).

Драйвер должен быть инициализирован экземпляром типа, реализующего определенный трейт embedded-hal, что обеспечивается через ограничение трейта, и предоставляет собственный экземпляр типа с пользовательским набором методов, позволяющих взаимодействовать с управляемым устройством.

Приложение

Приложение объединяет различные части и обеспечивает достижение желаемой функциональности. При переносе между различными системами именно эта часть требует наибольших усилий по адаптации, поскольку приложение должно правильно инициализировать реальное оборудование через реализацию HAL, а инициализация различного оборудования может существенно отличаться. Кроме того, выбор пользователя часто играет большую роль, поскольку компоненты могут быть физически подключены к разным терминалам, шины оборудования иногда требуют внешнего оборудования для соответствия конфигурации, или существуют различные компромиссы в использовании внутренних периферийных устройств (например, доступно несколько таймеров с разными возможностями, или периферийные устройства конфликтуют друг с другом).