Программирование регистров ATmega328P

Это вторая статья, которую я написал о программировании регистров ATmega328P. В первой статье мы рассмотрели, как можно мигать светодиодом, используя аппаратные регистры и прерывания. Теперь мы рассмотрим АЦП и то, как мы можем использовать его для измерения напряжения на одном из аналоговых входов (AI) Arduino UNO.

Библиотеки Arduino IDE упрощают считывание напряжения на ИИ:

int value = analogRead(A0);

При использовании настроек по умолчанию возвращаемое значение 0 будет представлять 0 В, а возвращаемое значение 1023 (максимум) будет представлять примерно 5 В. Понимая, что происходит за кулисами, вы можете выйти за рамки этой базовой реализации и делать такие вещи, как:

  • Изменить скорость конвертации через прескейлер;
  • Используйте разные опорные напряжения;
  • Сделать чтение АЦП неблокирующим;
  • Используйте прерывание, которое возвращается, когда значение читается;
  • Читайте другие вещи, такие как внутренняя температура чипа, GND и VCC;

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

Arduino ATmega328P имеет 10-битный АЦП, что означает, что он может возвращать 2¹⁰ (т. е. 0–1023) значений. Вот откуда 1024 в уравнении из таблицы данных:

ADC = (Vin * 1024)/Vref

Если вы хотите преобразовать значение АЦП в напряжение, используйте следующую формулу (аппаратное округление в меньшую сторону):

float voltage = ((float) rawADC + 0.5 ) / 1024.0 * Vref;

Выберите диапазон напряжения АЦП

Чтобы преобразовать аналоговое напряжение в цифровое значение на любом АЦП, преобразователь должен быть обеспечен диапазоном напряжений. Нижний предел всегда равен GND на Arduino (т. е. 0 В), но вы можете выбрать один из трех источников, который будет служить верхним эталоном:

  • AREF — это отдельный вывод на микроконтроллере, который можно использовать для обеспечения любого высокого опорного напряжения, если оно находится в диапазоне от 1,0 В до VCC. На Uno он подключен к одному из черных разъемов.
  • AVCC — этот вывод на микроконтроллере обеспечивает питание схемы АЦП на микросхеме. На Uno он подключен к VCC. Это самый простой вариант использования при условии, что AVCC подключен к VCC.
  • 1,1 В — микроконтроллер имеет внутреннее опорное напряжение 1,1 В, которое можно использовать

Независимо от выбранного источника, в спецификации рекомендуется установить конденсатор между AREF и землей, чтобы сгладить напряжение на этом выводе. Таблица 24–3 (лист технических данных) используется для выбора источника опорного напряжения, который вы хотите использовать. Обратите внимание, что если вы используете любой из внутренних источников опорного напряжения (AVCC или 1,1 В), то это напряжение подключается к выводу AREF внутри. В этой ситуации не подключайте никакие другие источники напряжения к этому контакту, иначе он будет замкнут на внутреннюю ссылку (и, возможно, выпустит волшебный дым).

Я бы по умолчанию использовал AVCC, если у вас не было причин поступать иначе. Это даст вам номинальный диапазон 0–5 В.

Обратите внимание, что все 8 аналоговых входов в UNO подключены к одному и тому же АЦП (см. рис. 1). Таким образом, вы можете сэмплировать только один вход за раз. Для этого используется таблица 24–4 (лист данных) и регистр ADMUX.

Регистры управления АЦП (ADCSRA и ADCSRB)

Модуль АЦП ATmega328P имеет два регистра управления и состояния, ADCSRA и ADCSRB. Для основных операций АЦП необходимо изменить только биты в регистрах ADMUX и ADCSRA.

Предполагая, что мы используем AVCC, из таблицы 24-3 нам нужно, чтобы REFS1 = 0 и REFS0 = 1. Чтобы получить 10-битный результат, ADLAR = 1, и если мы используем A0 в качестве нашего входа, то из таблицы 24-4, MUX3– 0 = 0000. Таким образом

ADMUX = 0b01000000

Вещи, о которых нам нужно беспокоиться для ADCSRA:

Бит 7 —бит ADEN включает модуль АЦП. Должен быть установлен в 1 для выполнения любых операций АЦП.

Бит 6 —установка бита ADSC в 1 инициирует одиночное преобразование. Этот бит останется равным 1, пока преобразование не будет завершено. Если ваша программа использует метод опроса для определения завершения преобразования, она может проверить состояние этого бита, чтобы определить, когда можно считать результат преобразования из регистров данных. Пока этот бит равен единице, регистры данных еще не содержат допустимого результата.

Бит 5 — ADATE: включение автоматического запуска АЦП. Когда этот бит записывается в единицу, автоматический запуск АЦП включен. АЦП начнет преобразование по положительному фронту выбранного триггерного сигнала. Источник запуска выбирается установкой битов выбора запуска АЦП, ADTS в ADCSRB. Нам не нужен автоматический запуск, поэтому выберите 0 для этого бита.

