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

15а. АЦП нового образца на G4: дифференциальный вход, передискретизация до 16 бит, две калибровки

Раздел 14 разобрал АЦП F1 и F4 и отдельно — упрощённый АЦП F0 и G0 (14.10). У G4, L4, H7 и U5 АЦП другого поколения: регистры CR, ISR и CFGR вместо CR1 и CR2, регулятор, который надо включать, глубокий сон после сброса, дифференциальные входы, аппаратная передискретизация до 16 бит, смещение и коррекция усиления прямо в блоке. Здесь — на G474: что это даёт, как включить по регистрам и где этот АЦП ловит переносящего код.

Опоры: справочное руководство RM0440 (G4, глава ADC) и даташит STM32G474 (DS12288) из пака Keil, заголовок LL G4. Таблица результатов передискретизации из RM0440 воспроизведена расчётом полностью; код собран armclang для G474. QEMU этот АЦП не моделирует.

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

Оригинал Перевод
single-ended, differential input несимметричный и дифференциальный вход
oversampling ratio (OVSR), shift (OVSS) сколько измерений сложить и на сколько бит сдвинуть сумму
ADCALDIF калибровка для дифференциальных входов
CALFACT_S, CALFACT_D калибровочные коэффициенты: несимметричный и дифференциальный
deep power-down (DEEPPWD) глубокий сон АЦП после сброса
voltage regulator (ADVREGEN) внутренний регулятор АЦП
gain compensation (GCOMP) коррекция усиления: код умножается на коэффициент
offset (OFRy) вычитание постоянного смещения из результата

15а.1 Что нового по сравнению с F4

У G474 пять 12-битных АЦП; даташит обещает измерение за 0,25 мкс и до 16 бит с аппаратной передискретизацией. Отличия от F1 и F4:

Регистры другие: код с F4 (CR2, SQR3) не соберётся — и это хорошо, ошибку видно сразу.

АЦП G4 с передискретизацией: 256 измерений по 0,25 мкс складываются в 20-битную сумму, сдвиг на 4 бита оставляет 16 бит, результат раз в 64 мкс; без сдвига сумма больше 16 бит и старшие биты теряются


15а.2 Такт и время измерения

Время одного измерения (RM0440, 21.4.12):

TCONV = время выборки + 12,5 такта АЦП.

Самое короткое время выборки — 2,5 такта, самое длинное — 640,5. Такт АЦП по даташиту — не выше 60 МГц для одного АЦП (52 МГц, если работают все при VDDA не ниже 2,7 В):

Такт АЦП Выборка Измерение Измерений в секунду
60 МГц 2,5 0,250 мкс 4,000 млн
52 МГц 2,5 0,288 мкс 3,467 млн
42,5 МГц 2,5 0,353 мкс 2,833 млн
60 МГц 640,5 10,883 мкс 0,092 млн

Такт выбирает CKMODE в общем регистре ADC12_COMMON->CCR (RM0440, 21.4.3): 00 — отдельный асинхронный такт от RCC (системный или PLL «P»), 01, 10, 11 — HCLK, делённый на 1, 2 или 4. У G474 HCLK — до 170 МГц, поэтому HCLK / 1 и HCLK / 2 для АЦП слишком быстрые; синхронно остаётся HCLK / 4 = 42,5 МГц. Для 60 МГц и 0,25 мкс нужен асинхронный такт от PLL. Синхронный такт хорош другим: задержка от события таймера до начала измерения без дрожания.


15а.3 Включение и две калибровки

Калибровка на G4 обязательна и делается при выключенном АЦП (ADEN = 0). Коэффициент для несимметричных входов и для дифференциальных — разный (RM0440, 21.4.8): бит ADCALDIF выбирает, какой считать. Если АЦП будет мерить и те и другие, калибруют дважды — и дальше АЦП сам подставляет нужный коэффициент при переходе между каналами:

void adc_power_and_calibrate(void)
{
    RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN;
    ADC12_COMMON->CCR = 3u << ADC_CCR_CKMODE_Pos;   /* HCLK / 4: 170 / 4 = 42,5 МГц, не больше 60 */
    ADC1->CR &= ~ADC_CR_DEEPPWD;                    /* после сброса АЦП в глубоком сне */
    ADC1->CR |= ADC_CR_ADVREGEN;
    wait_us(20);                                    /* LL_ADC_DELAY_INTERNAL_REGUL_STAB_US */

    ADC1->CR &= ~ADC_CR_ADCALDIF;                   /* 1: несимметричные входы → CALFACT_S */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR |= ADC_CR_ADCALDIF;                    /* 2: дифференциальные → CALFACT_D */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR &= ~ADC_CR_ADCALDIF;

    ADC1->ISR = ADC_ISR_ADRDY;
    ADC1->CR |= ADC_CR_ADEN;
    while (!(ADC1->ISR & ADC_ISR_ADRDY)) {
    }
}

Коэффициенты — 7 бит каждый — лежат в ADC_CALFACT. Питание АЦП пропадает в Standby и VBAT, и калибровка вместе с ним; чтобы не калибровать заново, их можно сохранить и записать обратно (RM0440, 21.4.8).


15а.4 Дифференциальный вход

Бит канала в DIFSEL переводит его в дифференциальный режим; писать DIFSEL можно только при выключенном АЦП. Код по RM0440: разность −VREF+ — 0x000, ноль — 0x800, +VREF+ — 0xFFF. То есть

код = 2048 + 2048 × (VINP − VINN) / VREF+.

VINP − VINN при VREF+ = 3,3 В Код
−3,3 В 0x000
−1,0 В 0x593
0 0x800
+1,0 В 0xA6D
+3,3 В 0xFFF

Единица кода — 1,611 мВ против 0,806 мВ у несимметричного входа: RM прямо пишет, что чувствительность вдвое ниже. Зато синфазная помеха вычитается, а знак разности виден сразу. Оба входа надо держать около VREF+/2 — синфазное напряжение не должно гулять.

Отрицательный вход дифференциального канала — вывод другого канала: тот канал больше нельзя мерить ни как несимметричный, ни как дифференциальный. Внутренние каналы — VREFINT, датчик температуры — только несимметричные.


15а.5 Передискретизация до 16 бит

АЦП сам складывает N измерений (от 2 до 256, поле OVSR), сдвигает сумму вправо на M бит (0–8, поле OVSS) с округлением и кладёт в DR. Регистр данных — 16 бит, и если сумма после сдвига длиннее, старшие биты теряются. Наибольший результат для кода 0xFFF (RM0440, таблица 172; расчёт совпал с ней во всех 80 ячейках):

N Сумма Сдвиг 0 2 4 8
16 0xFFF0 0xFFF0 0x3FFC 0x0FFF 0x0100
64 0x3FFC0 0xFFC0 (усечено) 0xFFF0 0x3FFC 0x0400
256 0xFFF00 0xFF00 (усечено) 0xFFC0 (усечено) 0xFFF0 0x0FFF

Три сочетания дают полные 16 бит без усечения: 16 измерений без сдвига, 64 со сдвигом на 2 и 256 со сдвигом на 4 — везде 0xFFF0. 256 измерений по 0,25 мкс — результат раз в 64 мкс, 15,6 кГц; 16 измерений — раз в 4 мкс, 250 кГц.

void adc_oversample_16bit(void)
{
    ADC1->CFGR2 = ADC_CFGR2_ROVSE | (OVSR_256 << ADC_CFGR2_OVSR_Pos) | (OVSS_4 << ADC_CFGR2_OVSS_Pos);
}

armclang превращает её в одну запись 0x9D по адресу 0x5000 0010 — это ADC1->CFGR2.

Сколько бит на самом деле даёт сумма, зависит от шума: складывать одинаковые коды бесполезно. Модель на ПК — АЦП с шумом 1 единица кода, случайные напряжения:

N Сдвиг Остаточный шум, единиц 12 бит Бит сверх 12
1 0 1,039 0,0
4 1 0,552 0,9
16 2 0,271 1,9
64 3 0,136 2,9
256 4 0,069 3,9

Каждые четыре измерения — около одного бита: шум падает как √N (раздел 165). 256 измерений дают почти 4 бита — это и есть 16 бит из 12-битного АЦП. Если шума нет, дополнительные биты ложные: их добавляют подмешиванием (раздел 165).


