Atmega8 ацп меньше 8 бит. AVR

АЦП — Аналого-цифровой преобразователь. Из названия можно догадаться, что на вход подается аналоговый сигнал, который преобразуется в число.

Первое о чем нужно сказать — АЦП микроконтроллера умеет измерять только напряжение. Чтобы произвести измерение других физических величин, их нужно вначале преобразовать в напряжение. Сигнал всегда измеряется относительно точки называемой опорное напряжение, эта же точка является максимумом который можно измерить. В качестве источника опорного напряжения (ИОН), рекомендуется выбирать высокостабильный источник напряжения, иначе все измерения будут плясать вместе с опорным.

Одной из важнейших характеристик является разрешающая способность, которая влияет на точность измерения. Весь диапазон измерения разбивается на части. Минимум ноль, максимум напряжение ИОН. Для 8 битного АЦП это 2^8=256 значений, для 10 битного 2^10=1024 значения. Таким образом, чем выше разрядность тем точнее можно измерять сигнал.

Допустим вы измеряете сигнал от 0 до 10В. Микроконтроллер используем Atmega8, с 10 битным АЦП. Это значит что диапазон 10В будет разделен на 1024 значений. 10В/1024=0,0097В — с таким шагом мы сможем измерять напряжение. Но учтите, что микроконтроллер будет считать, величину 0.0097, 0.0098, 0.0099… одинаковыми.

Тем не менее шаг в 0,01 это достаточно неплохо. Однако, есть несколько рекомендаций, без которых эта точность не будет соблюдена, например для измерения с точностью 10бит, частота на которой работает АЦП должна быть 50-200 кГц. Первое преобразование занимает 25 циклов и 13 циклов далее. Таким образом, при частоте 200кГц мы сможем максимум выжать
200 000/13 = 15 384 измерений.

В качестве источника опорного напряжения можно использовать внутренний источник и внешний. Напряжение внутреннего источника (2,3-2,7В) не рекомендуется использовать, по причине низкой стабильности. Внешний источник подключается к ножке AVCC или Aref, в зависимости от настроек программы.

При использовании АЦП ножка AVCC должна быть подключена. Напряжение AVCC не должно отличаться от напряжения питания микроконтроллера более чем на 0,3В. Как было сказано, максимальное измеряемое напряжение равно опорному напряжению(Vref), находится оно в диапазоне 2В-AVCC. Таким образом, микроконтроллер не может измерить более 5В.

Чтобы расширить диапазон измерения, нужно измерять сигнал через делитель напряжения. Например, максимальное измеряемое напряжение 10В, опорное напряжение 5В. Чтобы расширить диапазон измерения, нужно уменьшить измеряемый сигнал в 2 раза.

Формула для расчета делителя выглядит так:

U вых = U вх R 2 /(R 1 + R 2)

Подставим наши значения в формулу:

5 = 10*R2/(R1+R2)

т.е. можно взять любые два одинаковых резистора и подключить их по схеме

Следовательно, когда мы измеряем напряжение через делитель, нужно полученное значение АЦП умножить на коэффициент=Uвых/Uвх.

Полная формула вычисления измеряемого напряжения будет выглядеть так:
U=(опорное напряжение*значение АЦП*коэффициент делителя)/число разрядов АЦП

Пример: опорное 5В, измеренное значение АЦП = 512, коэффициент делителя =2, АЦП 10разрядный.

(5*512*2)/1024=5В — реальное измеренное значение напряжения.

Некоторые программисты пишут программу так, чтобы микроконтроллер автоматически вычислял коэффициент делителя, для этого выходной сигнал измеряют образцовым прибором и заносят это значение в программу. Микроконтроллер сам соотносит истинное напряжение каждому значению АЦП, сам процесс однократный и носит название калибровки.

Перейдем к программной реализации. Создаем проект с указанными параметрами. Также подключим дисплей на порт D для отображения информации.

Измерение будет производиться в автоматическом режиме, обработка кода в прерывании, опорное напряжение подключаем к ножке AVCC. По сути нам нужно только обрабатывать получаемые данные. Измеренные данные хранятся в переменной adc_data. Если нужно опрашивать несколько каналов, то выбираем какие каналы сканировать, а данные будут для ножки 0 в adc_data, для ножки 1 в adc_data и т.д.

В основном цикле добавим строки:

result=((5.00*adc_data)/1024.00); //пересчитываем значение АЦП в вольты
sprintf(lcd_buffer,»U=%.2fV»,result); //помещаем во временную переменную результат
lcd_puts(lcd_buffer); //выводим на экран

Небольшое замечание, чтобы использовать числа с плавающей точкой, нужно в настройках проекта изменить (s)printf Features: int, width на float, width, precision. Если этого не сделать десятые и сотые мы не увидим.

Таким образом, мы всего лишь перевели значение АЦП в вольты и вывели на дисплей. Результат в протеусе выглядит так:

Резистором можно менять напряжение, измеряемое напряжение выведено на дисплей. При сборке на реальном железе к ножке Aref нужно подключить конденсатор на 0,1мкФ. Урок получился немного сложным, но думаю он вам понравится.

Файл протеуса и прошивка:

Update:
Измерение тока:

Основные особенности АЦП

Микроконтроллер stm32f1xx имеет на борту 3 12-ти разрядных АЦП. Каждое АЦП может быть подключено к любому из 16-ти аналоговых входов. Более того, каждое из АЦП может сканировать эти входы, снимая с них данные в заданном пользователем порядке.
По окончании преобразования АЦП может выдать прерывание. Вообще АЦП может выдать одно из трёх прерываний: Об окончании преобразования обычного (регулярного) канала, об окончании преобразования по инжекторному каналу и событие по Watchdog.
В режиме сканирования прерывание об окончании преобразования выдаётся только по завершении всего сканирования. И при использовании регулярных каналов, в которых данные записываются всегда в один и тот же регистр, вы будете получать результаты только последнего преобразования.
Что бы этого не происходило, в микроконтроллере предусмотрено наличие так называемых инжекторных каналом, имеющих в своём наличии 4 разных регистра для записи данных. Т.е. если вам надо сканировать не более 4-х каналов, то результаты преобразований вы не потеряете. Т.к. каждый канал будет писать данные в свой регистр.
Для параллельного снятия данных сразу по нескольким каналам, предусмотрена возможность одновременного запуска нескольких АЦП. Данный режим получил название Dual Mode.

