Embedded HandbookПриложения
Все разделы › III. Периферия
STM32F103, STM32F407 · уровень средний · 20 мин чтения
Открыть в интерактивном справочнике

14. Аналого-цифровой преобразователь (АЦП)

Всё, что приходит от реального мира — температура, ток, положение движка, напряжение батареи — приходит аналоговым. АЦП превращает его в число. Блок кажется простым, но именно здесь чаще всего получают правдоподобные, но неверные результаты.

Как это называется в документации

Оригинал Перевод
successive approximation последовательное приближение
sampling time время выборки
conversion time время преобразования
regular / injected channel регулярный / инжектируемый канал
scan mode режим сканирования
continuous mode непрерывный режим
external trigger внешний запуск
analog watchdog аналоговый сторож
calibration калибровка
reference voltage опорное напряжение
source impedance сопротивление источника
factory calibration value заводское калибровочное значение
chip temperature sensor датчик температуры кристалла

14.1 Как он работает и что из этого следует

Внутри — конденсатор, который заряжается от входа, и схема, шаг за шагом подбирающая двоичный код. Отсюда два следствия, определяющих всё остальное.

Конденсатору нужно время зарядиться. Чем выше сопротивление источника, тем дольше. Не дали времени — результат сместится к напряжению, что было на конденсаторе, то есть к предыдущему каналу, причём ошибка стабильная и правдоподобная.

Вход не бесплатен. АЦП подгружает измеряемую цепь. Делитель из мегаомных резисторов будет измеряться неверно просто потому, что не успевает перезаряжать конденсатор.

Разрядность и опора

У F1 и F4 разрядность 12 бит — 4096 градаций. Код связан с напряжением так:

напряжение = код × Vref / 4095

При опоре 3,3 В один код — примерно 0,8 мВ. Точность результата не может быть лучше точности опорного напряжения: если питание «плавает» от 3,2 до 3,4 В, все измерения плавают вместе с ним.


14.2 Время преобразования

время = (время выборки + время приближения) / ADCCLK

У F1 время приближения — 12,5 такта, время выборки настраивается от 1,5 до 239,5 такта на каждый канал отдельно.

Выборка Всего тактов При 12 МГц
1,5 14 1,17 мкс
13,5 26 2,17 мкс
55,5 68 5,67 мкс
239,5 252 21 мкс

Как выбирать. Минимума — 1,5 такта — хватает только очень низкоомному источнику, например выходу операционного усилителя. По паспорту F1 при ADCCLK = 14 МГц сопротивление источника должно быть не больше (при 12 МГц, как в этом разделе, пределы чуть выше):

Выборка, тактов 1,5 7,5 13,5 28,5 41,5 55,5
Источник, не больше 0,4 кОм 5,9 кОм 11,4 кОм 25,2 кОм 37,2 кОм 50 кОм

Уже делитель 10 кОм / 10 кОм (5 кОм на выходе) требует не меньше 7,5 такта. Для датчика с высоким сопротивлением или длинных проводов берут 55,5 и больше; если выборка должна быть короткой, на вход ставят конденсатор на землю — заряд берётся из него. Признак нехватки — показания занижены и зависят от того, какой канал измерялся перед этим.

Как это выглядит на конденсаторе выборки, если источник — датчик на 50 кОм, а предыдущий канал оставил на конденсаторе ноль:

Заряд конденсатора выборки АЦП от источника 50 кОм: за 1,5 такта он не успевает, за 55,5 такта — успевает

⚠️ Тактовая АЦП у F1 не должна превышать 14 МГц. При PCLK2 = 72 МГц доступны делители 2, 4, 6, 8 — и только делитель 6 или 8 укладываются в предел. Поставите 2 или 4 — блок продолжит работать и выдавать числа, но они будут неверными. Ошибки не возникнет, диагностики никакой:

c RCC->CFGR &= ~RCC_CFGR_ADCPRE; RCC->CFGR |= RCC_CFGR_ADCPRE_DIV6; /* 12 МГц */

У F4 предел выше — 36 МГц, и делители другие.


14.3 ⚠️ Калибровка: обязательна у F1

Ключевое расхождение платформ.

STM32F1 STM32F4
калибровка выполняется программой программной калибровки нет, запускать нечего

У F1 после включения блока положено запустить калибровку — иначе погрешность заметно выше паспортной:

ADC1->CR2 |= ADC_CR2_ADON;              /* первое включение: просыпается */
for (volatile int i = 0; i < 1000; i++) { }   /* пауза на стабилизацию */

ADC1->CR2 |= ADC_CR2_RSTCAL;            /* сброс калибровочного регистра */
while (ADC1->CR2 & ADC_CR2_RSTCAL) { }
ADC1->CR2 |= ADC_CR2_CAL;               /* сама калибровка */
while (ADC1->CR2 & ADC_CR2_CAL) { }

⚠️ Первая запись ADON только включает блок, не запускает преобразование. Между включением и калибровкой нужна пауза — блоку требуется время на стабилизацию. Запуск преобразования — это повторная запись ADON в уже включённый блок. Логика неочевидная, и на ней спотыкаются.


14.4 🔧 Одиночное измерение

uint16_t adc1_read(uint8_t channel)
{
    ADC1->SQR3 = channel;               /* первым в очереди — нужный канал */
    ADC1->SQR1 = 0;                     /* длина очереди 1 */

    ADC1->CR2 |= ADC_CR2_ADON;          /* запуск */
    while (!(ADC1->SR & ADC_SR_EOC)) { }

    return (uint16_t)ADC1->DR;          /* чтение сбрасывает флаг */
}

Не забудьте перевести вывод в аналоговый режим — для F1 это CNF = 00 при MODE = 00 (раздел 8). Оставленный цифровым вход даёт показания, похожие на правду, но искажённые.


14.5 Очередь каналов и режимы

АЦП обходит не один вход, а очередь — до шестнадцати каналов в любом порядке, с повторами. Порядок задаётся регистрами SQR.

Режим Что делает
одиночный одно преобразование по команде
сканирование (SCAN) пройти всю очередь за один запуск
непрерывный (CONT) начинать заново сразу после завершения
по внешнему запуску преобразование стартует от таймера или вывода

Регулярные и инжектируемые каналы. Помимо основной очереди есть вторая, приоритетная: инжектируемые каналы прерывают обычное преобразование, делают своё и возвращают управление. Так измеряют что-то срочное — например, ток в фазе двигателя, синхронно с ШИМ, — не ломая фоновый опрос датчиков.


14.6 🔧 Сканирование с DMA

Сканирование без DMA почти бессмысленно: у всех каналов один регистр результата, и следующее преобразование затрёт предыдущее раньше, чем процессор успеет прочитать. DMA забирает каждый результат сразу.

/* АЦП уже включён и откалиброван (14.3): последняя запись ADON — запуск.
   Время выборки каналов 0–3 при необходимости задают в SMPR2 (14.1). */
void adc1_scan_dma_init(void)
{
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    /* ADC1 жёстко привязан к каналу 1 DMA1 — выбора нет. */
    DMA1_Channel1->CCR &= ~DMA_CCR1_EN;
    DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
    DMA1_Channel1->CMAR = (uint32_t)adc_buf;
    DMA1_Channel1->CNDTR = 4;
    DMA1_Channel1->CCR = DMA_CCR1_MINC        /* адрес в памяти растёт */
                       | DMA_CCR1_CIRC        /* по кругу */
                       | DMA_CCR1_PSIZE_0     /* периферия: полуслово */
                       | DMA_CCR1_MSIZE_0     /* память: полуслово */
                       | DMA_CCR1_PL_1;
    DMA1_Channel1->CCR |= DMA_CCR1_EN;

    /* Очередь из четырёх каналов: 0, 1, 2, 3. */
    ADC1->SQR1 = (4u - 1u) << 20;             /* длина очереди */
    ADC1->SQR3 = (0u << 0) | (1u << 5) | (2u << 10) | (3u << 15);

    ADC1->CR1 |= ADC_CR1_SCAN;
    ADC1->CR2 |= ADC_CR2_DMA | ADC_CR2_CONT;
    ADC1->CR2 |= ADC_CR2_ADON;
}

