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

14а. Ошибки АЦП: смещение, усиление, нелинейность и шум — что калибруется, а что нет

Раздел 14 научил получать код и переводить его в милливольты, раздел 15а — включать АЦП G4 с двумя калибровками, раздел 180 — не портить сигнал до входа. Здесь — насколько этому коду можно верить: из чего складывается ошибка, что обещает даташит, что убирает встроенная калибровка, что приходится убирать самому и что не убирается ничем.

Опоры: даташиты STM32F103x8/B (DS5319, он же DocID13587, ред. 17), STM32F405/407 (DS8626, ред. 9) и STM32G474 (DS12288, ред. 5); справочные руководства RM0008, RM0090 и RM0440; заголовки CMSIS трёх серий и LL для F4 и G4. Числа опытов — модель АЦП на компьютере: программа на C и независимый пересчёт на Python дали одни и те же строки. Калибровочные функции собраны для Cortex-M3 и Cortex-M4. На железе не проверено.

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

Оригинал Перевод
offset error (EO) ошибка смещения
gain error (EG) ошибка усиления
differential linearity error (ED, DNL) дифференциальная нелинейность
integral linearity error (EL, INL) интегральная нелинейность
total unadjusted error (ET, TUE) полная ошибка без поправок
end point correlation line прямая через крайние точки
effective number of bits (ENOB) эффективное число разрядов
signal-to-noise and distortion ratio (SINAD) отношение сигнала к шуму и искажениям
code density test проверка плотностью кодов
calibration factor (CALFACT) калибровочный коэффициент
input leakage current (Ilkg) ток утечки входа
dither подмешивание шума

14а.1 Из чего складывается ошибка

Идеальный 12-битный АЦП меняет код каждый раз, когда вход проходит очередную долю опоры: 1 LSB = VREF+ / 4096 — при 3,3 В это 805,7 мкВ. Настоящий меняет код в других местах. Все ошибки даташит меряет в LSB и определяет через пороги переходов (DS8626, рисунок 49; DS12288, рисунок 28; в рисунке 37 DS5319 слова те же, но в строке про EO опечатка — «между первым и последним настоящим переходом»):

ET — не сумма остальных: ошибки достигают наибольших значений в разных местах шкалы и бывают разного знака. У F103 в полном диапазоне питания наибольшие EO + EG + EL дают 8,5 LSB, корень из суммы квадратов — 4,9, а в таблице ET стоит 5.

К этим пяти добавляются ещё четыре источника, которых в строке «ET» нет:

Что из этого чем убирается — на модели из 14а.4:

Четыре состояния модели АЦП: без поправок при опоре 3,25 В вместо 3,3 В наибольшая ошибка 56,5 LSB, после пересчёта опоры по VREFINT_CAL 6,7 LSB, после калибровки по двум точкам 2,0 LSB, после ухода VREFINT на 3 мВ от нагрева снова 10,4 LSB; шум этими поправками не убирается


14а.2 Что обещают даташиты

Типовое значение и наибольшее, в LSB:

F103, табл. 48 F103, табл. 49 F407, табл. 68 G474, табл. 68
условия 25 °C, 3–3,6 В 2,4–3,6 В 1,8–3,6 В 25 °C, 3 В
ET ±1,3 / ±2 ±2 / ±5 ±2 / ±5 5,9 / 6,9
EO ±1 / ±1,5 ±1,5 / ±2,5 ±1,5 / ±2,5 2,5 / 4
EG ±0,5 / ±1,5 ±1,5 / ±3 ±1,5 / ±3 4,6 / 6,6
ED ±0,7 / ±1 ±1 / ±2 ±1 / ±2 1,1 / 1,9
EL ±0,8 / ±1,5 ±1,5 / ±3 ±1,5 / ±3 2,3 / 3,4

Как читать эти числа:

Для G474 даташит даёт и динамику — на синусе, для того же быстрого канала: SINAD не меньше 64,4 дБ, типовое 65,6 дБ; ENOB не меньше 10,4, типовое 10,6 разряда. Формула из 14а.1 даёт из этих SINAD 10,41 и 10,60 — то есть ENOB в таблице просто пересчитан из SINAD. Почти полтора разряда из двенадцати съедают шум и искажения.

Отдельно — внутренняя опора VREFINT, по которой считают питание:

F103 F407 G474
VREFINT: от / типовое / до, В 1,16 / 1,20 / 1,26 1,18 / 1,21 / 1,24 1,182 / 1,212 / 1,232
разброс от типового −3,3 % / +5,0 % −2,5 % / +2,5 % −2,5 % / +1,7 %
уход по температуре, типовой / наибольший — / 10 мВ 3 / 5 мВ 5 / 7,5 мВ
тот же уход на полной шкале 34 LSB 10 / 17 LSB 17 / 25 LSB
температурный коэффициент, ppm/°C до 100 30 / 50 30 / 50
заводской код нет 30 °C, 3,3 В 30 °C, 3,0 В

