Перейти к содержанию

АЦП и DMA: измеряем без участия ядра

Встроенный 12-битный АЦП STM32F103 умеет сам перебирать каналы по списку, а контроллер DMA — сам переносить каждый результат в массив в ОЗУ. Настроим их так, чтобы два входа измерялись непрерывно, а программа просто читала свежие значения, когда они нужны.

Обновлено Редакция GAW

STM32 с нуляурок 8 из 845 мин

Цель урока: Настроить АЦП STM32F103 на непрерывное сканирование двух каналов с калибровкой и правильным временем выборки, а DMA — на перенос результатов в массив по кругу.

У STM32F103C8 два 12-битных АЦП последовательного приближения — ADC1 и ADC2 — и 10 внешних входов на контактах Blue Pill. По разделу 11.2 RM0008 преобразование у STM32F103 занимает 1 мкс при частоте PCLK2 56 МГц (1,17 мкс при 72 МГц), а результат — 12-битное число от 0 до 4095. Как вообще работает такой АЦП и откуда берутся его ошибки, рассказано в статье «АЦП»; режимы и подводные камни АЦП STM32 разных серий — в статье «АЦП STM32».

Частота АЦП и время преобразования

АЦП тактируется отдельной частотой ADCCLK — от PCLK2 через делитель ADCPRE на 2, 4, 6 или 8 (разд. 7.2), и она не должна превышать 14 МГц (разд. 11.1, с. 215). При PCLK2 = 72 МГц делитель 6 даёт 12 МГц — его мы уже выставили в clock_init() урока 4. Делитель 4 дал бы 18 МГц — больше допустимого.

Время одного преобразования (разд. 11.6, с. 225):

Tconv = время выборки + 12,5 такта ADCCLK

Время выборки задают для каждого канала поля SMPx регистров ADC_SMPR1 и ADC_SMPR2: 1,5; 7,5; 13,5; 28,5; 41,5; 55,5; 71,5 или 239,5 такта. Пример RM0008: при 14 МГц и 1,5 такта Tconv = 14 тактов = 1 мкс.

Время выборки — это время, за которое внутренний конденсатор АЦП (до 8 пФ по таблице 47 DS5319) успевает зарядиться от источника. Чем больше сопротивление источника, тем дольше надо ждать. Таблица 48 DS5319 даёт наибольшее сопротивление источника для ошибки меньше 1/4 младшего разряда при fADC = 14 МГц:

Выборка, тактов1,57,513,528,541,555,5
Наибольшее RAIN, кОм0,45,911,425,237,250

Потенциометр 10 кОм в среднем положении — это источник с сопротивлением 2,5 кОм (две половины по 5 кОм параллельно, расчёт наш), и формально хватило бы 7,5 такта. Возьмём 239,5 такта с запасом: тогда Tconv = 252 такта = 21 мкс на канал при 12 МГц, а два канала обновляются примерно 24 тысячи раз в секунду (расчёт наш) — для ручек и датчиков более чем достаточно. Время выборки и сопротивление источника для любых условий посчитает калькулятор АЦП STM32.

Каналы, последовательность и режимы

По таблице 5 DS5319 PA0 — вход ADC12_IN0, PA1 — ADC12_IN1. Оба вывода переводятся в аналоговый режим (CNF = 00, MODE = 00): в нём триггер Шмитта выключен и не тратит ток при промежуточном напряжении (RM0008, разд. 9.1.10).

Что и в каком порядке измерять, задаёт регулярная последовательность: поле L регистра ADC_SQR1 — число преобразований минус один, поля SQ1, SQ2… регистров ADC_SQR3, ADC_SQR2, ADC_SQR1 — номера каналов по порядку (разд. 11.3.3). У нас L = 1 (два преобразования), SQ1 = 0, SQ2 = 1.

