АЦП — Аналого-цифровой преобразователь. Из названия можно догадаться, что на вход подается аналоговый сигнал, который преобразуется в число.
Первое о чем нужно сказать — АЦП микроконтроллера умеет измерять только напряжение. Чтобы произвести измерение других физических величин, их нужно вначале преобразовать в напряжение. Сигнал всегда измеряется относительно точки называемой опорное напряжение, эта же точка является максимумом который можно измерить. В качестве источника опорного напряжения (ИОН), рекомендуется выбирать высокостабильный источник напряжения, иначе все измерения будут плясать вместе с опорным.
Одной из важнейших характеристик является разрешающая способность, которая влияет на точность измерения. Весь диапазон измерения разбивается на части. Минимум ноль, максимум напряжение ИОН. Для 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вхR2/(R1 + R2)
Подставим наши значения в формулу:
5 = 10*R2/(R1+R2)
(R1+R2)=2*R2
R1=R2
т.е. можно взять любые два одинаковых резистора и подключить их по схеме
Следовательно, когда мы измеряем напряжение через делитель, нужно полученное значение АЦП умножить на коэффициент=Uвых/Uвх.
Полная формула вычисления измеряемого напряжения будет выглядеть так:
U=(опорное напряжение*значение АЦП*коэффициент делителя)/число разрядов АЦП
Пример: опорное 5В, измеренное значение АЦП = 512, коэффициент делителя =2, АЦП 10разрядный.
(5*512*2)/1024=5В — реальное измеренное значение напряжения.
Некоторые программисты пишут программу так, чтобы микроконтроллер автоматически вычислял коэффициент делителя, для этого выходной сигнал измеряют образцовым прибором и заносят это значение в программу. Микроконтроллер сам соотносит истинное напряжение каждому значению АЦП, сам процесс однократный и носит название калибровки.
Перейдем к программной реализации. Создаем проект с указанными параметрами. Также подключим дисплей на порт D для отображения информации.
Измерение будет производиться в автоматическом режиме, обработка кода в прерывании, опорное напряжение подключаем к ножке AVCC. По сути нам нужно только обрабатывать получаемые данные. Измеренные данные хранятся в переменной adc_data[0]. Если нужно опрашивать несколько каналов, то выбираем какие каналы сканировать, а данные будут для ножки 0 в adc_data[0], для ножки 1 в adc_data[1] и т.д.
В основном цикле добавим строки:
result=((5.00*adc_data[0])/1024.00); //пересчитываем значение АЦП в вольты
sprintf(lcd_buffer,»U=%.2fV»,result); //помещаем во временную переменную результат
lcd_puts(lcd_buffer); //выводим на экран
Небольшое замечание, чтобы использовать числа с плавающей точкой, нужно в настройках проекта изменить (s)printf Features: int, width на float, width, precision. Если этого не сделать десятые и сотые мы не увидим.
Таким образом, мы всего лишь перевели значение АЦП в вольты и вывели на дисплей. Результат в протеусе выглядит так:
Резистором можно менять напряжение, измеряемое напряжение выведено на дисплей. При сборке на реальном железе к ножке Aref нужно подключить конденсатор на 0,1мкФ. Урок получился немного сложным, но думаю он вам понравится.
Файл протеуса и прошивка:
Update:
Измерение тока:
если внимательно посмотреть, то можно увидеть что adc_data двухбайтовое число.
И куда там внимательно смотреть? Я уже несколько раз пересмотрел. Кода как такового нет.adc_data[0] больше на элемент массив похоже. CodeVision возможно и удобен для тех кто уже в теме, но для начинающих Атмел Студия более понятна. Тут всё на ладони. И да двухбитное оно или нет, но всё равно не понятно каким образом там происходит считывание. Я вообще сдвинул влево и взял только 8 высших разрядов из ADCH. Ибо как отзываются многие, то на практике низкие разряды ловят шумы.
не понятно, что вам не понятно 🙂 есть прерывание которое вызывается постоянно по окончанию преобразования, внутри читается регистр ацп двухбайтный(!) (который содержит ADCL и ADCH !), в массив adc_data[input_index]=ADCW; Конкретно в данной ситуации, Ваши претензии к CAVR совершенно не понятны, не хотите использовать генератор кода — не используйте.
Дело в том, что тематика сайта адресована в первую очередь на начинающих. Нужно разбираться в базовых понятиях, работать с даташитами и ручками код вбивать. Тогда не будет кучи вопросов и спецов по «вершкам». А так все заучивают определённый алгоритм работы с IDE, а когда контроллер другой или настройки надо изменить попадают в стопор.
Спасибо за Ваш труд, но некоторые вещи не совсем понятны.
Доброго времени сутак , помогите объединить АЦП и ШИМ в одну систему , просто делаю кантролер аборотов BLCD двигатель .что то вроде китайского ,но получше ❗
я скопировал проект у меня на выходе знак вопроса U=?U немогу понять в чём дело правда работаю(учусь в Atmel Studio7)
кто подскажет как запустить таймер от ацп или подскажите
и делать лучше в прерываниях.или как
не понятно зачем таймер от ацп
я немного неправильно задал вопрос.вот функция unsigned int ADC_result(unsigned char adc_input)
{
ADMUX=adc_input | (ADMUX & 0xF0);
//задержка для стабилизации входного напряжения
_delay_us(10);
//начинаем преобразование (ADSC = 1)
ADCSRA=ADCSRA|(1<<ADSC);
while(ADCSRA & (1<<ADSC));//ждем, пока АЦП закончит преобразование (ADIF = 0)
return ADCW;//ADC — содержит ADCH и ADCL
}
это работа в многоканальном ацп.А как сделать переключения каналов в прерывании по ацп .Подскажите алгоритм пожайлуста.Спасибо вам за ваш сайт и за ваши уроки.
Я перешёл в atmel Studio7.или копировать с CVAVR.? в AS7 как это можна сделать
поставьте галку генерить прерывание от ацп и будет вам счастье. регистры называются одинаково и в цавр и в авр студии. разница только в самих названиях прерываний
пошло в симуляторе протеус .Я как то думал об этом .Спасибо.ваш сайт .Мне помогает.Дай Бог вам здоровья.Я кажется начинаю только понимать .За счёт директивы Дефайн .Результат то же.А в чём приймущество?
Добрый вечер ! Админ, уважаемый помоги, найти ошибку, вторая мега камушком пошла 😥
Сделал проект на основании урока, сперва все работало и в протеусе и на железе, как часики, затем решил добавить внешние прерывания, немножко поменял порты, компилирую, тестирую в протеусе работает, заливаю… тишина да и только 😐 Если можно… Сам проект залил в облако : https://cloud.mail.ru/public/4RSj/msfC9fmHe
поменяйте фьюзы на RC 8 МГц
Тактируется от внутреннего на 8 МГц…
не увидел настройки внешнего прерывания
Здравствуй, админ!
Теряюсь в догадках, не знаю как работать с другой периферией, такой как замер сопротивление, замер тока и может что ещё…
Подскажи где прочесть?!
Также есть проблемы при программировании из за разницы между тем что пишется у Дейтела о «си» и тем что используется для программирования МК, где об программировании именно МК прочесть???
Спасибо.
Вы должны понимать, что мк умеет измерять только напряжение и ничего больше. Поэтому все остальные величины измеряют косвенно. Нужно почитать принципы того, как преобразуется тот же ток в напряжение. Не могу посоветовать читать какие то конкретные книги, ибо информации полно в гугле. По поводу Дейтеля, честно не припомню чтобы у него была какая то разница, если действительно что то такое есть, то можно здесь спросить. Скорее всего вопрос не в языке Си, а в архитектуре мк, опять же по моему опыту, хороших книжек как таковых не могу посоветовать, первоисточник это даташит, остальное все собирается по крупицам с разных сайтов.
Спасибо, я вас понял.
Ключевое слово даташит…
Скажите, хочу сделать вольтметр пробник, как сделать так, чтобы при изменении полярности на щупах, вольтметр так же показывал результат измерения, как в мультиметре. Т.е добавлялся «-» перед цифрами? Хоть какую то ссылку для понимания схемотехники , ничего не могу найти по этому вопросу
ацп не умеет измерять отрицательное напряжение, первое что приходит в голову — сделать делитель, так чтобы при подаче самого большого отрицательного напряжения, на входе было 0В.
Почему значение одного канала проникает в другой??
#include
#include
#include
#define FIRST_ADC_INPUT 6
#define LAST_ADC_INPUT 7
unsigned int adc_data[LAST_ADC_INPUT-FIRST_ADC_INPUT+1];
#define ADC_VREF_TYPE 0x40
// ADC interrupt service routine
// with auto input scanning
interrupt [ADC_INT] void adc_isr(void)
{
static unsigned char input_index=0;
// Read the AD conversion result
adc_data[input_index]=ADCW;
// Select next ADC input
if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
input_index=0;
ADMUX=(FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff))+input_index;
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
}
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
IND_Update();
TCNT1H=0x00;
TCNT1L=0x00;
}
int countD=0;
float V=0;
float A=0;
int dpV=0;
float f1=0;
float f2=0;
void main(void)
{
IND_Init();
PORTD=0b00000000;
DDRD= 0b11111111;
PORTB=0b00000000;
DDRB= 0b11011101;
PORTC=0b00000000;
DDRC= 0b00100000;
ADMUX=FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff);
ADCSRA=0xCE;
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1A=50;
OCR1BH=0x00;
OCR1BL=0x00;
TIMSK=0x10;
#asm(«sei»)
while (1)
{
V=V+adc_data[0];
A=A+adc_data[1];
countD++;
if (countD==500)
{
f1= ((5.00*(V/500))/1024.00)*6.35;
IND_OutUintFormat(f1*100,2,1,4);
f2=((5.00*(A/500))/1024.00);
IND_OutUintFormat(f2*100,5,5,7);
V=0;
A=0;
countD=0;
};
}
}
влиять каналы могут только если в воздухе болтаются или сопля на плате
Добрый день! Для измерения давления я использую преобразователь давления MPX5010GP. У него на выходе есть начально смещение 0,14 вольта. При выводе на дисплей при нулевом давлении показывает 280 Pa (я умнажаю на 2000 что бы получить Паскали. Подскажите, если можно, как программно уйти в ноль. Схема АЦП взята с вашего урока. Программа на CodeVisionAVR v3.10. Спасибо.
Владимир, для начала стоит посмотреть в даташит на датчик, там иногда приводят формулы. Либо снять график зависимости и посмотреть линейная ли она, если линейная то все просто обычная kx+b
Зависимость линейная, только я не поня что обозначают kx+b
kx+b это уравнение прямой, школа, математика, 7 класс 🙂 b как раз задает наклон прямой, т.е. то что вам и нужно. Если совсем ломово, можно взять пару точек, забить их в excel и он по ним выдаст готовое уравнение прямой, с нужными коэффициентами.
Спасибо!
Как рассчитать шунт для подобного рода амперметра? Во всех формулах учитывается R самого прибора, но тут как такового нет.
тот резистор на котором вы мерите и есть шунт, на нем падает напряжение, поэтому обычно стараются делать так чтобы он был как можно меньше, чтобы на нем падало как можно меньше напряжения. С другой стороны нужно подобрать его таким, чтобы падение напряжения на нем можно было завести на АЦП и что то померить. Чаще всего шунт делают достаточно маленьким, чтобы он не оказывал влияние, а чтобы можно было комфортно измерять, ставят усилитель.
Здравствуйте! Подскажите, пожалуйста, можно ли этот код переделать так, чтобы МК сам проводил измерения (например, 3 раза) и выводил на дисплей уже среднее число?
можно
Есть необходимость измерять диапазон напряжений от 0 до 0,05В. Как быть в таком случае, я так понял к aref нельзя подключить 0,1В?
Как на АЦП запомнить максимальное значение например напряжения, не обращаясь к EEPROM, допустим произошел скачок напряжения или бросок тока, и АЦП запомнил именно эти максимальные значения(ну или сохранил в переменную) и относительно их выполнил расчет.
Ну так в переменной и хранишь эти значения, какие проблемы? Создаем переменную Vmax и после каждого измерения сравниваме ее с новым значение, если оно больше пишем ее в Vmax. А EEPROM нужен только если вы хотите хранить это значение после снятия питания с контроллера.
AVCC must not differ more than ±0.3V from VCC. Либо усиливаете сигнал, либо берете контроллер с 12битным ацп, при питании 2.5В, получите разрешение в 0.02В.
Алексей, в том то и проблема, что я не понимаю как программно реализовать перезапись нового значения в Vmax.
записываете значение в переменную, сравниваете с предыдущей максимальной, если новая переменная больше, то записываете в максимум новое значение.
А как узнать какое максимальное значение было предыдущим?