8-900-374-94-44
[email protected]
Slide Image
Меню

Пример adc stm32f4 – 7. STM32. Программирование STM32F103. ADC

АЦП в STM32. Часть 1 — EasySTM32

Микроконтроллер штука цифровая и обменивается с внешним миром цифоровыми сигналами: нулями и единицами. Однако иногда перед микроконтроллером встает задача произвести измерение какой либо плавно изменяющейся величины. Это может быть всё то, что принимает несколько промежуточных состояний (а не только два) например это может быть темепература, напряжение, сила тока, освещенность и так далее, примеров много. Однако, нога контроллера настроенная на вход различает только два состояния — присутствие на ноге напряжения (лог. 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] — Этими битами выбирается источник который будет запускать преобразование каналов в регулярной группе. Доступны следующие битовые комбинации:  

EXTSEL2EXTSEL1EXTSEL0Источник
000Timer 1 CC1 event
001Timer 1 CC2 event
010Timer 1 CC3 event
011Timer 2 CC2 event
100Timer 3 TRGO event
101Timer 4 CC4 event
110EXTI line 11
111SWSTART

JEXTTRIG — Разрешает использовать внешнее событие для старта преобразования каналов в инжектированой группе

JEXTSEL[2:0] — Этими битами выбирается источник который будет запускать преобразование каналов в инжектированой группе. Доступны следующие битовые комбинации: 

JEXTSEL2JEXTSEL1JEXTSEL0Источник
000Timer 1 TRGO event
0
0
1Timer 1 CC4 event
010Timer 2 TRGO event
011Timer 2 CC1 event
100Timer 3 CC4 event
101Timer 4 TRGO event
110EXTI line15
111JSWSTART

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_JDR1ADC_JDR2ADC_JDR3ADC_JDR4
4JSQ1JSQ2JSQ3JSQ4
3JSQ2JSQ3JSQ4
2JSQ3JSQ4
1JSQ4

Из таблицы видно, что если мы включаем в группу только один канал то его мы выбираем битами JSQ4 а читаем из регистра ADC_JDR

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

easystm32.ru

STM32 сохранение данных АЦП с помощью DMA. » Хабстаб

DMA(Direct Memory Access – прямой доступ к памяти) – позволяет передавать данные без участия ядра. То есть, если использовать DMA, то по завершении преобразования АЦП, нам не надо забирать результат преобразования и сохранять его в памяти, за нас это сделает DMA. Для этого при конфигурации DMA достаточно указать адрес откуда брать данные и куда сохранять, а также ещё несколько простых настроек, которые мы рассмотрим ниже.

Передача данных осуществляется по специально отведённым каналам, у STM32F103 их целых 12 штук, они распределены между двумя контроллерами, 7 каналов у первого и 5 у второго. Картинки можно увеличить кликнув по ним.



Как видим, каждое периферийное устройство закреплено за определённым каналом DMA.

Каждому запросу можно назначить один из приоритетов:

  • очень высокий
  • высокий
  • средний
  • низкий

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

Каждый канал DMA может формировать запрос на прерывание по одному из событий:

  • отправлена половина буфера
  • отправка завершена
  • ошибка при передаче

Как писалось выше, при конфигурации DMA надо указать адреса отправителя и получателя - эти адреса являются базовыми, также можно инкрементировать значение адреса отправителя/получателя после каждой посылки, это может пригодиться, когда полученные данные надо сохранить в массив.

При инициализации необходимо указать размерность данных, которые будем отправлять и получать с помощью DMA, она может быть равна 8, 16 или 32 бита. Если размерность данных отправителя и получателя не совпадают, контроллер DMA выравняет их, в RM0008 есть целая таблица, в которой можно посмотреть как происходит выравнивание.

Максимальный размер буфера DMA составляет 65535 значений.

Давайте теперь рассмотрим как сохранить в память данные, полученные с ADC1.
В существующей инициализации АЦП достаточно в регистре CR2 разрешить отправку по DMA.


ADC1->CR2  |=    ADC_CR2_DMA;

Теперь можно переходить к конфигурации DMA.

