Аналогово-цифров преобразувател за AVR микроконтролери. Общият принцип на работа на ADC на микроконтролера е да преобразува кода в истинска стойност

Всеки микроконтролер комуникира с периферни устройства, използвайки I/O портове. В същото време той е в състояние да "възприема" само цифрови сигнали - логическа нула или единица. Например, за ATmega8 MK, със захранващо напрежение от 5 V, логическата нула е напрежение в диапазона от 0 до 1,3 V, а единицата е от 1,8 до 5 V. Доста често в радиолюбителската практика има необходимост от измерване на напрежението, което може да получи всяка стойност в диапазона от нула до нивото на захранващото напрежение. За тези задачи всички AVR микроконтролери включват аналогово-цифров преобразувател.


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

Резолюцияили резолюция - тази характеристика на ADC помага да се разграничат две стойности на входния сигнал. Дефинира се като реципрочна стойност на най-големия брой ADC кодови комбинации на изхода. Нашият MK има десетбитов ADC, така че максималният брой възможни кодови комбинации ще бъде равен на 2 10 = 1024 , а неговата разделителна способност е равна на 1/1024 от пълната скала на допустимите входни напрежения.

За правилната работа на ADC е необходим източник на референтно напрежение (VS). Във връзка с което АЦП измерва сигналите, постъпващи на неговия вход. AVR MCU ви позволяват да използвате захранващото напрежение като ION, техният вътрешен референтен източник е 2,56 V, а изходното напрежение е AREF (външен ION).

Тъй като нашата верига се захранва с 5 V, тогава 1/1024 от пълната скала ще бъде 0,0048 V или около 5 mV. С тази стъпка ADC ще определи нивото на входното напрежение. Ако двете най-близки стойности на входа на преобразувателя се различават една от друга с по-малко от 5 mV, ADC ще ги счита за равни. На практика разделителната способност на всеки ADC е ограничена от шума.

Абсолютна точност на ADCтова е отклонението на реалната трансформация от идеалната. Това е комбиниран резултат от няколко грешки на конвертора. Математически описан от гледна точка на броя на най-малко значимите битове (LSB). Максималната абсолютна грешка на Atmega8 ADC е 1,5 LSB. За нашия случай абсолютната точност е 2 × 5 mV = ±10 mV

Ограничете честотата на дискретизациятова е скоростта на ADC, която се измерва в Hz или брой проби в секунда (SPS - samples per second). За AVR MK е 15 kSPS (кило проби в секунда).

Стандартен 16x2 LCD дисплей е свързан към порт B - AVR MK. Изводите AREF и AVCC са свързани към 5V захранващо напрежение. Това е ION. В порт С на Atmega, контакт с волтметър и променливо съпротивление е свързан към нулевия разряд, за да промени нивото на входното напрежение. Нашата задача в този урок е следната: Искаме да покажем стойност на напрежението на LCD екрана, подобно на измерването на волтметър.

Нека да преминем към програмирането и да стартираме нов проект в програмата. В настройките на чипа изберете Atmega8 MK, задайте честотата на 4.00000000 MHz. (вижте пример с мигащ светодиод). Отидете в раздела LCD и изберете PORTB. И запазваме проекта, наречен ADC (съкращение от ADC на чужд език). Първо трябва да добавите две директиви за препроцесор за работа с текст и забавяне. За да направите това, след директивата LCD вмъкнете два реда.

#включи
#включи

Първият ред е необходим за закъснения, а вторият за работа на дисплея с текст. След това създаваме масив за междинно съхранение на форматиран текст. След текста в кода пишем.

След това, след като отворим основната функция, трябва да добавим още две променливи. Единият от тях се използва за съхраняване на стойността след вземане на проби, а другият се използва за съхраняване на стойността, показана на екрана.

Сега нека конфигурираме самия ADC. За да направите това, след като настроите компаратора, ще напишем следното.

// Инициализация на аналоговия компаратор
// Аналогов компаратор: Изкл
// Улавяне на вход за аналогов компаратор от таймер/брояч 1: Изкл

ACSR=0x80;
SFIOR=0x00;

ADMUX=0; // Първи ред, номер на порт.
ADCSR=0x85; // Настройки на втори ред ADC.(в двоичен x85=10000101)

За да започне работа с ADC, MK има ADCSR регистър. Ето какво има в него.

