Продолжим изучение работы АЦП в различных режимах. Узнаем об управлении внешним запуском АЦП. Особое внимание обратим на циклический запуск от таймера. Разработаем проект 4х канального вольтметра среднего значения.
Предыдущий урок Список уроков Следующий урок
Немного подробнее расскажу то, о чем ранее упоминал вскользь. А именно, об управлении инжектированными каналами.
Внешний запуск опроса инжектированной группы.
По своему определению инжектированные каналы предназначены для внеочередного, неожиданного, незапланированного опроса АЦП.
Если во время преобразования каналов регулярной группы происходит внешний запуск преобразования инжектированных каналов, то:
- Текущее регулярное преобразование сбрасывается.
- Происходит однократное преобразование последовательности инжектированных каналов.
- По окончании обработки инжектированных каналов возобновляется опрос регулярной группы начиная с прерванного канала.
Если во время выполнения преобразования инжектированной группы произойдет внешний запуск опроса регулярной группы, то преобразование прервано не будет. Опрос регулярных каналов произойдет после завершения обработки инжектированной группы.
Автоматический запуск опроса инжектированных каналов.
Опять же, как следует из слова ”инжектированный” каналы такого типа не предназначены для непрерывных преобразований. Можно запускать их опрос с помощью регулярного внешнего события, например по таймеру. Но это будет искусственная непрерывность. В отличие от регулярных каналов в логике управляющего модуля АЦП для инжектированных каналов режим непрерывных преобразований не предусмотрен.
Но можно “прицепить” опрос инжектированных каналов к регулярным.
Если установить в управляющем регистре ADC_CR1 бит JAUTO, то каждый раз после завершения обработки регулярной группы будет запускаться обработка инжектированных каналов. Таким образом, инжектированные каналы превратятся в регулярные, а общее число последовательности опроса увеличится на 4 канала.
Обработка такой “удлиненной” последовательности будет работать и в непрерывном режиме. Для этого, как обычно, необходимо установить бит CONT. Такой режим мы использовали в последнем примере предыдущего урока.
Внешний запуск инжектированных каналов в режиме автозапуска запрещен.
Прерывистый режим.
Не знаю, насколько востребован этот режим. Я его никогда не использовал. Объясняю согласно документации производителя STM32.
В уроке 26 описан способ задания последовательности опроса каналов. Число каналов и последовательность опроса задается в регистрах ADC_SQR1- ADC_SQR3 для регулярных каналов и в регистре ADC_JSQR для инжектированных.
Так вот эту последовательность в прерывистом режиме можно разбить на более короткие отрезки, подгруппы.
Для регулярных каналов.
- Необходимо установить разрешение прерывистого режима в бите DISCEN регистра ADC_CR1.
- В поле битов DISCNUM того же регистра задать количество преобразований в подгруппе.
- Теперь по внешнему запуску будут опрашиваться подгруппы с общей последовательностью каналов, заданной в регистрах ADC_SQR1- ADC_SQR3.
- По завершению преобразований подгруппы АЦП ждет следующего внешнего запуска.
Проще продемонстрировать на примере.
Допустим в регистрах ADC_SQR1- ADC_SQR3 мы задали общую последовательность 0, 1, 2, 4, 6, 7, 8, 9. В обычном режиме по каждому внешнему запуску будут последовательно опрашиваться 8 каналов.
Если в битах DISCNUM задать длину подгруппы равную 3 и разрешить прерывистый режим, то будут происходить следующие события:
- 1й запуск - опрос каналов: 0, 1, 2. Событие EOC формируется на каждом преобразовании.
- 2й запуск - опрос каналов: 4, 6, 7. Событие EOC формируется на каждом преобразовании.
- 3й запуск - опрос каналов: 8, 9. Событие EOC формируется на каждом преобразовании.
- 4й запуск - опрос каналов: 0, 1, 2. Событие EOC формируется на каждом преобразовании.
Заметьте, что при 3м запуске было обработано только 2 канала. Если в конце основной последовательности не хватает каналов для опроса полной подгруппы, то обрабатываются только оставшиеся каналы.
Для инжектированных каналов.
- Прерывистый режим разрешается установкой бита JDISCEN регистра ADC_CR1.
- По внешнему запуску будут опрашиваться подгруппы с общей последовательностью каналов, заданной в регистре ADC_JSQR.
- По запуску происходит опрос только одного канала. Для инжектированных каналов количество преобразований в подгруппе всегда равно 1.
Пример.
Допустим, в регистре ADC_JSQR задана последовательность 0, 2, 3.
- 1й запуск - опрос канала 0.
- 2й запуск - опрос канала 2.
- 3й запуск - опрос канала 3. Формируется JEOC событие.
- 4й запуск - опрос канала 0.
Существует ограничение – прерывистый режим может использоваться только для одного типа каналов, регулярных или инжектированных.
Выравнивание результата преобразований.
Результат преобразований может быть выровнен по правому или левому краю 16ти разрядного регистра результата. Способ выравнивания определяется состоянием бита ALIGN в регистре ADC_CR2.
Для инжектированных каналов в регистрах ADC_JOFR1-4 может быть задано математическое смещение результата. Содержимое соответствующих регистров ADC_JOFR x вычитается из результатов преобразований АЦП. Таким образом, для инжектированных каналов результат может оказаться отрицательным. В этом случае при правом выравнивании появляется знаковый разряд SEXT, который расширяется на все старшие разряды.
Смещение позволяет реализовать на однополярном АЦП измерение сигналов отрицательной полярности. Для этого необходимо добавить аппаратное положительное смещение входного сигнала, например, с помощью резисторного делителя. После этого нулевому уровню сигнала на входе будет соответствовать код напряжения смещения. А при подаче отрицательного сигнала напряжение на входе АЦП уменьшится, но останется положительным. Если в соответствующий регистр ADC_JOFR x записать код напряжения аппаратного смещения, то мы получим двуполярный измеритель.
Выбор источника внешнего запуска.
В предыдущих уроках мы запускали преобразование АЦП программно, установкой битов SWSTART и JSWSTART. Но преобразование может быть запущено от внешнего, по отношению к модулю АЦП, события. Им может быть внешний сигнал или аппаратное событие таймера.
Выбор источника определяется в регистре ADC_CR2 битами EXTSEL для регулярной группы и битами JEXTSEL для инжектированных каналов.
Должен быть разрешен запуск от внешнего сигнала. В регистре ADC_CR2 необходимо установить биты EXTTRIG и JEXTTRIG для регулярной и инжектированной групп соответственно.
Источник внешнего запуска для регулярных каналов.
Источник | Тип | EXTSEL[2:0] |
TIM1_CC1 | Внутренние сигналы от таймеров | 000 |
TIM1_CC2 | 001 | |
TIM1_CC3 | 010 | |
TIM2_CC2 | 011 | |
TIM3_TRGO | 100 | |
TIM4_CC4 | 101 | |
EXTI line 11 | Внешний вывод | 110 |
SWSTART | Программный запуск | 111 |
Источник внешнего запуска для инжектированных каналов.
Источник | Тип | JEXTSEL[2:0] |
TIM1_TRGO | Внутренние сигналы от таймеров | 000 |
TIM1_CC4 | 001 | |
TIM2_TRGO | 010 | |
TIM2_CC1 | 011 | |
TIM3_CC4 | 100 | |
TIM4_TRGO | 101 | |
EXTI line 15 | Внешний вывод | 110 |
JSWSTART | Программный запуск | 111 |
Мы пока изучили работу таймеров только в самом простом режиме. Поэтому события, перечисленные в первых столбцах таблиц, могут быть непонятными. Поговорим о них в последующих уроках.
Запуск преобразований АЦП от таймера, чтение результата с использованием прерываний. Разработка 4х канального вольтметра с измерением среднего значения.
На практике часто необходимо запускать опрос АЦП циклически, через одинаковые промежутки времени. Операция называется квантованием сигнала по времени и используется для математической обработки сигналов.
Например, вольтметры, которые мы разработали в предыдущих уроках, производили одно измерение с периодом 300 мс и выводили результат на дисплей. Если мы будем таким вольтметром измерять напряжение сигнала с большим уровнем пульсаций, то увидим, что результат все время ”скачет”. Вот пример из урока Ардуино на эту тему.
- Выборки вольтметра попадают на случайные места в периодически изменяющемся сигнале. Поэтому соседние измеренные значения могут отличаться друг от друга.
- Кроме того, вольтметр с одиночными измерениями подвержен разного рода электромагнитным помехам и наводкам.
У нашего АЦП высокая точность измерений, и это дополнительно усугубляет проблему.
Выход – производить несколько регулярных выборок сигнала и считать их среднее значение. Чем чаще будут происходить выборки, и чем большее число их будет усредняться, тем лучше будут подавляться пульсации.
Давайте решим практическую задачу – разработаем 4х канальный вольтметр среднего значения. Заодно разберемся, как циклически запускать преобразования АЦП от таймера. Для чтения результатов будем использовать прерывания.
Алгоритм работы устройства такой.
- С периодом 100 мкс таймер циклически запускает преобразование 4х инжектированных каналов.
- После окончания преобразования АЦП генерирует прерывание, в обработчике которого усредняются измеренные значения.
- Время усреднения 250 мс или 2500 выборок по 0,1 мс.
- По истечению периода усреднения измеренные значения выводятся на LCD-дисплей и через UART на компьютер.
За основу возьмем проект из урока 26, в котором уже выполнены базовые настройки АЦП.
Для циклического запуска АЦП будем использовать таймер 2. Через каждые 100 мкс он должен запускать преобразование инжектированных каналов АЦП. Это будет время дискретизации входных сигналов.
Зададим новую конфигурацию АЦП.
// выбор каналов
ADC1->JSQR = 0b00000000001100011000100000100000; // 0, 1, 2, 3 посл. инж. каналов
// источник запуска TIM2_TRGO
ADC1->CR2 &= ~(ADC_CR2_JEXTSEL_0 | ADC_CR2_JEXTSEL_2);
ADC1->CR2 |= ADC_CR2_JEXTSEL_1;
ADC1->CR2 |= ADC_CR2_JEXTTRIG;
ADC1->CR2 &= ~ADC_CR2_CONT; // запрет непрерывного режима
ADC1->CR1 |= ADC_CR1_SCAN; // разрешение режима сканирования
Разрешим прерывания АЦП в контроллере прерываний STM32 и прерывание по событию EOC.
NVIC_EnableIRQ (ADC1_2_IRQn); // разрешения прерываний АЦП в контроллере прерываний
ADC1->CR1 |= ADC_CR1_EOCIE; //разрешение прерываний по окончанию преобразования АЦП
Сделаем установку конфигурации таймера 2, как счетчика с периодом 100 мкс. Этому мы научились в уроке 19.
// установки таймера 2 на период 100 мкс
RCC -> APB1ENR |= RCC_APB1ENR_TIM2EN; // разрешение тактирования таймера
TIM2->SMCR &= ~ TIM_SMCR_SMS; // внутреннее тактирование, шина APB
TIM2->PSC = 719; // 10 мкс
TIM2->ARR = 9; // * 10
Мы намерены по перезагрузке таймера 2 запускать преобразование АЦП. И делать это аппаратным способом, без использования прерываний.
В качестве источника запуска АЦП мы уже задали событие TIM2_TRGO – выходной сигнал таймера 2. Теперь нам необходимо этот сигнал в таймере коммутировать к событию ”перезагрузка”. Это мы еще не изучали. Поэтому просто скажу, что необходимо в поле MMS регистра таймера TIM2_CR2 задать источник ”перезагрузка” (Update, код 010).
TIM2->CR2 &= ~(TIM_CR2_MMS_0 | TIM_CR2_MMS_2); //разрешение TRGO
TIM2->CR2 |= TIM_CR2_MMS_1;
В файле stm32f1xx_it.c создаем обработчик прерываний АЦП. Все, как в уроках о прерываниях таймеров или UART, только имя функции обработки другое.
void ADC1_2_IRQHandler(void) {
// накопление результатов АЦП
. . . . . . . . . . . . . .
averageCounter++;
if( averageCounter >= 2500 ) {
//перегрузка
. . . . . . . . . .
flagReady= 1; // признак готовности
}
ADC1->SR=0; // сброс флагов
}
- В каждом прерывании АЦП накапливаем результаты преобразования в специальных переменных.
- При выходе из прерывания необходимо сбрасывать флаги, которые это прерывание инициировали.
- Счетчик averageCounter считает число выборок, и при достижении его равным 2500 перегружает накопленные значения в переменные среднего результата измерения.
- Формируется признак flagReady, сообщающий о том, что появился новый результат измерений.
В основном цикле мы проверяем флаг готовности, и выводим измеренные значения на дисплей и в UART.
if( flagReady != 0 ) {
flagReady=0;
res= (float)averageADC[0] * VREF / 4096. / 2500.; // пересчет в напряжение
// вывод на LCD
. . . . . . . .
Полностью программу можно загрузить по ссылке.
Это уже вполне рабочий вольтметр среднего значения. При измерении сигналов с большим уровнем пульсаций он выведет среднее значение напряжение с периодом усреднения 250 мс.
Программа верхнего уровня.
Раз уж назвал вольтметр рабочим, то добавлю к нему программу верхнего уровня с функцией регистрации сигналов.
Загрузить ее можно по ссылке.
Программа отображает на компьютере текущее состояние напряжений на 4х входах.
Кроме того она имеет встроенный регистратор.
Регистрация происходит 4 раза в секунду.
В следующем уроке продолжим изучение АЦП. Скорее всего, будем разбираться в режиме сдвоенных преобразований.
Потрясающе! За полтора месяца — 6 превосходных нужных уроков! Спасибо, Эдуард, Огромное! Теперь надо перемолоть.
Спасибо. Приятно слышать.
Как раз сижу в раздумье АЦП STM32 закончить, или про ESP8266 начать писать.
Большое человеческое спасибо за Ваше дело. Уже две недели осваиваю STM32, даже близко не программист, совсем из другой области знаний, Ваш сайт просто находка. Пишите пожалуйста и дальше про STM — в интернете очень мало информации для тех кто уже помигал светодиодом на плате и при этом так и остался «салагой».
В качестве урока интересно было бы увидеть связку STM32 (мастер, управление оборудованием) + ESP8266 (связь с внешним миром). В интернете искал — ничего подобного для новичков нет. Flprog+ESP хорош, можно сделать всё что угодно и любой сложности, но безопасность своих близких и свой дом такому устройству не доверишь.
Спасибо за добрые слова.
Поддерживаю рассмотрение вопроса связки STM32+ESP8266.
Иначе STM32 по соотношению «цена-качество-доступность применения» начинает проигрывать ESP32.
Возможно задавали этот вопрос. Сейчас вместо TrueStudio логично использовать STM32CUBEIDE. Но у меня в примерах на месте Ваших комментариев вопросы.(неправильная кодировка)….Наскоком попытки поменять кодировку в IDE успехом не увенчались…Может кто сталкивался и решил этот нюанс…
Cпасибо. Познавательно весьма.
Спасибо. Интересно бы прочитать урок о формировании трехфазной модифицированой синусоиды для управления трехфазным мотором и изменения его оборотов потенциометром
Сайт хороший, много полезного, спасибо автору. Для меня интересная тема синхронизации с сетью. В нашей стране, это 50Гц. Раньше был разработчиком, писал всё на ассемблере. Не было альтернативы. На C написал только программу управления ввода — вывода и кусочно-линейную апроксимацию функции, прошивал обращение к ячейке по значению (естественно со смещением). Для разработки печатных плат мне понравился пакет PROTEUS
Собираетесь ли делать уроки по ПДП?
Здравствуйте!
Да, это следующая тема.
А как можно измерить линейное напряжение?
Здравствуйте, очень полезные уроки.
Имеется вопрос по программе Вольтметр, как делали, на чем? Очень уже полезная вещь.
Спасибо)
Здравствуйте!
В среде Borland C++ Builder.