//Адрес регистра результата преобразования АЦП
#define ADC1_DR_Address    ((u32)0x40012400+0x4c)

#define DMA_BUFF_SIZE 10//Размер буфера

uint16_t buff[DMA_BUFF_SIZE];//Буфер

RCC->AHBENR |= RCC_AHBENR_DMA1EN; //Разрешаем тактирование первого DMA модуля
DMA1_Channel1->CPAR = ADC1_DR_Address; //Указываем адрес периферии - регистр результата преобразования АЦП для регулярных каналов
DMA1_Channel1->CMAR = (uint32_t)buff; //Задаем адрес памяти - базовый адрес массива в RAM
DMA1_Channel1->CCR &= ~DMA_CCR1_DIR; //Указываем направление передачи данных, из периферии в память
DMA1_Channel1->CNDTR = DMA_BUFF_SIZE; //Количество пересылаемых значений
DMA1_Channel1->CCR &= ~DMA_CCR1_PINC; //Адрес периферии не инкрементируем после каждой пересылки
DMA1_Channel1->CCR |= DMA_CCR1_MINC; //Адрес памяти инкрементируем после каждой пересылки.
DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0; //Размерность данных периферии - 16 бит
DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0; //Размерность данных памяти - 16 бит
DMA1_Channel1->CCR |= DMA_CCR1_PL; //Приоритет - очень высокий 
DMA1_Channel1->CCR |= DMA_CCR1_CIRC; //Разрешаем работу DMA в циклическом режиме
DMA1_Channel1->CCR |= DMA_CCR1_EN; //Разрешаем работу 1-го канала DMA

Циклический режим работает следующим образом, после запуска DMA значение в регистре DMA_CNTDRх будет автоматически декрементироваться(уменьшаться) после каждой посылки, пока оно не станет равным нулю, затем значение в регистре автоматически обновится на DMA_BUFF_SIZE и всё начнётся сначала. С помощью этого режима очень просто организовать кольцевой буфер, что мы и сделали.Теперь в массиве buff всегда будут свежие значения, полученные от АЦП. Осталось только вовремя остановить эту карусель и вывести полученные значения на экран.
Для чего? У меня, например, по такому принципу работает прототип осциллографа, посмотреть на него можно тут.

hubstub.ru

Демоны в АЦП STM32 / STM32 / Сообщество EasyElectronics.ru

Современные АЦП обычно содержат внутри УВХ. В последнее время АЦП часто строятся по схеме уравновешивания заряда, а не уравновешивания токов, как это было в АЦП на основе резисторной матрицы R-2R. В случае АЦП с уравновешиванием заряда роль емкости УВХ выполняет внутренняя матрица конденсаторов. Конденсаторы матрицы в процессе преобразования перезаряжаются. Поэтому вход такого АЦП ведет себя достаточно нетривиально. Именно к такому типу АЦП относится и внутренний АЦП микроконтроллеров STM32. Довольно много информации по этому поводу содержится в документе AN2834. Но там полезные рекомендации, как на стр. 37: «Do not add any external capacitor (Cext) to the input pin when applying this above workaround» чередуются с вредными советами, как на стр. 39: «An extra large Cext enables sampling more often». Это подтолкнуло к написанию данного поста.


УВХ внутреннего АЦП STM32 подключен к входному пину через аналоговый мультиплексор без всяких буферов, поскольку при однополярном питании сделать буфер проблематично. В некоторых сигма-дельта АЦП такой буфер есть, но он обычно отключаемый, так как при его использовании накладываются ограничения на допустимый диапазон входного напряжения. В STM32 входного буфера нет. Во время выборки к входному пину через ключ подключается емкость УВХ. Но что происходит с этой емкостью дальше? К моменту следующей выборки на ней будет прежний заряд? Вряд ли. Современные интегральные АЦП подобного класса обычно строятся на базе емкостного ЦАП. Во время преобразования методом последовательного приближения производится балансировка зарядов (а не токов, как было в АЦП на основе R-2R ЦАПа). Поэтому заряд входного конденсатора «расходуется» во время преобразования. Да и вообще, во многих АЦП конденсатор УВХ — это не какой-то отдельный элемент, а вся матрица конденсаторов емкостного ЦАП. Так что же будет с конденсатором в конце преобразования, он разрядится до нуля, или будет заряжен каким-то другим зарядом? Это зависит от конкретной схемы АЦП. Документация внятного ответа на этот вопрос не дает, придется прибегнуть к измерениям.