Два бита управляют повторением:

  • SCAN в ADC_CR1 — пройти всю последовательность, а не один канал. По разделу 11.3.8 (с. 221) в этом режиме обязательно включать DMA: регистр результата ADC_DR один, и следующее преобразование перезапишет предыдущее;
  • CONT в ADC_CR2 — после последнего канала начать последовательность сначала.

Включение и калибровка

Последовательность включения по разделам 11.3.1 и 11.4 RM0008:

  1. ADON = 1 — разбудить АЦП. Первая запись ADON выводит его из выключенного состояния, повторная после времени стабилизации tSTAB запускает преобразование; по таблице 47 DS5319 tSTAB не больше 1 мкс.
  2. Подождать: перед калибровкой АЦП должен быть включён не меньше двух тактов ADCCLK (разд. 11.4, с. 223).
  3. CAL = 1 — запустить калибровку. Аппаратура сама сбросит бит, когда закончит; по таблице 47 DS5319 калибровка длится 83 такта ADCCLK. RM0008 рекомендует калибровать после каждого включения.
  4. Запустить преобразования.

Есть тонкость с ADON: если в той же записи в ADC_CR2 меняется любой другой бит, преобразование не запускается (примечание к разд. 11.12.3, с. 243). Поэтому запуск через ADON легко сделать случайно или, наоборот, случайно не сделать. Надёжнее запускать битом SWSTART: для этого поле EXTSEL = 111 выбирает SWSTART источником запуска, а бит EXTTRIG разрешает запуск по выбранному событию (разд. 11.7 и 11.12.3).

DMA: перенос без участия ядра

Контроллер DMA переносит данные между периферией и памятью по запросу. По таблице 78 RM0008 (с. 282) запросы ADC1 обслуживает канал 1 контроллера DMA1, а запрос DMA приходит только по окончании регулярного преобразования (разд. 11.8, с. 227). Канал настраивают в порядке из раздела 13.3.3 (с. 278):

  1. CPAR — адрес периферии: &ADC1->DR;
  2. CMAR — адрес массива в ОЗУ;
  3. CNDTR — число переносов: 2. Писать в него можно только при выключенном канале (разд. 13.4.4, с. 287);
  4. CCR — направление DIR = 0 (из периферии в память), PSIZE = MSIZE = 01 (16 бит), MINC — увеличивать адрес памяти, но не периферии, CIRC — кольцевой режим: после последнего переноса счётчик сам перезагружается исходным значением и адрес возвращается к началу массива;
  5. бит EN — включить канал.

Тактирование DMA1 включает бит DMA1EN в RCC_AHBENR. Что такое кольцевой режим и где его ещё применяют, рассказывает статья глоссария «Кольцевой режим DMA», а потоки, каналы и FIFO у разных серий STM32 — статья «DMA в STM32».

Программа

Средний вывод потенциометра 10 кОм — на PA0, крайние — на 3.3 и G платы; второй потенциометр или фоторезистор с резистором — на PA1. Преобразователь USB-UART — как в уроке 5.

/* main.c, lesson 8: ADC1 scans PA0 and PA1 continuously, DMA1 channel 1 stores the results */
#include "stm32f1xx.h"
#include "clock.h"
#include "uart.h"

static volatile uint16_t adc_buf[2];        /* [0] = IN0 (PA0), [1] = IN1 (PA1) */

