АЦП в 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, он отражает состояние АЦП на данный момент: 

STRT — этот бит устанавливается когда стартует преобразование для регулярного канала
JSTRT — тоже самое но для инжектированного канала
JEOC — закончилось преобразование для инжектированного канала
EOC — закончилось преобразование инжектированного или регулярного канала
AWD — Analog watchdog сработал 
Все биты сбрасываются программным путём, а EOC может быть сброшен еще и автоматически когда из регистра ADC_DR заберут данные. Для настройки основных АЦП используют два регистра ADC_CR1 и ADC_CR2. Структура первого из них выглядит следующим образом: 

AWDEN — Analog watchdog мониторит состояние всех каналов регулярной группы
JAWDEN — Analog watchdog мониторит состояние всех каналов инжектированной группы
AWDSGL — Analog watchdog мониторит состояние одного канала указанного в битах AWDCH
AWDCH[4:0] — Номер канала АЦП который будет мониторить Analog watchdog

Как правильно выставлять все эти биты, проясняет следующая таблица: 

JAUTO — разрешает автоматическое преобразование для каналов в инжектированной группе после каналов  регулярной группы.
SCAN — не совсем понятно что такое, судя по всему нужно ставить этот бит если опрашиваем несколько каналов ацп за раз. 
JEOCIE —  Включает или выключает прерывания для инжектированных каналов
AWDIE — Включает или выключает прерывания от Analog watchdog
EOCIE — Включает или выключает прерывания по окончанию преобразования в регулярной или инжектированной группе

Следующий регистр настроек это ADC_CR2

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

ALIGN — Устанавливает выравнивание результата в регистре данных (0 — по правому, 1 — по левому)
RSTCAL — Сбрасывает значение калибровки
CAL — Запускает калибровку АЦП. После завершения калибровки бит сбраывается в ноль
CONT — Разрешает непрерывное преобразование
ADON — Включает/выключает модуль АЦП
DMA — Разрешает использовать DMA 
Самое страшное позади 🙂 , теперь рассмотрим регистры ADC_SMPR1 и ADC_SMPR2. В них задается время выборки индивидуально для каждого канала. 

Четыре регистра ADC_JOFRx хранят значения которые будут вычитаться из соответствующих регистров ADC_JDRx. Для чего нужна такая фича, я пока не очень догадываюсь. Но как говорится если звёзды зажигают значит это кому-нибудь нужно.

Регистры ADC_HTR и ADC_LTR задают верхнюю и нижню границу с которыми Analog watchdog сравнивает значение выбранного канала АЦП. Рисовать табличку тут смысла особого нет, так как в регистр просто записываются данные в первые 12 бит. Следующие три регистра ADC_SQRx определяют, какие каналы входят в регулярную группу. Всего можно добавить не более 16-ти каналов. Битами L[3:0] нужно задать число каналов в группе (0000 — 1 канал … 1111 — 16 каналов). На всякий случай нарисую все три регистра тут, чтоб в даташит лишний раз не лазить: 

И последний регистр на сегодня это ADC_JSQR. Он уже успел принести мне немало хлопот пока я не прочитал то что про него написано мелким  шрифтом в примечании. Для начала нужно сказать, что он хранит в себе номера каналов которые входят с состав инжектированной группы. Сам регистр выглядит так:

Битами JSQx задаются номера каналов, а в JL[1:0] записывается количество каналов в инжектированной группе. Вот тут-то и начинаются сложности. Если в инжектированную группу добавлены все четыре канала то путаницы ни какой нет: В регистр ADC_JDR1 будут записывать данные с канала указанного битами JSQ1, в ADC_JDR2 с канала JSQ2 итд. Но все меняется если мы хотим включить в состав инжектированной группы не все 4 канала, а например только один. Было бы логично предположить, что если канал один то выбирать его нужно битами JSQ1, если каналов 2 то битами JSQ1 и JSQ2 и так далее. Но это не так. В даташите написано примерно тоже самое что я изобразил в виде таблицы:

Кол-во каналов ADC_JDR1 ADC_JDR2 ADC_JDR3 ADC_JDR4
4 JSQ1 JSQ2 JSQ3 JSQ4
3 JSQ2 JSQ3 JSQ4
2 JSQ3 JSQ4
1 JSQ4

Из таблицы видно, что если мы включаем в группу только один канал то его мы выбираем битами JSQ4 а читаем из регистра ADC_JDR1. Вот такие дела. Ну на сегодня хватит таблиц и какой либо информации вообще. В следующей статье будет практика применения. Спасибо что дочитали до конца 🙂

Добавить комментарий