Учебный курс. АЦП микроконтроллера AVR или как подключить 4 кнопки к одному выводу. Часть 1

АЦП микроконтроллера AVR

   Микроконтроллер общается с внешним миром посредством портов ввода/вывода. В общем случае он может “воспринимать” только цифровые сигналы – логический ноль или логическую единицу. Например, для микроконтроллера ATmega8535 при напряжении питания 5 В логический ноль – это напряжение от 0 до 1,3 В, а логическая единица – от 1,8 до 5 В. Довольно часто возникает потребность измерять напряжения, которые могут принимать любое значение в диапазоне от 0 до напряжения питания. Для этих целей в составе микроконтроллеров AVR есть аналого-цифровой преобразователь (АЦП).
  Не вдаваясь в подробности внутреннего устройства АЦП, представим его в виде черного ящика. На вход АЦП подается непрерывный аналоговый сигнал, а на выходе получается последовательность цифровых значений. АЦП имеет много характеристик, но в качестве основных можно назвать  разрешающую способность, абсолютную точность, предельную частоту дискретизации и диапазон входных напряжений.
   Разрешающая способность (разрешение)  – это способность АЦП различать два значения входного сигнала. Определяется как величина обратная максимальному числу кодовых комбинаций на выходе АЦП. У AVRа АЦП 10-ти разрядный. Максимальное число кодовых комбинаций будет равно 210 = 1024. Разрешающая способность равна 1/1024 от всей шкалы допустимых входных напряжений.
  Для работы АЦП необходим источник опорного напряжения (ИОН). Для него это эталон, по отношению к которому он измеряет входные сигналы. Микроконтроллеры AVR позволяют в качестве ИОНа использовать напряжение питания, внутренний опорный источник на 2,56 В и напряжение на выводе AREF (внешний ИОН).
   Напряжение питания в нашей схеме 5 В, тогда 1/1024 от всей шкалы это 5 * 1/1024 = 0,0048 В или примерно 5 мВ. С таким шагом (это называется шаг квантования) АЦП будет измерять входное напряжение. Если два ближайших значения сигнала на входе АЦП будут отличаться между собой на величину < 5 мВ, АЦП воспримет их как одинаковые. На практике разрешающая способность АЦП ограничена его шумами.
   Абсолютная точность – отклонение реального преобразования от идеального. Это составной результат нескольких погрешностей АЦП. Выражается в количестве младших значащих разрядов (LSB - least significant bit) АЦП. Для AVRа абсолютная погрешность АЦП  =  ±2LSB. Для нашего примера абсолютная точность будет равна 2 * 5 мВ = ±10 мВ.
  Предельная частота дискретизации определяет быстродействие АЦП и измеряется в герцах или количестве выборок в секунду (SPS – samples per second). Для микроконтроллеров AVR эта величина равна 15 kSPS (килло семплов в секунду). Практически АЦП AVRа работает и быстрее, но при этом  его точность ухудшается.
   Теорема Котельникова (теорема Найквиста-Шеннона, теорема о выборке) гласит, что аналоговый сигнал имеющий ограниченный спектр, может быть восстановлен однозначно и без потерь по своим дискретным отсчётам, если частота выборки (дискретизации) превышает максимальную частоту спектра  сигнала более чем в 2 раза. Выражаясь по-простому - если вам нужно оцифровать аналоговый сигнал с полосой спектра 0 - 7 КГц, то в идеальном случае частота дискретизации должна быть > удвоенной максимальной частоты спектра этого сигнала, то есть > 14 КГц. На практике все намного сложнее. Перед входом АЦП всегда ставят НЧ фильтр, чтобы ограничить спектр сигнала, а частота дискретизации выбирается еще более высокой. 
   Диапазон входных напряжений – это минимальное и максимальное значение напряжения, которое можно подавать на вход АЦП. Для микроконтроллера AVR он равен 0 – Vcc (напряжение питания)


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


Пояснения к схеме

   Для знакомства с модулем АЦП микроконтроллера AVR я выбрал простую, но интересную схему. Взгляните на рисунок. Четыре кнопки подключены к делителю напряжения (резисторы R8…R11). При нажатии они коммутируют на вход нулевого канала АЦП разные напряжения. Измеряя эти напряжения в прерывании АЦП и определяя в какой диапазон они попадают, микроконтроллер будет распознавать номер нажатой кнопки.
   Резистор R7 нужен чтобы вход АЦП не “болтался в воздухе”, когда все кнопки отпущены. Если этого не сделать АЦП будет ловить помехи. Номинал резистора R7 выбран большим, чтобы не оказывать влияние на делитель напряжения.
   R6 и C7 - низкочастотный фильтр для защита от дребезга кнопок и помех. (Вообще НЧ фильтры перед АЦП обычно используются для защиты от такого явления как наложение, но это отдельная история). Дополнительно резистор R6 играет функцию токоограничения, без него при нажатии кнопки S4 вывод микроконтроллера напрямую соединялся бы с плюсом питания. А это нежелательно.
   Для индикации номера нажатой кнопки в схеме используются 4 светодиода.
   Микроконтроллер - ATMega8535. Описание регистров АЦП ниже по тексту приводится именно для него. Для других микроконтроллеров могут быть некоторые отличия.
схема подключения кнопок к АЦП AVRа

Задача

Для приведенной выше схемы, написать программу определяющую номер нажатой кнопки.

Алгоритм программы такой:

