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

analogRead изнутри: АЦП, делитель и опорное

analogRead выбирает канал, запускает преобразование и ждёт его конца — от 104 до 112 мкс, в течение которых процессор ничего не делает. Разберём исходник, найдём каналы, до которых analogRead не достаёт, и запустим АЦП по прерыванию.

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

От Arduino к регистрамурок 4 из 830 мин

Цель урока: Понимать, какие биты ADMUX и ADCSRA выставляет ядро Arduino, безопасно выбирать опорное напряжение, измерять напряжение питания через внутренний источник 1,1 В и читать АЦП без ожидания.

analogRead(A0) возвращает число от 0 до 1023 — результат 10-битного АЦП последовательного приближения. Регистры АЦП и выбор частоты подробно разобраны в уроке об АЦП трека «AVR с нуля». Здесь — что делает ядро ArduinoCore-avr 1.8.8: функции analogRead() и analogReference() из wiring_analog.c и настройка АЦП в init().

Что настроено до setup()

В init() (строки 353–382 wiring.c) ядро выбирает делитель частоты АЦП по F_CPU и включает АЦП:

// ArduinoCore-avr 1.8.8, cores/arduino/wiring.c (abridged)
#if F_CPU >= 16000000 // 16 MHz / 128 = 125 KHz
    sbi(ADCSRA, ADPS2);
    sbi(ADCSRA, ADPS1);
    sbi(ADCSRA, ADPS0);
#endif
    sbi(ADCSRA, ADEN);

ADPS2:0 = 111 — деление на 128 (табл. 24-5), то есть 125 кГц при 16 МГц. Даташит требует 50–200 кГц для полного разрешения в 10 бит (раздел 24.4), так что ядро выбирает ближайший безопасный делитель. На 8-мегагерцовых платах ядро ставит 64 — снова 125 кГц.

Что делает analogRead

// ArduinoCore-avr 1.8.8, cores/arduino/wiring_analog.c, lines 38-90 (ATmega328P branch)
int analogRead(uint8_t pin)
{
    if (pin >= 14) pin -= 14;                    // allow for channel or pin numbers
    ADMUX = (analog_reference << 6) | (pin & 0x07);
    sbi(ADCSRA, ADSC);                           // start the conversion
    while (bit_is_set(ADCSRA, ADSC));            // ADSC is cleared when it finishes
    return ADC;                                  // ADCL is read first
}

Четыре шага:

  1. A0 в pins_arduino.h — это 14, поэтому analogRead(A0) и analogRead(0) читают один канал ADC0. A6 и A7 у Nano — каналы 6 и 7.
  2. ADMUX записывается целиком при каждом вызове: два старших бита — опорное (REFS1:0), три младших — канал. Бит ADLAR — ноль (результат выровнен вправо), бит MUX3 — тоже всегда ноль.
  3. Запуск преобразования битом ADSC.
  4. Ожидание в цикле, пока ADSC не сбросится, и чтение 16-битного регистра ADC — компилятор читает ADCL раньше ADCH, как требует даташит.

Само преобразование — 13 тактов АЦП, 13 / 125 000 = 104 мкс, а первое после включения — 25 тактов, 200 мкс (раздел 24.4, расчёт наш). Но запуск битом ADSC срабатывает только по следующему фронту такта АЦП (раздел 24.4), а такт АЦП при 125 кГц длится 8 мкс, — поэтому вызов analogRead занимает от 104 до 112 мкс плюс время самой функции, и в секунду получается около 1 / 112 мкс ≈ 8 900 отсчётов (расчёт наш). Входное напряжение запоминается не в момент вызова, а через 1,5 такта АЦП после старта (табл. 24-1), то есть через 12 мкс (расчёт наш).

analogReference и вывод AREF

// ArduinoCore-avr 1.8.8, cores/arduino/wiring_analog.c, lines 28-36
uint8_t analog_reference = DEFAULT;

void analogReference(uint8_t mode)
{
    // can't actually set the register here because the default setting
    // will connect AVCC and the AREF pin, which would cause a short if
    // there's something connected to AREF.
    analog_reference = mode;
}

Для ATmega328P константы в Arduino.h подобраны прямо под биты REFS1:0 (табл. 24-3):

Константа ArduinoЗначениеREFS1:0Опорное
EXTERNAL000вывод AREF, внутреннее опорное выключено
DEFAULT101AVCC, конденсатор на AREF
INTERNAL311внутреннее 1,1 В, конденсатор на AREF

При AVCC и 1,1 В выбранное опорное выходит на вывод AREF (раздел 24.5.2). Если на AREF подано внешнее напряжение, выбирать другие опорные нельзя: по даташиту они замкнутся с внешним источником. Ядро поэтому не пишет ADMUX в analogReference(). Но analogRead() пишет его при каждом вызове, и если первый analogRead случится до analogReference(EXTERNAL), он подключит AVCC к AREF. Правило: при внешнем опорном analogReference(EXTERNAL) — первая строка setup(). Подробнее — в термине «AREF и AVCC» и в ответе о REF5025 на входе AREF.