0-ти бит ADPS0Настройка на честотата на преобразуване
1-ви бит ADPS1 -/-/-
2-ри бит ADPS2 -/-/-
3-ти бит ADIE Разрешаване на прекъсване
4-ти бит ADIF флаг за прекъсване
5-ти бит ADFR Избира работа на ADC. 1-непрекъснато или 0-на ADSC тригер
6-ти бит ADSC Стартиране на преобразуване 1-старт. След преобразуването той се нулира от хардуера.
7-ми бит ADEN ADC разрешение за работа 1-да 0-не

За да го включим в ADC, записваме 1 в 7-ия бит, 0 в 6-ия, 5-тия, 3-тия и 4-тия бит. Сега избираме честотата, тъй като имаме кварц на 4000 kHz, трябва да го разделим (за стабилна работа на ADC той трябва да бъде тактован с честота в диапазона 50 kHz - 200 kHz), точно по-долу е таблица на коефициентите на делене.

Нека вземем делителя на 32 и получим честота от 125 kHz, което е напълно достатъчно за стабилна работа на ADC. И така, трябва да запишем стойността 10000101 в регистъра ADCSR.

Надявам се настройките на ADC да са ясни. Сега нека покажем нашите намерения на първия ред на екрана. За да направите това, след като инициализирате LCD дисплея, запишете реда. lcd_putsf("Работа с ADC");

// Инициализация на LCD модула
lcd_init(16);
lcd_putsf("Работа с ADC"); // Изведе записа

Сега, когато стартирате програмата в MK, ще видим този надпис. След това в безкраен цикъл записваме тялото на основната програма.

докато (1)
{
delay_ms(20); // Задайте забавянето на 20 милисекунди
ADCSR |= 0x40; // Записване на 1 в ADSC
данни = ADCW; // Извадете стойността
V = (float) данни*0,0048828; // Преобразуване във волтове
sprintf(низ, "Данни: %1.2f", V); // формат
lcd_gotoxy(0,1); // Задаване на курсора
lcd_puts(низ); // Изведете стойността

delay_ms(20);забавяне от 20 милисекунди.
ADCSR |= 0x40;бит ИЛИ. Числото 0x40 в двоична форма изглежда така: 0b01000000. Ако извършим побитово ИЛИ с 0x85 (0b10000101), тогава 1 ще бъде записано в 6-ия бит. За да започне преобразуването в 6-ия бит, трябва да напишем 1. И след преобразуването, то ще бъде нулирано на 0 по хардуер.
данни = ADCW;След преобразуването микроконтролерът записва получената стойност в ADCW. Оттам ще го вземем
V = (float) данни*0,0048828;Нека преобразуваме получената стойност във волтове, тъй като ION = 5V, а стойността на регистъра е 1024, тогава получаваме коефициента на напрежение 5/1024 = 0,0048828. Минималната стойност на напрежението ще бъде при минималната стойност на регистъра ADCW. Тоест, ако съдържа стойността 1, тогава напрежението ще бъде 0,0048828 V. Следователно в реда умножаваме ADCW данните по тази стойност - 0,0048828. Думата float в скобите се използва за преобразуване на променливата с данни от цяло число в стойност с плаваща запетая.
sprintf(низ, "Данни: %1.2f", V);Въвеждаме стойността на напрежението в низовия масив и след това го форматираме. Първо въведете данни: . След това поставяме знак за процент. 1.2f - казва, че искаме да покажем един десетичен знак и два десетични знака след него, а буквата f означава, че тази стойност е стойност с плаваща запетая.
lcd_gotoxy(0,1);Курсорът е на позиция нула във втория ред.
lcd_puts(низ);Показваме стойността на дисплея.

Преди да сглобите проекта, трябва да направите някои малки настройки в CodeVisionAVR. В менюто щракнете върху "Проект->Конфигуриране" и в прозореца, който се отваря, отидете на раздела "C Compiler", след което в долния ляв ъгъл променете стойността на (s)printf Характеристики: от int, width към float, ширина, прецизност.

Резултатът от програмата е показан на фигурата по-долу:


Можете да изтеглите архива на проекта за CodeVisionAVR и Proteus от зелената връзка по-горе. След това разопаковайте архива в корена на устройство C и проектът може да бъде стартиран.

Основни характеристики на ADC

Микроконтролерът stm32f1xx има 3 12-битови ADC на борда. Всеки ADC може да бъде свързан към всеки от 16-те аналогови входа. Освен това всеки ADC може да сканира тези входове, като взема данни от тях в определен от потребителя ред.
В края на преобразуването ADC може да издаде прекъсване. Като цяло, ADC може да издаде едно от трите прекъсвания: За края на преобразуването на редовен (обикновен) канал, за края на преобразуването през канала за инжектиране и събитие Watchdog.
В режим на сканиране прекъсването за завършване на преобразуването се издава само след приключване на цялото сканиране. И когато използвате обикновени канали, в които данните винаги се записват в един и същ регистър, ще получите само резултатите от последното преобразуване.
За да не се случи това, микроконтролерът е снабден с така наречените канали за инжектиране, които имат 4 различни регистъра за запис на данни. Тези. ако трябва да сканирате не повече от 4 канала, тогава няма да загубите резултатите от трансформациите. защото Всеки канал ще записва данни в собствен регистър.
За паралелно събиране на данни през няколко канала наведнъж е възможно едновременно стартиране на няколко ADC. Този режим се нарича двоен режим.

ADC връзка

Първо, нека да разгледаме свързването на ADC. За какво е необходим всеки крак е показано в таблица 1.

маса 1

От изброените крака интересни са -Vop и +Vop. Те определят обхвата на напреженията, възприемани от ADC. Ако свържете -Vop към маса и +Vop към захранване, ADC ще може да дигитализира аналогови сигнали в целия диапазон от 0 до мощност. защото Захранването на MK е 3.3V, а капацитетът на ADC е 12, т.е. имаме 2^12=4096 квантови нива, шумът на ADC ще бъде 3,3/4096=0,8 mV.

Видове АЦП

В микроконтролера има 2 вида ADC канали: обикновени и инжекционни. Тези 2 канала се конфигурират независимо. Но само един от тях може да работи за всеки канал. Основната разлика между тези канали е, че само един регистър се използва за съхраняване на данни, получени чрез обикновен канал. Това не е лошо, ако трябва да улавяте данни само от един канал за всеки ADC наведнъж. Но ако трябва да сканирате данни, тогава всички заснети данни ще бъдат записани в същия регистър. Че. Когато четете данни в прекъсване в края на преобразуването, ще получите само най-скоро прочетените данни. Инжекционните канали са предназначени да коригират този проблем. Имат 4 регистъра за съхранение на данни. Тези. Можете да съхранявате данни от 4 канала за сканиране. Недостатъкът на каналите за инжектиране е малко по-сложна система за конфигурация, в която е необходимо да се опишат данните от кой канал ще бъдат записани в кой регистър.

Настройка на обикновен канал

Нека помислим за настройка на обикновен ADC канал. Нека конфигурираме ADC на щифт A4. На първо място, трябва да разберете кои ADC имат достъп до този щифт и кои канали са свързани към него. По-специално, това е 4-ти канал на първия ADC.
Както обикновено, използваме стандартната схема:
1) Активирайте тактовата честота на порта
2) Настройте изхода
3) Разрешете тактоване на ADC
4) Настройте ADC
5) Разрешете необходимите прекъсвания
6) Разрешете глобалните прекъсвания
7) Активирайте ADC