static void adc1_dma_init(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;
    RCC->AHBENR  |= RCC_AHBENR_DMA1EN;

    /* PA0, PA1: analog mode (CNF = 00, MODE = 00) */
    GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0 | GPIO_CRL_CNF1 | GPIO_CRL_MODE1);

    /* DMA1 channel 1 serves ADC1 (RM0008, Table 78) */
    DMA1_Channel1->CCR   = 0u;                          /* channel off while configuring */
    DMA1_Channel1->CPAR  = (uint32_t)&ADC1->DR;
    DMA1_Channel1->CMAR  = (uint32_t)adc_buf;
    DMA1_Channel1->CNDTR = 2u;
    DMA1_Channel1->CCR   = DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0    /* 16 bit to 16 bit */
                         | DMA_CCR_MINC | DMA_CCR_CIRC;         /* DIR = 0: from ADC */
    DMA1_Channel1->CCR  |= DMA_CCR_EN;

    /* ADC1: IN0 then IN1, 239.5 cycles each, scan */
    ADC1->SMPR2 = (0x7u << ADC_SMPR2_SMP0_Pos) | (0x7u << ADC_SMPR2_SMP1_Pos);
    ADC1->SQR1  = (2u - 1u) << ADC_SQR1_L_Pos;          /* two conversions */
    ADC1->SQR3  = (0u << ADC_SQR3_SQ1_Pos) | (1u << ADC_SQR3_SQ2_Pos);
    ADC1->CR1   = ADC_CR1_SCAN;

    /* Power up, wait, calibrate (RM0008, 11.3.1 and 11.4) */
    ADC1->CR2 = ADC_CR2_ADON;
    delay_ms(1u);                       /* >> tSTAB (1 us) and 2 ADC clock cycles */
    ADC1->CR2 |= ADC_CR2_CAL;
    while (ADC1->CR2 & ADC_CR2_CAL) { }

    /* Continuous, DMA requests, start by SWSTART (EXTSEL = 111) */
    ADC1->CR2 |= ADC_CR2_CONT | ADC_CR2_DMA | ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG;
    ADC1->CR2 |= ADC_CR2_SWSTART;
}

static uint32_t to_mv(uint32_t raw)
{
    return (raw * 3300u + 2047u) / 4095u;   /* VREF+ = VDDA, taken as 3.3 V */
}

int main(void)
{
    uint32_t hclk = clock_init();
    systick_init(hclk);
    uart1_init(hclk, 115200u);
    adc1_dma_init();

    for (;;) {
        uint32_t a0 = adc_buf[0];
        uint32_t a1 = adc_buf[1];
        uart1_puts("PA0: ");
        uart1_put_uint(to_mv(a0));
        uart1_puts(" mV, PA1: ");
        uart1_put_uint(to_mv(a1));
        uart1_puts(" mV\n");
        delay_ms(200u);
    }
}

delay_ms(1) вызывается после systick_init(), так что SysTick уже работает; миллисекунда — с огромным запасом больше и tSTAB, и двух тактов ADCCLK. Обе записи ADC1->CR2 |= … после калибровки меняют вместе с ADON и другие биты, поэтому сами по себе преобразование не запускают — его запускает SWSTART. Массив объявлен volatile: его меняет DMA, а не код программы, и компилятор не должен держать значения в регистрах.

Пересчёт в милливольты предполагает, что опорное напряжение равно 3,3 В. По примечанию 3 к таблице 47 DS5319 в корпусах LQFP опорный вход VREF+ внутри соединён с VDDA, так что погрешность питания платы прямо входит в результат. Округление + 2047 в to_mv — наш выбор: так ошибка пересчёта не больше половины милливольта.

Значения двух каналов в массиве обновляются независимо, и программа может прочитать adc_buf[0] из одного прохода, а adc_buf[1] — из следующего. Для ручек это неважно; если нужна строгая пара, включают прерывания DMA по половине и концу передачи (биты HTIE и TCIE) и читают ту половину буфера, которую DMA сейчас не пишет.

На STM32F4

АЦП STM32F411 тоже 12-битный, но устроен иначе: калибровочного бита CAL у него нет (в заголовке stm32f411xe.h нет ADC_CR2_CAL), делитель частоты ADCPRE находится в общем регистре ADC_CCR (у F405/F407 — RM0090, разд. 13.13.16), а DMA — с потоками и каналами: у F405/F407 запросы ADC1 обслуживает DMA2, поток 0 или 4, канал 0 (RM0090, табл. 44). Для F411 номер потока и канала сверяйте с таблицей запросов DMA2 в RM0383. Регистры последовательности SQR, времени выборки SMPR и биты SCAN, CONT, DMA и SWSTART сохранили названия, но вместо бита EXTTRIG в ADC_CR2 у F4 поле EXTEN и появился бит DDS, поэтому код переносится не построчно.

