Взаимодействие с современными датчиками:драйверы АЦП с опросом
В последнем посте мы рассмотрели, как в современном встроенном приложении разработчик должен создать интерфейс, который отделяет детали реализации низкоуровневого драйвера от кода приложения. Этот интерфейс обеспечивает архитектурную абстракцию, которая увеличивает масштабируемость и переносимость кода приложения, делая его менее зависимым от оборудования.
Теперь мы собираемся рассмотреть несколько различных способов, с помощью которых разработчик может реализовать драйвер АЦП на основе методов, которые мы обсуждали в 3 методах проектирования драйверов для микроконтроллеров. В этой статье мы более подробно рассмотрим, как можно использовать метод опроса, и обсудим разницу между блокирующими и неблокирующими драйверами.
Блокировать или не блокировать, вот в чем вопрос
При разработке любого драйвера для микроконтроллера разработчик должен решить, будет ли его драйвер блокирующим или неблокирующим. Блокирующий драйвер по существу задерживает выполнение кода до тех пор, пока драйвер не завершит свою задачу. Например, типичная реализация printf, сопоставленная с UART, является блокирующей.
Когда вы звоните вроде:
printf («Привет, мир!»);
Разработчик знает, что любая строка кода, следующая за этим оператором, не будет выполняться до тех пор, пока не появится сообщение «Hello World!». заявление было распечатано UART. "Привет мир!" содержит двенадцать байтов, 96 бит, но время, в течение которого оператор будет блокироваться, зависит от скорости передачи UART. Для UART, настроенного на 1 Мбит / с, вы ожидаете около 96 микросекунд. Для UART, настроенного на 9600 бит / с, вы ожидаете около 10 000 микросекунд! Это большая разница в зависимости от того, как настроено оборудование, и она может существенно повлиять на выполнение программы, если драйвер UART настроен как блокирующий драйвер.
Неблокирующий драйвер - это драйвер, который не останавливает выполнение программы, пока драйвер выполняет свою задачу. Например, printf и драйвер UART из предыдущего примера можно настроить так, чтобы он не блокировал и вместо этого позволял приложению продолжать выполнение, пока каждый байт передается через UART. Это может сделать приложение более эффективным при определенных обстоятельствах, но требует дополнительных настроек, таких как использование прерываний, DMA или, по крайней мере, буфера передачи.
Выбор способа разработки драйвера зависит от вашего приложения и оборудования. Например, если UART настроен на 1 Мбит / с, написание неблокирующего драйвера, вероятно, не сильно выиграет с точки зрения эффективности и на самом деле может вызвать больше проблем, чем исправить, за счет дополнительной сложности программы. Однако, если приложение требует 9600 бит / с, когда код приложения блокируется на 10 миллисекунд, наличие неблокирующего драйвера может значительно повысить эффективность программы, и риск возникновения дополнительных проблем с синхронизацией намного меньше и более управляем.
Обзор встроенного драйвера АЦП
Важно отметить, что в одном блоге я не могу пройти все шаги, необходимые для написания полного драйвера ADC. Я мог бы легко написать по нему доклад на двадцати страницах или провести целый веб-семинар, и он, вероятно, не охватил бы все детали, но мы, по крайней мере, можем взглянуть на некоторые из основных частей.
Есть несколько способов организовать драйвер ADC, но тот способ, который мне нравится организовывать, требует трех компонентов:
- Драйвер нижнего уровня
- Код приложения
- Модуль конфигурации
Драйвер нижнего уровня берет модуль конфигурации во время инициализации и настраивает оборудование на основе конфигурации. Драйвер низкого уровня обеспечивает общий уровень абстракции оборудования (HAL), который затем может использовать код приложения. Вызовы ADC HAL должны быть универсальными, чтобы высокоуровневое приложение могло конфигурировать оборудование любым необходимым образом и чтобы его можно было повторно использовать и масштабировать. Например, несколько вызовов ADC HAL, которые я использовал в прошлом, включают:
- AdcError_t Adc_Init (const AdcConfig_t * Config);
- AdcError_t Adc_StartConversion (недействительно);
- bool Adc_ConversionComplete (void);
- void Adc_RegisterWrite (uint32_t const Address, uint32_t const Value);
- uint32_t Adc_RegisterRead (адрес uint32_t);
- void Adc_CallbackRegister (AdcCallback_t const Function, TYPE (* CallbackFunction) (type));
Первые три API предоставляют возможность инициализировать оборудование АЦП, начать преобразование и затем проверить статус преобразования. Последние три функции предназначены для обеспечения масштабируемости до низкоуровневого оборудования. Например, если HAL не предоставляет параметр, необходимый для приложения, такой как преобразование одного канала АЦП, HAL можно расширить с помощью функций Adc_RegisterRead и Adc_RegisterWrite. Это обеспечивает гибкость в зависимости от потребностей приложения, не создавая громоздкого API.
Написание простого блокирующего драйвера АЦП
Мы можем написать действительно простой драйвер АЦП, который находится выше аппаратного уровня. Например, мы можем создать простую функцию с именем Adc_Sample, которая запускает оборудование АЦП, а затем сохраняет все результаты в буфере, к которому затем может получить доступ приложение. Буфер, в котором хранятся значения счетчика аналоговых значений, не обязательно должен хранить только одно значение, но может хранить несколько значений, которые впоследствии могут быть усреднены или отфильтрованы в зависимости от потребностей приложения. Версия блокировки для функции выборки может выглядеть примерно так:
Как видно из этого кода, цикл while блокирует выполнение до тех пор, пока оборудование АЦП не завершит преобразование, а затем сохраняет значения в буфере приложения.
Написание простого неблокирующего драйвера АЦП
Преобразовать блокирующий драйвер в неблокирующий код довольно просто, но это потребует изменений в коде приложения более высокого уровня. Например, прямо сейчас, если приложение хочет протестировать датчики, разработчик звонит:
Adc_Sample ();
В неблокирующей версии разработчик должен проверить возвращаемое значение из Adc_Sample, чтобы увидеть, завершены ли образцы и готовы ли они к использованию. Это позволяет запускать образцы в фоновом режиме, а код приложения продолжать работу со следующими обновлениями кода нашего драйвера:
Выводы
Как мы видели в этом посте, существует несколько способов написания ADC, и реализация может быть блокирующей или неблокирующей в зависимости от наших потребностей. Блокирующие драйверы обычно проще и менее полны, чем неблокирующие драйверы, но они могут быть неэффективными. Неблокирующие драйверы позволяют запускать другой код, пока драйвер работает, но код приложения по-прежнему должен проверять статус, что само по себе неэффективно в реализации с опросом.
В следующей статье этой серии мы рассмотрим, как написать приложение, которое производит выборку датчика через периферийное устройство АЦП, использующее прерывания.
Встроенный
- Типы датчиков и их принципиальные схемы
- Bulgin:экономичные решения IIoT с новыми тонкими фотоэлектрическими датчиками
- AMS, чтобы осветить Sensors Expo 2019 инновационными демонстрациями
- DATA MODUL расширяет портфолио сенсорных датчиков, добавляя еще большие размеры
- Contrinex:интеллектуальные датчики с поддержкой облачных вычислений и световые завесы безопасности с интерфейсо…
- Интегрированные драйверы упрощают разработку шаговых двигателей
- Управление эффектом с помощью реальных датчиков
- Считывание аналоговых датчиков с одним контактом GPIO
- Взаимодействие датчика движения HC-SR501 PIR с Raspberry Pi
- Улучшение мониторинга загрязнения воздуха с помощью датчиков Интернета вещей