Сканирование АЦП с DMA: очередь из четырёх каналов, один регистр результата и массив по кругу

Дальше значения просто лежат в массиве и обновляются сами. Массив обязан быть volatile — его меняет DMA помимо кода (раздел 3).

Запуск от таймера — следующий шаг. Непрерывный режим оцифровывает с максимальной скоростью, что редко нужно: получается гора данных с неравномерными интервалами. Правильнее запускать преобразование таймером, тогда отсчёты идут строго с заданным периодом — а это обязательное условие для любой последующей обработки сигнала.

Связка «таймер → АЦП → DMA» работает вообще без участия процессора: он лишь разбирает готовый буфер.


14.7 Встроенные каналы

Кроме внешних входов есть два внутренних.

Датчик температуры кристалла. Измеряет не окружающую среду, а сам кристалл, который греется от работы. Годится для контроля перегрева и грубой оценки, но не для метеостанции: разброс между экземплярами велик.

Опорное напряжение VREFINT — примерно 1,2 В, стабильное независимо от питания. Отсюда полезный приём: измерить собственное питание без единой внешней детали.

Логика такая: код VREFINT тем меньше, чем выше питание — ведь опора для преобразования и есть питание. Зная точное значение VREFINT, из его кода вычисляют VDDA. У F4 в системной памяти лежит заводское калибровочное значение, что делает оценку точной.

Практическая ценность: устройство на батарее может следить за её разрядом, не тратя ни выводов, ни резисторов делителя, ни тока на этот делитель.


14.8 Аналоговый сторож

Блок сравнивает результат с двумя порогами и вызывает прерывание при выходе за границы. Процессор не опрашивает значения вовсе — реагирует только на событие.

Применение очевидное: следить за перегревом, обрывом датчика, просадкой питания. Всё это без единой строки в основном цикле.


14.9 🔧 Милливольты без внешней опоры

Код АЦП — доля от опорного напряжения VREF+, а не милливольты: код 0 — ноль вольт, 4095 — само VREF+, каким бы оно сейчас ни было. В корпусах до 64 выводов VREF+ соединён внутри кристалла с выводом аналогового питания VDDA; в больших корпусах это отдельный вывод. На большинстве плат его подключают к тому же питанию, и тогда опора — это VDDA: пока питание ровно 3,3 В, код × 3300 / 4095 верен; сядет батарея до 3,0 В — все показания съедут на 10 %. Если же на VREF+ стоит отдельный точный источник (2,5 В, 2,048 В), шкала от питания не зависит, и пересчёт ниже не нужен.

Узнавать опору при каждом измерении помогает внутренний источник VREFINT (14.7): его напряжение от питания не зависит. Способа два:

У F4 доступны оба, у F1 заводского кода нет — только первый. Дальше в коде опора названа vdda: на платах она и есть питание.

F4: заводские коды. В системной памяти лежат три числа, снятые на заводе при питании 3,3 В:

Адрес Что Условия
0x1FFF 7A2A код VREFINT 3,3 В, 30 °C
0x1FFF 7A2C код датчика температуры 30 °C
0x1FFF 7A2E код датчика температуры 110 °C

Адреса — из драйвера LL для всего семейства F4 (stm32f4xx_ll_adc.h), условия — оттуда же: VREF+ = 3,3 В. Перед использованием ячейку стоит проверить: незаписанная флеш-память читается как 0xFFFF — значит, заводского значения нет, и остаётся номинал, как у F1. Из заводского кода — опора, а через неё милливольты любого канала и температура по двум точкам. Всё в целых числах:

/* питание VDDA по коду VREFINT: у F4 — по заводскому коду cal, снятому при 3,3 В */
uint32_t vdda_from_cal(uint16_t vref_code, uint16_t cal)
{
    return (3300u * cal + vref_code / 2u) / vref_code;
}

uint32_t code_to_mv(uint16_t code, uint32_t vdda_mv)
{
    return (code * vdda_mv + 2047u) / 4095u;
}

/* F4: температура кристалла по двум заводским точкам, в десятых долях градуса */
int32_t temp_x10(uint16_t ts_code, uint32_t vdda_mv, uint16_t cal30, uint16_t cal110)
{
    int32_t ts = (int32_t)((ts_code * vdda_mv + 1650u) / 3300u);   /* код, как при 3,3 В */
    return 300 + (ts - cal30) * 800 / (cal110 - cal30);
}

F1: калибровки нет. Остаётся номинал 1,2 В:

/* у F1 калибровки нет — только номинальные 1200 мВ */
uint32_t vdda_from_nominal(uint16_t vref_code)
{
    return (1200u * 4095u + vref_code / 2u) / vref_code;
}

Ошибка опоры переходит в ошибку питания один к одному: опора на 1 % ниже номинала — и 3,3 В посчитаются как 3334 мВ.

Включение внутренних каналов. Бит TSVREFE у F1 стоит в ADC1->CR2, у F4 — в общем ADC->CCR. VREFINT — канал 17, датчик у F405/407 — канал 16. Время выборки берут самое длинное, потому что оба источника высокоомные: 239,5 такта при 12 МГц — 20 мкс, 480 тактов при 21 МГц — 23 мкс.

void internal_channels_on(void)
{
    ADC->CCR    |= ADC_CCR_TSVREFE;                     /* включить датчик и VREFINT */
    ADC1->SMPR1 |= ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17;   /* 111: 480 тактов — 23 мкс при 21 МГц */
}

uint32_t vdda_now(void)
{
    return vdda_from_cal(adc_read_ch(17), VREFINT_CAL_F4);
}

int32_t chip_temp_x10(void)                             /* канал 16 — датчик у F405/407 */
{
    uint32_t vdda = vdda_now();
    return temp_x10(adc_read_ch(16), vdda, TS_CAL30_F4, TS_CAL110_F4);
}

Что показал пересчёт. Функции прогнаны на компьютере против идеального АЦП: питание от 2,0 до 3,6 В через 1 мВ, температура от −40 до 125 °C при питании 2,4–3,6 В. Заводские коды взяты для примера: VREFINT 1,21 В (код 1502), датчик 0,76 В при 25 °C и 2,5 мВ/°C (коды 959 и 1207).

Что считали Наибольшая ошибка
VDDA по коду VREFINT 2 мВ
милливольты канала (1234 мВ) 1 мВ
температура кристалла 0,4 °C

Это ошибки одного лишь округления — от разрядности и целой арифметики. Сам датчик ошибается на градусы и греется вместе с кристаллом (14.7).


14.10 F0 и G0: АЦП без очереди

У F0 и G0 один АЦП, и устроен он не как у F1 и F4. Главное отличие — нет очереди SQR1…SQR3 с позициями каналов: каналы просто отмечаются битами в CHSELR, и АЦП проходит их по возрастанию номера (SCANDIR — в обратную сторону). Данные — в DR по смещению 0x40, запуск — бит ADSTART, конец измерения — EOC, конец всей маски — EOS. Калибровка обязательна у обеих серий и делается при выключенном АЦП (ADEN = 0) — в отличие от F1, где АЦП перед калибровкой, наоборот, включают (14.3):

    ADC1->CR |= ADC_CR_ADCAL;                   /* калибровка — при выключенном АЦП */
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR |= ADC_CR_ADEN;
    while (!(ADC1->ISR & ADC_ISR_ADRDY)) {
    }