Подключение АЦП

Прежде всего рассмотрим подключение АЦП. Для чего нужна каждая ножка показано в таблице 1.

Таблица 1

Из перечисленных ножек интересны -Vоп и +Vоп. Они определяют диапазон напряжений, воспринимаемых АЦП. Если подключить -Vоп к земле, а +Vоп к питанию, то АЦП сможет оцифровать аналоговые сигналы во всём диапазоне от 0, до питания. Т.к. питания МК составляет 3,3В, а разрядность АЦП равна 12-ти, т.е. мы имеем 2^12=4096 уровней квантовая, шум АЦП составит 3,3/4096=0,8 мВ.

Виды АЦП

В микроконтроллере существует 2 вида каналов АЦП: регулярные и инжекторные. Эти 2 канала настраиваются независимо. Но работать может только один из них для каждого канала. Основным различием этих каналов является то, что для хранения данных, получаемых с помощью регулярного канала используется только один регистр. Это не плохо, если вам надо снять за один раз данные только с одного канала для каждого АЦП. Но, если Вам надо производить сканирование данных, то все снятые данные будут записываться с один и тот же регистр. Т.о. при чтении данных в прерывании по окончании преобразования Вы будете получать только последние снятые данные. Эту проблему призваны исправить инжекторные каналы. У них предусмотрены 4 регистра для хранения данных. Т.е. Вы сможете хранить данные с 4-х каналов сканирования. Недостатком инжекторных каналов является несколько более сложная система настройки, в которой надо описать данные, с какого канала в какой регистр будут записаны.

Настройка регулярного канала

Рассмотрим настройку регулярного канала АЦП. Настроим АЦП на ножке А4. Прежде всего, надо узнать какие АЦП имеют доступ к этой ножке и какие каналы на неё выведены. В частности это 4-й канал первого АЦП.
Как обычно используем стандартную схему:
1) Включить тактирование порта
2) Настроить вывод
3) Включить тактирование АЦП
4) Настроить АЦП
5) Включить нужные прерывания
6) Включить глобальные прерывания
7) Включить АЦП

При настройке порта главное в режиме задать аналоговый режим.

Настройка вывода в аналоговом режиме

GPIO_InitTypeDef GPIO_Init_user;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

GPIO_Init_user.GPIO_Pin = GPIO_Pin_4;
GPIO_Init_user.GPIO_Mode = GPIO_Mode_AN;
GPIO_Init_user.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init_user.GPIO_OType = GPIO_OType_PP;
GPIO_Init_user.GPIO_PuPd = GPIO_PuPd_NOPULL;

GPIO_Init(GPIOA, & GPIO_Init_user);


Включаем тактирование АЦП:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

Настраиваем АЦП:

Настройка регулярного канала АЦП

ADC_InitTypeDef ADC_InitType;

ADC_InitType.ADC_ContinuousConvMode = DISABLE;
ADC_InitType.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitType.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
ADC_InitType.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitType.ADC_NbrOfConversion = 1;
ADC_InitType.ADC_Resolution = ADC_Resolution_12b;
ADC_InitType.ADC_ScanConvMode = DISABLE;

ADC_Init(ADC1, &ADC_InitType);


Рассмотрим настройки подробнее:
ContinuousConvMode – Этот режим, если включен, запускает следующее преобразование сразу по окончании предыдущего. Так можно добиться максимальной скорости работы АЦП. В нашем случае это не надо и данная функция отключена.
DataAlign – выравнивание данных в 2-хбайтном слове. Есть 2 варианта. ADC_DataAlign_Right при котором данные выравниваются по правому краю, а неиспользуемые биты при этом равны нулю. Т.е. мы получаем обычные числа в 2-х байтах от 0 до 8192. При ADC_DataAlign_Left данные выравниваются по левому краю. Т.е. фактически для 12-ти битного преобразования они увеличиваются в 16 раз. Это может быть использовано например при передаче их через SPI, поддерживающий 12-ти битную передачу данных. Если настроить SPI на передачу начиная со старшего разряда. ExternalTrigConvEdge – настраивает запуск преобразования по какому либо событию, например переполнению таймера. В нашем случае не требуется.
ExternalTrigConv – Устанавливает какие именно события запустят АЦП. Т.к. триггер отключен, то эта функция не используется.
NbrOfConversion – число каналов, которые будет сканировать МК. Сюда записывается требуемое значение, а ниже, если это число больше 1 и ADC_ ScanConvMode=ENABLE, описывается какие каналы и в какой последовательности они будут сканироваться
ScanConvMode – Этот параметр определяет будет ли АЦП сканировать несколько каналов. Если этот режим включен, то АЦП будет последовательно оцифровывать данные с заданных каналов в заданной последовательности. И каналы и последовательность легко можно задать. Но возникает небольшая проблема со снятием данных.

Настраиваем конкретный канал. В нашем случае это всего один канал, потому настройка будет выглядеть так:

ADC_RegularChannelConfig(ADC1,ADC_Channel_4,1, DC_SampleTime_56Cycles);

Из параметров тут:
ADC1 – номер настраиваемого АЦП.
ADC_Channel_4 задаёт снимаемый канал.
1 – так называемый rank. Показывает в каком порядке этот канал будет оцифровываться. В нашем случае канал один, потому и rank=1.
DC_SampleTime_56Cycles – задаёт за какое время будет произведена оцифровка. Чем медленнее, тем точнее.

Теперь осталось настроить прерывания и включить:

NVIC_EnableIRQ(ADC_IRQn);
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

ADC_Cmd(ADC1, ENABLE);

На этом настройка закончена.

Чтобы запустить преобразование, используйте функцию:

ADC_SoftwareStartConv(ADC1);

По окончании преобразования программа попадёт в функцию прерывания:

Void ADC_IRQHandler(void)
{
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
ADC_result = ADC_GetConversionValue(ADC1);
}

