24 | 06 | 2017
Главное меню
Смотри
Статистика
Пользователи : 1
Статьи : 2732
Просмотры материалов : 7439717

Посетители
Рейтинг@Mail.ru
Online
  • [Bot]
  • [Google]
  • [Yahoo]
Сейчас на сайте:
  • 38 гостей
  • 3 роботов
Новые пользователи:
  • Administrator
Всего пользователей: 1
RSS
Подписка на новости
АЦП микроконтроллера ATmega8, цифровой вольтметр PDF Печать E-mail
Автор: Administrator   
21.11.2011 20:24

АЦП микроконтроллера ATmega8, цифровой вольтметр

АЦП – аналогово-цифровой преобразователь (ADC- Analog-to-Digital Converter). Преобразует некий аналоговый сигнал в цифровой. Битность АЦП определяет точность преобразования сигнала. Время преобразования – соответственно скорость работы АЦП. АЦП встроен во многих микроконтроллерах семейства AVR и упрощает использование микроконтроллера во всяких схемах регулирования, где требуется оцифровывать некий аналоговый сигнал.


Рассмотрим принцип работы АЦП. Для преобразования нужен источник опорного напряжения и собственно напряжение, которое мы хотим оцифровать (напряжение, которое преобразуется должно быть меньше опорного). Также нужен регистр, где будет храниться преобразованное значение, назовем его Z. Входное напряжение = Опорное напряжение*Z/2^N, где N – битность АЦП. Условимся, что этот регистр, как у ATmega8, 10-ти битный. Преобразование в нашем случае проходит в 10 стадий. Старший бит Z9 выставляется в единицу. Далее генерируется напряжение (Опорное напряжение*Z/1024), это напряжение, с помощью аналогового компаратора сравнивается с входным, если оно больше входного, бит Z9 становиться равным нулю, а если меньше – остается единицей. Далее переходим к биту Z8 и вышеописанным способом получаем его значения. После того, как вычисление регистра Z окончено, выставляется некий флаг, который сигнализирует, что преобразование закончено и можно считывать полученное значение. На точность преобразования могут очень сильно влиять наводки и помехи, а также скорость преобразования. Чем медленнее происходит преобразования – тем оно точней. С наводками и помехами следует бороться с помощью индуктивности и емкости, как советует производитель в даташите:

01

В микроконтроллерах AVR как источник опорного напряжения может использоваться вывод AREF, или внутренние источники 2,56В или 1,23В. Также источником опорного напряжения может быть напряжение питания. В некоторых корпусах и моделях микроконтроллеров есть отдельные выводы для питания АЦП: AVCC и AGND. Выводы ADCn – каналы АЦП. С какого канала будет оцифровываться сигнал можно выбрать с помощью мультиплексора.
Теперь продемонстрируем примером сказанное выше. Соорудим макет, который будет работать как вольтметр с цифровой шкалой. Условимся, что максимальное измеряемое напряжение будет 10В. Также пусть наш макет выводит на ЖКИ содержимое регистра ADC.

02

Для увеличения кликните на схему.

Обвязка микроконтроллера и ЖКИ WH1602A стандартна. X1 – кварцевый резонатор на 4 Мгц, конденсаторы С1,С2 – 18-20 пФ. R1-C7 цепочка на выводе reset по 10 кОм и 0,1 мкФ соответственно. Сигнальный светодиод D1 и ограничивающий резистор R2 200 Ом и R3 – 20 Ом. Регулировка контраста ЖКИ – VR1 на 10 кОм. Источник опорного напряжения мы будем использовать встроенный на 2,56В. С помощью делителя R4-R5 мы добьемся максимального напряжения 2,5В на входе PC0, при напряжении на щупе 10В. R4 – 3 кОм, R5 – 1 кОм, в их номиналу нужно отнестись тщательно, но если не возможности подобрать точно такие, можно сделать любой резистивный делитель 1:4 и программно подкорректировать показания, если это потребуется. Дроссель на 10мкГн и конденсатор на 0,1 мкФ для устранения шумов и наводок на АЦП на схеме не показан. Их наличие подразумевается само собой, если используется АЦП. Теперь дело за программой:

001.#include <avr io="" h="">
002.
003.#define RS 2 //RS=PD2
004.#define E 3 //E=PD3
005.
006.#define TIME 10 //Константа временной задержки для ЖКИ
007.//Частота тактирование МК - 4Мгц
008.
009.#define R_division 3.837524 //=R4/R5 константа
010.
011.unsigned int u=0; //Глобальная переменная с содержимым преобразования
012.
013.void pause (unsigned int a)
014.{
015.unsigned int i;
016.for (i=a;i>0;i--);
017.}
018.
019.void lcd_com (unsigned char lcd) //Передача команды ЖКИ
020.{
021.unsigned char temp;
022.
023.temp=(lcd&~(1<<rs))|(1 e="" rs="0" br=""> PORTD=temp; //Выводим на portD старшую тетраду команды, сигналы RS, E
024.asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации
025.PORTD=temp&~(1<<e); br="">
026.temp=((lcd*16)&~(1<<rs))|(1 e="" rs="0" br=""> PORTD=temp; //Выводим на portD младшую тетраду команды, сигналы RS, E
027.asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации
028.PORTD=temp&~(1<<e); br="">
029.pause(10*TIME); //Пауза для выполнения команды
030.}
031.
032.void lcd_dat (unsigned char lcd) //Запись данных в ЖКИ
033.{
034.unsigned char temp;
035.
036.temp=(lcd|(1<<rs))|(1 e="" rs="1" br=""> PORTD=temp; //Выводим на portD старшую тетраду данных, сигналы RS, E
037.asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации
038.PORTD=temp&~(1<<e); br="">
039.temp=((lcd*16)|(1<<rs))|(1 e="" rs="1" br=""> PORTD=temp; //Выводим на portD младшую тетраду данных, сигналы RS, E
040.asm("nop"); //Небольшая задержка в 1 такт МК, для стабилизации
041.PORTD=temp&~(1<<e); br="">
042.pause(TIME); //Пауза для вывода данных
043.}
044.
045.void lcd_init (void) //Иниализация ЖКИ
046.{
047.lcd_com(0x2c); //4-проводный интерфейс, 5x8 размер символа
048.pause(100*TIME);
049.lcd_com(0x0c); //Показать изображение, курсор не показывать
050.pause(100*TIME);
051.lcd_com(0x01); //Очистить DDRAM и установить курсор на 0x00
052.pause (100*TIME);
053.}
054.
055.unsigned int getADC(void) //Считывание АЦП
056.{ unsigned int v;
057.
058.ADCSRA|=(1<<adsc); br="">
059.while ((ADCSRA&_BV(ADIF))==0x00) //Дождатся окончания преобразования
060.;
061.
062.v=(ADCL|ADCH<<8); br=""><!--8);--> return v;
063.}
064.
065.void write_data (unsigned int u)
066.{ unsigned char i;
067.double voltage=0;
068.
069.lcd_com(0x84); //Вывод регистра ADC на ЖКИ
070.for (i=0;i<10;i++) br=""><!--10;i++)--> if ((u&_BV(9-i))==0x00) lcd_dat (0x30);
071.else lcd_dat (0x31);
072.
073.lcd_com(0xc2);
074.voltage= R_division*2.56*u*1.024; //Расчет напряжения
075.
076.i=voltage/10000; //Выведение напряжения на ЖКИ
077.voltage=voltage-i*10000;
078.if (i!=0) lcd_dat(0x30+i);
079.
080.i=voltage/1000;
081.voltage=voltage-i*1000;
082.lcd_dat(0x30+i);
083.
084.lcd_dat(',');
085.
086.i=voltage/100;
087.voltage=voltage-i*100;
088.lcd_dat(0x30+i);
089.
090.i=voltage/10;
091.voltage=voltage-i*10;
092.lcd_dat(0x30+i);
093.
094.lcd_dat('v');
095.}
096.
097.int main(void)
098.{
099.DDRD=0xfc;
100.
101.pause(3000); //Задержка для включения ЖКИ
102.lcd_init(); //Инициализация ЖКИ
103.
104.lcd_dat('A'); //Пишем "ADC=" и "U=" на ЖКИ
105.lcd_dat('D');
106.lcd_dat('C');
107.lcd_dat('=');
108.lcd_com(0xc0);
109.lcd_dat('U');
110.lcd_dat('=');
111.
112.ADCSRA=(1<<aden)|(1 adps1="" 1="" adps0="" br=""></aden)|(1> //Включаем АЦП, тактовая частота бреобразователя =/8 от тактовой микроконтроллера
113.ADMUX=(1<<refs1)|(1 refs0="" 0="" mux0="" mux1="" mux2="" mux3="" br=""></refs1)|(1> //Внутренний источник опорного напряжения Vref=2,56, входом АЦП является PC0
114.
115.while(1)
116.{
117.u=getADC(); //Считываем данные
118.write_data(u); //Выводим их на ЖКИ
119.pause(30000);
120.}
121.
122.return 1;
123.}</adsc);></e);></rs))|(1></e);></rs))|(1></e);></rs))|(1></e);></rs))|(1></avr>

Программа проста. В начале мы инициализируем порты ввода/вывода. Для того, чтобы служить входом АЦП, пин PC0 должен работать на вход. Далее проводим инициализацию ЖКИ и АЦП. Инициализация АЦП заключается в его включении битом ADEN в регистре ADCSRA. И выбора частоты преобразования битами ADPS2, ADPS1, ADPS0 в том же регистре. Также выбираем источник опорного напряжения, биты REFS1 REFS0 в регистре ADMUX и вход АЦП: биты MUX0,MUX1,MUX2, MUX3 (в нашем случаем входом АЦП является PC0, поэтому MUX0.3=0). Далее, в вечном цикле, начинаем преобразования установкой бита ADSC в регистре ADCSRA. Дожидаемся окончания преобразования (бит ADIF в ADCSRA становиться равным 1). Далее вынимаем данные из регистра ADC и выводим их на ЖКИ. Вынимать данные из ADC нужно в такой последовательности: v=(ADCL+ADCH*256); если использовать v=(ADCH*256+ADCL); - в упор не работает. Также есть хитрость, чтобы не работать с дробными числами. Когда производиться вычисления входного напряжения в вольтах. Мы просто будем хранить наше напряжения в милливольтах. Например, значение переменной voltage 4234 означает, что мы имеем 4,234 вольта. Вообще операции с дробными числами кушают очень много памяти микроконтроллера (наша прошивка вольтметра весит чуть больше 4 килобай, это половина памяти программ ATmega8!), их рекомендуется использовать только при особой необходимости. Вычисления входного напряжения в милливольтах просто: voltage=R_division*2.56*u*1.024;
Здесь R_division – коефициент резистивного делителя R4-R5. Так, как реальный коефициент делителя может отличаться от расчетного, то наш вольтметр будет врать. Но подкорректировать это просто. С помощью тестера меряем некое напряжение, получаем X вольт, а наш вольтметр пускай показывает Y вольт. Тогда R_division = 4*X/Y, если Y больше X и 4*Y/X если X больше Y. На этом настройка вольтметра завершена, и им можно пользоваться.

Также можно доработать свой блок питания. Вставив в него цифровой вольтметр-амперметр на ЖКИ и защиту от перегрузки (для измерения тока нам понадобиться мощный шунт сопротивлением порядка 1 Ом).

3

 

 

 
Для тебя