Последовательно со входом АЦП я включил резистор 10 кОм, падение напряжения на котором контролировал осциллографом. На всех осциллограммах по горизонтали 2 мкс/дел, по вертикали 100 мВ/дел. АЦП работает с частотой дискретизации 100 кГц. Измерения, конечно, не совсем достоверны в плане формы выброса, так как ко входу АЦП я подключил небольшую дополнительную емкость (щуп 1:10 имеет емкость около 18 пФ). Но качественно картина не меняется.

При нулевом входном напряжении АЦП видим броски вверх. Это означает, что ко входу подключается заряженный до некоторого напряжения конденсатор, который во время выборки разряжается через источник сигнала. Имеем вытекающий входной ток.

При напряжении, равном половине шкалы, выбросы исчезают.

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

Чтобы не появлялась погрешность измерения, за время выборки конденсатор УВХ должен успевать заряжаться до входного напряжения с точностью не хуже 1 LSB. Получить это несложно, так как емкость конденсатора маленькая, около 8 пФ. В datasheet приведена соответствующая табличка, величина сопротивления на входе АЦП может быть порядка десятков кОм. Для источников сигнала с высоким входным сопротивлением можно увеличить время выборки АЦП, в STM32 такая возможность есть.

Но что будет происходить, если на входе АЦП включить емкость на землю? Это типичный прием, рекомендуемый почти везде. Вместе с входным резистором этот конденсатор образует anti-alias фильтр, который всегда необходим, плюс образуется ФНЧ, подавляющий сигнал помех. Если включить на входе АЦП емкость, выбросы напряжения «размажутся» во времени и к концу выборки напряжение может не успеть достигнуть входного с точностью 1 LSB. Придется увеличивать время выборки, чтобы все стало на свои места. Но это возможно для небольших емкостей порядка десятков пФ. Однако обычно речь идет о емкостях порядка 10 – 100 нФ. Тут никакое увеличение времени выборки не поможет. На входе АЦП выбросы исчезнут, мы будем видеть некоторое среднее значение напряжения. Но это значение не будет равно измеряемому напряжению. При увеличении емкости ошибка АЦП стремится к своему теоретическому максимуму. Хотя, казалось бы, какой может быть вред от емкости на входе АЦП, кроме сужения полосы сигнала?

Проверим это на практике. На входе АЦП те же 10 кОм, показания АЦП 4001, что соответствует поданному входному напряжению. Подключаем на вход АЦП емкость 10 нФ. Показания становятся 3987, т.е. АЦП начал врать на 14 единиц вниз. Теперь уменьшим входное напряжение. Показания АЦП без емкости 101. Подключаем 10 нФ. Показания становятся 116, т.е. АЦП начал врать на 15 единиц вверх. Емкость на входе АЦП приводит к погрешности измерения постоянного напряжения!

Рассмотренные эффекты легко рассчитать, оперируя средним входным током АЦП. Этот ток легко измерить, тестер на основе АЦП двойного интегрирования покажет именно средний входной ток. При нулевом входном напряжении ток оказался равным -1.13 мкА, на середине шкалы он равен нулю, при максимальном входном напряжении он равен +1.09 мкА. Что хорошо согласуется с расчетами. Согласно datasheet, конденсатор УВХ имеет емкость 8 пФ. Перезаряжается он максимум на 1/2 шкалы, что при опорном напряжении 3.3 В составляет 1.65 В. Заряд будет равен Q = V * C. А средний ток при частоте дискретизации Fs будет равен Iavg = V * C * Fs = 1.65 * 8E-12 * 1E5 = 1.32 мкА. Что практически совпадает с измеренным значением. Входной ток пропорционален частоте дискретизации, измерение при нулевом входном напряжении и частоте дискретизации 50 кГц дало значение -0.56 мкА, что тоже соответствует теории.