Когато настройвате порт, основното е да настроите режима на аналогов режим.

Конфигуриране на аналогов изход

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);


Включете ADC часовника:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, РАЗРЕШАВАНЕ);

Настройка на ADC:

Настройка на обикновен ADC канал

ADC_InitTypeDef ADC_InitType;

ADC_InitType.ADC_ContinuousConvMode = ИЗКЛЮЧЕН;
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 = ИЗКЛЮЧЕН;

ADC_Init(ADC1, &ADC_InitType);


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

Настройка на конкретен канал. В нашия случай това е само един канал, така че настройката ще изглежда така:

ADC_RegularChannelConfig(ADC1,ADC_Channel_4,1, DC_SampleTime_56Cycles);

От параметрите тук:
ADC1 – номер на конфигурирания ADC.
ADC_Channel_4 указва канала, който да бъде уловен.
1 – т. нар. ранг. Показва в какъв ред този канал ще бъде цифровизиран. В нашия случай има само един канал, следователно ранг=1.
DC_SampleTime_56Cycles – определя колко време ще отнеме цифровизацията. Колкото по-бавно, толкова по-точно.

Сега всичко, което остава, е да конфигурирате прекъсванията и да ги активирате:

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

ADC_Cmd(ADC1, РАЗРЕШАВАНЕ);

Това завършва настройката.

За да стартирате преобразуването, използвайте функцията:

ADC_SoftwareStartConv(ADC1);

След като преобразуването приключи, програмата ще премине към функцията за прекъсване:

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

Нулираме флага и четем резултата от преобразуването.
Можете да изтеглите пример за работа от