Разброс между кристаллами снимает заводской код (14а.5). Уход по температуре не снимает ничто: 5 мВ из 1,21 В — это 0,41 %, на полной шкале 17 LSB. Это больше, чем все ошибки самого АЦП. У G474 даташит называет ещё и старение: типично 300 ppm за 1000 часов, до 1000 ppm.


14а.3 Что исправляет встроенная калибровка

F1: биты CAL и RSTCAL в ADC_CR2. RM0008 (11.4): калибровка уменьшает ошибки от разброса внутренних конденсаторов — для каждого конденсатора вычисляется поправочный код, и при следующих измерениях его вклад вычитается. Перед калибровкой АЦП должен быть включён не меньше двух тактов; длится она 83 такта — 5,9 мкс при 14 МГц. Коды калибровки по её окончании попадают в ADC_DR. И RM0008, и даташит советуют калибровать после каждого включения питания.

G4: бит ADCAL в ADC_CR. RM0440 (21.4.8): калибровка убирает ошибку смещения, которая меняется от кристалла к кристаллу. Коэффициент — 7 бит, для несимметричных и дифференциальных входов свой (ADCALDIF), оба лежат в ADC_CALFACT. Длится 116 тактов — 1,93 мкс при 60 МГц. Коэффициент теряется, когда с АЦП снимают питание (Standby, VBAT) и в глубоком сне АЦП; его можно сохранить и записать обратно, когда АЦП включён и не измеряет. RM советует калибровать заново, если VREF+ изменилась больше чем на 10 %.

F4: калибровки нет. В главе об АЦП RM0090 слова calibration нет, в заголовке stm32f407xx.h нет бита ADC_CR2_CAL, в таблице точности DS8626 нет сноски «после калибровки». Запускать нечего. Есть другое — заводские коды VREFINT и датчика температуры в системной памяти.

/* F103. АЦП уже включён первой записью ADON и выждал tSTAB (раздел 14.3). */
uint16_t adc_f1_calibrate(void)
{
    ADC1->CR2 |= ADC_CR2_RSTCAL;                /* сбросить калибровочные регистры */
    while (ADC1->CR2 & ADC_CR2_RSTCAL) {
    }
    ADC1->CR2 |= ADC_CR2_CAL;                   /* поправки на разброс конденсаторов */
    while (ADC1->CR2 & ADC_CR2_CAL) {
    }
    return (uint16_t)ADC1->DR;                  /* RM0008: коды калибровки попадают в ADC_DR */
}
/* G474. АЦП выключен (ADEN = 0), регулятор включён и выждал 20 мкс (раздел 15а.3).
   Возвращает ADC_CALFACT: его сохраняют перед Standby. */
uint32_t adc_g4_calibrate(void)
{
    ADC1->CR &= ~ADC_CR_ADCALDIF;               /* несимметричные входы: CALFACT_S */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR |= ADC_CR_ADCALDIF;                /* дифференциальные входы: CALFACT_D */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR &= ~ADC_CR_ADCALDIF;
    return ADC1->CALFACT;
}

/* Вернуть сохранённые коэффициенты: АЦП включён, измерений нет (RM0440, 21.4.8). */
int adc_g4_calfact_restore(uint32_t calfact)
{
    if (!(ADC1->CR & ADC_CR_ADEN) || (ADC1->CR & (ADC_CR_ADSTART | ADC_CR_JADSTART))) {
        return -1;
    }
    ADC1->CALFACT = calfact & (ADC_CALFACT_CALFACT_S | ADC_CALFACT_CALFACT_D);
    return 0;
}
/* F407. Калибровки в АЦП нет — есть заводские коды в системной памяти (DS8626, таблицы 70 и 73). */
#define F4_VREFINT_CAL  (*(const uint16_t *)0x1FFF7A2Au)    /* 30 °C, VDDA = 3,3 В */
#define F4_TS_CAL1      (*(const uint16_t *)0x1FFF7A2Cu)    /* 30 °C */
#define F4_TS_CAL2      (*(const uint16_t *)0x1FFF7A2Eu)    /* 110 °C */