Казалось бы, ну и ладно, появившуюся погрешность можно учесть при калибровке. При этом надо помнить, что частота выборок АЦП должна быть постоянной, иначе такая калибровка окажется неправильной. Но тут поджидает еще одна проблема. АЦП ведь многоканальный, поэтому емкость с каким-то зарядом, полученным после преобразования в одном канале, подключается ко входу другого канала. Появляется взаимное влияние каналов. На осциллограммах ниже показан вход АЦП канала 1 при работающих двух каналах. Приведен также средний входной ток канала 1.

АЦП1 = 0, АЦП2 = 4000. Iavg(АЦП1) = -2.45 мкА.

АЦП1 = 0, АЦП2 = 2048. Iavg(АЦП1) = -1.64 мкА.

АЦП1 = 4000, АЦП2 = 2048. Iavg(АЦП1) = +1.99 мкА.

АЦП1 = 4000, АЦП2 = 0. Iavg(АЦП1) = +2.51 мкА.

Как видим, напряжение на входе канала 2 непосредственно влияет на входной ток канала 1. Причем этот ток может быть вдвое выше, чем при работе одного канала. На показания это тоже влияет, при наличии на входе RC-цепочки показания канала 1 менялись при изменении напряжения на входе канала 2, ошибка достигала 30 единиц. Для худшего случая ответ на вопрос «что с конденсатором УВХ происходит потом» можно дать таким образом: во время каждой выборки конденсатор УВХ нужно перезаряжать на величину Vref. Средний входной ток АЦП для худшего случая нужно принимать Iavg = Vref * Cувх * Fs.

Вывод первый: чтобы иметь право подключать конденсатор большой емкости на вход АЦП, сопротивление резистора на входе АЦП (вместе с внутренним сопротивлением источника сигнала) не должно превышать такого значения, когда средний входной ток создает на нем падение напряжения не больше 1/2 LSB. Т.е. R < 1 / (2^(N+1) * Cувх * Fs), где N — разрядность АЦП, Cувх — емкость внутреннего УВХ, Fs — частота дискретизации. Для встроенного АЦП STM32 и Fs = 100 кГц сопротивление должно быть не более 153 Ом. При понижении частоты дискретизации сопротивление можно пропорционально увеличить. А вот время выборки в данном случае не влияет.

Вывод второй: если по каким-то причинам резистор не может быть таким маленьким (например, если источник сигнала сам обладает более высоким выходным сопротивлением), тогда на вход АЦП не может быть подключена произвольная емкость. Если емкость будет превышать некоторое значение, будет появляться ошибка, которая с ростом емкости достигнет своего максимума, о чем было сказано выше. Возникает вопрос, какой номинал емкости является допустимым для выбранного резистора. Чем ниже частота выборок и чем больше время выборки, тем емкость может быть больше. Можно, наверное, составить формулу, но я вместо этого составил модель входной части АЦП. Кроме внешней RC-цепочки учитывается внутренняя емкость УВХ и сопротивление ключа.

Для частоты выборок Fs = 100 кГц и времени выборки 2 мкс для получения ошибки не более 1/2 LSB при сопротивлении резистора 100 Ом емкость не должна превышать 10 нФ, а при сопротивлении 1 кОм емкость должна быть не более 330 пФ.

Следующий график снят для резистора 1 кОм и емкости 4.7 нФ. Видно, что напряжение на входе АЦП никогда не достигает входного, поэтому будет присутствовать ошибка. При еще большем увеличении емкости пульсации на входе АЦП уменьшаются, но эти пульсации происходят не вокруг входного измеряемого напряжения, а вокруг некого среднего входного напряжения АЦП, которое отличается от измеряемого напряжения на величину падения на входном резисторе.