Урок 22

Част 2

Изучаване на ADC

Днес продължаваме да изучаваме много интересна технология, а за микроконтролера - периферни устройства - аналогово-цифров преобразувателили както там го наричат ADC. В нашия урок научихме какво е ADC като цяло, научихме също как е организиран в AVR контролера, а също така създадохме нов проект и го конфигурирахме.

Следващата задача е внедряването на ADC в нашия проект.

Е, за да изпълним тази задача, ще ни трябват определени функции за достъп до ADC на контролера.

За да направите това, нека отидем на файла adc.cи създайте функция за инициализиране на нашия ADC

#включи"adc.h"

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

невалиденADC_Инициал( невалиден)

{

}

Също така ще създадем прототип за тази функция в заглавния файл adc.h за видимост от външни модули и в същото време ще разгледаме цялото съдържание на този файл

#ifndefADC_H_

#дефинирайADC_H_

#включи"main.h"

невалиденADC_Инициал( невалиден);

#endif/* ADC_H_ */

Нека сега продължим да попълваме тялото на тази функция с код. Тъй като сме запознати подробно с регистрите, това няма да ни затрудни.

Да започнем с контролния регистър

невалиденADC_Инициал( невалиден)

ADCSRA|= (1<< АДЕН)

|(1<< ADPS2)|(1<< ADPS1)|(1<< ADPS0); //Делител 128 = 64 kHz

Това не са два реда, а един и е възможно и дори необходимо да се пише по този начин в студиото, тъй като кодът става по-ясен. И едно, защото няма знак за край на реда - точка и запетая.

Тук включваме бита АДЕН, като по този начин включихме ADC модула като цяло и също така настроихме делителя на 128, като по този начин си спомнихме, че нашата тактова честота е 8 MHz и разделихме стойността й на 128, получихме ADC да работи на честота от 64 kHz, което е доста нормален и надежден, далеч не е 200 на границата. Както можете да видите, няма нищо сложно в инициализацията на регистъра.

Също така в тази функция трябва да изберем канала, към който ще свържем измереното напрежение. Съдейки по диаграмата, имаме канал 0, така че ще включим съответния MUX. И съответният MUX е само нули в тези битове, така че нищо не трябва да се включва. Но ние все още помним това в регистъра ADMUXОсвен всичко друго, имаме и контролни битове, а именно битовете REFS1 и REFS0, с които ще зададем вътрешния източник на 2,56 волта като източник на референтно напрежение и не използваме ADLAR

ADCSRA|= (1<< АДЕН) // Разрешете използването на ADC

|(1<< ADPS2)|(1<< ADPS1)|(1<< ADPS0); //Делител 128 = 64 kHz

ADMUX|= (1<< REFS1)|(1<< REFS0); //Вътрешен източник OH 2.56V, вход ADC0

Е, това е общо взето цялата инициализация.

Нека извикаме тази функция в основния модул на програмата във функцията main() някъде тук

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

ADC_Инициал(); //Инициализиране на ADC

clearlcd(); //Изчистване на дисплея

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

неподписанвътрADC_конвертиране( невалиден)

{

}

Разбира се, ще ви трябва прототип за него в заглавния файл

невалиденADC_Инициал( невалиден);

неподписанвътрADC_конвертиране( невалиден);

Тази функция ще върне стойността от регистровата двойка ADC, който ще съдържа стойността на нашия електрически сигнал в единици, изразяващи съотношението на измерения сигнал към еталонния и умножен по броя на възможните сегменти, от които имаме 1023 или 1024. Има много слухове за това, но в техническа документация за контролера формулата за изчисление съдържа точно 1024. Но това не е толкова важно за нас.

Нека включим преобразуването с малко ADSC

неподписанвътрADC_конвертиране( невалиден)

ADCSRA |= (1<< ADSC); //Стартиране на трансформация

Сега трябва по някакъв начин да проследим момента, в който тази трансформация завършва. И това се прави доста лесно чрез наблюдение на същия ADSC бит, който в края на процеса на преобразуване се нулира 0 (Когато преобразуването приключи, то се връща на нула). Този бит се наблюдава с помощта на условен цикъл

ADCSRA|= (1<< ADSC); //Стартиране на трансформация

докато(( ADCSRA& (1<< ADSC)));

Е, накрая ще върнем резултата като стойност без знак

докато(( ADCSRA& (1<< ADSC))); //проверете дали аналогово-цифровото преобразуване е завършено

връщане( неподписанвътр) ADC;