Задание

  1. Добавьте третий канал — внутренний опорный источник VREFINT на IN17 — и по нему оцените фактическое напряжение VDDA. Что нужно включить и какое время выборки взять?
  2. Сколько памяти займёт буфер на 256 последовательностей по два канала и как изменятся настройки DMA?
Ответ и решение
  1. VREFINT подключён к каналу IN17 только у ADC1 и включается битом TSVREFE регистра ADC_CR2 (разд. 11.3.3 и 11.12.3). Время выборки — не меньше 17,1 мкс (DS5319, табл. 12): при 12 МГц это не меньше 206 тактов, то есть 239,5 такта (≈ 20 мкс) в поле SMP17 регистра ADC_SMPR1. Последовательность: L = 2, SQ3 = 17 (биты поля SQ3 в ADC_SQR3), CNDTR = 3, массив на три элемента. Тогда VDDA ≈ 1,20 В × 4095 / adc_buf[2]. Погрешность определяется разбросом самого VREFINT — от 1,16 до 1,24 В по таблице 12, то есть около ±3 % (расчёт наш).

  2. 256 × 2 × 2 байта = 1024 байта (расчёт наш) — заметная доля из 20 КБ ОЗУ, но помещается. В DMA меняется только CNDTR = 512 и размер массива; MINC и CIRC остаются. Чтобы данные не перезаписывались во время обработки, включают прерывания HTIE и TCIE в DMA1_Channel1->CCR и в обработчике DMA1_Channel1_IRQHandler обрабатывают ту половину, которую DMA только что заполнил.

Что дальше

Это последний урок первой части трека: у вас есть свой стартовый файл, тактирование 72 МГц, UART, прерывания, таймеры и АЦП с DMA — всё на регистрах. Следующие уроки — SPI, I²C, запись во флеш-память и те же задачи на LL и HAL — уже в плане трека на странице «STM32 с нуля». Регистры АЦП и DMA STM32F1 с битами — на страницах атласа регистров ADC STM32F1 и регистров DMA STM32F1. Подать на вход больше 3,3 В поможет делитель напряжения для АЦП, а термины урока объясняют статьи глоссария «Время выборки АЦП», «Калибровка АЦП», «Режим сканирования» и DMA.

Частые вопросы

Почему АЦП STM32F103 показывает неточные значения?

Частые причины: не сделана калибровка после включения, слишком короткое время выборки для источника с большим сопротивлением, нестабильное питание VDDA, которое у корпуса LQFP48 служит и опорным напряжением. По таблице 48 DS5319 при 1,5 такта выборки источник должен иметь сопротивление не больше 0,4 кОм, а при 55,5 такта — до 50 кОм. Для делителя из резисторов по 100 кОм берите 239,5 такта или ставьте конденсатор на вход.

Зачем DMA, если можно читать ADC_DR в прерывании?

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

Можно ли подавать на вход АЦП 5 В?

Нет. Выводы PA0–PA7, PB0 и PB1, на которых у STM32F103C8 входы АЦП, не отмечены в таблице 5 DS5319 как FT, а сам АЦП измеряет только от 0 до VREF+, которое у корпуса LQFP48 равно VDDA, то есть 3,3 В на Blue Pill. Напряжение 5 В подают через резистивный делитель — рассчитать его поможет страница о делителе для АЦП.

