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

Re: АЦП у Мега8

Добавлено: Чт янв 17, 2013 10:02:55
kk042fjv
Все понятно теперь я разобрался
Спасибо за помощь

Re: АЦП у Мега8

Добавлено: Вт фев 25, 2014 14:15:05
kk042fjv
Добрый день!
Подскажите, как высчитать средние показания т.е. (замерить 32 отсчетов, сложить, разделить на 32, среднее вывести на экран). Как правильно это написать?

while (1)
{
static unsigned char port=0;
ADMUX=0;
ADCSRA |= 0x40;
while((ADCSRA&0x10) == 0);
ADCSRA |= 0x10;
data = ADCW*50/1023;
sprintf(string, "%d.%d", data/10, data%10);
lcd_gotoxy(5,port);
lcd_puts(string);

}

}

Re: АЦП у Мега8

Добавлено: Вт фев 25, 2014 14:51:52
WiseLord
Самый простой вариант - как-то так:

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

void adcInit()
{
    ADMUX = 0;
    ADCSRA |= (1<<ADPS2) | (1<<ADPS0);  /* Делитель ADC = 32 */
    ADCSRA |= (1<<ADEN);                /* Включаем ADC */
    return;
}

uint16_t getAverage32()
{
    uint8_t i;
    int16_t adc_result, result;
    uint32_t total = 0;
    for (i = 0; i < 32; i++) {
        ADCSRA |= (1<<ADSC);            /* Запускаем преобразование */
        while (ADCSRA & (1<<ADSC));     /* Ждём конца измерений */
        adc_result = ADCL;
        adc_result |= (ADCH << 8);
        total += adc_result;
    }
    result = total >> 5;                /* Делим результат на 32 */
    return;
}
В коде выше делается просто: 32 измерения, сложили, поделили, дальше - снова 32 измерения, сложили, поделили...

А можно сделать и интереснее. Чтобы всегда иметь реально усреднение последних 32 измерений.
1. Создать массив из 32 элементов для хранения последних измерений.
2. Запускать преобразование по таймеру (период таймера должен быть больше, чем скорость ADC-преобразования).
3. Когда преобразование заканчивается, ADC вызывает прерывание, в обработчике которого считывается значение из ADC и сохраняется в ячейку массива. Следующее преобразование - в другую ячейку, и так далее по циклу.
4. Функция getAverage32 будет просто считать среднее значение массива.

Достоинство - преобразования делаются независимо, сами по себе, и в массиве всегда хранятся последние несколько значений, среднее от которых доступно в любой момент времени, без ожидания измерений.

Re: АЦП у Мега8

Добавлено: Вт фев 25, 2014 18:20:06
kk042fjv
WiseLord писал(а):Самый простой вариант - как-то так:

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

void adcInit()
{
    ADMUX = 0;
    ADCSRA |= (1<<ADPS2) | (1<<ADPS0);  /* Делитель ADC = 32 */
    ADCSRA |= (1<<ADEN);                /* Включаем ADC */
    return;
}

uint16_t getAverage32()
{
    uint8_t i;
    int16_t adc_result, result;
    uint32_t total = 0;
    for (i = 0; i < 32; i++) {
        ADCSRA |= (1<<ADSC);            /* Запускаем преобразование */
        while (ADCSRA & (1<<ADSC));     /* Ждём конца измерений */
        adc_result = ADCL;
        adc_result |= (ADCH << 8);
        total += adc_result;
    }
    result = total >> 5;                /* Делим результат на 32 */
    return;
}
В коде выше делается просто: 32 измерения, сложили, поделили, дальше - снова 32 измерения, сложили, поделили...

А можно сделать и интереснее. Чтобы всегда иметь реально усреднение последних 32 измерений.
1. Создать массив из 32 элементов для хранения последних измерений.
2. Запускать преобразование по таймеру (период таймера должен быть больше, чем скорость ADC-преобразования).
3. Когда преобразование заканчивается, ADC вызывает прерывание, в обработчике которого считывается значение из ADC и сохраняется в ячейку массива. Следующее преобразование - в другую ячейку, и так далее по циклу.
4. Функция getAverage32 будет просто считать среднее значение массива.

Достоинство - преобразования делаются независимо, сами по себе, и в массиве всегда хранятся последние несколько значений, среднее от которых доступно в любой момент времени, без ожидания измерений.
Спасибо что откликнулись.
Я первый понимаю смысл, но у меня CodeVisionAVR ругается на ADPS2 и ADPS0, ADEN. В чем может быть причина?

Re: АЦП у Мега8

Добавлено: Вт фев 25, 2014 19:22:37
WiseLord
Не знаю. Это стандартные названия битов регистра ADC для ATMega16. Может, у вас другой контроллер, у которого они иначе называются. Может, Codevision их как-то иначе называет. Может, надо подключать какой-то заголовочный файл с определениями. А вообще, можно вместо них просто номера записать (2, 0 и 7 соответственно)

Да, и не нужно цитировать чужое последнее сообщение, тем более целиком. Оно и так видно. А то настучат по голове за 2.7

Re: АЦП у Мега8

Добавлено: Ср фев 26, 2014 07:14:58
kk042fjv
да обычный ATMega8

Re: АЦП у Мега8

Добавлено: Ср фев 26, 2014 08:00:28
КРАМ
WiseLord писал(а): А можно сделать и интереснее. Чтобы всегда иметь реально усреднение последних 32 измерений.
1. Создать массив из 32 элементов для хранения последних измерений.
2. Запускать преобразование по таймеру (период таймера должен быть больше, чем скорость ADC-преобразования).
3. Когда преобразование заканчивается, ADC вызывает прерывание, в обработчике которого считывается значение из ADC и сохраняется в ячейку массива. Следующее преобразование - в другую ячейку, и так далее по циклу.
4. Функция getAverage32 будет просто считать среднее значение массива.
В прерывании, помимо прочего, нужно взводить флаг готовности данных, который гасится в теле getAverage32, а вызов ее осуществлять по взведенному флагу.
Иначе при невысокой (относительно исполнения цикла main) частоте семплирования АЦП МК будет впустую исполнять getAverage32, загружая вычислительные ресурсы.

Re: АЦП у Мега8

Добавлено: Ср фев 26, 2014 10:43:23
WiseLord
Тоже верно. Если getAverage из основной программы вызывается чаще, чем запускается АЦП, нет смысла обсчитывать результат, если он не отличается от старого, поскольку новых данных в массив не поступило.

Re: АЦП у Мега8

Добавлено: Чт фев 27, 2014 00:12:59
НАПАЛМ
Здравствуйте.
Вопрос не по программе, а по железу, связанный с АЦП mega8.
Какие лучше индуктивности применять для отфильровки питания (которое к AVCC идет)?
Картинки приветствуются!
Вот то, что у меня есть на данный момент:
СпойлерИзображение
Изображение
Изображение
Изображение
Изображение
Изображение
Также есть вариант с намоткой провода на резистор.

Re: АЦП у Мега8

Добавлено: Чт мар 06, 2014 08:16:48
kk042fjv
Добрый день!
Так и не могу я добиться чтоб на АЦП у меня не прыгали показания.
Можете посмотреть, подсказать может я зря стараюсь.
Сигнал с датчика подается на АЦП с частотой 1Гц.

Re: АЦП у Мега8

Добавлено: Чт мар 06, 2014 09:54:16
КРАМ
kk042fjv писал(а):Добрый день!
Так и не могу я добиться...
Вы ничего не сказали, а ЧТО ВЫ СДЕЛАЛИ для этого.
Кроме того, приводить проект в Протеусе с хексом как то бессмысленно...
Нужен исходник и схема в обычном виде. Далеко не все держат протеус на своем компьютере...

Re: АЦП у Мега8

Добавлено: Чт мар 06, 2014 11:50:50
kk042fjv

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

#include <mega8.h>
#asm
   .equ __lcd_port=0x18 ;PORTB
#endasm
#include <lcd.h>
#include <delay.h>
#include <stdio.h>

char lcd_buffer[30];

void main(void)
{
int data;
float V;

ACSR=0x80;
SFIOR=0x00;
 
ADMUX=0;
ADCSRA=0x85;

lcd_init(16);
lcd_clear();

while (1)
      {                   
      delay_ms(20);
      ADCSRA |= 0x40;
      while((ADCSRA&0x10) == 0);
      ADCSRA |= 0x10;     
      data = ADCW;      
      V = (float) data*0.0048828125;
      sprintf(lcd_buffer, " %1.2f ",V);
      lcd_gotoxy(5,0);
      lcd_puts(lcd_buffer);
              
};
}
Начну лучше сначала, вот обычный исходник обычного АЦП с делителем 32.
С датчика поступает сигнал, допустим 5В с частотой 1Гц.
естественно на экране будет показывать

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

50505050505050
хочется что бы на экране цифры не прыгали, а показывали или всегда 5 или среднее, я бы его отрегулировал как мне надо.

Re: АЦП у Мега8

Добавлено: Чт мар 06, 2014 14:04:13
WiseLord
Я не совсем понимаю, в чём проблема.
АЦП снимает некоторое "мгновенное" значение, оно же на дисплее и отображается. Приблизительно раз в (20мс + время замера)
Чтобы видеть какое-то среднее значение, можно, например, завести массив из нескольких элементов. Сколько нужно элементов - зависит от минимальной частоты сигнала и количестве выборок. Хорошо бы перекрыть хотя бы пару периодов сигнала.
В массив поочерёдно заносите показания ADC, после заполнения последнего элемента возвращаетесь в первый. Потом уже усредняете и переводите в double с отображением на экран.
Заполнение массива хорошо бы делать по прерыванию от таймера, а не в основном цикле программы, который пусть только берёт готовые результаты.

Re: АЦП у Мега8

Добавлено: Ср мар 12, 2014 18:33:38
phanis
А как еще можно усреднять, если например используется 8 каналов?
Желательно с минимальными затратами памяти.В моем случае, чем больше усреднений тем больше озу используется.
Спойлер

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


#define ADC_start_ch 0 // канал с которого начать измерение 0..7
#define ADC_ch       8 // количество измеряемых каналов     1..8
#define ADC_aver     32 // усреднений 1..256

void ADC_init(void);
void ADC_read(void);


unsigned int ADC_raw[ADC_ch][ADC_aver];  // 2-х мерный массив [канал0..7][ значение АЦП 0..1023]  
unsigned int ADC_value[ADC_ch];          // усредненное значение  по каждому каналу
unsigned long ADC_temp_buff[ADC_ch];     // буфер хранения суммы накопленных значений замеров по каждому каналу
unsigned char ADC_cur_ch;                // текущий канал АЦП 0..7
unsigned char ADC_curr_aver;             // текущий замер 

#pragma vector =   TIMER2_COMP_vect // прерывание по совпадению TCNT2 = OCR2 100 Гц
__interrupt void TIMER2_COMP(void)
{
 BUT_Debrief(); // опрос кнопок
 ADC_read(); // опрос АЦП
};

void ADC_init(void)
{
 ADMUX  = (0<<REFS1)|(0<< REFS0)|(0<< ADLAR)|(0<<     0)|(0<<  MUX3)|(0<<  MUX2)|(0<<  MUX1)|(0<<  MUX0);
 ADCSR  = (1<< ADEN)|(0<<  ADSC)|(0<<  ADFR)|(0<<  ADIF)|(0<<  ADIE)|(1<< ADPS2)|(1<< ADPS1)|(0<< ADPS0);
 
 ADC_cur_ch = ADC_start_ch;  
 
 ADMUX = ADC_cur_ch|(ADMUX & 0xF0);
 
 ADC_curr_aver=0; // 
 
 ADCSR |= (1<<ADSC); //start convertion

};

void ADC_read(void)
{
unsigned int ADC_temp;

if((ADCSR & (1<<ADSC)) == 0) //Adc complete
{
 

 ADC_temp  = ADCL;
 ADC_temp  |=(int) ADCH<<8;
  
 ADC_temp_buff[ADC_cur_ch] += ADC_temp;
 ADC_temp_buff[ADC_cur_ch] -= ADC_raw[ADC_cur_ch][ADC_curr_aver];
 ADC_raw[ADC_cur_ch][ADC_curr_aver] = ADC_temp;
 ADC_value[ADC_cur_ch] = ADC_temp_buff[ADC_cur_ch] / ADC_aver;
 
 if(++ADC_cur_ch>=ADC_ch)
 {
 ADC_cur_ch = ADC_start_ch;

 if (++ADC_curr_aver >= ADC_aver) ADC_curr_aver=0;
 }
 
 ADMUX = ADC_cur_ch|(ADMUX & 0xF0);
 ADCSR |= (1<<ADSC); //start convertion

}

};

while (1)// основной цикл
    {
    //ADC_value[0..7]; усредненные значения по каждому каналу   
  
   }
    
}


Re: АЦП у Мега8

Добавлено: Чт мар 13, 2014 04:46:38
КРАМ
Усреднение в скользящем окне называется фильтром КИХ (с конечной импульсной характеристикой).
А можно усреднять (фильтровать) с помощью рекурсивного фильтра (БИХ - с бесконечной импульсной характеристикой). Этот вариант не требует массива, но требует расчета УСТОЙЧИВОГО фильтра).
Подробности, полагаю, Вы найдете самостоятельно...

Re: АЦП у Мега8

Добавлено: Ср мар 19, 2014 11:23:31
kk042fjv
Добрый день!
Возник вопрос, как сделать, чтобы на жк когда напряжение (для примера) меньше 1В загоралась надпись "ЗАРЯДКА", когда напряжение поднимается (больше 1В) загоралась надпись "ок"
Вот пример кода:

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

...
 delay_ms(20);
      ADCSRA |= 0x40;
      while((ADCSRA&0x10) == 0);
      ADCSRA |= 0x10;     
      data = ADCW;      
      V = (float) data*0.0048828125;
      sprintf(lcd_buffer, " %1.2f ",V);
      lcd_gotoxy(5,0);
      lcd_puts(lcd_buffer);
      
             if (V < 1) 
                   sprintf(lcd_buffer ,"ЗАРЯДКА", V);
             else
                   sprintf(lcd_buffer ,"ok", V); 
                   
                   lcd_gotoxy(4,1); 
                   lcd_puts(lcd_buffer);
у меня получается: горит "ок", как напряжение опускается меньше 1В загорается "ЗАРЯДКА", как увеличивается больше 1В загорается "окРЯДКА".
Как нужно правильно сделать?

Re: АЦП у Мега8

Добавлено: Ср мар 19, 2014 16:11:11
КРАМ
добавить пробелы к "ОК" так, чтобы суммарная длина ОК и ЗАРЯДКА совпадала.

Re: АЦП у Мега8

Добавлено: Чт мар 20, 2014 06:09:06
kk042fjv
КРАМ писал(а):добавить пробелы к "ОК" так, чтобы суммарная длина ОК и ЗАРЯДКА совпадала.
Так и сделал.