Нека сега се върнем към нашата основна функция main() и създадем локална променлива там, за да съхраним резултата от трансформацията за по-нататъшна работа с нея

вътросновен( невалиден)

неподписанвътрadc_стойност;

Нека извикаме функцията за преобразуване, която ще постави резултата от преобразуването в нашата променлива

докато(1)

adc_стойност = ADC_конвертиране(); //Извикване на трансформацията

Setpos(0,0);

Нека първо да покажем тази необработена стойност, поне да видим какво има в нея. Засега ще вземем кода от нашия часовник като основа, функцията sprintf ще дойде на помощ в по-късните уроци, времето й все още не е дошло и трябва да разберем като цяло как се преобразуват знаците. Това ще ни бъде много полезно при програмирането на LED индикатори

Setpos(0,0);

sendcharlcd( adc_стойност/1000+0x30);

sendcharlcd(( adc_стойност%1000)/100+0x30);//Преобразуване на числото в цифров код

sendcharlcd(( adc_стойност%100)/10+0x30);//Преобразуване на числото в цифров код

sendcharlcd( adc_стойност%10+0x30);//Преобразуване на числото в цифров код

Delay_ms(500);

Тук разделяме четирицифрената стойност по числа.

Сега ще сглобим кода, ще флашнем контролера и ще видим резултатите, като завъртим резистора от 10 килоома

Ето как работи всичко.

Сега нека се опитаме да покажем всичко във волтове на дисплея, за да определим какво напрежение имаме при централния контакт на нашия променлив резистор. За да направите това, създайте променлива от плаващ тип

неподписанвътрadc_стойност;

плавамн;

Нека също така да забравим за съществуването на функцията sprintf и да се опитаме програмно да получим плаващ тип на дисплея. За да направим това, първо преобразуваме нашия необработен резултат изрично в плаващ тип, тоест същото число ще бъде, но само типът е различен, като не забравяме, разбира се, първо да поставим курсора на правилното място на дисплея . За да направите това, в езика SI има концепция за изрично преобразуване на типа и разделяне на преобразувания резултат на 400

sendcharlcd( adc_стойност%10+0x30);//Преобразуване на числото в цифров код

setpos(8,0);

н= ( плавам) adc_стойност/ 400;

Тук, разбира се, възниква въпросът защо делим на 400. Ето защо.

Това не е нищо повече от 1024 делено на 2,56, което е нашето референтно напрежение. Очевидно не напразно разработчиците на контролера избраха точно тази стойност на референтното напрежение, така че всичко да бъде разделено без следа. Защо използваме това разделение? Но тъй като имаме формула в техническата документация

Ето защо изчислихме последната му част. Сега остава само да го обърнем, като от тук изразим входното напрежение, тъй като точно то ни е непознато. И получаваме, че ще бъде равно на ADC делено на 400, което всъщност направихме по-горе в кода. Мисля, че вече всичко стана напълно ясно за всички.

Остава най-интересното - да изведем всичко това на екрана, знаейки, че не можем да работим с дисплей от плаващ тип. Но се оказва, че всичко е просто. Всичко може да се реши с този код

н= ( плавам) adc_стойност/ 400;

sendcharlcd(( неподписанвъглен) н+0x30);//Преобразуване на числото в цифров код

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

sendcharlcd((( неподписанвъглен) ( н*10))%10 +0x30);//Преобразуване на числото в цифров код

sendcharlcd((( неподписанвъглен) ( н*100))%10 +0x30);//Преобразуване на числото в цифров код

Delay_ms(500);

Не се тревожете, сега ще оправим всичко тук.

Първо, отрязваме цялата дроб, използвайки преобразуване на обратен тип и като знаем, че няма да стигнем повече от 9 и ще имаме само една цифра, и дори няма да отидем повече от 2 тук, имаме максимум 2.56, ние просто показваме тази цифра.

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

Ще направим същото със следващото число, само че тук умножаваме резултата по 100, което прехвърля втората цифра след десетичната запетая в единици. Можем да продължим, но две числа са ни достатъчни.

Това е всичко!

Събираме кода, флашваме контролера и разглеждаме нашите интересни резултати, като завъртаме нашия резистор

