Atmega8 ацп не работает

AVR Lab устройства на микроконтроллерах AVR

Форум по AVR

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

Работа с АЦП микроконтроллера ATmega8

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

Далее генерируется напряжение (Опорное напряжение*Z/1024), это напряжение, с помощью аналогового компаратора сравнивается с входным, если оно больше входного, бит Z9 становиться равным нулю, а если меньше – остается единицей. Далее переходим к биту Z8 и вышеописанным способом получаем его значения. После того, как вычисление регистра Z окончено, выставляется некий флаг, который сигнализирует, что преобразование закончено и можно считывать полученное значение. На точность преобразования могут очень сильно влиять наводки и помехи, а также скорость преобразования. Чем медленнее происходит преобразования – тем оно точней. С наводками и помехами следует бороться с помощью индуктивности и емкости, как советует производитель в даташите:

В микроконтроллерах AVR как источник опорного напряжения может использоваться вывод AREF, или внутренние источники 2,56В или 1,23В. Также источником опорного напряжения может быть напряжение питания. В некоторых корпусах и моделях микроконтроллеров есть отдельные выводы для питания АЦП: AVCC и AGND. Выводы ADCn – каналы АЦП.

С какого канала будет оцифровываться сигнал можно выбрать с помощью мультиплексора.
Теперь продемонстрируем примером сказанное выше. Соорудим макет, который будет работать как вольтметр с цифровой шкалой. Условимся, что максимальное измеряемое напряжение будет 10В. Также пусть наш макет выводит на ЖКИ содержимое регистра ADC.

Схема подключения:


Обвязка микроконтроллера и ЖКИ 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 мкФ для устранения шумов и наводок на АЦП на схеме не показан. Их наличие подразумевается само собой, если используется АЦП. Теперь дело за программой:

Читайте также:  Prestigio как настроить аккаунт

Программа на языке Си:

Так-же есть хитрость, чтобы не работать с дробными числами. Когда производиться вычисления входного напряжения в вольтах. Мы просто будем хранить наше напряжения в милливольтах. Например, значение переменной 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 Ом).

В свой блок питания я встроил еще защиту от перегрузки, когда ток превышает 2А, то пьезо пищалка начинает усердно пищать, сигнализируя о перегрузке:

Зачем в этом выражении

Зачем в этом выражении voltage=R_division*2.56*u*1.024;
1,024 ? Кто может объяснить?

Если в этой формуле совсем другие величины. Входное напряжение = Опорное напряжение*Z/2^N, где N – битность АЦП.

Источник

roboforum.ru

Технический форум по робототехнике.

Текущее время: 19 окт 2021, 10:42

Часовой пояс: UTC + 4 часа

Прерывание от АЦП в ATMEGA8 не срабатывает

Страница 1 из 2 [ Сообщений: 19 ] На страницу 1 , 2 След.
Версия для печати Пред. тема | След. тема
Читайте также:  Не работает трамблер тойота
Автор Сообщение
C-r-o-w

Зарегистрирован: 20 июн 2009, 23:06
Сообщения: 2
Откуда: Omsk

#include
#include
#include

interrupt [TIM0_OVF] void timer0_ovf_isr(void);
interrupt [ADC_INT] void adc_isr(void);
void lcd_putnum(unsigned char num);
void main(void);

//======== TIMER0 INTERRUPT HANDLER ========//
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
<
ADCSRA.6 = 1;
// delay_ms(500);
>
//==== END OF TIMER0 INTERRUPT HANDLER =====//

//======== ADC INTERRUPT HANDLER ========//
interrupt [ADC_INT] void adc_isr(void)
<
unsigned char adc_data;
adc_data = ADCH;
lcd_clear();
lcd_putsf(«ADC = «);
lcd_putnum(adc_data);
delay_ms(200);
>
//==== END OF ADC INTERRUPT HANDLER =====//

void lcd_putnum(unsigned char num)
<
unsigned char tmp1, tmp2;
tmp1 = num / 100;
tmp2 = ( num / 10 ) % 10;
if (tmp1)
lcd_putchar(tmp1 + 48);
if ( ((!tmp2) && (tmp1)) ||(tmp2))
lcd_putchar(tmp2 + 48);
lcd_putchar((num%100)%10 + 48);
>

void main(void)
<
DDRC = 0b00110110;
PORTC = 0b00111110;

ADMUX = 0b01100000;
ADCSRA = 0x8B;

yak-40

Зарегистрирован: 23 окт 2007, 22:03
Сообщения: 3037
Откуда: Москва
прог. языки: С
ФИО: Евгений Яковец

SERGEY_M

Зарегистрирован: 20 мар 2009, 12:45
Сообщения: 682
Откуда: Украина, Одесса
ФИО: Сергей

yak-40

Зарегистрирован: 23 окт 2007, 22:03
Сообщения: 3037
Откуда: Москва
прог. языки: С
ФИО: Евгений Яковец

SERGEY_M

Зарегистрирован: 20 мар 2009, 12:45
Сообщения: 682
Откуда: Украина, Одесса
ФИО: Сергей

а блин. точно.
пардон, надо кофе выпить

Добавлено спустя 6 минут 16 секунд:
Какая тактовая у МК?

avr123.nm.ru
отсылающий читать курс

Зарегистрирован: 06 ноя 2005, 04:18
Сообщения: 14195
Откуда: Москва

Механизм прерываний AVR ATmega описан на страничке 3 учебного курса по AVR — http://avr123.nm.ru/03.htm

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

Т.е. у вас перед флагом АЦП всегда есть флаг таймера (если он устанавливается за время большой паузы) — его и обрабатывает мега8.

Там же КРУПНО написано:

Делайте функции обработчики прерывания как можно короче !
Не засиживайтесь в них .

Нужно тщательно продумывать алгоритм программы чтоб успевать обрабатывать все прерывания — т.е. не пропускать нужные события
и обрабатывать их вовремя.

yak-40

Зарегистрирован: 23 окт 2007, 22:03
Сообщения: 3037
Откуда: Москва
прог. языки: С
ФИО: Евгений Яковец

SERGEY_M

Зарегистрирован: 20 мар 2009, 12:45
Сообщения: 682
Откуда: Украина, Одесса
ФИО: Сергей

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

Т.е. у вас перед флагом АЦП всегда есть флаг таймера (если он устанавливается за время большой паузы) — его и обрабатывает мега8.

avr123.nm.ru
отсылающий читать курс

Зарегистрирован: 06 ноя 2005, 04:18
Сообщения: 14195
Откуда: Москва

Я об этом и написал. Внимательно перечитайте если интересно.

Добавлено спустя 2 минуты 31 секунду:

Нет. Прога почти всегда находится в прерывании от таймера. Таймер там не находится

Когда прога выходит из этого прерывания то обнаруживает два флага — от таймера (который внось возник за время длинной паузы) и от АЦП и обрабоатывает ПЕРВЫЙ их них по списку в даташите.

Если в конце прерывания сделать очистку флага от таймера то вероятно прога сможет попасть в прерывание от АЦП.

SERGEY_M

Зарегистрирован: 20 мар 2009, 12:45
Сообщения: 682
Откуда: Украина, Одесса
ФИО: Сергей

C-r-o-w

Зарегистрирован: 20 июн 2009, 23:06
Сообщения: 2
Откуда: Omsk

boez

Зарегистрирован: 27 авг 2008, 10:45
Сообщения: 1981
Откуда: Харьков
прог. языки: С/С++

USER777

Зарегистрирован: 03 июн 2009, 21:16
Сообщения: 50
Откуда: пос. Актюбинский
Skype: ffaanniiss777

Обратите внимание, что при выполнении команды «чтение-модификация-запись» с регистром ADCSRA ожидаемое прерывание может быть отключено.
http://www.gaw.ru/html.cgi/txt/doc/micros/avr/arh128/12.htm

И таймер 0 здесь вроде только 1-раз срабатывает.
А если так попробовать

#include
#include
#include

interrupt [TIM0_OVF] void timer0_ovf_isr(void);
interrupt [ADC_INT] void adc_isr(void);
void lcd_putnum(unsigned char num);
void main(void);

//======== TIMER0 INTERRUPT HANDLER ========//
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
<
TCNT0 = 0x??;
ADCSRA = 0xCB;

// delay_ms(500);
>
//==== END OF TIMER0 INTERRUPT HANDLER =====//

//======== ADC INTERRUPT HANDLER ========//
interrupt [ADC_INT] void adc_isr(void)
<
unsigned char adc_data;
ADCSRA=0x00;
adc_data = ADCH;
lcd_clear();
lcd_putsf(«ADC = «);
lcd_putnum(adc_data);
delay_ms(200);
>
//==== END OF ADC INTERRUPT HANDLER =====//

void lcd_putnum(unsigned char num)
<
unsigned char tmp1, tmp2;
tmp1 = num / 100;
tmp2 = ( num / 10 ) % 10;
if (tmp1)
lcd_putchar(tmp1 + 48);
if ( ((!tmp2) && (tmp1)) ||(tmp2))
lcd_putchar(tmp2 + 48);
lcd_putchar((num%100)%10 + 48);
>

void main(void)
<
DDRC = 0b00110110;
PORTC = 0b00111110;

Последний раз редактировалось USER777 24 июн 2009, 22:30, всего редактировалось 2 раз(а).

avr123.nm.ru
отсылающий читать курс

Зарегистрирован: 06 ноя 2005, 04:18
Сообщения: 14195
Откуда: Москва

Duhas

Зарегистрирован: 15 сен 2007, 13:03
Сообщения: 6338
Откуда: Красноярск
прог. языки: ASM(МК), C(PC)
ФИО: Гагарский Андрей Александрович

Страница 1 из 2 [ Сообщений: 19 ] На страницу 1 , 2 След.

Часовой пояс: UTC + 4 часа

Кто сейчас на конференции

Сейчас этот форум просматривают: Yandex [Bot] и гости: 1

Источник

Оцените статью