Одно измерение — маска канала вместо номера:

uint16_t adc_read_f0(uint32_t channel_mask)
{
    ADC1->CHSELR = channel_mask;                /* один бит на канал, не номер в SQR */
    ADC1->CR |= ADC_CR_ADSTART;
    while (!(ADC1->ISR & ADC_ISR_EOC)) {
    }
    return (uint16_t)ADC1->DR;
}

Код с F1 здесь не собирается — и это хорошо, ошибка ловится сразу: no member named 'SQR3' in 'ADC_TypeDef', то же для CR2.

Что добавляет G0. Три вещи, каждая ловит переносящего код с F0:

    ADC1->CR |= ADC_CR_ADVREGEN;                /* сначала регулятор — и 20 мкс ожидания */
    wait_us(LL_ADC_DELAY_INTERNAL_REGUL_STAB_US);
    ADC1->ISR = ADC_ISR_CCRDY;                  /* флаг сбрасывается записью единицы */
    ADC1->CHSELR = channel_mask;
    while (!(ADC1->ISR & ADC_ISR_CCRDY)) {      /* у G0 смена каналов вступает не сразу */
    }

Ещё у G0 есть передискретизация: АЦП сам складывает до 256 измерений и сдвигает сумму — 16-битный результат без участия ядра:

    ADC1->CFGR2 |= ADC_CFGR2_OVSE | LL_ADC_OVS_RATIO_256 | LL_ADC_OVS_SHIFT_RIGHT_4;

Такт и время. Такт АЦП — синхронный от PCLK (CKMODE: /2, /4, у G0 ещё /1) или свой асинхронный; у G0 асинхронный можно делить PRESC до 256. Время выборки: у F0 от 1,5 до 239,5 такта, у G0 от 1,5 до 160,5, причём G0 держит два разных времени (SMP1, SMP2) и назначает каждому каналу одно из них битами SMPSEL. В листингах — самое долгое время для VREFINT:

F0 при 12 МГц G0 при 16 МГц
выборка + преобразование, тактов 239,5 + 12,5 = 252 160,5 + 12,5 = 173
одно измерение 21 мкс 10,8 мкс

Внутренние каналы и заводские коды. Номера каналов у серий разные, и условия заводской калибровки — тоже:

F0 G0
канал VREFINT 17 13
канал датчика температуры 16 12
код VREFINT снят при VREF+ 3,3 В 3,0 В
адрес кода VREFINT 0x1FFF F7BA 0x1FFF 75AA
точки датчика температуры 30 и 110 °C 30 и 130 °C

Две точки датчика есть не у всех F0: у F030 и F070 заводская точка одна — TS_CAL1 при 30 °C, и наклон берут паспортный.

Значит, формула из 14.9 у G0 берёт 3000, а не 3300 — иначе питание выйдет завышенным на 10 %:

/* питание VDDA по коду VREFINT и заводскому коду, снятому при 3,0 В */
uint32_t vdda_mv_g0(void)
{
    uint16_t code = adc_read_g0(CH_VREFINT);
    return (VREFINT_CAL_VREF * (uint32_t)*VREFINT_CAL_ADDR + code / 2u) / code;
}

VREFINT_CAL_VREF и VREFINT_CAL_ADDR — из заголовка LL своей серии (stm32f0xx_ll_adc.h, stm32g0xx_ll_adc.h): подключите его, и числа подставятся верные сами; те же имена есть и в LL для F4.


14.11 Грабли

Показания занижены и «плавают» между каналами. Мало времени выборки — конденсатор не успевает зарядиться, а остаточный заряд от предыдущего канала тянет результат за собой. Увеличьте время выборки.