Сбрасываем флаг и считываем результат преобразования.
Можно скачать пример работы от


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

Основные характеристики АЦП

  • Частота преобразования - это сколько раз в секунду АЦП сможет измерить напряжение
  • Разрядность - количество дискретных значений напряжения, на который делится весь рабочий диапазон входных напряжений. АЦП в AVR десяти разрядные. То есть, максимальное напряжение на входе АЦП будет переводиться в 2 10 =1024
  • Диапазон входных напряжений - это минимальное и максимальное напряжение, которое можно подавать на входы АЦП. Для avr это диапазон от 0 до напряжения питания микроконтроллера
Для работы АЦП необходим источник опорного напряжения (ИОН). Это эталон, по отношению к которому он измеряет напряжение на входе. В AVR в качестве источника опорного напряжения может выступать напряжения питания МК, источник опорного напряжения, подключенный к ножке ARef и внутренний ИОН на 2,56 в. ИОН должен быть как можно стабильней, от этого зависит точность измерений. Чтобы пощупать все это, давайте сделаем простой вольтметр на 5в. Запускаем CVAVR, на вопрос запустить CodeWizardAVR кликаем "да" и переходим во вкладку ADC

Нам для нашего вольтметра нужно установить источник опорного напряжения на ножке AVCC (ножка питание АЦП ), частота преобразования 500 килогерц

Мы наши измерения с АЦП будем выводить на lcd-дисплей, для его инициализации переходим во вкладку LCD и устанавливаем все, как на скриншоте

Теперь все настройки выполнены, кликаем file->Generate. save and exit . Дописываем код, который сгенерировал CWAVR, и убираем в нём инициализации периферии МК, которые мы не используем, получается следующий код:

#include #include #include // Alphanumeric LCD Module functions #asm .equ __lcd_port=0x12 ;PORTD #endasm #include #define ADC_VREF_TYPE 0x40 // Read the AD conversion result unsigned int read_adc(unsigned char adc_input) { ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); // Delay needed for the stabilization of the ADC input voltage delay_us(10); // Start the AD conversion ADCSRA|=0x40; // Wait for the AD conversion to complete while ((ADCSRA & 0x10)==0); ADCSRA|=0x10; return ADCW; } void main(void) { char lcd_buffer; unsigned int u; // ADC initialization // ADC Clock frequency: 500,000 kHz // ADC Voltage Reference: AVCC pin ADMUX=ADC_VREF_TYPE & 0xff; ADCSRA=0x81; // LCD module initialization lcd_init(16); while (1) { /*так как АЦП у нас 10-битный, то максимальное число, которое вернет функция, read_adc() будет равно 1024, это число будет эквивалентом напряжения на входе adc0. Например, если read_adc() вернул 512, то это значит, что на вход adc0 мы подали половину опорного напряжения Чтобы вычислить реальное напряжение, нам нужно составить пропорцию опорное напряжение - 1024 искомое напряжение - adc У нас опорное напряжение = 5 Искомое напряжение = 5 * adc/1024, или Искомое напряжение = 0,005*adc для простоты переведём вольты в миливольты, домножив на 1000 Искомое напряжение = 0,005*adc*1000 */ u=read_adc(0) * 5;//вызываем функцию для измерения напряжения и передаем ей номер ножки, на которой нужно измерить напряжение lcd_clear(); //чистим дисплей перед выводом lcd_gotoxy(0,0); // перевод курсор в положение x=0 y=0 sprintf(lcd_buffer,"U = %i mv",u); // формируем строку для вывода lcd_puts(lcd_buffer); //выводим строку на дисплей delay_us(500); //делаем задержку 500 мл }; }

Программа готова, дело за схемой

Схема очень простая, на ней мы видим микроконтроллер atmega8 и lcd-дисплей знакосинтезирующий 16х2 (пример работы с lcd описан ). Наш простой вольтметр измеряет напряжения до 5 в. Как измерять напряжения больше 5 в Схема выполнена в Proteus, все необходимые файлы для этого урока находятся в архиве

Урок 22

Часть 2

Изучаем АЦП

Сегодня мы продолжаем изучать очень интересную технологию, а для микроконтроллера — периферию — аналго-цифровой преобразователь или как его называют АЦП . В нашего занятия мы познакомились, что такое вообще АЦП, также познакомились, как он организован в контроллере AVR, а также создали новый проект и настроили его.

Дальнейшая задача — реализация АЦП в нашем проекта.

Ну и чтобы нам данную задачу выполнить, нам нужны будут определённые функции для обращения к АЦП контроллера.

Для этого зайдём в файл adc.c и создадим функцию инициализацию нашего АЦП

#include "adc.h"

//—————————————-

void ADC_Init ( void )

{

}

Также создадим на данную функцию прототип в хедер-файле adc.h для видимости её из внешних модулей, а также заодно и посмотрим всё содержимое данного файла

#ifndef ADC_H_

#define ADC_H_

#include "main.h"

void ADC_Init ( void );

#endif /* ADC_H_ */

Продолжим теперь заполнять кодом тело данной функции. Так как в мы хорошенечко ознакомились с регистрами, нам это особого труда не составит.

Начнем с управляющего регистра

void ADC_Init ( void )

ADCSRA |= (1<< ADEN )

|(1<< ADPS2 )|(1<< ADPS1 )|(1<< ADPS0 ); //Делитель 128 = 64 кГц

Это не две строки, а одна, так писать в студии можно и даже нужно, так как код становится понятнее. А одна, потому что нет символа конца строки — точки с запятой.

Здесь мы включили бит ADEN , тем самым включили вообще модуль АЦП, а также установили делитель на 128, тем самым, помня то, что частота тактирования у нас 8 МГц и разделив её значение на 128, мы получили работу АЦП на частоте 64 кГц, что вполне нормально и надёжно, до 200 граничных далеко. Как видим, ничего сложного в инициализации регистра нет.