Каналы, до которых analogRead не достаёт

Маска pin & 0x07 оставляет каналы 0–7, а бит MUX3 всегда нулевой. Внутренние входы мультиплексора (табл. 24-4) требуют MUX3 = 1:

MUX3:0Вход
1000ADC8 — датчик температуры, нужно опорное 1,1 В (раздел 24.8)
11101,1 В — внутренний источник bandgap VBG
11110 В (земля)

Напряжение питания без делителя

Если измерить известное напряжение 1,1 В относительно AVCC, можно найти само AVCC. Из формулы ADC = VIN · 1024 / VREF (раздел 24.7) следует VREF = VBG · 1024 / ADC:

// Returns AVCC in millivolts, measured against the internal bandgap.
uint16_t readVccMillivolts(void)
{
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);  // AVCC ref, channel 1110 = VBG
  delay(1);                         // bandgap start-up: up to 70 us (table 29-11)
  for (uint8_t i = 0; i < 2; i++) { // the first result after the switch is discarded
    ADCSRA |= _BV(ADSC);
    while (ADCSRA & _BV(ADSC)) {}
  }
  uint16_t code = ADC;
  if (code == 0) return 0;               // no valid reading: avoid dividing by zero
  return (uint32_t)1100 * 1024 / code;   // VBG = 1.1 V typical
}

При 5 В код будет около 1,1 · 1024 / 5 ≈ 225 (расчёт наш). Точность ограничена самим источником: VBG от 1,0 до 1,2 В (табл. 29-11), то есть до ±9 %. Чтобы получить точный результат, один раз измерьте AVCC мультиметром и подставьте вместо 1100 своё значение VBG = AVCC · code / 1024. Следующий analogRead() снова запишет ADMUX полностью, так что смешивать эту функцию с обычным analogRead можно — но первый его результат после смены опорного тоже лучше отбросить (раздел 24.5.2).

АЦП без ожидания

analogRead 104–112 мкс крутится в цикле. Регистры позволяют запустить преобразование и узнать о его конце по прерыванию ADC_vect — бит ADIE в ADCSRA:

volatile uint16_t adcValue;
volatile bool adcReady;
bool discardNext = true;       // REFS changes below: the first result may be wrong

ISR(ADC_vect)
{
  adcValue = ADC;
  adcReady = true;
}

void setup() {
  Serial.begin(115200);
  ADMUX = _BV(REFS0);          // AVCC reference (ADMUX was 0, AREF), channel ADC0 (A0)
  ADCSRA |= _BV(ADIE);         // ADEN and the /128 prescaler are already set by init()
  ADCSRA |= _BV(ADSC);         // first conversion
}

void loop() {
  if (adcReady) {
    noInterrupts();            // a 16-bit value: read it with interrupts off
    uint16_t v = adcValue;
    adcReady = false;
    interrupts();
    if (discardNext) {
      discardNext = false;     // section 24.5.2: discard the first result after the switch
    } else {
      Serial.println(v);
    }
    ADCSRA |= _BV(ADSC);       // next conversion
  }
  // the loop is free while the ADC converts
}

Регистр ADCSRA лежит в расширенной области ввода-вывода, и ADCSRA |= … — это чтение, изменение и запись. Даташит предупреждает: если флаг ADIF уже установлен, такая запись вернёт в него единицу и сбросит ожидающее прерывание (раздел 24.9.2). Здесь это не мешает — к моменту следующего запуска обработчик уже сбросил флаг, — но помнить об этом нужно. Первый результат скетч выбрасывает: init() не пишет ADMUX, после сброса в нём 0 — опорное AREF, — и запись REFS0 меняет опорное, а первый результат после такой смены может быть неточным (раздел 24.5.2). Не вызывайте analogRead() в таком скетче: она сама ждёт ADSC и запишет ADMUX заново.

Напряжение на входах

Вход АЦП — обычный вывод порта C, и для него действует абсолютный максимум от −0,5 В до VCC + 0,5 В (раздел 29.1). Датчик с выходом 0–10 В или 12 В подключают только через делитель — его посчитает калькулятор делителя напряжения. При опорном 1,1 В всё, что выше 1,1 В, даёт коды около 1023 (раздел 24.5.2), но допустимым это напряжение остаётся только до VCC + 0,5 В.

Задание

  1. Скетч выполнил analogReference(INTERNAL); и analogRead(A1);. Какое значение запишется в ADMUX?
  2. В скетче делитель АЦП заменили на 16: ADCSRA = (ADCSRA & 0xF8) | _BV(ADPS2);. Какая частота АЦП и сколько длится преобразование? Что говорит даташит о точности при такой частоте?
  3. readVccMillivolts() вернула 4 654, а мультиметр на выводе 5V показывает 4,90 В. Каков VBG этого кристалла?
