|
Переключение между каналами АЦП, ATmega32 |
|
|
|
Sep 28 2008, 18:41
|

Участник

Группа: Новичок
Сообщений: 17
Регистрация: 28-07-08
Из: г. Санкт-Петербург
Пользователь №: 39 246

|
Всем привет. Как можно переключаться между каналами АЦП? К примеру, мне необходимо: сделать преобразование, прочитать один канал (Vbg), затем опять сделать преобразование и прочитать канал ADC0. Выкладываю код, переключения между каналами не происходит. Код #include <util/delay.h> #include <avr/io.h> #include <avr/interrupt.h>
#define F_CPU 16000000
int i, flag, t; void main(void) { DDRA |= (1 << 1)|(1 << 2)|(1 << 6)|(1 << 7); // Инициализация светодиодов
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Установка частоты преобразования 125KHz
ADCSRA |= (1 << ADEN); // Вкл. АЦП
ADCSRA |= (1 << ADIE); // Разрешение прерывания от компаратора sei();
for(;;) { flag = 1; ADMUX |= (1 << 1)|(1 << 2)|(1 << 3)|(1 << 4); // Выбор входного канала АЦП - Vbg ADCSRA |= (1 << ADSC); // Запуск преобразования
flag = 0; ADMUX |= 0; // Выбор входного канала АЦП - ADC0 ADCSRA |= (1 << ADSC); // Запуск преобразования }
}
//********************************************************************************//
ISR(ADC_vect) { if (flag == 1) { i = ADCL; // Чтение младших 8 битов первыми i += (int)ADCH << 8; // Чтение старших 2 битов, умножение их на 256 и сложение с мл. б. if (i < 255) { // Питание контроллера падает _delay_ms(200);
PORTA |= (1 << 6); _delay_ms(200); PORTA &= ~(1 << 6); } }
if (flag == 0) { t = ADCL; // Чтение младших 8 битов первыми t += (int)ADCH << 8; // Чтение старших 2 битов, умножение их на 256 и сложение с мл. б. if (t < 830) { _delay_ms(200);
PORTA |= (1 << 1); _delay_ms(200); PORTA &= ~(1 << 1); } else { _delay_ms(200);
PORTA |= (1 << 2); _delay_ms(200); PORTA &= ~(1 << 2); }
}
} Не знаю, необходим ли reti из прерывания?
|
|
|
|
|
Sep 28 2008, 21:23
|
Частый гость
 
Группа: Участник
Сообщений: 149
Регистрация: 2-06-08
Из: Москва
Пользователь №: 38 003

|
Цитата(B_Sergey_N @ Sep 28 2008, 22:41)  ... Выкладываю код, переключения между каналами не происходит. Я конечно не специалист в С, но 1. По-моему у Вас в основном цикле (тот что for) не происходит ожидания результата преобразования (прерывание не успевает вызваться), а сразу присваивается 0 переменной flag и далее следует переключение канала. К сожалению не скажу на память, что происходит если переключить канал не дожидась окончания преобразования, но уверен, что результат преобразования точно будет некорректным. Таким образом, при вызове прерывания в первый раз, скорее всего флаг уже равен 0, (далее распределение флага случайно) и канал уже переключен. 2. Использование больших задержек в прерывании (как минимум 2х200 мс) - это очень плохо. 3. Зачем соблюдать порядок чтения результата АЦП при написании программ на С? Как правило компилятор сам будет соблюдать порядок. Еслия не ошибаюсь прочитать результат можно так i=ADCW 4. По-моему (см. первую строку моего поста) компилятор сам заботится о корректном выходе из процедуры обработки прерывания, так что ставить reti самостоятельно, наверное, не стоит.
Сообщение отредактировал smac - Sep 28 2008, 21:24
|
|
|
|
|
Sep 28 2008, 22:36
|

Чайник, 1 литр
   
Группа: Свой
Сообщений: 655
Регистрация: 17-05-06
Из: Moscow
Пользователь №: 17 168

|
0) F_CPU следует определить в makefile, или до включения delay.h; 1) Здесь прерывание следует использовать лишь в том случае, если между запуском АЦ-преобразования и собстно его завершением вы занимаетесь какой-то другой полезной работой; 2) Прерывание будет срабатывать не по вашей задумке, т.к., как уже подметил smac, или необходимо ждать завершения текущего преобразования перед сменой флага и канала (просто ждать нельзя, см пункт 2 -- теряется целесообразность вообще прерывания), либо исключить ситуацию что флаг меняется до срабатывания прерывания (например, АЦ-преобразование по второму каналу пусть запускается из прерывания, после оконцания работы с 1-м каналом). Регистры АЦП-блока буферизуются (если идет АЦ-преобразование, то запись в регистры не повлияет на текущее преобразование), и таким образом у вас преобразование выполнится для одного канала, а результаты воспримутся как для другого... Примерчик простой реализация преобразования без прерываний: CODE /* ATMega103 (128 with M103C programmed */
#define BIT(x) (1 << (x))
WORD DoADC(BYTE _ADMUX) { WORD value;
ADMUX = _ADMUX; _delay_ms(250); // Wait till signals are stabilized ADCSR |= BIT(ADSC); // Start conversion while(ADCSR & BIT(ADSC)); // Wait till conversion done *((BYTE *) &value) = ADCL; // Read lower byte *(((BYTE *) &value) + 1) = ADCH; // Read higher byte return value; }
void GetVoltages(void) { ADCSR = BIT(ADEN) | BIT(ADSC) | BIT(ADPS2) | BIT(ADPS1) | BIT(ADPS0); // Enable ADC, start dummy conversion to init ADC while(ADCSR & BIT(ADSC)); // Wait till dummy conversion done
voltage1 = DoADC(0x00); // AREF, single ended input - PORTF0 voltage2 = DoADC(BIT(MUX0)); // AREF, single ended input - PORTF1 voltage3 = DoADC(BIT(MUX1)); // AREF, single ended input - PORTF2 voltage4 = DoADC(BIT(MUX1) | BIT(MUX0)); // AREF, single ended input - PORTF3 ADCSR = 0; // disable ADC }
|
|
|
|
|
Sep 29 2008, 09:47
|
Местный
  
Группа: Свой
Сообщений: 426
Регистрация: 5-04-07
Из: Санкт-Петербург
Пользователь №: 26 782

|
Цитата(B_Sergey_N @ Sep 28 2008, 22:41)  Всем привет. Как можно переключаться между каналами АЦП? К примеру, мне необходимо: сделать преобразование, прочитать один канал (Vbg), затем опять сделать преобразование и прочитать канал ADC0.
Выкладываю код, переключения между каналами не происходит.
[code] #include <util/delay.h> #include <avr/io.h> #include <avr/interrupt.h>
#define F_CPU 16000000
int i, flag, t; void main(void) { .......
flag = 0; [b]ADMUX |= 0; // Выбор входного канала АЦП - ADC0 ADCSRA |= (1 << ADSC); // Запуск преобразования }
} Может в этом и ничего страшного нет в Вашем случае, но в глаза бросилось.
|
|
|
|
|
Oct 4 2008, 20:15
|
Участник

Группа: Новичок
Сообщений: 16
Регистрация: 4-10-08
Пользователь №: 40 693

|
по ДШ вроде 125 мкС мин. паузу рекомендуют, а напрактике чато делают "холостое" преобраование.
|
|
|
|
|
Oct 4 2008, 20:38
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(Николай Иванович Приходько @ Sep 28 2008, 23:02)  Не забывайте делать задержку после включения ADC битом ADEN на время затухания переходных процессов, Странно, а я думал что эта задержка уже включена в первое преобразование. Цитата задержку после переключения канала, необходимую для разряда Sample-Hold-ёмкости, чтобы избежать взаимовлияния каналов. Не подскажите в какой момент времени после переключения канала в ADMUX, соответствующий пин подключаеться к Sample-Hold ? Цитата Если же у Вас выходное сопротивление источника сигнала низкое то по-любому результаты первого после переключения канала АЦП лучше выбросить А если выходное сопротивление ИСТОЧНИКА очень высокое, то первый результат верный ? Цитата(domowoj @ Sep 29 2008, 06:09)  АЦП можно переключать сразу после начала предыдущего преобр. включится он по окончании предыдущего преобр. А как Вы определяете начало очередного преобразования ? Цитата(hainiken @ Oct 5 2008, 00:15)  по ДШ вроде 125 мкС мин. паузу рекомендуют 125 мкС это требование при переключении только дифференциальных каналов. 2 B_Sergey_N1.flag используется и в прерывании и в основной проге, исчем по форуму слово volatile 2. задержки по много мс в прерывании не есть хорошо 3. ADMUX |= 0; просто не меняет ничего
|
|
|
|
|
Oct 5 2008, 15:47
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(domowoj @ Oct 5 2008, 18:22)  УВХ начинает работать вместе с АЦП, на то оно и УВХ (или аналоговое запоминающее устройство) и ему до лампочки что запоминать, а аналоговый MUX уже подал на вход сигнал (это по определению). Не очень понял что Вы этим хотели сказать ? Что УВХ всегда подключен к какому-то пину ? Или что после занесения значения в ADMUX соответствующий пин подключается к УВХ ? Или что после ADCSRA |= (1 << ADSC); уже началось преобразование и можно менять ADMUX ? Разъясните что Вы имели в виду...
|
|
|
|
|
Oct 5 2008, 16:43
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(domowoj @ Oct 5 2008, 20:21)  На конденцаторе хранения УВХ напряж.запоминается в момент ADCSRA |= (1 << ADSC) при этом ADMUX уже переключен на нужный канал. Если же мультиплексор переключить после ADCSRA |= (1 << ADSC), то он пеключится после окончания текущего преобразования. Это не совсем так, реальный запуск преобразования начинается не в момент подачи ADCSRA |= (1 << ADSC), а через некоторое время после этого. Это время зависит от выбранного прескейлера и от времени начального включения АЦП битом ADEN. Те если по-простому, АЦП запустится через 0 - (N-1) тактов проца после ADCSRA |= (1 << ADSC), где N - это прескайлер АЦП. То есть переключение каналов безопасно делать только после реального начала преобразования.
|
|
|
|
|
Oct 5 2008, 18:34
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(rudy_b @ Oct 5 2008, 22:23)  Что-то я не понимаю, вы про какой проц говорите? Если записать новое значение в мультиплексор в процессе преобразования, оно запомнится, но реально занесется в регистр мультиплексора только на последнем такте. И нельзя переписывать мультиплексор сразу после запуска, нужно, чтобы прошел хотя-бы один такт АЦП. Там есть только одно противное место. Если для измерения на разных каналах используются разные источники Uref, то при переключении их следует дожидаться перезаряда емкости фильтра Uref, иначе получите ерунду. Мысль высказанная про отсутствие УВХ в AVR была свежей...  Но Вы быстро исправились, а в остальном логика работы примерно как Вы и описали.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|