Также ещё в данной функции нам необходимо выбрать канал, к которому мы будем подключать измеряемое напряжение. У нас судя по схеме канал 0, поэтому соответствующий MUX мы и включим. А соответствующий MUX — это все нули в данных битах, поэтому ничего-то и включать не надо. Но мы ещё помним, что в регистре ADMUX у нас помимо всего прочего есть и управляющие биты, а именно биты REFS1 и REFS0, с помощью которых мы установим в качестве источника опорного напряжения внутренний источник на 2,56 вольта, а ADLAR мы не используем

ADCSRA |= (1<< ADEN ) // Разрешение использования АЦП

|(1<< ADPS2 )|(1<< ADPS1 )|(1<< ADPS0 ); //Делитель 128 = 64 кГц

ADMUX |= (1<< REFS1 )|(1<< REFS0 ); //Внутренний Источник ОН 2,56в, вход ADC0

Ну вот, в принципе, и вся инициализация.

Вызовем эту функцию в главном модуле программы в функции main() где-нибудь вот тут

LCD_ini (); //Инициализируем дисплей

ADC_Init (); //Инициализируем АЦП

clearlcd (); //Очистим дисплей

Ну и также нам нужна будет в модуле adc.c ещё одна функция, которая будет инициализировать непосредственно начало процесса аналого-цифрового преобразования в нашем ADC

unsigned int ADC_convert ( void )

{

}

Само собой нужен будет в хедер-файле прототип на неё

void ADC_Init ( void );

unsigned int ADC_convert ( void );

Данная функция нам вернёт значение из регистровой пары ADC , которая и будет содержать величину нашего электрического сигнала в единицах, выражающих отношение измеряемого сигнала к опорному и умноженных на количество возможных отрезков, которых у нас 1023, ну или 1024. Насчёт этого ходят много слухов, но в технической документации на контроллер в расчетной формуле содержится именно 1024. Но это нам не так важно.

Включим преобразование с помощью бита ADSC

unsigned int ADC_convert ( void )

ADCSRA |= (1<< ADSC ); //Начинаем преобразование

Теперь нам надо как-то отследить тот момент, когда данное преобразование закончится. А делается это достаточно легко с помощью мониторинга того же бита ADSC, который по окончании процесса преобразования сам сбрасывается в 0 (When the conversion is complete, it returns to zero). Отслеживается данный бит с помощью условного цикла

ADCSRA |= (1<< ADSC ); //Начинаем преобразование

while (( ADCSRA & (1<< ADSC )));

Ну и по окончании вернём результат в виде беззнаковой величины

while (( ADCSRA & (1<< ADSC ))); //проверим закончилось ли аналого-цифровое преобразование

return ( unsigned int ) ADC ;

Вернёмся теперь в нашу главную функцию main() и создадим там локальную переменную для хранения результата преобразования для дальнейшей с ним работы

int main ( void )

unsigned int adc_value ;

Вызовем функцию преобразования, которая нам положит в нашу переменную результат преобразования

while (1)

adc_value = ADC_convert (); //Вызовем преобразование

Setpos (0,0);

Давайте сначала отобразим данную сырую величину, хотя бы посмотрим, что в ней есть. За основу мы пока возьмём код из наших часов, функция sprintf на помощь придёт в более поздних занятиях, время её пока не пришло и нам надо вообще понять, как преобразовываются символы. Это нам ой как пригодится в программировании светодиодных индикаторов

Setpos (0,0);

sendcharlcd ( adc_value /1000+0x30);

sendcharlcd (( adc_value %1000)/100+0x30); //Преобразуем число в код числа

sendcharlcd (( adc_value %100)/10+0x30); //Преобразуем число в код числа

sendcharlcd ( adc_value %10+0x30); //Преобразуем число в код числа

Delay_ms (500);

Здесь мы разбиваем по цифрам четырёхзначную величину.

Теперь мы соберём код, прошьём контроллер и посмотрим наши результаты, покрутив резистор на 10 килоом

Вот так вот оно всё и работает.

Теперь давайте ещё на дисплее попробуем отобразить всё в вольтах, чтобы определить, какое у нас всё-таки напряжение на центральном контакте нашего переменного резистора. Для этого создадим переменную плавающего типа

unsigned int adc_value ;

float n ;

Также забудем про существование функции sprintf и попробуем получить плавающий тип на дисплее программным путём. Для этого сначала преобразуем наш сырой результат в плавающий тип явным образом, то есть та же цифра будет, но только тип другой, не забыв, конечно, перед этим поставить курсор в нужное место на дисплее. Для этого существует понятие в языке СИ явного преобразования типов и разделим преобразованный результат на 400

sendcharlcd ( adc_value %10+0x30); //Преобразуем число в код числа

setpos (8,0);

n = ( float ) adc_value / 400;

Тут, конечно, возникает вопрос, а почему мы делим именно на 400. А вот почему.

Это ничто иное как 1024, разделённое на 2,56, то есть на наше опорное напряжение. Видимо, не зря разработчики контроллера выбрали именно такую величину опроного напряжения, чтобы всё делилось без остатка. Почему мы именно такое деление применяем. А потому что у нас есть формула в технической документации

Вот поэтому и мы и вычислили её самую последнюю часть. Осталось теперь лишь только перевернуть ещё наоборот, выразив отсюда входное напряжение, так как неизвестное у нас именно оно. И мы получим, что оно будет у нас равно ADC, делённому на 400, что мы, собственно и сделали выше в коде. Я думаю, всё предельно стало теперь всем понятно.

Осталось самое интересное — отобразить всё это на экран, зная, то.что мы не можем работать с дисплеем с плавающим типом. А оказывается всё просто. Всё решается вот таким кусочком кода

N = ( float ) adc_value / 400;

sendcharlcd (( unsigned char ) n +0x30); //Преобразуем число в код числа

sendcharlcd ("."); //Преобразуем число в код числа

sendcharlcd ((( unsigned char ) ( n *10))%10 +0x30); //Преобразуем число в код числа

sendcharlcd ((( unsigned char ) ( n *100))%10 +0x30); //Преобразуем число в код числа

Delay_ms (500);

Не пугайтесь, сейчас мы всё тут разрулим.

Сначала мы обратным преобразованием типов отсекаем вооще всю дробь и, зная, что дальше 9 мы не уйдём и у нас будет только одна цифра, да мы даже и дальше 2 тут не уйдём, у нас максимум 2,56, мы просто отображаем данную цифру.