int adc_f4_factory(uint16_t *vrefint_cal, uint16_t *ts_cal1, uint16_t *ts_cal2)
{
    *vrefint_cal = F4_VREFINT_CAL;
    *ts_cal1 = F4_TS_CAL1;
    *ts_cal2 = F4_TS_CAL2;
    /* незаписанная ячейка читается как 0xFFFF: заводских кодов нет */
    return (*vrefint_cal == 0xFFFFu || *ts_cal1 == 0xFFFFu || *ts_cal2 <= *ts_cal1) ? -1 : 0;
}

Чего встроенная калибровка не делает. Она ничего не знает о напряжении снаружи: внешнего эталона у неё нет. Поэтому ошибку опоры она не трогает вовсе. Что остаётся после неё у самого АЦП, видно в таблицах 14а.2: у F103 и G474 смещение, усиление и нелинейность стоят там уже после калибровки. Шум, утечку и недозаряд конденсатора выборки она тоже не видит.


14а.4 Модель АЦП и как меряют ошибки

Чтобы увидеть, что убирает каждая поправка, нужен АЦП с известными ошибками. Модель — массив из 4095 порогов. В ней смещение 1,8 LSB, ошибка наклона +0,07 %, плавный прогиб 1,5 LSB в середине шкалы и неравные веса пяти старших разрядов (от −0,06 % до +0,12 %) — как у настоящего АЦП последовательного приближения, где веса задают конденсаторы. Шум — 0,7 LSB.

static void adc_build(const adc_par_t *p, double *T)
{
    double w[12];
    for (int b = 0; b < 12; b++) {
        w[b] = (double)(1 << b) * (1.0 + p->e[b]);
    }
    T[0] = -1e30;
    for (int k = 1; k < NC; k++) {
        double W = 0.0, u = (double)k / 4096.0;
        for (int b = 0; b < 12; b++) {
            W += (double)((k >> b) & 1) * w[b];
        }
        T[k] = p->offs + (1.0 + p->gain) * (W + 4.0 * p->bow * u * (1.0 - u));
    }
}

static int adc_code(const double *T, double x)      /* сколько порогов не выше x */
{
    int lo = 0, hi = NC - 1;
    while (lo < hi) {
        int mid = (lo + hi + 1) / 2;
        if (T[mid] <= x) {
            lo = mid;
        } else {
            hi = mid - 1;
        }
    }
    return lo;
}

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