Преглеждания на публикация: 6 917
Често има нужда от измерване на напрежение. За тези цели микроконтролерът има ADC (аналогово-цифров преобразувател). ADC е устройство, което преобразува аналогов сигнал в неговото цифрово представяне. На входа на ADC се подава аналогов сигнал, а на изхода получаваме еквивалентен цифров сигнал.

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

  • Честота на преобразуване- това е колко пъти в секунда ADC може да измерва напрежението
  • Битова дълбочина- броят на дискретните стойности на напрежението, на които е разделен целият работен диапазон на входните напрежения. ADCв AVR десет бита. Това означава, че максималното напрежение на входа на ADC ще бъде преобразувано в 2 10 =1024
  • Диапазон на входното напрежение- това е минималното и максималното напрежение, което може да се подаде към входовете на ADC. За avr това е диапазонът от 0 до захранващото напрежение на микроконтролера
За работа ADCе необходим източник на референтно напрежение (VS). Това е стандартът, спрямо който измерва входното напрежение. IN AVRИзточникът на референтно напрежение може да бъде захранващото напрежение MK, източник на референтно напрежение, свързан към щифта ARef и вътрешен 2,56 V ION. Йонът трябва да е възможно най-стабилен, от това зависи точността на измерванията. За да усетите всичко това, нека направим прост 5V волтметър. Стартирайте CVAVR, когато бъдете помолени да стартирате CodeWizardAVR, щракнете върху „да“ и отидете на раздела ADC

За нашия волтметър трябва да инсталираме източник на референтно напрежение на AVCC крака (захранващ крак ADC), честота на преобразуване 500 килохерца

Ще покажем нашите измервания от ADC на LCD дисплея; за да го инициализирате, отидете в раздела LCD и задайте всичко, както е на екранната снимка

Сега всички настройки са завършени, щракнете файл->Генериране. запази и излез. Добавяме кода, генериран от CWAVR, и премахваме инициализациите на MK периферните устройства, които не използваме, получаваме следния код:

#включи #включи #включи // Функции на буквено-цифров LCD модул #asm .equ __lcd_port=0x12 ;PORTD #endasm #include #define ADC_VREF_TYPE 0x40 // Прочетете резултата от преобразуването на AD unsigned int read_adc(unsigned char adc_input) ( ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); // Закъснение, необходимо за стабилизиране на входното напрежение на ADC delay_us(10); // Старт преобразуването на AD ADCSRA|=0x40; // Изчакайте преобразуването на AD да завърши, докато ((ADCSRA & 0x10)==0); ADCSRA|=0x10; връща ADCW; ) void main(void) ( char lcd_buffer; unsigned int u ; // Инициализация на ADC // Тактова честота на ADC: 500 000 kHz // Еталонно напрежение на ADC: AVCC щифт ADMUX=ADC_VREF_TYPE & 0xff; ADCSRA=0x81; // Инициализация на LCD модул lcd_init(16); while (1) ( /*тъй като ние имате 10-битов ADC, тогава максималният брой, който функцията read_adc() ще върне, ще бъде равен на 1024, това число ще бъде еквивалентно на напрежението на входа adc0. Например, ако read_adc() върне 512, тогава това означава, че сме приложили половината от референтното напрежение. За да изчислим реалното напрежение, трябва да направим пропорция референтно напрежение - 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 ml); )

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

Схемата е много проста, на нея виждаме микроконтролер atmega8 и 16x2 символен синтезиращ LCD дисплей (описан е пример за работа с LCD). Нашият прост волтметър измерва напрежение до 5 V. Как да измервате напрежения по-големи от 5 V Веригата е направена в Proteus, всички необходими файлове за този урок са в архива

много AVRимат на борда ADCпоследователно приближение.
ADCтова е десет бита, но с точност от +/- 2 най-малко значими цифри, може спокойно да се счита за осем бита :) Тъй като долните две цифри винаги са някакъв боклук, не подобен на полезен сигнал. Въпреки това, това е добър инструмент за наблюдение на напрежението, в осем-битов режим има 256 брояи извеждане на честотата на семплиране до 15 kHz(15 хиляди проби в секунда).

Конфигурация на източника
Входен сигнал ADCдоставяни чрез мултиплексор, от един от осем (в най-добрия случай, често по-малко) входа. Изборът на вход се извършва чрез регистър ADMUX, или по-скоро неговите части MUX3…MUX0. Записаното там число определя избрания вход. Например ако MUX3..0 = 0100 , тогава изходът е свързан ADC4.

Освен това има няколко комбинации от служебни битове MUX, използвани за калибриране.
Например, 1110 се свързва с ADC вътрешен референтен 1,22 волта. И ако пишем в MUX3..0тогава всички единици ADCще бъдат засадени на земята отвътре. Това е полезно за идентифициране на различни шумове и смущения.