Ответ и решение
  1. INTERNAL = 3, канал A1 = 15 − 14 = 1: ADMUX = (3 << 6) | 1 = 0xC1 — REFS1:0 = 11, MUX = 0001.

  2. 16 МГц / 16 = 1 МГц, преобразование 13 тактов — 13 мкс (расчёт наш). Это выше 200 кГц, а по разделу 24.4 полное разрешение 10 бит гарантировано только в диапазоне 50–200 кГц; на большей частоте АЦП работает с меньшим числом точных разрядов.

  3. Функция считала VBG = 1100 мВ, значит, код АЦП был 242: 1 126 400 / 242 ≈ 4654. Настоящий VBG = 4900 · 242 / 1024 ≈ 1158 мВ (расчёт наш) — в пределах 1,0–1,2 В из табл. 29-11. Это число и подставляют вместо 1100.

Что дальше

В уроке 5 разберём millis(), micros() и delay(): прерывание Timer0 и дробную поправку, из-за которой millis() иногда прыгает на 2. Регистры ADMUX, ADCSRA и ADC с битами — на странице регистров АЦП ATmega328P. Код по числу и опорному напряжению и обратно считает калькулятор АЦП AVR, а из чего складывается погрешность — калькулятор бюджета погрешности АЦП и термин «Время преобразования».

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

Почему analogReference() не меняет опорное сразу?

Функция только запоминает режим в переменной, а в регистр ADMUX он попадает при следующем analogRead. Комментарий в исходнике ядра объясняет причину: запись режима по умолчанию сразу соединила бы AVCC с выводом AREF, и внешний источник на AREF оказался бы замкнут на питание. Поэтому analogReference(EXTERNAL) нужно вызвать до первого analogRead, иначе этот analogRead успеет подключить AVCC к AREF.

Почему analogRead не читает датчик температуры ATmega328P?

Ядро записывает в ADMUX только три младших бита номера канала (pin & 0x07), а бит MUX3 всегда оставляет нулевым. Внутренние каналы — датчик температуры (1000), источник 1,1 В (1110) и земля (1111) — требуют MUX3 = 1, поэтому до них analogRead не достаёт. Читать их приходится записью в ADMUX напрямую.

Сколько раз в секунду можно вызвать analogRead?

Одно преобразование при делителе 128 — 13 тактов АЦП на 125 кГц, то есть 104 мкс. Но начинается оно только со следующего фронта такта АЦП после записи ADSC (разд. 24.4 даташита) — это до 8 мкс ожидания, — и ещё немного занимает сама функция. Получается от 104 до 112 мкс на вызов, около 8 900 отсчётов в секунду. Самое первое после включения АЦП длится 25 тактов, 200 мкс. Всё это время analogRead ждёт в цикле, а программа стоит.

Источники

  1. Arduino. ArduinoCore-avr 1.8.8: cores/arduino/wiring_analog.c, wiring.c, Arduino.h — тег 1.8.8 — коммит 86df345 от 21 мая 2026 (GitHub API, tags и commits); wiring_analog.c, стр. 28–36: analogReference только запоминает режим в переменной analog_reference — запись в регистр сразу соединила бы AVCC с выводом AREF; стр. 38–90: analogRead — номер вывода от 14 и выше уменьшается на 14, ADMUX = (analog_reference << 6) | (pin & 0x07), запуск битом ADSC, ожидание его сброса, возврат ADC; wiring.c, стр. 353–382: init() ставит ADPS2:0 = 111 (делитель 128) при F_CPU от 16 МГц и включает АЦП битом ADEN; Arduino.h, стр. 81–84: для ATmega328P INTERNAL = 3, DEFAULT = 1, EXTERNAL = 0
  2. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — разд. 24.4: частота АЦП 50–200 кГц для полного разрешения, преобразование начинается по следующему фронту такта АЦП после записи ADSC, выше — меньше разрядов, преобразование 13 тактов, первое после включения — 25, при выборе bandgap первый результат может быть неверным; табл. 24-1: выборка через 1,5 такта АЦП после старта; разд. 24.5.2: опорное AVCC, 1,1 В или AREF, при внешнем напряжении на AREF другие опорные выбирать нельзя — они замкнутся с ним, первый результат после смены опорного отбросить; разд. 24.7: ADC = VIN · 1024 / VREF; разд. 24.8: канал ADC8 — датчик температуры, нужно опорное 1,1 В; разд. 24.9.1, табл. 24-3 и 24-4: ADMUX, REFS1:0, MUX3:0 = 1110 — 1,1 В (VBG), 1111 — 0 В, начальное значение ADMUX 0; разд. 24.9.2, табл. 24-5: ADCSRA, ADSC, ADIF, ADIE, ADPS; чтение-изменение-запись ADCSRA может сбросить ожидающий флаг ADIF; табл. 29-11: VBG от 1,0 до 1,2 В, типично 1,1 В, время установления до 70 мкс; разд. 29.1: напряжение на выводе от −0,5 В до VCC + 0,5 В
  3. AVR-LibC 2.3.2: avr/interrupt.h, iom328p.h (github.com/avrdudes/avr-libc) — макрос ISR, вектор ADC_vect (номер 21) — ADC Conversion Complete; 16-битный регистр ADC читается в правильном порядке, ADCL первым

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