АЦП в STM32. Часть 1
Микроконтроллер штука цифровая и обменивается с внешним миром цифоровыми сигналами: нулями и единицами. Однако иногда перед микроконтроллером встает задача произвести измерение какой либо плавно изменяющейся величины. Это может быть всё то, что принимает несколько промежуточных состояний (а не только два) например это может быть темепература, напряжение, сила тока, освещенность и так далее, примеров много. Однако, нога контроллера настроенная на вход различает только два состояния - присутствие на ноге напряжения (лог. 1) и его отсутствие (лог. 0). Для измерения температуры это малопригодно, ведь мало кому интересен градусник имеющий только два состояния -50 и +200 градусов :) Для решения проблем измерения аналоговых величин придумали АЦП – Аналого-цифровой преобразователь. Принцип работы с АЦП в двух словах: На вход АЦП поступает аналоговый сигнал и через некоторое время из АЦП можно прочитать результат преобразования, тоесть цифровое представление аналогово сигнала. Существуют микроконтроллеры STM32 со встроенным АЦП, то есть ничего подключать к контроллеру не надо, точнее почти ничего. Посмотрим на картинку:
Тут нарисована обвязка которая должна быть обязательно, если мы хотим использовать АЦП. К ней относятся два конденсатора C4 и С3 плюс дроссель L1. Все это добро предназначено для того чтоб обеспечить АЦП контроллера качественным питанием без каких либо помех. Располагать конденсаторы желательно как можно ближе к выводу AVCC. На всякий случай уточню, что питается модуль АЦП отдельно от всего остального контроллера. На плате STM32VL Discovery уже вся эта обвязка имеется, что очень удобно для нас. Остальные детальки будут использованы нами в следующей статье, где мы будем применять АЦП на практике. Забегая вперед скажу, что в STM32 с АЦП всё не так просто как например в AVR. Имеется очень много параметров, о значении некоторых мне пока мало что понятно. Но попробуем разобраться что к чему. Начать следует с того что контроллер может измерять напряжения только с определённых ножек в названии которых присутствует слово ADC1_INx, где х некоторый уникальный номер канала. АЦП контроллера может опрашивать каждый из каналов поочередно. И вот тут начинается то, что взорвало мне мозг в клочья поначалу. В STM32 есть два варианта чтения данных с ножек ADC1_INx. Первый называется "Регулярные каналы" (regular channels в даташите). Использование этого метода опроса состоит в том, что АЦП опрашивает по очереди некоторый заранее настроенный список каналов, после каждого опроса результат записывается в один и тот же регистр. Это означает, что нужно своевременно забирать результат преобразования из этого регистра, в противном случае результат будет перетираться. Совсем иначе дело обстоит с "Инжектированными каналами" (Injected channels). В случае использования этого метода опроса можно записывать результат измерения каждого канала АЦП в свой отдельный регистр ничего не перезатирая. Но к сожалению таких регистров всего 4 а ножек АЦП у контроллера 16 штук. Но лично мне еще никогда не доводилось использоваь более двух каналов АЦП, так что использование инжектированных каналов мне пришлось больше по душе :) Еще стоит отметить, что АЦП STM32 обладает приятной штуковиной под названием Analog watchdog. Его предназначение в том, чтоб подать сигнал в случае если напряжние на определённом канале выйдет за допустимый диапазон, это позволяет сэкономить процессорное время за счёт того, что нам не придётся программно заставлять АЦП производить измерение и потом сравнивать полученное значение с пороговыми. Удобная вещь, но пока не использовал. Следующая плюшка - встроенный термометр. В даташите пишут, что использовать его для измерения абсолютных температур лучше не стоит, а вот для того чтоб отследить изменение температуры оно вполне годно. Начнем наше знакомство с АЦП как обычно с краткого описания регистров, тех назначение которых я более или менее понял. В первую очередь стоит сказать о регистрах в которые записывается результат измерения АЦП, таковых у нас пять. Первый регистр это ADC_DR:
В него последовательно пишутся данные, полученные в результате опроса каналов состоящих в регулярной группе. Каждое последующее измерение одного канала перетирает предыдущее. Для того чтоб своевременно забирать данные и записывать их в определённые переменные можно и нужно использовать прерывания или еще лучше DMA, но я пока с этим не заморачивался и DMA с прерываниями не трогал. О них обязательно пойдет речь в следующих статьях на этом сайте. Таким образом, если мы не используем ни прерываний ни DMA то использовать регулярные группы можно только если канал в этой группе всего один (так у нас не будет перетираться регистр DR данными с разных каналов). Что касается самого регистра, то видно, что он 16-ти битный. А АЦП у нас 12-ти битный. Для удобства данные могут выравниваться как по левому краю регистра так и по правому. Это решается битом ALIGN регистра ADC_CR2. Мы еще вернемся к нему позже. В следующие четыре регистра ADC_JDRx записываются результаты преобразования для каждого из 4-х инжектированных каналов.
Ни каких других особенностей этот регистр не имеет. Другой не маловажный регистр который нужно рассмотреть это ADC_SR, он отражает состояние АЦП на данный момент:
Как правильно выставлять все эти биты, проясняет следующая таблица:
AWDIE - Включает или выключает прерывания от Analog watchdog
EOCIE - Включает или выключает прерывания по окончанию преобразования в регулярной или инжектированной группе
TSVREFE - включает температурный сенсор. До установки этого бита читать что-то с канала к которому он подключен - бесполезно.
SWSTART - запускает преобразование для каналов входящих в состав регулярной группы. Для того, чтоб преобразование было запущено этим битом нужно предварительно установить биты EXTSEL[2:0] в единицы.
JSWSTART - запускает преобразование для каналов входящих в состав инжектированной группы. Для того, чтоб преобразование было запущено этим битом нужно предварительно установить биты JEXTSEL[2:0] в единицы.
EXTTRIG - Разрешает использовать внешнее событие для старта преобразования каналов в регулярной группе
EXTSEL[2:0] - Этими битами выбирается источник который будет запускать преобразование каналов в регулярной группе. Доступны следующие битовые комбинации:
EXTSEL2 |
EXTSEL1 |
EXTSEL0 |
Источник |
0 |
0 |
0 |
Timer 1 CC1 event |
0 |
0 |
1 |
Timer 1 CC2 event |
0 |
1 |
0 |
Timer 1 CC3 event |
0 |
1 |
1 |
Timer 2 CC2 event |
1 |
0 |
0 |
Timer 3 TRGO event |
1 |
0 |
1 |
Timer 4 CC4 event |
1 |
1 |
0 |
EXTI line 11 |
1 |
1 |
1 |
SWSTART |
JEXTTRIG - Разрешает использовать внешнее событие для старта преобразования каналов в инжектированой группе
JEXTSEL[2:0] - Этими битами выбирается источник который будет запускать преобразование каналов в инжектированой группе. Доступны следующие битовые комбинации:
JEXTSEL2 |
JEXTSEL1 |
JEXTSEL0 |
Источник |
0 |
0 |
0 |
Timer 1 TRGO event |
0 |
0 |
1 |
Timer 1 CC4 event |
0 |
1 |
0 |
Timer 2 TRGO event |
0 |
1 |
1 |
Timer 2 CC1 event |
1 |
0 |
0 |
Timer 3 CC4 event |
1 |
0 |
1 |
Timer 4 TRGO event |
1 |
1 |
0 |
EXTI line15 |
1 |
1 |
1 |
JSWSTART |
Кол-во каналов |
ADC_JDR1 |
ADC_JDR2 |
ADC_JDR3 |
ADC_JDR4 |
4 |
JSQ1 |
JSQ2 |
JSQ3 |
JSQ4 |
3 |
JSQ2 |
JSQ3 |
JSQ4 |
|
2 |
JSQ3 |
JSQ4 |
||
1 |
JSQ4 |