Чтобы не прибегать к моделированию, номиналы RC-цепочки можно рассчитать, пользуясь упрощенными эмпирическими формулами. В [1] рекомендуют выбирать внешнюю емкость примерно в 20 раз больше емкости УВХ. Резистор выбирается из соображений времени установления напряжения на емкости с точностью 1/2 LSB. При этом можно ввести некий коэффициент k, который для выбранной разрядности АЦП показывает, во сколько раз постоянная времени цепочки должна быть меньше времени выборки. Для 10 бит k = 8, для 12 бит k = 9, для 14 бит k = 11, для 16 бит k = 12. Надо сказать, что значения этого коэффициента приблизительные, так как не учитывается отношение внешней емкости к емкости УВХ. Но этими значениями вполне можно пользоваться, взяв постоянную времени с некоторым запасом меньше расчетной. Для приведенного выше примера расчет для 12 бит, Tвыборки = 2 мкс, C = 330 пФ дает R примерно 700 Ом. По результатам моделирования R = 1000 Ом. Вполне хорошее соответствие.

Есть и еще одна функция у RC-цепочки на входе АЦП. Она изолирует выход буферного ОУ от входа АЦП и предотвращает паразитные колебания. Если источником сигнала служит ОУ, то при резком изменении тока нагрузки (что происходит в момент начала выборки) на его выходе может начаться затухающий колебательный процесс. В этом случае при малых временах выборки можно получить непредсказуемую величину погрешности. Вот как ведет себя встроенный буферный ОУ ЦАП STM32, нагруженный на вход АЦП через резистор 100 Ом (масштаб — 100 мв/делю и 1 мкс/дел.):

Видны затухающие колебания, которые длятся почти 2 мкс. Увеличение резистора до 1 кОм увеличивает выброс на входе АЦП и немного укорачивает колебательный процесс. Но полностью его не устраняет:

При подключении после резистора 1 кОм даже небольшой емкости, такой как 100 пФ, колебания исчезают:

Поэтому совет «Do not add any external capacitor» тоже нельзя отнести к полезным. В datasheets на большинство подобных АЦП рекомендуется на входе включать RC-цепочку. Главное — правильно выбрать номиналы. Подключая RC-цепочку на выход ОУ, нужно убедиться в устойчивости ОУ при работе на такую нагрузку. Разные типы ОУ имеют разные способности по работе на емкостную нагрузку, определяющим параметром здесь является выходное сопротивление ОУ с разомкнутой петлей ОС. Обычно соответствующие данные есть в datasheet на ОУ.

Озвученный выше выбор емкости «в 20 раз больше емкости УВХ» является во многом произвольным, никакого физического смысла этот коэффициент не несет. Внешнюю емкость часто называют «резервуаром заряда» для емкости УВХ. В идеале величина этой емкости должна быть такой, чтобы при зарядке емкости УВХ напряжение на ней упало не более, чем на 1/2 LSB. Т.е. величина внешней емкости для 12-разрядного АЦП должна быть больше емкости УВХ в 8192 раза. Соответствующий номинал R тогда будет очень малым, реальные ОУ вряд ли смогут работать на такую нагрузку. Поэтому используют менее жесткий критерий выбора емкости. Тогда часть заряда емкость УВХ получает от внешней емкости, а часть — за счет выходного тока ОУ.

Если АЦП используется для оцифровки переменного напряжения, то нужно еще учесть, что сопротивление на входе влияет на уровень искажений из-за зависимости входного тока АЦП от напряжения. Искажения начинают повышаться при увеличении номинала резистора выше критического значения, о котором говорилось выше. Природа искажений такая же, как и природа статической ошибки при измерении постоянного напряжения.

И еще один аспект: если аналоговый сигнал, который поступает на вход АЦП, используется еще для чего-нибудь, то вход АЦП может явиться дополнительным источником помех — «иголок» с частотой сэмплирования. Лучший вариант — развязать вход АЦП от остальной аналоговой схемы отдельным буфером на ОУ. Тогда и необходимую фильтрацию можно выполнить на входе буфера, не ограничивая себя рамками допустимого сопротивления на входе.

Иногда для повышения эффективной разрядности АЦП используют оверсемплинг. В этом случае частота выборок выбирается побольше, что влечет за собой увеличение среднего входного тока. К тому же, в расчетах нужно брать ошибку 1/2 LSB не для исходного АЦП, а для выходного кода повышенной разрядности. Это еще больше ограничит значения номиналов R и C сверху.