Бит 4 —ADIF: Флаг прерывания АЦП. Этот бит устанавливается, когда преобразование АЦП завершается и регистры данных обновляются. Прерывание завершения преобразования АЦП выполняется, если установлены бит ADIE и бит I в SREG. ADIF очищается аппаратно при выполнении соответствующего вектора обработки прерывания. В качестве альтернативы ADIF очищается путем записи логической единицы во флаг. Имейте в виду, что при выполнении чтения-модификации-записи на ADCSRA ожидающее прерывание может быть отключено. Это также применимо, если используются инструкции SBI и CBI. Установите ADIF на 0.

Бит 3 — установка бита ADIE в 1 разрешает прерывания. Прерывание будет сгенерировано по завершении преобразования. Имя вектора прерывания — «ADC_vect».

Биты 2:0 — биты ADPS2, ADPS1 и ADPS0 выбирают значение делителя перед масштабированием. Доступны:

Схема АЦП должна иметь тактовый сигнал в диапазоне от 50 кГц до 200 кГц. Тактовая частота ATmega328P слишком высока (16 МГц на Uno), поэтому чип включает в себя регулируемый предварительный делитель, чтобы разделить тактовую частоту процессора до чего-то пригодного для использования. Тактовая частота процессора делится на значение предварительного делителя, чтобы получить тактовую частоту АЦП. Использование более низкого предварительного масштабирования сделает преобразование быстрее, но за счет точности.

Наименьшее используемое значение — предварительное масштабирование 16 для UNO. Библиотека AnalogRead использует предварительный масштабатор 128, чтобы получить максимальное разрешение. Если вы не понимаете последствий, придерживайтесь 128.

Значение ADCSRA будет зависеть от того, какой подход к преобразованию вы используете (см. ниже).

Регистр данных АЦП (ADCH и ADCL — старший и младший байты)

Результаты преобразования сохраняются в двух байтах регистра данных. Старшие значащие биты результата сохраняются в ADCH, а младшие значащие биты — в ADCL. Для 10-битного преобразования бит ADLAR в регистре ADMUX должен быть равен нулю.

При использовании 10-битных результатов (ADLAR=0) результаты можно прочитать в вашей программе с помощью:

int value = ADC;

АЦП измеряет напряжение, заряжая внутренний конденсатор емкостью 14 пФ, а затем измеряет это напряжение с последовательными приближениями. АЦП выполняет преобразование за 13 тактовых циклов, за исключением первого включения АЦП, когда требуется 25 тактов АЦП из-за дополнительных затрат на инициализацию. Выборка и удержание АЦП занимает примерно 12 мкс, а весь процесс преобразования может занять до 260 мкс (в зависимости от выбранного предварительного делителя). Таким образом, есть как минимум 3 способа приблизиться к этому:

  1. Поместите достаточно длинную задержку в цикле while, чтобы вы знали, что преобразование выполнено. Это наименее элегантный метод!
  2. Установите бит ADSC в ADCSRA на единицу. Это запускает первое преобразование. Затем опросите бит ADSC в ADCSRA, пока он не станет равным нулю, и прочитайте значение ADC.
  3. Использовать прерывание от АЦП и обрабатывать чтение в соответствующем ISR — ADC_vect()

Я предлагаю использовать подходы 2 или 3.

Не забывайте, что вам нужно установить бит ADSC в ADCSRA на единицу каждый раз, когда вы хотите выполнить преобразование.

Пример кода опроса регистра АЦП

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

Несколько замечаний по этому коду:

  • Нам нужно использовать стандартный цикл Arduino(), чтобы разрешить обработку последовательных событий (т. е. вывод результата на последовательный монитор).
  • C5:: Задержка (500) предназначена только для того, чтобы мы могли увидеть напечатанный результат. Функционально это не требуется, это работа опроса (т. е. проверка бита ADSC), чтобы убедиться, что результат готов.
  • Когда ничего не подключено к A0, контакт плавает, и я считывал значения около 650 (но это может быть что угодно из-за электрических помех от окружающей среды или емкостной связи с соседним контактом). Подключите A0 к GND, и вы должны увидеть 0, а при подключении к 5V должно отображаться 1023.

Пример кода прерывания регистра АЦП

В нашем последнем примере мы будем измерять АЦП с помощью прерываний.

Что касается прерывания версии кода:

  • Мы должны разрешить прерывания, установив бит ADIE в регистре ADCSRA.
  • Команда sei() необязательна, для включения этого прерывания достаточно установки ADIE.
  • Обратите внимание, что мы стараемся делать как можно меньше в ISR.
  • Это немного сложнее, чем версия с опросом, но ненамного.
  • Как и в примере с опросом, если к A0 ничего не подключено, то контакт плавает, и вы получите случайный результат. Подключите A0 к GND, и вы должны увидеть 0, а при подключении к 5V должно отображаться 1023.

Первоначально опубликовано на http://reefwingrobotics.blogspot.com.