Потом мы умножаем наш результат, преобразованный к плавающему типу на 10, тем самым, передвигаем запятую на один разряд в нём вправо и, преобразовав результат вычисления обратно в целочисленный тип, берём из него известным образом младшую цифру и отображаем её на дисплее после запятой.

Подобным образом поступим с цифрой следующей, только здесь мы умножаем результат на 100, что переносит в единицы уже вторую цифру после запятой. Можно продолжить дальше, но нам и двух цифр хватит.

Вот и всё!

Собираем код, прошиваем контроллер и смотрим наши интересные результаты, крутя наш резистор

Post Views: 6 917

Аналого-цифровые преобразователи (АЦП) являются устройствами, которые принимают входные аналоговые сигналы и генерируют соответствующие им цифровые сигналы, пригодные для обработки микропроцессорами и другими цифровыми устройствами. АЦП входит во многие современные модели МК AVR , он многоканальный. Обычно число каналов равно 8, но в разных моделях оно может варьировать от 4 каналов в младших моделях семейства Tiny, 6 в ATmega8, до 16 каналов в ATmega2560.

Многоканальность означает, что на входе единственного модуля АЦП установлен аналоговый мультиплексор, который может подключать этот вход к различным выводам МК для осуществления измерений нескольких независимых аналоговых величин с разнесением по времени. Входы мультиплексора могут работать по отдельности (в несимметричном режиме для измерения напряжения относительно "земли") или (в некоторых моделях) объединяться в пары для измерения дифференциальных сигналов. Иногда АЦП дополнительно снабжается усилителем напряжения с фиксированными значениями коэффициента усиления 10 и 200.

Сам АЦП представляет собой преобразователь последовательного приближения с устройством выборки-хранения и фиксированным числом тактов преобразования, равным 13 (или 14 для дифференциального входа; первое преобразование после включения потребует 25 тактов для инициализации АЦП ). Тактовая частота формируется аналогично тому, как это делается для таймеров- с помощью специального предделителя тактовой частоты МК, который может иметь коэффициенты деления от 1 до 128. Но в отличие от таймеров, выбор тактовой частоты АЦП не совсем произволен, т. к. быстродействие аналоговых компонентов ограничено. Поэтому коэффициент деления следует выбирать таким, чтобы при заданном "кварце" тактовая частота АЦП укладывалась в рекомендованный диапазон 50-200 кГц (т. е. максимум около 15 тыс. измерений в секунду). Увеличение частоты выборки допустимо, если не требуется достижение наивысшей точности преобразования.

Разрешающая способность АЦП в МК AVR - 10 двоичных разрядов, чего для большинства типовых применений достаточно. Абсолютная погрешность преобразования зависит от ряда факторов и в идеальном случае не превышает ±2 младших разрядов, что соответствует общей точности измерения примерно 8 двоичных разрядов. Для достижения этого результата необходимо принимать специальные меры: не только "вгонять" тактовую частоту в рекомендованный диапазон, но и снижать по максимуму интенсивность цифровых шумов. Для этого рекомендуется, как минимум, не использовать оставшиеся выводы того же порта, к которому подключен АЦП, для обработки цифровых сигналов, правильно разводить платы, а как максимум - дополнительно к тому еще и включать специальный режим ADC Noise Reduction .

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

ADCSR

Режим непрерывных измерений активизируется установкой бита ADFR (бит 5) этого же регистра. В ряде моделей Mega этот бит носит наименование ADATE , и управление режимом работы производится сложнее: там добавляются несколько режимов запуска через различные прерывания (в т. ч. прерывание от компаратора, при наступлении различных событий от таймера и т. п.), и выбирать их следует, задавая биты ADTS регистра SFIOR , а установка бита ADATE разрешает запуск АЦП по этим событиям.

Разряд Название Описание
5 ADFR(ADATE) Выбор режима работы АЦП

Так как нулевые значения всех битов ADTS (по умолчанию) означают режим непрерывного преобразования, то в случае, когда вы их значения не трогали, функции битов ADATE и ADFR в других моделях будут совпадать.

ADTS2 ADTS1 ADTS0 Источник стартового сигнала
0 0 0 Режим непрерывного преобразования
0 0 1 Прерывание от аналогового компаратора
0 1 0 Внешнее прерывание INT0
0 1 1 Прерывание по событию "Совпадение" таймера/счетчика Т0
1 0 0 Прерывание по переполнению таймера/счетчика Т0
1 0 1 Прерывание по событию "Совпадение" таймера/счетчика Т1
1 1 0 Прерывание по переполнению таймера/счетчика Т1
1 1 1 Прерывание по событию "Захват" таймера/счетчика Т1

Если выбран режим запуска не от внешнего источника, то преобразование запускается установкой бита ADSС (бит 6). При непрерывном режиме установка этого бита запустит первое преобразование, затем они будут автоматически повторяться. В режиме однократного преобразования, а также независимо от установленного режима при запуске через прерывания (в тех моделях, где это возможно) установка бита ADSС просто запускает одно преобразование. При наступлении прерывания, запускающего преобразование, бит ADSС устанавливается аппаратно. Отметим, что преобразование начинается по-фронту первого тактового импульса (тактового сигнала АЦП, а не самого контроллера!) после установки ADSС . По окончании любого преобразования (и в одиночном, и в непрерывном режиме) устанавливается бит ADIF (бит 4. флаг прерывания). Разрешение прерывания АЦП осуществляется установкой бита ADIE (бит 3) все того же регистра ADCSR/ADCSRA .

Для работы с АЦП необходимо еще установить его тактовую частоту. Это делается тремя младшими битами регистра ADCSR/ADCSRA под названием ADPS0..2. Коэффициент деления частоты тактового генератора МК устанавливается по степеням двойки, все нули в этих трех битах соответствуют коэффициенту 2, все единицы - 128. Оптимальная частота преобразования лежит в диапазоне 50-200 кГц, так что, например, для тактовой частоты МК, равной 4 МГц, коэффициент может иметь значение только 32 (состояние битов ADPS0..2 = 101, частота 125 кГц) или 64 (состояние битов ADPS0..2 = 110, частота 62,5 кГц). При тактовой частоте 16 МГц в допустимый диапазон укладывается только коэффициент 128.

