Добрый день.
На предыдущем занятии мы поставили для себя "грандиозную" цель - сделать вольт-ампер метр своими руками.
И пока что от поставленной задачи нас отделяет мелочь - мы не умеем измерять ни напряжение, ни силу тока.
По этому на сегодняшнем занятии займемся устранением этих пробелов в наших знаниях.
Для начала попробуем разобраться как именно при помощи STM32 можно измерить напряжение.
Предварительно я добавил к нашей предыдущей схеме фоторезистор, подключенный в качестве одного из плеч делителя напряжения.
Это позволит нам получить на одном из выводов микроконтроллера напряжение, которое будет изменяться в зависимости от освещённости.
На сегодняшнее занятие у нас будет следующая задача:
Необходимо, не сломав всё, что мы сделали на предыдущем занятии добавить измерение напряжения, и выводить измеренное значение в первые четыре сегмента нашего импровизированного дисплея.
Для преобразования различных аналоговых сигналов в цифровой вид, у микроконтроллера есть блок Аналогово-Цифрового Преобразователя.
Что бы разобраться как с ним работать - заглянем в документацию.
Нас тут интересует:
1. При желании выполните калибровку АЦП, для повышения точности, вызвав функцию HAL_ADCEx_Calibration_Start()
2. АЦП может работать в 3 режимах: запрос, прерывание и ДМА. Далее расписано какую последовательность действий необходимо выполнить для каждого из режимов. Для знакомства - попробуем реализовать самый простой режим - Преобразование по запросу.
Отлично с тем как будем вызывать преобразование разобрались. Далее идёт - настройка АЦП в кубе. Для этого запускаем куб, и в подменю Analog выбираем ADC.
Как видно - часть каналов недоступна, они нами уже заняты другими устройствами. Фоторезистор мы подключили к выводу PA1. Если кликнуть по нему, то там можно найти что на этот вывод может быть активирован канал ацп-вход 1. Активируем его.
В результате открылся список с настройками. Начинаем разбираться.
- Clock Prescaler - От какого источника тактировать АЦП. Варианта три - первый - от собственного источника тактирования. Второй и третий - тактироваться от AHB с соответствующим делителем. откроем вкладку Clock Configuration и посмотрим на частоты. Видно что собственный источник тактирования выдаёт 14 мегагерц, а на шине AHB используется тактирование 8 мегагерц. Оставляем первый вариант.
- Resolution - разрешение преобразования. Мы же хотим лучшее - по этому оставляем 12 бит
- Data Alignment - Так как ацп нам выдаст двенадцатибитный результат, который не помещается в 1 байт, то тут мы можем выбрать какое выравнивание будет использоваться для значащих данных. Мне больше нравится вариант правого выравнивания, выбираем его.
- Scan Conversion Mode - Как будут обходится каналы АЦП, с нулевого по последний, или с последнего по нулевой. У нас пока что использоваться будет только 1 канал, по этому оставляем как есть.
- Continuous Conversion Mode - Указывает используется ли преобразование в одиночном, или в непрерывном режиме. Мы будем использовать одиночные вызовы. Так что оставляем Disable.
- Discontinuous Conversion Mode. Я честно пытался минут пять понять что это такое, но в итоге плюнул. Возможно позже и понадобится, но пока от греха подальше - отключим.
- DMA Continuous Requests - Что-то про ДМА, и работает только если используется режим ДМА. мы такого не планируем, по этому оставляем отключенным.
- End Of Conversion Selection - Что считать окончанием преобразования, окончание преобразования одного сигнала, или всей последовательности. Так как мы будем использовать в одиночном режиме - нам необходим первый вариант.
- Overrun behaviour - Выбираем поведение, в случае если преобразование завершилось, а мы не успели забрать предыдущие данные. Укажем что бы данные перезаписывались.
- Low Power Auto Wait - Опция для экономии электричества. Следующее преобразование будет запущено только после того, как пользователь заберёт предыдущее значение.
- Low Power Auto Power Off - так же опция для экономии энергии. Блок АЦП после преобразования выключится, и автоматически включится когда будет запущено новое преобразование.
Вот бы посмотреть что на самом деле дают такие опции в общей массе энергопотребления. Для этого в первую очередь, я и хочу собрать этот амперметр. Но что то мы отвлеклись, настройка еще не закончена.
- Sampling Time - Время выборки данных для преобразования. Оставим пока что 1,5 цикла, хотя на первый взгляд этого очень мало, что бы получить образец напряжения.
- External Trigger Conversion Source - что будет являться триггером для запуска преобразования. тут мы видим, что можно использовать таймеры первый и третий для запуска, но пока что оставим программный запуск.
- External Trigger Conversion Edge - по какому фронту сигнала будет запущено преобразование, в случае использования таймера для его старта. Пока что мы останосились на том, что будем запускать програмно, по этому игнорируем.
Оставшиеся пункты - это ватчдог, который может следить за напряжением на выводе, и в зависимости от напряжения предпринимать какие либо действия. Нам пока он не интересен.
Вот и вся настройка, далее - перегенерируем проект, и попробуем измерять напряжение.
После перегенерации проекта, вспоминаем что нам необходимо восстановить удалённые CubeMX строки. На сайте STMicroElectronics уже зарегистрирован этот баг, ждём исправления.
Напишем то, что мы видели в документации, и посмотрим что получится.
Сначала - калибровка АЦП. После калибровки - запустим первое преобразование.
Далее в цикле - ждём окончания преобразования. Получаем данные. Останавливаем ацп. После чего - запускаем преобразование заново.
Полученное от ацп значение - сохраним в новую переменную.
Запустим отладку, и посмотрим, что же мы получили в нашей переменной. Отлично, у нас есть число, визуально похожее на двенадцатибитное значение, и оно меняется в зависимости от освещенности.
Напишем формулу пересчета, с учетом того, что напряжение питания у нас 3,3 вольта.
Далее - соберём в эту переменную всё, что хотели вывести, а именно напряжение и частоту моргания светодиода.
И немножко реорганизуем программу.
Запускаем, и смотрим.
Нуууу... конечно можно сказать что поставленная задача поставлена, но смотреть на ЭТО неприятно. цифры очень быстро меняются.
Предлагаю обновление информации на дисплее сделать не чаще чем 10 раз в секунду. Для этого - воспользуемся таймером, и настроем его на генерацию прерываний 10 раз в секунду. Для этого правда придётся нашу структуру дисплея объявить глобально, а в файле с прерываниями - подключить заголовочный файл и объявить эту же структуру внешней. Запускаем таймер. Запуск обязательно надо делать после инициализации дисплея, а то он в первом же прерывании попробует обновить его, а он еще не инициализирован.
Проверяем. Уже лучше. старый функционал не сломали, новый - прикрутили, молодцы!
Что бы было ещё красивее - надо делать программную фильтрацию входящих данных, и их усреднение, но пока мы разбираемся как работать с периферией, а не как программировать, по этому оставим эту задачу на потом.
На последок давайте посмотрим при помощи мультиметра, точно ли там то, что показывает наш индикатор. Сюрприз наш показометр - врёт, при чем сильно, процентов на 10 Смотрим напряжение на линии питания. Там 3,2 вольта, вместо положенных 3,3. Хорошо. Поправим формулу рассчета.
Перепроверяем. Вот теперь показания более-менее похожи. Но... как мы сейчас увидели - показания нашего с позволения сказать "прибора" зависят от стабильности питающего напряжения.
А на этом на сегодня всё, спасибо за внимание.