static void histogram(double sigma)
{
    static uint32_t H[NC];
    uint32_t total = (uint32_t)HIST_LSB * HIST_PER_LSB, cum = 0;
    double dmax = 0.0;

    memset(H, 0, sizeof H);
    for (uint32_t i = 0; i < total; i++) {
        double x = -8.0 + ((double)i + 0.5) / (double)HIST_PER_LSB;
        H[adc_code(TR, x + sigma * gauss(101u, i))]++;
    }
    TE[0] = -1e30;
    for (int k = 1; k < NC; k++) {
        cum += H[k - 1];
        TE[k] = -8.0 + (double)cum / (double)HIST_PER_LSB;
        if (fabs(TE[k] - TR[k]) > dmax) {
            dmax = fabs(TE[k] - TR[k]);
        }
    }

Пила в 1024 шага на LSB — 4 210 688 измерений с шумом. Пороги найдены с ошибкой не больше 0,072 LSB:

Ошибка, LSB По порогам модели По гистограмме
EO 1,802 1,815
EG 4,975 5,003
наклон в чистом виде, EG − EO 3,173 3,188
ED −0,513 −0,511
EL, прямая через крайние точки 1,918 1,932
EL, наилучшая прямая −1,068 −1,099
ET 5,830 5,878

EL зависит от того, какую прямую считать идеальной. Даташиты STM32 берут прямую через крайние переходы; наилучшая прямая (по наименьшим квадратам) даёт число почти вдвое меньше — сравнивая АЦП разных фирм, надо смотреть, какой способ назван. Наибольшая ED — на переходе с кода 3071 на 3072: там десять младших разрядов сбрасываются и включается следующий.

Шум на постоянном входе. Вход стоит на месте, 65 536 измерений идеального АЦП с шумом 0,7 LSB: выпало 6 разных кодов, среднеквадратичное отклонение — 0,755 LSB (к шуму добавилось округление). В разрядах это 12 − log2(0,755 · √12) = 10,61. Синус на том же АЦП дал SINAD 65,48 дБ и ENOB 10,58 — два способа сошлись. И это тот же порядок, что типовые 10,6 разряда в даташите G474: им отвечает среднеквадратичное отклонение 0,762 LSB.


14а.5 Что калибрует программа

Всё в целых числах. Код — в формате Q4: сумма 16 измерений или один код, умноженный на 16; полная шкала — 65 536.

Опора по VREFINT_CAL. RM0440 (21.4.33): VREF+ = VREF+ при заводском измерении × VREFINT_CAL / код VREFINT. Заводское измерение у F407 — 30 °C и VDDA = 3,3 В, код по адресу 0x1FFF 7A2A (DS8626, таблица 73); у G474 — 30 °C и VDDA = VREF+ = 3,0 В, адрес 0x1FFF 75AA (DS12288, таблица 6). У F103 заводского кода нет. Тем же отношением любой код приводят к заводской опоре — так его можно сравнивать с заводскими точками датчика температуры и со своей калибровкой:

/* Микровольты по номиналу: середина кода, опора vref_uv. */
uint32_t adc_code_to_uv(uint32_t code_q4, uint32_t vref_uv)
{
    return (uint32_t)(((uint64_t)(code_q4 + 8u) * vref_uv) >> 16);
}

/* Опора VREF+ в микровольтах по коду VREFINT и заводскому коду, снятому при cal_vref_mv. */
uint32_t adc_vref_uv(uint32_t vrefint_q4, uint16_t vrefint_cal, uint32_t cal_vref_mv)
{
    uint64_t num = (uint64_t)cal_vref_mv * 1000u * ((uint32_t)vrefint_cal * 16u);
    return (uint32_t)((num + vrefint_q4 / 2u) / vrefint_q4);
}

/* Код, каким он был бы при заводской опоре: умножить на VREFINT_CAL / код VREFINT. */
uint32_t adc_norm_q4(uint32_t code_q4, uint32_t vrefint_q4, uint16_t vrefint_cal)
{
    return (code_q4 * ((uint32_t)vrefint_cal * 16u) + vrefint_q4 / 2u) / vrefint_q4;
}

Заводская точка сама неточна: опора при измерении выдержана с допуском ±10 мВ, температура — ±5 °C (у G474 это написано в даташите, у F4 — в комментариях заголовка LL). 10 мВ из 3,0 В — это 0,33 %, 13,7 LSB на полной шкале; из 3,3 В — 0,30 %, 12,4 LSB.

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

typedef struct {
    int32_t uv_lo;      /* напряжение нижней точки, мкВ */
    int32_t c_lo_q4;    /* код в нижней точке, Q4 */
    int32_t k_q16;      /* наклон: микровольт на единицу кода Q4, умноженный на 65536 */
} adc_cal2_t;
/* Одна точка: убрать только смещение, наклон — номинальный (vref_uv на 65536 единиц Q4). */
void adc_cal1_make(adc_cal2_t *cal, uint32_t c_q4, uint32_t uv, uint32_t vref_uv)
{
    cal->uv_lo = (int32_t)uv;
    cal->c_lo_q4 = (int32_t)c_q4;
    cal->k_q16 = (int32_t)vref_uv;
}

/* Две точки: коды c_lo_q4 и c_hi_q4 сняты при известных напряжениях uv_lo и uv_hi. */
int adc_cal2_make(adc_cal2_t *cal, uint32_t c_lo_q4, uint32_t uv_lo, uint32_t c_hi_q4, uint32_t uv_hi)
{
    if (c_hi_q4 <= c_lo_q4 || uv_hi <= uv_lo) {
        return -1;
    }
    cal->uv_lo = (int32_t)uv_lo;
    cal->c_lo_q4 = (int32_t)c_lo_q4;
    cal->k_q16 = (int32_t)((((uint64_t)(uv_hi - uv_lo)) << 16) / (c_hi_q4 - c_lo_q4));
    return 0;
}

/* Микровольты по калибровке. Сдвиг знакового числа у gcc и armclang арифметический. */
int32_t adc_cal2_apply(const adc_cal2_t *cal, uint32_t code_q4)
{
    int64_t p = (int64_t)((int32_t)code_q4 - cal->c_lo_q4) * cal->k_q16 + 32768;
    return cal->uv_lo + (int32_t)(p >> 16);
}

Что получилось. Модель калибруют при опоре 3,300 В по точкам 0,3 и 3,0 В (по 16 измерений с шумом), потом проходят вход от 0,05 до 3,2 В без шума и смотрят ошибку результата. Знаки опущены:

Что сделано Опора при измерении Наибольшая ошибка, LSB мВ Среднеквадратичная, LSB
ничего, опора считается 3,3 В 3,25 В 56,48 45,5 31,28
ничего, опора считается 3,3 В 3,30 В 6,33 5,1 4,54
одна точка: смещение 3,30 В 3,96 3,2 2,32
две точки 3,30 В 1,91 1,5 0,82
две точки и таблица нелинейности 3,30 В 1,07 0,9 0,37
две точки, потом питание село 3,25 В 62,08 50,0 35,10
опора по VREFINT_CAL 3,25 В 6,70 5,4 4,87
опора по VREFINT_CAL и две точки 3,25 В 1,98 1,6 0,85
то же, VREFINT ушёл на +3 мВ 3,25 В 10,38 8,4 6,56
то же, VREFINT ушёл на +5 мВ 3,25 В 16,52 13,3 10,13

Читается таблица так:

Датчик температуры. Заводские точки — 30 и 110 °C: у F407 при 3,3 В (0x1FFF 7A2C и 0x1FFF 7A2E), у G474 при 3,0 В (0x1FFF 75A8 и 0x1FFF 75CA). Код датчика сначала приводят к заводской опоре, иначе ошибка питания превращается в ошибку температуры:

/* Температура в десятых долях градуса по двум заводским точкам. ts_q4 — код датчика,
   уже приведённый к заводской опоре (adc_norm_q4). */
int32_t adc_ts_x10(uint32_t ts_q4, uint16_t cal1, uint16_t cal2, int32_t t1_x10, int32_t t2_x10)
{
    int32_t den = ((int32_t)cal2 - (int32_t)cal1) * 16;
    int32_t num = ((int32_t)ts_q4 - (int32_t)cal1 * 16) * (t2_x10 - t1_x10);

    if (den <= 0) {
        return t1_x10;
    }
    return t1_x10 + (num + (num >= 0 ? den / 2 : -den / 2)) / den;
}

В модели кристалл при 85 °C, опора 3,25 В: с приведением вышло 85,0 °C, без него — 90,6 °C.

Таблица нелинейности. Прогиб можно вычесть таблицей: 17 узлов через 256 кодов, между узлами — по прямой.

/* Поправка нелинейности: 17 узлов через 256 кодов, значения в единицах Q4, между узлами — по прямой. */
uint32_t adc_inl_apply(const int8_t lut[17], uint32_t code_q4)
{
    uint32_t seg = code_q4 >> 12;
    int32_t frac = (int32_t)(code_q4 & 0xFFFu);
    int32_t corr;

    if (seg > 15u) {
        return code_q4;
    }
    corr = (lut[seg] * (4096 - frac) + lut[seg + 1u] * frac + 2048) >> 12;
    return (uint32_t)((int32_t)code_q4 + corr);
}

В модели она снизила наибольшую ошибку с 1,91 до 1,07 LSB. Но таблицу пришлось снять с этого же экземпляра гистограммой в четыре миллиона измерений, от источника, который линейнее самого АЦП. Даташит нелинейность кристалла не сообщает — только границы для партии. Выигрыш меньше 1 LSB при шуме 0,7 LSB. Если нужна такая линейность, дешевле взять внешний АЦП.

Размер. Все десять функций файла — 294 байта и для Cortex-M3, и для Cortex-M4 (arm-none-eabi-gcc -Os); adc_cal2_apply — 30 байт, adc_norm_q4 — 14. Двум функциям нужно деление 64-битных чисел: компилятор зовёт библиотечные __aeabi_uldivmod и __aeabi_ldivmod. Обе работают редко — при калибровке и при пересчёте опоры, а не в каждом измерении.


14а.6 Сложение измерений: что оно даёт и чего не даёт

Раздел 15а.5 показал: при шуме в 1 LSB каждые четыре сложенных измерения дают около разряда. Здесь — границы этого приёма.

/* Сумма n измерений. 16 измерений 12-битного АЦП — код Q4. */
uint32_t adc_sum(uint16_t (*read)(void), unsigned n)
{
    uint32_t s = 0;
    while (n--) {
        s += read();
    }
    return s;
}

/* Передискретизация: 4^bits измерений дают результат на bits разрядов длиннее (bits = 0…4, до 16 бит). */
uint32_t adc_oversample(uint16_t (*read)(void), unsigned bits)
{
    uint32_t s = adc_sum(read, 1u << (2u * bits));
    return bits ? (s + (1u << (bits - 1u))) >> bits : s;
}

ENOB по синусу (4096 точек, в каждой сложено N измерений):

Сложено N Идеальный АЦП, шум 0,7 LSB Идеальный, шум 0,05 LSB То же и пила в 1 LSB Модель с ошибками, шум 0,7 LSB
1 10,58 11,93 11,93 10,28
4 11,58 12,15 13,70 10,75
16 12,60 12,20 14,77 10,93
64 13,61 12,23 15,77 10,99
256 14,63 12,23 16,78 11,00

То же на постоянном входе — среднеквадратичная ошибка результата в LSB, 257 напряжений по всей шкале:

Сложено N Идеальный АЦП Модель без поправок Модель, две точки
1 0,768 4,622 1,078
16 0,188 4,547 0,772
256 0,050 4,551 0,765

У идеального АЦП ошибка падает вчетверо на каждые 16 измерений. У настоящего без калибровки она стоит на месте — 4,5 LSB смещения и наклона сложением не убрать. После калибровки по двум точкам остаётся 0,77 LSB нелинейности, и дальше 16 измерений складывать незачем. Шестнадцать бит в регистре — не шестнадцать бит точности.


14а.7 Грабли

«АЦП 12 бит, значит точность 0,8 мВ». 0,8 мВ — вес кода. Полная ошибка по даташиту — до 5 LSB у F103 и F407, до 6,9 у G474, и это без опоры.

Калибровку запустили, а показания по-прежнему смещены на проценты. Встроенная калибровка не знает опоры. Ошибка в проценты — это VREF+: её считают по VREFINT (14.9) или ставят отдельный источник опоры.

Откалибровали по двум точкам на стенде, в поле показания уплыли. Калибровка запомнила опору стенда: в модели 1,91 LSB превратились в 62,08, когда питание село с 3,3 до 3,25 В. Перед калибровкой и перед каждым измерением код приводят к заводской опоре (adc_norm_q4).

В формуле для G4 стоит 3300. Заводской код G474 снят при 3,0 В, у F407 — при 3,3 В. Перепутали — ошибка 10 % (14.10).

Температура кристалла врёт на несколько градусов при севшей батарее. Код датчика не привели к заводской опоре: в модели 90,6 °C вместо 85,0 при 3,25 В.

Сложили 256 измерений и считают результат 16-битным. Сложение убирает только случайный шум. Смещение, наклон и нелинейность остаются: в модели 4,5 LSB и до, и после.

Сложение ничего не дало. Шума меньше долей LSB — все измерения одинаковы. Нужна подмешанная пила или шум.

Таблица точности не действует. У F103 и G474 она дана после встроенной калибровки. У G4 калибровка теряется в Standby — её повторяют или возвращают сохранённый ADC_CALFACT.

Высокоомный источник смещает показания. Утечка входа до 1 мкА на 10 кОм — 12,4 LSB. Буфер или источник пониже (180.2).

Прогрев уводит показания, хотя всё откалибровано. VREFINT уходит с температурой на 3–7,5 мВ — 10–25 LSB на полной шкале. Нужна точность выше — внешний источник опорного напряжения (раздел 177).


14а.8 Итоги

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

adc_cal.h — пересчёт и калибровка результатов АЦП в целых числах — объявления.

/* Пересчёт и калибровка результатов АЦП в целых числах — раздел 14а.
   Код в формате Q4: сумма 16 измерений (или один код, умноженный на 16); полная шкала 12 бит — 65536. */
#ifndef ADC_CAL_H
#define ADC_CAL_H

#include <stdint.h>

typedef struct {
    int32_t uv_lo;      /* напряжение нижней точки, мкВ */
    int32_t c_lo_q4;    /* код в нижней точке, Q4 */
    int32_t k_q16;      /* наклон: микровольт на единицу кода Q4, умноженный на 65536 */
} adc_cal2_t;

uint32_t adc_sum(uint16_t (*read)(void), unsigned n);
uint32_t adc_oversample(uint16_t (*read)(void), unsigned bits);
uint32_t adc_code_to_uv(uint32_t code_q4, uint32_t vref_uv);
uint32_t adc_vref_uv(uint32_t vrefint_q4, uint16_t vrefint_cal, uint32_t cal_vref_mv);
uint32_t adc_norm_q4(uint32_t code_q4, uint32_t vrefint_q4, uint16_t vrefint_cal);
void adc_cal1_make(adc_cal2_t *cal, uint32_t c_q4, uint32_t uv, uint32_t vref_uv);
int adc_cal2_make(adc_cal2_t *cal, uint32_t c_lo_q4, uint32_t uv_lo, uint32_t c_hi_q4, uint32_t uv_hi);
int32_t adc_cal2_apply(const adc_cal2_t *cal, uint32_t code_q4);
uint32_t adc_inl_apply(const int8_t lut[17], uint32_t code_q4);
int32_t adc_ts_x10(uint32_t ts_q4, uint16_t cal1, uint16_t cal2, int32_t t1_x10, int32_t t2_x10);

#endif

adc_cal.c — пересчёт и калибровка результатов АЦП в целых числах.

/* Пересчёт и калибровка результатов АЦП в целых числах — раздел 14а.
   Файл без регистров: собирается и для Cortex-M, и на компьютере (модель adc_model.c вызывает эти же функции). */
#include "adc_cal.h"

/* Сумма n измерений. 16 измерений 12-битного АЦП — код Q4. */
uint32_t adc_sum(uint16_t (*read)(void), unsigned n)
{
    uint32_t s = 0;
    while (n--) {
        s += read();
    }
    return s;
}

/* Передискретизация: 4^bits измерений дают результат на bits разрядов длиннее (bits = 0…4, до 16 бит). */
uint32_t adc_oversample(uint16_t (*read)(void), unsigned bits)
{
    uint32_t s = adc_sum(read, 1u << (2u * bits));
    return bits ? (s + (1u << (bits - 1u))) >> bits : s;
}

/* Микровольты по номиналу: середина кода, опора vref_uv. */
uint32_t adc_code_to_uv(uint32_t code_q4, uint32_t vref_uv)
{
    return (uint32_t)(((uint64_t)(code_q4 + 8u) * vref_uv) >> 16);
}

/* Опора VREF+ в микровольтах по коду VREFINT и заводскому коду, снятому при cal_vref_mv. */
uint32_t adc_vref_uv(uint32_t vrefint_q4, uint16_t vrefint_cal, uint32_t cal_vref_mv)
{
    uint64_t num = (uint64_t)cal_vref_mv * 1000u * ((uint32_t)vrefint_cal * 16u);
    return (uint32_t)((num + vrefint_q4 / 2u) / vrefint_q4);
}

/* Код, каким он был бы при заводской опоре: умножить на VREFINT_CAL / код VREFINT. */
uint32_t adc_norm_q4(uint32_t code_q4, uint32_t vrefint_q4, uint16_t vrefint_cal)
{
    return (code_q4 * ((uint32_t)vrefint_cal * 16u) + vrefint_q4 / 2u) / vrefint_q4;
}

/* Одна точка: убрать только смещение, наклон — номинальный (vref_uv на 65536 единиц Q4). */
void adc_cal1_make(adc_cal2_t *cal, uint32_t c_q4, uint32_t uv, uint32_t vref_uv)
{
    cal->uv_lo = (int32_t)uv;
    cal->c_lo_q4 = (int32_t)c_q4;
    cal->k_q16 = (int32_t)vref_uv;
}

/* Две точки: коды c_lo_q4 и c_hi_q4 сняты при известных напряжениях uv_lo и uv_hi. */
int adc_cal2_make(adc_cal2_t *cal, uint32_t c_lo_q4, uint32_t uv_lo, uint32_t c_hi_q4, uint32_t uv_hi)
{
    if (c_hi_q4 <= c_lo_q4 || uv_hi <= uv_lo) {
        return -1;
    }
    cal->uv_lo = (int32_t)uv_lo;
    cal->c_lo_q4 = (int32_t)c_lo_q4;
    cal->k_q16 = (int32_t)((((uint64_t)(uv_hi - uv_lo)) << 16) / (c_hi_q4 - c_lo_q4));
    return 0;
}

/* Микровольты по калибровке. Сдвиг знакового числа у gcc и armclang арифметический. */
int32_t adc_cal2_apply(const adc_cal2_t *cal, uint32_t code_q4)
{
    int64_t p = (int64_t)((int32_t)code_q4 - cal->c_lo_q4) * cal->k_q16 + 32768;
    return cal->uv_lo + (int32_t)(p >> 16);
}

/* Поправка нелинейности: 17 узлов через 256 кодов, значения в единицах Q4, между узлами — по прямой. */
uint32_t adc_inl_apply(const int8_t lut[17], uint32_t code_q4)
{
    uint32_t seg = code_q4 >> 12;
    int32_t frac = (int32_t)(code_q4 & 0xFFFu);
    int32_t corr;

    if (seg > 15u) {
        return code_q4;
    }
    corr = (lut[seg] * (4096 - frac) + lut[seg + 1u] * frac + 2048) >> 12;
    return (uint32_t)((int32_t)code_q4 + corr);
}

/* Температура в десятых долях градуса по двум заводским точкам. ts_q4 — код датчика,
   уже приведённый к заводской опоре (adc_norm_q4). */
int32_t adc_ts_x10(uint32_t ts_q4, uint16_t cal1, uint16_t cal2, int32_t t1_x10, int32_t t2_x10)
{
    int32_t den = ((int32_t)cal2 - (int32_t)cal1) * 16;
    int32_t num = ((int32_t)ts_q4 - (int32_t)cal1 * 16) * (t2_x10 - t1_x10);

    if (den <= 0) {
        return t1_x10;
    }
    return t1_x10 + (num + (num >= 0 ? den / 2 : -den / 2)) / den;
}

adc_regs.c — встроенная калибровка и заводские коды по регистрам F1, F4 и G4.

/* Встроенная калибровка и заводские коды по регистрам — раздел 14а.
   Заголовок серии подключает сборка: -include stm32f10x.h, stm32f4xx.h или stm32g4xx.h. */
#include <stdint.h>

#if defined(STM32F10X_MD)

/* F103. АЦП уже включён первой записью ADON и выждал tSTAB (раздел 14.3). */
uint16_t adc_f1_calibrate(void)
{
    ADC1->CR2 |= ADC_CR2_RSTCAL;                /* сбросить калибровочные регистры */
    while (ADC1->CR2 & ADC_CR2_RSTCAL) {
    }
    ADC1->CR2 |= ADC_CR2_CAL;                   /* поправки на разброс конденсаторов */
    while (ADC1->CR2 & ADC_CR2_CAL) {
    }
    return (uint16_t)ADC1->DR;                  /* RM0008: коды калибровки попадают в ADC_DR */
}

#elif defined(STM32F407xx)

/* F407. Калибровки в АЦП нет — есть заводские коды в системной памяти (DS8626, таблицы 70 и 73). */
#define F4_VREFINT_CAL  (*(const uint16_t *)0x1FFF7A2Au)    /* 30 °C, VDDA = 3,3 В */
#define F4_TS_CAL1      (*(const uint16_t *)0x1FFF7A2Cu)    /* 30 °C */
#define F4_TS_CAL2      (*(const uint16_t *)0x1FFF7A2Eu)    /* 110 °C */

int adc_f4_factory(uint16_t *vrefint_cal, uint16_t *ts_cal1, uint16_t *ts_cal2)
{
    *vrefint_cal = F4_VREFINT_CAL;
    *ts_cal1 = F4_TS_CAL1;
    *ts_cal2 = F4_TS_CAL2;
    /* незаписанная ячейка читается как 0xFFFF: заводских кодов нет */
    return (*vrefint_cal == 0xFFFFu || *ts_cal1 == 0xFFFFu || *ts_cal2 <= *ts_cal1) ? -1 : 0;
}

void adc_f4_internal_on(void)
{
    ADC->CCR |= ADC_CCR_TSVREFE;                /* датчик температуры и VREFINT: каналы 16 и 17 */
}

#elif defined(STM32G474xx)

/* G474. АЦП выключен (ADEN = 0), регулятор включён и выждал 20 мкс (раздел 15а.3).
   Возвращает ADC_CALFACT: его сохраняют перед Standby. */
uint32_t adc_g4_calibrate(void)
{
    ADC1->CR &= ~ADC_CR_ADCALDIF;               /* несимметричные входы: CALFACT_S */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR |= ADC_CR_ADCALDIF;                /* дифференциальные входы: CALFACT_D */
    ADC1->CR |= ADC_CR_ADCAL;
    while (ADC1->CR & ADC_CR_ADCAL) {
    }
    ADC1->CR &= ~ADC_CR_ADCALDIF;
    return ADC1->CALFACT;
}

/* Вернуть сохранённые коэффициенты: АЦП включён, измерений нет (RM0440, 21.4.8). */
int adc_g4_calfact_restore(uint32_t calfact)
{
    if (!(ADC1->CR & ADC_CR_ADEN) || (ADC1->CR & (ADC_CR_ADSTART | ADC_CR_JADSTART))) {
        return -1;
    }
    ADC1->CALFACT = calfact & (ADC_CALFACT_CALFACT_S | ADC_CALFACT_CALFACT_D);
    return 0;
}

/* Заводские коды G474 сняты при 3,0 В (DS12288, таблицы 5 и 6). */
#define G4_VREFINT_CAL  (*(const uint16_t *)0x1FFF75AAu)    /* 30 °C, VDDA = VREF+ = 3,0 В */
#define G4_TS_CAL1      (*(const uint16_t *)0x1FFF75A8u)    /* 30 °C */
#define G4_TS_CAL2      (*(const uint16_t *)0x1FFF75CAu)    /* 110 °C */

int adc_g4_factory(uint16_t *vrefint_cal, uint16_t *ts_cal1, uint16_t *ts_cal2)
{
    *vrefint_cal = G4_VREFINT_CAL;
    *ts_cal1 = G4_TS_CAL1;
    *ts_cal2 = G4_TS_CAL2;
    return (*vrefint_cal == 0xFFFFu || *ts_cal1 == 0xFFFFu || *ts_cal2 <= *ts_cal1) ? -1 : 0;
}

#else
#error "серия не выбрана"
#endif

Дальше: раздел 15 «ЦАП» — обратная задача: получить из числа напряжение.

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