15а.6 Смещение и коррекция усиления

Смещение: регистр OFRy (их четыре) привязывается к каналу и вычитает из результата заданное число; результат становится знаковым. Коррекция усиления: код умножается на GCOMPCOEFF / 4096, от 0 до 3,999756 (RM0440); 0x1000 — ровно 1. Коэффициент 0x1040 (1,01562) превращает код 3000 примерно в 3047, 0x0FC0 (0,98438) — в 2953. Так поправляют заводской разброс делителя на входе, не тратя на это процессор. Включает коррекцию бит GCOMP в том же регистре, что и передискретизацию; запись в него целиком, как в листинге ниже, этот бит сбрасывает.

С передискретизацией смещение не работает: при включённой передискретизации биты OFFSETy_EN игнорируются (RM0440).


15а.7 Грабли


15а.8 Итоги

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

adc15a.c — АЦП STM32G474 по регистрам: калибровки, дифференциальный канал, передискретизация.

/* 15а: АЦП STM32G474 по регистрам — две калибровки, дифференциальный канал, передискретизация 256x.
   Собирается armclang: -DSTM32G474xx. */
#include <stdint.h>

extern void wait_us(uint32_t us);

#define OVSR_256  7u                    /* CFGR2.OVSR: 000 — 2x … 111 — 256x */
#define OVSS_4    4u                    /* сдвиг на 4 бита: 12 + 8 − 4 = 16 бит */

_Static_assert(ADC_CFGR2_OVSR_Pos == 2 && ADC_CFGR2_OVSS_Pos == 5, "поля передискретизации в CFGR2");
_Static_assert(((OVSR_256 << ADC_CFGR2_OVSR_Pos) | (OVSS_4 << ADC_CFGR2_OVSS_Pos)) == 0x9Cu, "256x, сдвиг 4");

void adc_power_and_calibrate(void)
{
    RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN;
    ADC12_COMMON->CCR = 3u << ADC_CCR_CKMODE_Pos;   /* HCLK / 4: 170 / 4 = 42,5 МГц, не больше 60 */
    ADC1->CR &= ~ADC_CR_DEEPPWD;                    /* после сброса АЦП в глубоком сне */
    ADC1->CR |= ADC_CR_ADVREGEN;
    wait_us(20);                                    /* LL_ADC_DELAY_INTERNAL_REGUL_STAB_US */

    ADC1->CR &= ~ADC_CR_ADCALDIF;                   /* 1: несимметричные входы → CALFACT_S */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR |= ADC_CR_ADCALDIF;                    /* 2: дифференциальные → CALFACT_D */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR &= ~ADC_CR_ADCALDIF;

    ADC1->ISR = ADC_ISR_ADRDY;
    ADC1->CR |= ADC_CR_ADEN;
    while (!(ADC1->ISR & ADC_ISR_ADRDY)) {
    }
}

/* канал ch — дифференциальный: его отрицательный вход занимает другой канал (RM0440) */
void adc_differential(uint32_t ch)
{
    ADC1->DIFSEL |= 1u << ch;                       /* DIFSEL пишут при ADEN = 0 */
}

/* 16 бит из 256 измерений: 64 мкс на результат при 0,25 мкс на измерение */
void adc_oversample_16bit(void)
{
    ADC1->CFGR2 = ADC_CFGR2_ROVSE | (OVSR_256 << ADC_CFGR2_OVSR_Pos) | (OVSS_4 << ADC_CFGR2_OVSS_Pos);
}

uint16_t adc_read(uint32_t ch)
{
    ADC1->SQR1 = ch << ADC_SQR1_SQ1_Pos;            /* L = 0: одно измерение в очереди */
    ADC1->CR |= ADC_CR_ADSTART;
    while (!(ADC1->ISR & ADC_ISR_EOC)) {
    }
    return (uint16_t)ADC1->DR;
}

Дальше: раздел 15б «Внешний АЦП ADS1115 по I2C: 16 бит, усилитель PGA, мультиплексор, режимы преобразования и вывод ALERT/RDY» — когда встроенных 12 бит мало: внешний дельта-сигма АЦП на шине I2C.

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