Источники

  1. STMicroelectronics. RM0008 Rev 21. Reference manual STM32F101xx–STM32F107xx — разд. 7.2: АЦП тактируется PCLK2/2, /4, /6 или /8; разд. 11.1 (с. 215): 12-битный АЦП последовательного приближения, ADCCLK не выше 14 МГц; разд. 11.2: у STM32F103 1 мкс при 56 МГц (1,17 мкс при 72 МГц), питание АЦП 2,4–3,6 В; разд. 11.3.1 (с. 218): первая установка ADON будит АЦП, повторная после tSTAB запускает преобразование; разд. 11.3.3: каналы IN16 — датчик температуры, IN17 — VREFINT; разд. 11.3.8 (с. 221): в режиме сканирования нужен бит DMA, при CONT сканирование повторяется с первого канала; разд. 11.4 (с. 223): калибровка битом CAL, рекомендуется после каждого включения, перед ней АЦП должен быть включён (ADON = 1) не меньше двух тактов ADCCLK; разд. 11.6 (с. 225): Tconv = время выборки + 12,5 такта, SMPx от 1,5 до 239,5 такта; разд. 11.8 (с. 227): запрос DMA только по окончании регулярного преобразования, DMA умеют ADC1 и ADC3; разд. 11.12.3 (с. 240–244): ADC_CR2 — TSVREFE (23), SWSTART (22), EXTTRIG (20), EXTSEL (19:17, 111 — SWSTART), DMA (8), CAL (2), CONT (1), ADON (0); при изменении других битов вместе с ADON преобразование не запускается; разд. 13.3.3 (с. 278): порядок настройки канала DMA — CPAR, CMAR, CNDTR, CCR, затем EN; кольцевой режим перезагружает счётчик; табл. 78 (с. 282): ADC1 — DMA1, канал 1; разд. 13.4.3–13.4.4 (с. 286–287): DMA_CCR — MSIZE, PSIZE (01 — 16 бит), MINC, CIRC, DIR, EN; CNDTR можно писать только при выключенном канале
  2. STMicroelectronics. DS5319 Rev 20. STM32F103x8, STM32F103xB datasheet — табл. 5: PA0 — ADC12_IN0, PA1 — ADC12_IN1, без отметки FT; табл. 12: VREFINT 1,16–1,24 В (типично 1,20 В) при −40…+85 °C, время выборки для VREFINT до 17,1 мкс; табл. 47: fADC 0,6–14 МГц, tSTAB не больше 1 мкс, tCAL — 83 такта ADCCLK, примечание 3: в корпусах LQFP и VFQFPN VREF+ внутри соединён с VDDA, VREF− — с VSSA; табл. 48: наибольшее сопротивление источника при fADC = 14 МГц — 0,4 кОм при 1,5 такта, 5,9 кОм при 7,5, 11,4 кОм при 13,5, 25,2 кОм при 28,5, 37,2 кОм при 41,5, 50 кОм при 55,5 такта
  3. STMicroelectronics. cmsis-device-f1 v4.3.5: Include/stm32f103xb.h — RCC_AHBENR_DMA1EN, RCC_APB2ENR_ADC1EN, ADC_CR2_*, ADC_CR1_SCAN, ADC_SMPR2_SMP0_Pos, ADC_SQR1_L_Pos (20), ADC_SQR3_SQ1_Pos и SQ2_Pos (0 и 5), DMA_CCR_MSIZE_0 (0x400), DMA_CCR_PSIZE_0 (0x100), DMA_CCR_MINC, DMA_CCR_CIRC, DMA_CCR_EN, DMA1_Channel1
  4. STMicroelectronics. RM0090 Rev 21 (STM32F405/407); cmsis-device-f4 v2.6.11, stm32f411xe.h — разд. 13.13.16: делитель ADCPRE (PCLK2 /2, /4, /6, /8) в общем регистре ADC_CCR; табл. 44 (с. 311): ADC1 — DMA2, поток 0 или 4, канал 0; в заголовке F411 нет бита ADC_CR2_CAL, есть ADC_CCR_ADCPRE и DMA2_Stream0

Нашли ошибку?