ADPS2 ADPS1 ADPS0 Коэффициент деления
0 0 0 2
0 0 1 2
0 1 0 4
0 1 1 8
1 0 0 16
1 0 1 32
1 1 0 64
1 1 1 128

Ниже приведена таблица с описанием регистра ADMUX.



Выборка источника опорного напряжения производится битами REFS1..0 регистра ADMUX (старшие биты 7 и 6), причем их нулевое значение (по умолчанию) соответствует внешнему источнику. Напряжение этого внешнего источника может лежать в пределах от 2 В до напряжения питания аналоговой части AVcc (а оно, в свою очередь, не должно отличаться от питания цифровой части более чем на 0,3 В в большую или меньшую сторону). Можно выбрать в качестве опорного и питание самой аналоговой части, причем двояким способом: либо просто соединить выводы AREF и AVcc микросхемы, либо установить биты REFS1..0 в состояние 01 (тогда соединение осуществляется внутренними схемами, но заметим, что внешний опорный источник при этом должен быть отключен). Предусмотрен и встроенный источник (задается REFS1..0 в состоянии 11, при этом к выводу AREF рекомендуется подключать фильтрующий конденсатор), имеющий номинальное напряжение 2,56В с большим разбросом от 2,4 до 2,7 В.

REFS1 REFS0 Источник опорного напряжения
0 0 Внешний ИОН, подключенный к выводу AREF, внутренний ИОН отключен
0 1 Напряжение питания AVcc*
1 0 Зарезервировано
1 1 Внутренний ИОН напряжением 2,56V, подключенный к ввыводу AREF*
*Если к выводу AREF подключен источник напряжения, данные варианты использоваться не могут

Результат преобразования АЦП оказывается в регистрах ADCH:ADCL . Поскольку результат 10-разрядный, то по умолчанию старшие 6 битов в регистре ADCH оказываются равными нулю. Чтение этих регистров производится, начиная с младшего ADCL , после чего регистр ADCH блокируется, пока не будет прочитан. Следовательно, даже если момент между чтением регистров попал на фронт 14 (15) такта АЦП, когда данные в них должны меняться, значения прочитанной пары будут соответствовать друг другу, пусть и результат этого преобразования пропадет. В противоположном порядке читать эти регистры не рекомендуется. Но бит ADLAR (бит 5 регистра ADMUX ) предоставляет интересную возможность: если его установить в 1, то результат преобразования в регистрах ADCH:ADCL выравнивается влево: бит 9 результата окажется в старшем бите ADCH , а незначащими будут младшие 6 битов регистра ADCL . В этом случае, если хватает 8-разрядного разрешения результата, можно прочесть только значение ADCH .

class="eliadunit">

Выбор каналов и режимов их взаимодействия в АЦП производится битами MUX0..3 в регистре ADMUX . Их значения выбирают нужный канал в обычном (недифференциальном) режиме, когда измеряемое напряжение отсчитывается от "земли". Последние два значения этих битов для семейства Mega (11110 и 11111 в большинстве моделей или 1110 и 1111 для ATmega8) выбирают режимы, когда вход АЦП подсоединяется к опорному источнику компаратора (1,22 В) или к "земле" соответственно, что может использоваться для автокалибровки устройства.

Управление входным мультиплексором в моделях Atmega8x

MUX3-MUX0 Несимметричный вход
0000 ADC0
0001 ADC1
0010 ADC2
0011 ADC3
0100 ADC4*
0101 ADC5*
0110 ADC6**
0111 ADC7**
1000-1101 Зарезервировано
1110 1,22V
1111 0V(GND)

*8-ми разрядное преобразование

**Имеются только в корпусах TQFP-32 и MLF-32.

Остальные комбинации разрядов MUX предназначены для установки различных дифференциальных режимов - в тех моделях, где они присутствуют, в других случаях эти биты зарезервированы (как в моделях Atmega8, ATmega163 и др.). В дифференциальном режиме АЦП измеряет напряжение между двумя выбранными выводами (например, между ADC0 и ADC1 ), причем не все выводы могут быть в таком режиме задействованы. В том числе дифференциальные входы АЦП можно подключать к одному и тому же входу для коррекции нуля. Дело в том, что в ряде моделей на входе АЦП имеется встроенный усилитель, с коэффициентом 1х, 10х и 200х (коэффициент выбирается теми же битами MUX0..4 ), и такой режим используется для его калибровки - в дальнейшем значение выхода при соединенных входах можно просто вычесть.

После завершения преобразования (при установке в «1» флага ADIF регистра ADCSR ) его результат сохраняется в регистре данных АЦП . Поскольку АЦП имеет 10 разрядов, этот регистр физически размещен в двух регистрах ввода/вывода ADCH:ADCL , доступных только для чтения. По умолчанию результат преобразования выравнивается вправо (старшие 6 разрядов регистра ADCH - незначащие). Однако он может выравниваться и влево (младшие 6 разрядов регистра ADCL - незначащие). Для управления выравниванием результата преобразования служит разряд ADLAR регистра ADMUX . Если этот разряд установлен в «1», результат преобразования выравнивается по левой границе 16-разрядного слова, если сброшен в «0» - по правой границе.

Обращение к регистрам ADCH и ADCL для получения результата преобразования должно выполняться в определенной последовательности: сначала необходимо прочитать регистр ADCL , а затем ADCH . Это требование связано с тем, что после обращения к регистру ADCL процессор блокирует доступ к регистрам данных со стороны АЦП до тех пор, пока не будет прочитан регистр ADCH. Благодаря этому можно быть уверенным, что при чтении регистров в них будут находиться составляющие одного и того же результата. Соответственно, если очередное преобразование завершится до обращения к регистру ADCH , результат преобразования будет потерян. С другой стороны, если результат преобразования выравнивается влево и достаточно точности 8-разрядного значения, для получения результата можно прочитать только содержимое регистра ADCH .

Для недифференциального режима АЦП, когда напряжение отсчитывается от "земли", результат преобразования определяется формулой:

Ка = 1024Uвх/Uref

Где Ка - значение выходного кода АЦП, Uвх и Uref - входное и опорное напряжения.

Дифференциальному измерению соответствует такая формула:

Ка = 512(Upos - Uneg)/Uref

Где Upos и Uneg - напряжения на положительном и отрицательном входах соответственно. Если напряжение на отрицательном входе больше, чем на положительном, то результат в дифференциальном режиме становится отрицательным и выражается в дополнительном коде от $200 (-512) до $3FF (-1). Реальная точность преобразования в дифференциальном режиме равна 8 разрядам.

Делаем светодиодный индикатор напряжения

Для практического изучения АЦП напишем программу светодиодного индикатора напряжения. Как и в прошлых примерах будем использовать микроконтроллер Atmega8. Восемь индикаторов подключаем к порту D контроллера, это будет линейная шкала уровня сигнала от 0 до 5V. Входом АЦП у нас будет вывод PC0(ADC0), к которому через переменный резистор сопротивлением 10кОм подается напряжение. Схема устройства представлена ниже:

К точности АЦП в этом устройстве предъявляются наименьшие требования. Источником опорного напряжения служит напряжение питания микроконтроллера - 5 Вольт, для этого вывод AREF соединяем с выводом Vcc микроконтроллера, также поступаем с выводами питания аналоговой части AVcc и AGND , подключаем их к плюсу и минусу соответственно, в программе битами REFS1 и REFS0 задаем источник ИОН .

Режим индикации работает следующим образом: после окончания преобразования, которое работает в непрерывном режиме, считываем биты ADCH и ADCL . Это значение потом сравниваем с предварительно расчитанными константами. Если значение ADC больше константы загорается один светодиод, если значение ADC больше второй константы загораются уже два светодиода и т.д.

Константы высчитываются так: так как АЦП 10-ти битный, число 1024 раскладываем на 8 равных частей, а по формуле уже вычисляем эти значения в Вольтах.

1020...5V(приблизительно)

Полный код программы показан ниже. Частота тактового генератора контроллера 8MHz.

/*** Использование АЦП. Светодиодная шкала ***/ #include #include int main (void) { DDRD = 0xFF; PORTD = 0x00; /*** Настройка АЦП ***/ ADCSRA |= (1 << ADEN) // Включение АЦП |(1 << ADPS1)|(1 << ADPS0); // предделитель преобразователя на 8 ADMUX |= (0 << REFS1)|(0 << REFS0) // внешний ИОН |(0 << MUX0)|(0 << MUX1)|(0 << MUX2)|(0 << MUX3); // вход PC0 while(1) { unsigned int u; ADCSRA |= (1 << ADSC); // Начинаем преобразование while ((ADCSRA&(1 << ADIF))== 0); // Ждем флага окончания преобразования u = (ADCL|ADCH << 8); // Считываем ADC if (u > 128) // 0.625V PORTD = 0b00000001; else PORTD = 0b00000000; if (u > 256) // 1.25V PORTD = 0b00000011; if (u > 384) // 1.875V PORTD = 0b00000111; if (u > 512) // 2.5V PORTD = 0b00001111; if (u > 640) // 3.125V PORTD = 0b00011111; if (u > 768) // 3.75V PORTD = 0b00111111; if (u > 896) // 4.375V PORTD = 0b01111111; if (u > 1020) // 5V PORTD = 0b11111111; _delay_ms(30); } }

В следующем примере мы разберем принципы создания вольтметра 0-30V на микроконтроллере Atmega8.

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

АЦП входит во многие современные модели МК AVR, он многоканальный. Обычно число каналов равно 8, но в разных моделях оно может варьировать от 4 каналов в младших моделях семейства Tiny, 6 в ATmega8, до 16 каналов в ATmega2560. Многоканальность означает, что на входе единственного модуля АЦП установлен аналоговый мультиплексор, который может подключать этот вход к различным выводам МК для осуществления измерений нескольких независимых аналоговых величин с разнесением по времени. Входы мультиплексора могут работать по отдельности (в несимметричном режиме для измерения напряжения относительно "земли") или (в некоторых моделях) объединяться в пары для измерения дифференциальных сигналов. Иногда АЦП дополнительно снабжается усилителем напряжения с фиксированными значениями коэффициента усиления 10 и 200.

Сам АЦП представляет собой преобразователь последовательного приближения с устройством выборки-хранения и фиксированным числом тактов преобразования, равным 13 (или 14 для дифференциального входа; первое преобразование после включения потребует 25 тактов для инициализации АЦП). Тактовая частота формируется аналогично тому, как это делается для таймеров- с помощью специального предделителя тактовой частоты МК, который может иметь коэффициенты деления от 1 до 128. Но в отличие от таймеров, выбор тактовой частоты АЦП не совсем произволен, т. к. быстродействие аналоговых компонентов ограничено. Поэтому коэффициент деления следует выбирать таким, чтобы при заданном "кварце" тактовая частота АЦП укладывалась в рекомендованный диапазон 50-200 кГц (т. е. максимум около 15 тыс. измерений в секунду). Увеличение частоты выборки допустимо, если не требуется достижение наивысшей точности преобразования.

Разрешающая способность АЦП в МК AVR - 10 двоичных разрядов, чего для большинства типовых применений достаточно. Абсолютная погрешность преобразования зависит от ряда факторов и в идеальном случае не превышает ±2 младших разрядов, что соответствует общей точности измерения примерно 8 двоичных разрядов. Для достижения этого результата необходимо принимать специальные меры: не только "вгонять" тактовую частоту в рекомендованный диапазон, но и снижать по максимуму интенсивность цифровых шумов. Для этого рекомендуется, как минимум, не использовать оставшиеся выводы того же порта, к которому подключен АЦП, для обработки цифровых сигналов, правильно разводить платы, а как максимум - дополнительно к тому еще и включать специальный режим ADC Noise Reduction.

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

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

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