При увеличении эффективной разрядности путем добавления на вход АЦП треугольного сигнала (подобно Figure 22 документа AN2834) номиналы нужно выбирать с учетом среднего входного тока АЦП. Если в такой схеме применяется емкостной делитель с относительно большой емкостью со входа АЦП на землю, то входной резистор должен выбираться малым, как было описано для первого случая.

Ниже я добавил несколько ссылок на документы, где обсуждаются подобные вопросы. Надо отметить, что нигде не рассмотрен момент, касающийся погрешности, связанной со средним входным током АЦП. Вероятно, это связано с тем, что обычно рассматривают внешние АЦП, а там постоянную времени входной RC-цепочки выбирают малой, чтобы не потерять полосу входного сигнала АЦП. В тех случаях, когда требуется оцифровка медленных сигналов, выбирают более медленный АЦП, у него за счет меньшей частоты сэмплирования будет меньше и средний входной ток. Особенность встроенного АЦП состоит в том, что мы его не выбираем. Он, обладая довольно высокой скоростью, может использоваться для оцифровки медленных сигналов. В этом случае легко столкнуться с ситуацией, когда RC-цепочка еще не ограничивает спектр полезного сигнала, но уже приводит к появлению ошибки по постоянному току. Средний входной ток иногда рассматривается при описании сигма-дельта АЦП, для них оцифровка более медленных сигналов, чем позволяет сам АЦП, является типичной ситуацией.

1. Optimize Your SAR ADC Design
2. External components improve SAR-ADC accuracy
3. Using a SAR ADC for Current Measurement in Motor Control Applications
4. The operation of the SAR-ADC based on charge redistribution
5. Start with the right op amp when driving SAR ADCs
6. ADS8342 SAR ADC Inputs
7. Front-End Amplifier and RC Filter Design for a Precision SAR Analog-to-Digital Converter

we.easyelectronics.ru

STM32. ADC (АЦП). Пример кода

Для самого запуска ADC, в нашем случае это ADC1->8 канал, и его работы по оцифровке входного сигнала нужно совсем немного:
1) Настройка тактирования нужного порта (GPIOB).
2) Настройка входного пина PB0 на котором находится 8 канал ADC1 (см. Документацию вывода ножек. Нас интересует "третье" состояние ножки - аналоговый режим, не путать с режимом альтернативной функции или тем более режимом обычного ввода/вывода). Тут нам понадобится всего один регистр GPIOB_MODER.
3) Настройка тактирования ADC1. Из физической схемы или карты адресного пространства видно, что ADC находятся на шине APB2. В регистрах тактирования RCC_APB2ENR находим бит ADC1EN, устанавливающий тактирование нужного нам ADC (ADC1).
4) Переходим к настройкам самого ADC. Устанавливаем бит ADDON регистра ADC1_CR2, который включит ADC. В этом режиме ADC потребляет незначительное количество энергии, и находится в состоянии покоя.
5) В этот же регистр ADC1_CR2 установим бит CONT (режим непрерывного преобразования).
6) В первые 5 битов регистра ADC1_SQR3 установим номер канала для преобразования (8 канал). В следующие биты этого регистра, задаются другие номера каналов если нужно их считывать по очереди.
7) Устанавливаем бит SWSTART регистра ADC1_CR2, который запускает процесс преобразования.
8) Ждем появления флага EOC в статусном регистре ADC1_SR, сигнализирующий о конце преобразования. Флаг сбрасывается аппаратно после чтения регистра данных ADC1_DR.
9) Читаем преобразованные данные с регистра ADC1_DR.
  1. int main(void){
  2. RCC_GPIO|=0x2;
  3. GPIOB_MODER|=0x3;
  4. RCC_ADC|=0x100;// Установка тактирования ADC1
  5. ADC1_CR2=0x1;// Включаем ADC1
  6. ADC1_CR2|=0x2;// Постоянное преобразование
  7. ADC1_SQR3=8;// Номер канала
  8. ADC1_CR2|=0x40000000;// Запуск процесса преобразования
  9. while(1){
  10. while((ADC1_SR&0x2)!=0x2){}
  11. USART2_DR=ADC1_DR;// Отправляем данные куда нужно
  12. }
  13. }

ivariousblog.blogspot.com

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *