Страница 1 из 1

ATmega8: проблемы с ADC

Добавлено: Ср ноя 11, 2009 17:15:54
kit_sergo
Доброго времени суток, Уважаемые Коты!
Продолжаю перетаскивать код из AT90S4433 в CodeVision...
Делаю настройки портов I/O

Код: Выделить всё

ACSR=0x80;
SFIOR=0x00;

#define ADC_VREF_TYPE 0x00
ADMUX=ADC_VREF_TYPE;
ADCSRA=0x85;
SFIOR&=0xEF;

WDTCR=0x1F;
WDTCR=0x0F;
Потом опрашиваю ADC с помощью процедур:

Код: Выделить всё

unsigned int read_adc_new(unsigned char adc_input)
{
#asm
    LD   R30,Y
    OUT  ADMUX,R30
    RCALL _read_adc
    IN   R30,ADCL
    IN   R31,ADCH
    STS  _r_tadc,R30
    STS  _r_tadc+1,R31
#endasm
}
void read_adc(void)
{
#asm
    SBI  ADCSRA,ADSC  ;Стартую преобразование
_read_adc_wait:
    wdr
    sbis ADCSRA,ADIF    ;Ожидаю пока идет преобразование
    rjmp _read_adc_wait
    SBI  ADCSRA,ADIF
#endasm
}
...
Но почему-то программа глючит, это выражается на выводе на LCD индикатор. Как только я
- убираю строку RCALL _read_adc, вывод на индикатор идет нормально: пропечатываются все 16 символов,
- возвращаю строку RCALL _read_adc, вывод на индикатор идет косячно, первые 8 символов выводятся, а остальные нет

Видимо во время ожидания окончания опроса ADC, что-то происходит?
Или я настройки портов I/O сделал не правильно?
Кстати кварц у меня внешний 4МГц, AREF и AVCC подключены через резистор 330 Ом к VCC, и непосредственно к лапам AREF и AVCC подключены два кондера 10мкФ и 0,1 (у меня плата разработана под AT90S4433 и с ним на этой плате все норм работает!).

Помогите плиз, откройте глаза слепому котенку??? :cry:

Добавлено: Ср ноя 11, 2009 18:50:59
ARV
я не понял, что за бред - писать ассемблерные функции? вы не можете просто обратиться к регистру ADC и получить его значение?! вы же на Си все-таки писать собрались...

Добавлено: Чт ноя 12, 2009 05:05:05
kit_sergo
Ой! извините, на Си это будет так

Код: Выделить всё

#define ADC_VREF_TYPE 0x00
// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input|ADC_VREF_TYPE;
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}
Дело в том, что оно не хотело на Си работать, и я его покоцал...
Хотя, по сути, ежели рассмотреть во что это компилируется, то оно примерно оно и будет, пожалуй единственное сколько-нибудь серьезное отличие - сброс сторожевого таймера в цикле ожидания окончания преобразования ADC...

Добавлено: Чт ноя 12, 2009 07:54:54
Rushack
Я предполагаю что ты с ADMUX ом что-то перепутал. Рабочий пример есть у меня на сайте.

Добавлено: Чт ноя 12, 2009 09:18:32
kit_sergo
Уважаемый Rushack, у Вас на сайте

Код: Выделить всё

// Настраиваем ADC                                                                                   
// ADLAR означает что мы пользуемся левосторонним преобразованием
 
  ADMUX|=(0< <REFS1)|(0<<REFS0)||(1<<ADLAR);
В даташите на ATmega8 я прочитал:
АЦП генерирует 10-разрядный результат, который помещается в пару регистров данных АЦП (ADC Data Registers) ADCH и ADCL. По умолчанию результат преобразования находится в младших 10-ти разрядах в ADCH и ADCL (выровнен вправо), но может он быть опционально размещен в старших 10-ти разрядах (выравнивание слева) путем установки бита ADLAR в регистре ADMUX.

Я понял, что левостороннее выравнивание мне не нужно, а биты REFS0 и REFS1 у меня итак нулю равны...
В даташите еще схемка приводится, по поводу запитки AVCC типа через дроссель 10мкГн и кондер 100нФ параллельно, но ведь я правильно понимаю, так же реально никто делать не будет?! или все-таки это важно и дроссель нужен...

Добавлено: Чт ноя 12, 2009 09:53:42
mr.Kirill
kit_sergo писал(а):...
В даташите еще схемка приводится, по поводу запитки AVCC типа через дроссель 10мкГн и кондер 100нФ параллельно, но ведь я правильно понимаю, так же реально никто делать не будет?! или все-таки это важно и дроссель нужен...
Действительно...дураки придумали так подключать питание на ADC...

Добавлено: Чт ноя 12, 2009 14:02:53
ARV
kit_sergo писал(а):Ой! извините, на Си это будет так

Код: Выделить всё

#define ADC_VREF_TYPE 0x00
// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input|ADC_VREF_TYPE;
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}
Дело в том, что оно не хотело на Си работать, и я его покоцал...
Хотя, по сути, ежели рассмотреть во что это компилируется, то оно примерно оно и будет, пожалуй единственное сколько-нибудь серьезное отличие - сброс сторожевого таймера в цикле ожидания окончания преобразования ADC...
оно у вас не хочет работать "не на Си" а вообще :) и правильно делает, точнее, работает, как вы и написали.

Код: Выделить всё

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input){
   ADMUX=adc_input|ADC_VREF_TYPE;
   // Start the AD conversion
   ADCSRA |= 1<<ADSC;
   // Wait for the AD conversion to complete
   while ((ADCSRA & (1<<ADSC));
   return ADCW;
}
вот такой код работать будет. найдите отличия. объясните, почему так.
P.S. не забудьте правильно определить ADC_VREF_TYPE

Добавлено: Чт ноя 12, 2009 16:29:24
kit_sergo
... найдите отличия. объясните, почему так.
уважаемый ARV, я нашел главное, как мне кажется, отличие: вместо того, чтобы ждать пока бит ADIF установится в единицу, Вы мне предлогаете ждать пока бит ADSC сбросится в ноль... Я сейчас это проделаю на "живом железе"!
Но разрешите промяукать, что функцию на Си я привел стандартно сгенерированную CodeVisionAVR - там же в нем есть такая прелесть как CodeWizardAVR. А, кстати сказать, в даташите "написано":
По завершении преобразования результат помещается в регистры данных АЦП и устанавливается флаг ADIF. В режиме одиночного преобразования одновременно с этим сбрасывается бит ADSC.
Прочитав эти строки, и посмотрев код на Си сгенерированный CodeWizardAVR'ом, я и верил, что все верно... Единственное, до чего я в данный момент могу догадаться, так это то, что установленный после завершения преобразования бит ADIF кто-то успевает сбросить еще до того, как я его увижу установленным... В принципе, это объяснило бы поведение программы...Но кто его успевает сбросить?
Или его все-таки ни кто не устанавливает?

Добавлено: Чт ноя 12, 2009 17:15:06
ARV
во-первых, даже в даташите приведен пример ожидания завершения АЦП именно по флагу ADSC, уж не знаю, чем он люб всем :)
во-вторых, я крайне отрицательно отношусь к мастерам CodeVision
в-третьих, у вас перед выходом из функции был запуск нового преобразования и получалось, что вы читаете ADCW после того, как запустили очередное измерение. так что возможно, причина вовсе не во флаге, а в этом моменте.

Добавлено: Чт ноя 12, 2009 17:45:03
Rushack
kit_sergo писал(а):Уважаемый Rushack, у Вас на сайте

Код: Выделить всё

// Настраиваем ADC                                                                                   
// ADLAR означает что мы пользуемся левосторонним преобразованием
 
  ADMUX|=(0< <REFS1)|(0<<REFS0)||(1<<ADLAR);
В даташите на ATmega8 я прочитал:
АЦП генерирует 10-разрядный результат, который помещается в пару регистров данных АЦП (ADC Data Registers) ADCH и ADCL. По умолчанию результат преобразования находится в младших 10-ти разрядах в ADCH и ADCL (выровнен вправо), но может он быть опционально размещен в старших 10-ти разрядах (выравнивание слева) путем установки бита ADLAR в регистре ADMUX.

Я понял, что левостороннее выравнивание мне не нужно, а биты REFS0 и REFS1 у меня итак нулю равны...
В даташите еще схемка приводится, по поводу запитки AVCC типа через дроссель 10мкГн и кондер 100нФ параллельно, но ведь я правильно понимаю, так же реально никто делать не будет?! или все-таки это важно и дроссель нужен...
Это важно если нужно точное определение напряжения.

А используется 10 битное преобразование?
Если 8 битное, тогда лучше использовать левостороннее преобразование, т.к. в ADCL всегда непонятные помехи сваливаются.

Ещё я отписывался по поводу преобразований на другом сайте, вот цитата:
Во время работы АЦП, все данные поступают в регистры ADCH (старший разряд) и ADCL (младший разряд). Так вот, если АЦП у нас 10 разрядов (регистры в сумме дают 16), то получается что один из регистров не всегда заполнен. Вот тут и пригодится нам наш бит ADLAR, чтобы выбрать какой разряд будет заполнен полностью а какой нет.
При левостороннем преобразовании регистр ADCH полностью заполняется старшими битами (ADCH=1), а ADCL только часть (2 бита).
Если АЦП у нас 8 разрядов , то получается что регистр ADCL можем не использовать совсем. И считывать значение только с ADCH. Предварительно установив бит ADLAR в 1 (левостороннее преобразование) .
А можно весь исходник глянуть?

Добавлено: Чт ноя 12, 2009 22:20:50
__Alexander
Rushack писал(а):
Во время работы АЦП, все данные поступают в регистры ADCH (старший разряд) и ADCL (младший разряд). Так вот, если АЦП у нас 10 разрядов (регистры в сумме дают 16), то получается что один из регистров не всегда заполнен.
10 разрядов в суме дают 1023. :)) И похрен куда что записано. Единственное что надо соблюдать, согласно даташитам, это очередность чтения результатов из младшего и старшего регистров. Но это для асм, сишные компиляторы правильно генерируют код, даже при обращении к одной переменной.

типа того:
unsigned int x;
x = ADC;


А автору могу посоветовать (т.к. тоже наблюдал такую фигню), завершение преобразования наблюдать по прерыванию. В мегах оно точно есть.


------------------------[/code]

Добавлено: Чт ноя 12, 2009 22:51:50
ARV
__Alexander писал(а):А автору могу посоветовать (т.к. тоже наблюдал такую фигню), завершение преобразования наблюдать по прерыванию. В мегах оно точно есть.
не подводите под свои непонятки какую-то якобы фактическую основу. прерывания - прерываниями, а работа с АЦП по ожиданию завершения преобразования - само собой. и то и другое отлично функционирует.

Добавлено: Чт ноя 12, 2009 23:05:10
__Alexander
Так и я том-же. Просто посоветовал как удобнее мне.
Нахрен мне ждать while'ом флаг окончания преобразования, если его может установить прерывание и при этом программа будет продолжать выполняться.
Но это все зависит от задачи. Согласен что в данном случае подходит и то и то.



----------------

Добавлено: Пт ноя 13, 2009 07:04:19
kit_sergo
Спешу выразить благодарность всем Уважаемым Котам!
Особенное отдельное спасибо хочу передать ARV'у! Действительно, ожидание сброса ADSC не "виснит" и "не глючит" и ни каких сбросов сторожевого таймера не надо в цикл ожидания вставлять!!!
Спасибо также и Rushack'у! Я действительно отдаю себе отчет, что мне нужно 10битное преобразование, т.к. в перспективе планирую работать через UART/RS-485/RS-232/ с компом, а там уж на Delphi разберемся "кто кому дорог и насколько"...
Спасибо за совет __Alexander'у! А вот подскажите с ходу, по поводу настройки прерывания по завершению ADC:

Код: Выделить всё

ADCSRA=0x85; // Вкл ADC и установка предделителя ADC
ADMUX=adc_input|ADC_VREF_TYPE; // Выбор номера канала и VREF
ADCSRA |= 1<<ADIE; // Разрешение прерывания по завершению преобразования ADC
ADCSRA |= 1<<ADSC; // Старт преобразования
В обработчике прерывания: прочитать регистр данных ADC, чего-то сделать с этими данными, и может еще-чего...
А вот как быть с ADIF?
Надо ли его сбросить в начале обработчика прерывания?
Надо ли его сбросить в конце обработчика прерывания?
Или он аппаратно сам сбросится, или это вообще не должно меня заботить?

Добавлено: Пт ноя 13, 2009 16:07:46
ARV
kit_sergo писал(а):Или он аппаратно сам сбросится, или это вообще не должно меня заботить?
ДА оба раза

Добавлено: Пт ноя 13, 2009 22:14:04
__Alexander
kit_sergo писал(а): Спасибо за совет __Alexander'у! А вот подскажите с ходу, по поводу настройки прерывания по завершению ADC:

Код: Выделить всё

ADCSRA=0x85; // Вкл ADC и установка предделителя ADC
ADMUX=adc_input|ADC_VREF_TYPE; // Выбор номера канала и VREF
ADCSRA |= 1<<ADIE; // Разрешение прерывания по завершению преобразования ADC
ADCSRA |= 1<<ADSC; // Старт преобразования
В обработчике прерывания: прочитать регистр данных ADC, чего-то сделать с этими данными, и может еще-чего...
Можно просто прочитать, можно вернуть значение от этого прерывания, а можно и просто в переменную записать... выбор широк.
А с ходу... ну вот для иара. Под свой компил замените сами.

Код: Выделить всё


#pragma vector = ADC_vect 
__interrupt void ADC_vector_int(void)
  {
//  return(ADC); (это при описании функции, что она может возвратить результат)
//   adc = ADC; // Просто запись в переменную.
   
//   flagADC = 1; // или тупо установить флаг.  
  }




------------------------------------------------



  

Добавлено: Ср ноя 18, 2009 15:36:58
kit_sergo
Извините за молчание. Просто вчера у меня наверное был "не мой день". Какой-то глюк в Пони Прог выставлял мне все галки в окошке программирования битов защиты и конфигурации. В итоге я погрузил в кому две ATmega8. Что характерно: выставлял необходимые галки как учили, нажимал ОК. Программировал, программирование шло, а в конце когда шла прошивка битов защиты и конфигурации, программа мне выдавала, что она не находит устройство (ошибка 24). Смотрю окошко битов защиты и конфигурации, блин! абс все галки установлены. Сегодня переустановил ПониПрог (остановился на англ версии 2.06с)...

Ну вот я и попробовал опрос ADC с использованием прерывания по завершению преобразования ADC...
Настроил порты:

Код: Выделить всё

#define ADC_VREF_TYPE 0x00
ADCSRA=0x85;
ADMUX=2|ADC_VREF_TYPE; // Запись номера канала и источника VREF
ADCSRA |= 1<<ADIE; // Разрешение прерывания по завершению преобразования ADC
SFIOR&=0xEF;
Обработчик прерывания настроил так:

Код: Выделить всё

// ADC interrupt service routine
interrupt [ADC_INT] void adc_isr(void)
{
unsigned int adc_data;
// Read the AD conversion result
r_tadc=ADCW; //т.е. просто считываю значение преобразования ADC
}
Для получения данных от ADC, примерно раз в секунду, в общем секундном цикле построенном с использованием прерывания по переполнению таймера 0 (Timer 0 overflow interrupt ), выполняю строчку:

Код: Выделить всё

ADCSRA |= 1<<ADSC; // Старт преобразования ADC
Работает волшебно!!! СПАСИБО ЗА ПОДДЕРЖКУ!!!