АЦП врёт после разгона. Превышена частота ADCCLK. Блок работает, но результат недостоверен, и никакого признака ошибки нет.

Вывод не переведён в аналоговый режим. Цифровая обвязка остаётся подключённой и искажает измерение.

Забыта калибровка на F1. Погрешность заметно выше ожидаемой.

При сканировании читается только первый канал. Не включён DMA — остальные результаты затёрты.

Массив под DMA не volatile. Компилятор кэширует значение, и программа видит одно и то же число.

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

Опора не там, где кажется. Точность привязана к VDDA. Если питание берётся от USB через диод, оно вовсе не 3,3 В — и все измерения смещены.

Показания плывут вместе с батареей. код × 3300 / 4095 верен только при 3,3 В. Питание считают по VREFINT при каждом измерении (14.9).

Внутренние каналы дают ноль. Не включён TSVREFE: у F1 он в ADC1->CR2, у F4 — в общем ADC->CCR.


14.12 Итоги

Полные исходники

adc_f0.c — АЦП STM32F072: калибровка, VREFINT, питание в милливольтах.

/* Раздел 14.10: АЦП STM32F072 — калибровка, VREFINT на канале 17, питание в мВ. */
#include <stddef.h>
#include "stm32f0xx.h"
#include "stm32f0xx_ll_adc.h"

_Static_assert(offsetof(ADC_TypeDef, CHSELR) == 0x28u, "очередь каналов — битовая маска CHSELR");
_Static_assert(offsetof(ADC_TypeDef, DR) == 0x40u, "данные");
_Static_assert(VREFINT_CAL_VREF == 3300u, "заводской код VREFINT снят при 3,3 В");
_Static_assert(TEMPSENSOR_CAL2_TEMP == 110, "вторая точка датчика температуры — 110 °C");
_Static_assert(LL_ADC_DELAY_VREFINT_STAB_US == 10u, "VREFINT устанавливается 10 мкс");
/* такт АЦП PCLK / 4 = 12 МГц; выборка 239,5 такта
 * + преобразование 12,5 — 252 такта, 21 мкс на одно измерение */
#define ADC_HZ 12000000u
_Static_assert((2395u + 125u) / 10u == 252u, "тактов на измерение");
_Static_assert(252u * 1000000u / ADC_HZ == 21u, "мкс на измерение при 12 МГц");

#define CH_VREFINT ADC_CHSELR_CHSEL17           /* у F0 VREFINT — канал 17 */

void adc_init_f0(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_ADCEN;
    ADC1->CFGR2 = ADC_CFGR2_CKMODE_1;           /* синхронный такт PCLK / 4 */
    ADC1->CR |= ADC_CR_ADCAL;                   /* калибровка — при выключенном АЦП */
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR |= ADC_CR_ADEN;
    while (!(ADC1->ISR & ADC_ISR_ADRDY)) {
    }
    ADC1_COMMON->CCR |= ADC_CCR_VREFEN;         /* внутренний источник — на вход АЦП */
    ADC1->SMPR = LL_ADC_SAMPLINGTIME_239CYCLES_5;
}

uint16_t adc_read_f0(uint32_t channel_mask)
{
    ADC1->CHSELR = channel_mask;                /* один бит на канал, не номер в SQR */
    ADC1->CR |= ADC_CR_ADSTART;
    while (!(ADC1->ISR & ADC_ISR_EOC)) {
    }
    return (uint16_t)ADC1->DR;
}

/* питание VDDA по коду VREFINT и заводскому коду, снятому при 3,3 В */
uint32_t vdda_mv_f0(void)
{
    uint16_t code = adc_read_f0(CH_VREFINT);
    return (VREFINT_CAL_VREF * (uint32_t)*VREFINT_CAL_ADDR + code / 2u) / code;
}

adc_g0.c — АЦП STM32G071: регулятор, калибровка, VREFINT, передискретизация до 16 бит.