В старейшините AVRсемейства мега(8535, 16, 32, 128) е възможно да се активира ADCв режим диференциален вход. Това е, когато два входа получават различни напрежения. Едното се изважда от другото и може също да се умножи по коефициента на усилване. Защо е необходимо това? И например, когато трябва да измерите дисбаланса на напрежението на измервателния мост. С някакъв тензометричен мост, с входно напрежение от пет волта, изходните сигнали ще се различават един от друг само с 30 mV, така че го хванете. И така приложих диференциалния вход, коригирах необходимото усилване и красота!

Таблица със стойности MUX3..0Няма да изброявам диференциалния превключвател тук, лесно се намира в листа с данни, нарича се „ Избор на входен канал и усилване". Ще обясня само една тънка точка. В режим на избор на диференциален вход възникват следните комбинации: първият вход е ADC0, а вторият вход също е ADC0Е, и факторът на усилване също. Как така? В края на краищата, за диференциален вход се нуждаете две различнивход! Първо си помислих, че има правописна грешка, извадих листа с данни на друг AVR - същият топ. След това погледнах текста по-долу и разбрах, че това е за калибриране на нулата. Тези. Преди да започнем да събираме диференциални данни, трябва да прекъснем входовете, за да разберем, че имаме нула. Така че комбинацията, когато два входа са свързани към един крак, е същото калибриращо късо съединение на входовете. Правите първата трансформация на такъв боклук, получавате нулево отместване. И след това го изваждате от всички получени стойности, което драстично увеличава точността.

Мултиплексирането на канали се извършва само след като преобразуването приключи, така че можете спокойно да започнете ADCза да изчислите входните стойности, запишете параметрите на друг вход в MUX3..0 и се подгответе да вземете данни от там.

Избор на референтен сигнал
Това е максималното напрежение, което ще бъде взето като максимално по време на измерванията. Референтното напрежение трябва да бъде възможно най-стабилно, без смущения или колебания - точността на работа зависи драматично от това ADC. Посочва се в битове REFS1..0регистрирам ADMUX.

  • По подразбиране е там REFS1..0 = 00- външен И ТОЙ, свързан към входа AREF. Това може да бъде напрежението от специална микросхема за референтно напрежение или от някакъв ценеров диод, ако трябва да измерите малко напрежение, забележимо по-ниско от захранващото напрежение, да речем от 0 до 1 волт, след което да бъде по-точно и така, че да не се губи на фона на петволтово захранване, тогава при AREF задаваме референтното напрежение на 1 волт.
  • REFS1..0 = 01- тук просто се взема захранващото напрежение. Почти всеки има Мег със себе си ADCима вход AVCC- това е захранващото напрежение за ADCи порта, на който се намира ADCобесен. Препоръчително е да сервирате плюс храна там L.C.филтър, за да избегнете изкривяване.
  • REFS1..0 = 11- вътрешно еталонно напрежение при 2,56 волта. Честно казано, наистина не ми хареса качеството на този източник. Свидетелство с него ADCТе плуват като лайна в ледена дупка. Но ако е невъзможно да се осигури гладко и стабилно захранване с напрежение AREFили AVCCтогава ще отиде да се вози. Между другото, вътрешни И ТОЙсвързан към изхода AREFтака че можете да закачите кондера там и да се опитате да го изгладите малко. Малко, но помага.
Избор на начален режим на преобразуване
В регистъра СФИОРпод ADCсе разпределят до три бита. ADTS2..0които контролират режимите на стартиране ADC.
  • По подразбиране ADTS2..0 = 000а това означава, че трансформацията продължава. Е, или чрез ръчно стартиране.
  • ADTS2..0 = 001— стартиране ADCот аналогов компаратор. Адски удобно. Например, за да не се измерва постоянно входната стойност, а да се програмира компаратора така, че щом получи нещо над прага, веднага да захване този въпрос за ADC.
  • ADTS2..0 = 010— тригер от външно прекъсване INT0
  • ADTS2..0 = 011- по съвпадение на таймера T0
  • ADTS2..0 = 100- при препълване на таймера T0
  • ADTS2..0 = 101- чрез съвпадение с таймера T1
  • ADTS2..0 = 110— При препълване на таймера T1
  • ADTS2..0 = 111— Чрез събитие „улавяне“ на таймера T1