Режим непрерывных измерений активизируется установкой бита ADFR (бит 5) этого же регистра. В ряде моделей Mega этот бит носит наименование ADATE, и управление режимом работы производится сложнее: там добавляются несколько режимов запуска через различные прерывания (в т. ч. прерывание от компаратора, при наступлении различных событий от таймера и т. п.), и выбирать их следует, задавая биты ADTS регистра SFIOR, а установка бита ADATE разрешает запуск АЦП по этим событиям. Так как нулевые значения всех битов ADTS (по умолчанию) означают режим непрерывного преобразования, то в случае, когда вы их значения не трогали, функции битов ADATE и ADFR в других моделях будут совпадать.

Если выбран режим запуска не от внешнего источника, то преобразование запускается установкой бита ADTS (бит 6 того же регистра ADCSR/ADCSRA). При непрерывном режиме установка этого бита запустит первое преобразование, затем они будут автоматически повторяться. В режиме однократного преобразования, а также независимо от установленного режима при запуске через прерывания (в тех моделях, где это возможно) установка бита ADCS просто запускает одно преобразование. При наступлении прерывания, запускающего преобразование, бит ADCS устанавливается аппаратно. Отметим, что преобразование начинается по-фронту первого тактового импульса (тактового сигнала АЦП, а не самого контроллера!) после установки ADCS. По окончании любого преобразования (и в одиночном, и в непрерывном режиме) устанавливается бит ADIF (бит 4. флаг прерывания). Разрешение прерывания АЦП осуществляется установкой бита ADIE (бит 3) все того же регистра ADCSR/ADCSRA.

Для работы с АЦП необходимо еще установить его тактовую частоту. Это делается тремя младшими битами регистра ADCSR/ADCSRA под названием ADPS0..2. Коэффициент деления частоты тактового генератора МК устанавливается по степеням двойки, все нули в этих трех битах соответствуют коэффициенту 2, все единицы - 128. Оптимальная частота преобразования лежит в диапазоне 50-200 кГц, так что, например, для тактовой частоты МК, равной 4 МГц, коэффициент может иметь значение только 32 (состояние битов ADPS0..2 = 101, частота 125 кГц) или 64 (состояние битов ADPS0..2 = 110, частота 62,5 кГц). При тактовой частоте 16 МГц в допустимый диапазон укладывается только коэффициент 128.

Выборка источника опорного напряжения производится битами REFS1..0 регистра ADMUX (старшие биты 7 и 6), причем их нулевое значение (по умолчанию) соответствует внешнему источнику. Напряжение этого внешнего источника может лежать в пределах от 2 В до напряжения питания аналоговой части AVcc (а оно, в свою очередь, не должно отличаться от питания цифровой части более чем на 0,3 В в большую или меньшую сторону). Можно выбрать в качестве опорного и питание самой аналоговой части, причем двояким способом: либо просто соединить выводы AREF и AVcc микросхемы, либо установить биты REFS1..0 в состояние 01 (тогда соединение осуществляется внутренними схемами, но заметим, что внешний опорный источник при этом должен быть отключен). Предусмотрен и встроенный источник (задается REFS1..0 в состоянии 11, при этом к выводу AREF рекомендуется подключать фильтрующий конденсатор), имеющий номинальное напряжение 2,56В с большим разбросом от 2,4 до 2,7 В.

*****REFS*******

Результат преобразования АЦП оказывается в регистрах ADCH:ADCL. Поскольку результат 10-разрядный, то по умолчанию старшие 6 битов в регистре ADCH оказываются равными нулю. Чтение этих регистров производится, начиная с младшего ADCL, после чего регистр ADCH блокируется, пока не будет прочитан. Следовательно, даже если момент между чтением регистров попал на фронт 14 (15) такта АЦП, когда данные в них должны меняться, значения прочитанной пары будут соответствовать друг другу, пусть и результат этого преобразования пропадет. В противоположном порядке читать эти регистры не рекомендуется. Но бит ADLAR (бит 5 регистра ADMUX) предоставляет интересную возможность: если его установить в 1, то результат преобразования в регистрах ADCH:ADCL выравнивается влево: бит 9 результата окажется в старшем бите ADCH, а незначащими будут младшие 6 битов регистра ADCL. В этом случае, если хватает 8-разрядного разрешения результата, можно прочесть только значение ADCH.

Выбор каналов и режимов их взаимодействия в АЦП производится битами MUX0..3 в регистре ADMUX. Их значения выбирают нужный канал в обычном (недифференциальном) режиме, когда измеряемое напряжение отсчитывается от "земли". Последние два значения этих битов для семейства Mega (11110 и 11111 в большинстве моделей или 1110 и 1111 для ATmega8) выбирают режимы, когда вход АЦП подсоединяется к опорному источнику компаратора (1,22 В) или к "земле" соответственно, что может использоваться для автокалибровки устройства.

Остальные комбинации разрядов MUX предназначены для установки различных дифференциальных режимов - в тех моделях, где они присутствуют, в других случаях эти биты зарезервированы (как в моделях Atmega8, ATmegal63 и др.). В дифференциальном режиме АЦП измеряет напряжение между двумя выбранными выводами (например, между ADC0 и ADC1), причем не все выводы могут быть в таком режиме задействованы. В том числе дифференциальные входы АЦП можно подключать к одному и тому же входу для коррекции нуля. Дело в том, что в ряде моделей на входе АЦП имеется встроенный усилитель, с коэффициентом 1х, 10х и 200х (коэффициент выбирается теми же битами MUX0..4), и такой режим используется для его калибровки - в дальнейшем значение выхода при соединенных входах можно просто вычесть.

Для недифференциального режима АЦП, когда напряжение отсчитывается от "земли", результат преобразования определяется формулой: Ка = 1024Uвх/Uref, где Ка - значение выходного кода АЦП, Uвх и Uref - входное и опорное напряжения. Дифференциальному измерению соответствует такая формула: Ка = 512(Upos - Uneg)/Uref, где Upos и Uneg - напряжения на положительном и отрицательном входах соответственно. Если напряжение на отрицательном входе больше, чем на положительном, то результат в дифференциальном режиме становится отрицательным и выражается в дополнительном коде от $200 (-512) до $3FF (-1). Реальная точность преобразования в дифференциальном режиме равна 8 разрядам.

Похожие статьи