Основная программа
   Инициализация портов
   Инициализация АЦП
   Разрешение прерываний
   Бесконечный цикл
   {
    Если кнопка нажата, зажечь нужный светодиод
    Если нет, погасить все светодиоды
   }
 
Обработчик прерывания АЦП
     Считать напряжение на входе АЦП
     Определить в какой диапазон оно попадает
     Записать номер кнопки в буфер

 

Начальный код программы


//программирование микроконтроллеров AVR на Си - осваиваем АЦП

#include <ioavr.h>
#include <intrinsics.h>
 
//макрос для запуска преобразования
#define StartConvAdc()  ADCSRA |= (1<<ADSC)

int main(void)
{

//ион - напряжение питания, выравнивание влево, нулевой канал
ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
//вкл. ацп, режим одиночного преобр., разрешение прерывания,частота преобр. = FCPU/128
ADCSRA = (1<<ADEN)|(0<<ADSC)|(0<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);


__enable_interrupt
();
StartConvAdc();
  while(1)
  {

    //какой-нибудь код

  }
  return 0;
}

//обработчик прерывания АЦП
#pragma vector=ADC_vect
__interrupt void adc_my(void)
{
   unsigned char AdcBuf = ADCH;
 
  //какой-нибудь код

  StartConvAdc();
}


Пояснения к коду

Макрос для запуска преобразования

StartConvAdc() – это макрос для запуска преобразования АЦП. Он устанавливает бит ADSC в регистре управления и состояния ADCSRA.
 
Инициализация АЦП

Чтобы запустить модуль АЦП, его нужно предварительно настроить. За это отвечают три регистра:
Регистр управления мультиплексором - ADMUX
Регистр управления и состояния - ADCSRA
Регистр специальных функций - SFIOR

Регистр ADMUX

   Модулю АЦП для работы нужен источник опорного напряжения (ИОН). За выбор ИОНа отвечают  биты REFS1, REFS0. В нашем случае опорное напряжение это напряжение питания (именно поэтому мы завели вывод AVcc  на +5В) поэтому REFS1 - 0, REFS0 - 1
   АЦП микроконтроллера AVR 10-ти разрядный, а сам камень 8-ми. Поэтому результат преобразования хранится в двух регистрах (ADCH, ADCL). Бит ADLAR задает направление выравнивания результата преобразования. 0 – выравнивание вправо (в ADCH заняты 2 младших бита,  ADCL занят весь), 1 – выравнивание влево (ADCH занят весь, в ADCL только 2 старших бита).  В нашем примере не требуется высокой точности преобразования, поэтому было бы удобно выровнять результат влево и работать только с регистром ADCH. Устанавливаем ADLAR - 1
  Чисто физически в микроконтроллере AVR всего один АЦП. Но перед ним стоит мультиплексор, который позволяет подключать любой из 8-ми выводов порта к входу АЦП. Номер выбранного в данный момент канала задается битами ADMUX3,  ADMUX2,  ADMUX1,  ADMUX0. Мы используем нулевой канал, поэтому все биты равны 0.
 
//ион - напряжение питания, выравнивание влево, нулевой канал
ADMUX = (0<<REFS1)|(1<<REFS0)|(1<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);

Регистр ADCSRA

   Чтобы АЦП заработал его надо включить, то есть установить бит ADEN – 1
    Запуск преобразования осуществляется установкой бита ADSC -1. Во время инициализации мы не будем запускать АЦП, поэтому ADSC – 0
   АЦП может работать в двух режимах: одиночное преобразование - когда каждое преобразование запускается программно и непрерывное - когда преобразование запускается один раз программно, а перезапускается автоматически. В нашем примере преобразование одиночное, бит ADATE – 0.
   Когда АЦП закончил преобразование, он подает запрос на прерывание. Чтобы разрешить прерывание нужно установить бит ADIE – 1
  Модулю АЦП для работы требуется тактовый сигнал. Он формируется из тактового сигнала микроконтроллера путем деления на фиксированные коэффициенты. Для установки коэффициентов предделителя предназначены биты ADSP2, ADSP1, ADSP0. Поскольку мы будем опрашивать с помощью АЦП кнопки, а это очень медленное устройство,  выбираем самый большой коэффициент (то есть самую низкую частоту). Все биты в 1.

//вкл. ацп, режим одиночного преобр., разрешение прерывания, F преобр. = FCPU/128
  ADCSRA = (1<<ADEN)|(0<<ADSC)|(0<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);

Про все биты рассказал, кроме ADIF. ADIF - это флаг прерывания. Он устанавливается аппаратно, когда преобразование завершено.

Регистр SFIOR

   Биты ADTS2, ADTS1, ADTS0 определяют источник, запускающий процедуру преобразования. Устанавливать эти биты нужно только в режиме непрерывного преобразования. У нас режим одиночный, поэтому регистр не трогаем.
 
Обработчик прерывания АЦП

#pragma vector=ADC_vect
__interrupt void adc_my(void)
{
  unsigned char AdcBuf = ADCH;

  //какой-нибудь код

  StartConvAdc();
}

  Здесь все должно быть понятно. Синтаксис обработчика прерывания разбирался. ADCH – старший регистр данных модуля АЦП. StartConvAdc() – запуск преобразования перед выходом из обработчика.
   Для особо нетерпеливых выкладываю файлы проекта. АЦП микроконтроллера AVR с чего начать.
 
 

У вас недостаточно прав для комментирования.