/* Раздел 14.10: АЦП STM32G071 — регулятор, калибровка, VREFINT на канале 13,
 * передискретизация до 16 бит. */
#include <stddef.h>
#include "stm32g0xx.h"
#include "stm32g0xx_ll_adc.h"

_Static_assert(offsetof(ADC_TypeDef, CHSELR) == 0x28u, "очередь каналов — как у F0");
_Static_assert(offsetof(ADC_TypeDef, CALFACT) == 0xB4u, "калибровочный коэффициент — свой регистр");
_Static_assert(VREFINT_CAL_VREF == 3000u, "заводской код VREFINT снят при 3,0 В, не 3,3");
_Static_assert(TEMPSENSOR_CAL2_TEMP == 130, "вторая точка датчика температуры — 130 °C");
_Static_assert(LL_ADC_DELAY_INTERNAL_REGUL_STAB_US == 20u, "регулятору АЦП нужно 20 мкс");
/* такт АЦП PCLK / 4 = 16 МГц; выборка 160,5 такта + 12,5 — 173 такта, 10,8 мкс */
#define ADC_HZ 16000000u
_Static_assert((1605u + 125u) / 10u == 173u, "тактов на измерение");
_Static_assert(173u * 1000u / (ADC_HZ / 1000000u) == 10812u, "10,8 мкс на измерение при 16 МГц");

#define CH_VREFINT ADC_CHSELR_CHSEL13           /* у G0 VREFINT — канал 13 */

static void wait_us(uint32_t us)
{
    for (volatile uint32_t n = us * (64000000u / 1000000u / 4u); n; n--) {
    }
}

void adc_init_g0(void)
{
    RCC->APBENR2 |= RCC_APBENR2_ADCEN;
    ADC1->CR |= ADC_CR_ADVREGEN;                /* сначала регулятор — и 20 мкс ожидания */
    wait_us(LL_ADC_DELAY_INTERNAL_REGUL_STAB_US);
    ADC1->CFGR2 = ADC_CFGR2_CKMODE_1;           /* синхронный такт PCLK / 4 */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR |= ADC_CR_ADEN;
    while (!(ADC1->ISR & ADC_ISR_ADRDY)) {
    }
    ADC1_COMMON->CCR |= ADC_CCR_VREFEN;
    ADC1->SMPR = LL_ADC_SAMPLINGTIME_160CYCLES_5;   /* поле SMP1; SMPSEL выбирает его для всех каналов */
}

uint16_t adc_read_g0(uint32_t channel_mask)
{
    ADC1->ISR = ADC_ISR_CCRDY;                  /* флаг сбрасывается записью единицы */
    ADC1->CHSELR = channel_mask;
    while (!(ADC1->ISR & ADC_ISR_CCRDY)) {      /* у G0 смена каналов вступает не сразу */
    }
    ADC1->CR |= ADC_CR_ADSTART;
    while (!(ADC1->ISR & ADC_ISR_EOC)) {
    }
    return (uint16_t)ADC1->DR;
}

/* 256 измерений складываются в АЦП и делятся на 16: 16-битный результат без участия ядра */
void adc_oversample16_g0(void)
{
    ADC1->CFGR2 |= ADC_CFGR2_OVSE | LL_ADC_OVS_RATIO_256 | LL_ADC_OVS_SHIFT_RIGHT_4;
}

/* питание VDDA по коду VREFINT и заводскому коду, снятому при 3,0 В */
uint32_t vdda_mv_g0(void)
{
    uint16_t code = adc_read_g0(CH_VREFINT);
    return (VREFINT_CAL_VREF * (uint32_t)*VREFINT_CAL_ADDR + code / 2u) / code;
}

Дальше: раздел 14а «Ошибки АЦП: смещение, усиление, нелинейность и шум» — насколько этому коду можно верить и что из его ошибок калибруется.

Открыть в интерактивном справочнике