Работна скорост на ADC
Честота на вземане на проби ADCзададени в битове за прескалер ADPS2…0регистрирам ADCSR. Самата таблица може да се види в листа с данни за съответния MK; Ще кажа само, че най-оптималната точност на работата на модула е ADCе вътре 50…200 kHz, така че пределителят трябва да се регулира въз основа на тези съображения. С увеличаване на честотата точността намалява.

Прекъсва.
Естествено ADCима прекъсвания. В този случай това е прекъсване в края на преобразуването. Може да се активира чрез бит АДИ, а то нагло ръчно стреля по знамето ADIF(регистрирам ADCSRA). Флаг ADIFпремахва се автоматично при преминаване към вектора за прекъсване ADC.

Данни от ADCсвиване в регистрова двойка ADCH:ADCLот къде могат да се вземат? И тук има един готин момент. Нашата регистрова двойка е 16-битова, но ADCима малка дълбочина 10 бита. В резултат на това само един регистър е напълно зает, а вторият заема само останалите два бита. Така че подравняването може да бъде или дясно подравнено - най-значимите два бита ADCH, а по-младите в ADCL, или вляво - най-значимите битове в ADCHи двата най-малко значими бита в ADCL.

[x][x][x][x][x][x] : или : [x][x][x][x][x][x]


Защо беше направено това? И този битова дълбочина на пробитолкова уникално организиран. Както вече казах, в долните редици все още има боклук и шум (поне не можах да се отърва от тях, колкото и да се опитвах). И така, ето го. Подравнете вляво. И ние рейкваме в старшите чинове само от ADCH регистъра, и вкарваме на по-младия. Общо нашият брой проби става 256. Битът отговаря за подравняването АДЛАРв регистъра ADMUX 0 — подравняване към дясната граница, 1 — към лявата.

Стартирайте преобразуването ръчно или непрекъснато.
За да започнете преобразуването, първо трябва да активирате работата ADC, настройка на бита АДЕНв регистъра ADCSRи в същия регистър бръкнете малко ADSC. За да започнете непрекъснато преобразуване (едно след друго), трябва също да зададете бита ADFR (ДАТАВ някои AVR).

Повишаване на точността чрез преминаване в хибернация.
За подобряване на точността, така че вътрешните вериги ADCне правете бъркотия с шума си, можете да започнете ADC V спящ режим. Тези. процесът спира, всичко замръзва. Само работи WatchDogи блокирайте ADC. Веднага след като данните се преброят, се генерира прекъсване, което събужда процесора, отива към манипулатора на прекъсвания от ADCи тогава всичко си отива.

Сега ще дам няколко примера за проста инициализация и работа с ADC. Микроконтролер ATMega16

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <Входният сигнал идва от нулевия канал на ADC.

; Любимият ми макрос за писане на портове:)))) .MACRO outi LDI R16,@1 OUT @0,R16 .ENDM ; ADC Init - Инициализира ADC. Това може да се постави някъде в началото на кода OUTI ADCSRA, (1<Входният сигнал идва от нулевия канал на ADC.

Какво да правя след това? Нищо! Седнете и изчакайте прекъсване!
Когато пристигне, процесорът ще го хвърли на вектора и след това можете или да пренапишете данните от ADCH:ADCLна друго място или някаква проста обработка точно там, без да излизате от касата. Като усредняване.

Вариант втори, с хибернация. По принцип всичко е същото, просто трябва да изключите автоматичното рестартиране на конвертирането. Следващ в регистъра MCUCRна битове SM2..0изберете режим Намаляване на шума на ADC SM2..0 = 001, и след това, веднага след стартиране, изпратете процесора в хибернация с командата СЪН. Веднага щом заспи, ще проработи ADC,ще извърши преобразуването и ще се събуди при прекъсването.

Изглежда така:

; ADC Init - Инициализира ADC. Това може да бъде поставено някъде в началото на кода OUTI ADMUX, 0b01000101; И тук избираме откъде ще получим сигнала; REFS -- 0b000101 първите два бита са напрежението от AVCC входа; ADLAR --0b0100101 следващият битът е подравнен вдясно; MUX -- 0b010Сигналът за влизане идва от петия крак. OUTI MCUCR,0b10010000; Задайте битовете за режим на заспиване на Намаляване на шума; И това е тялото на основната програма Main Prog: OUTI ADCSRA,(1<

Е, за да увеличите точността, трябва да следвате редица правила за свързване на захранването ADCмодул, например, подава напрежение към входа AVCCпрез дросела, монтирайте кондензатори и повече земя наоколо. Всичко за това е в листа с данни. Скоро ще публикувам примерна работеща програма - пример за ADCИ UART.