Спасибо за помощь
АЦП у Мега8
- Сообщения: 261
- Зарегистрирован: Ср авг 01, 2012 10:45:42
- Реклама
- Сообщения: 261
- Зарегистрирован: Ср авг 01, 2012 10:45:42
Добрый день!
Подскажите, как высчитать средние показания т.е. (замерить 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);
}
}
Подскажите, как высчитать средние показания т.е. (замерить 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);
}
}
Самый простой вариант - как-то так:
В коде выше делается просто: 32 измерения, сложили, поделили, дальше - снова 32 измерения, сложили, поделили...
А можно сделать и интереснее. Чтобы всегда иметь реально усреднение последних 32 измерений.
1. Создать массив из 32 элементов для хранения последних измерений.
2. Запускать преобразование по таймеру (период таймера должен быть больше, чем скорость ADC-преобразования).
3. Когда преобразование заканчивается, ADC вызывает прерывание, в обработчике которого считывается значение из ADC и сохраняется в ячейку массива. Следующее преобразование - в другую ячейку, и так далее по циклу.
4. Функция getAverage32 будет просто считать среднее значение массива.
Достоинство - преобразования делаются независимо, сами по себе, и в массиве всегда хранятся последние несколько значений, среднее от которых доступно в любой момент времени, без ожидания измерений.
Код: Выделить всё
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 измерений.
1. Создать массив из 32 элементов для хранения последних измерений.
2. Запускать преобразование по таймеру (период таймера должен быть больше, чем скорость ADC-преобразования).
3. Когда преобразование заканчивается, ADC вызывает прерывание, в обработчике которого считывается значение из ADC и сохраняется в ячейку массива. Следующее преобразование - в другую ячейку, и так далее по циклу.
4. Функция getAverage32 будет просто считать среднее значение массива.
Достоинство - преобразования делаются независимо, сами по себе, и в массиве всегда хранятся последние несколько значений, среднее от которых доступно в любой момент времени, без ожидания измерений.
- Сообщения: 261
- Зарегистрирован: Ср авг 01, 2012 10:45:42
Спасибо что откликнулись.WiseLord писал(а):Самый простой вариант - как-то так:В коде выше делается просто: 32 измерения, сложили, поделили, дальше - снова 32 измерения, сложили, поделили...Код: Выделить всё
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 измерений.
1. Создать массив из 32 элементов для хранения последних измерений.
2. Запускать преобразование по таймеру (период таймера должен быть больше, чем скорость ADC-преобразования).
3. Когда преобразование заканчивается, ADC вызывает прерывание, в обработчике которого считывается значение из ADC и сохраняется в ячейку массива. Следующее преобразование - в другую ячейку, и так далее по циклу.
4. Функция getAverage32 будет просто считать среднее значение массива.
Достоинство - преобразования делаются независимо, сами по себе, и в массиве всегда хранятся последние несколько значений, среднее от которых доступно в любой момент времени, без ожидания измерений.
Я первый понимаю смысл, но у меня CodeVisionAVR ругается на ADPS2 и ADPS0, ADEN. В чем может быть причина?
Не знаю. Это стандартные названия битов регистра ADC для ATMega16. Может, у вас другой контроллер, у которого они иначе называются. Может, Codevision их как-то иначе называет. Может, надо подключать какой-то заголовочный файл с определениями. А вообще, можно вместо них просто номера записать (2, 0 и 7 соответственно)
Да, и не нужно цитировать чужое последнее сообщение, тем более целиком. Оно и так видно. А то настучат по голове за 2.7
Да, и не нужно цитировать чужое последнее сообщение, тем более целиком. Оно и так видно. А то настучат по голове за 2.7
- Реклама
- Сообщения: 261
- Зарегистрирован: Ср авг 01, 2012 10:45:42
В прерывании, помимо прочего, нужно взводить флаг готовности данных, который гасится в теле getAverage32, а вызов ее осуществлять по взведенному флагу.WiseLord писал(а): А можно сделать и интереснее. Чтобы всегда иметь реально усреднение последних 32 измерений.
1. Создать массив из 32 элементов для хранения последних измерений.
2. Запускать преобразование по таймеру (период таймера должен быть больше, чем скорость ADC-преобразования).
3. Когда преобразование заканчивается, ADC вызывает прерывание, в обработчике которого считывается значение из ADC и сохраняется в ячейку массива. Следующее преобразование - в другую ячейку, и так далее по циклу.
4. Функция getAverage32 будет просто считать среднее значение массива.
Иначе при невысокой (относительно исполнения цикла main) частоте семплирования АЦП МК будет впустую исполнять getAverage32, загружая вычислительные ресурсы.
Тоже верно. Если getAverage из основной программы вызывается чаще, чем запускается АЦП, нет смысла обсчитывать результат, если он не отличается от старого, поскольку новых данных в массив не поступило.
Здравствуйте.
Вопрос не по программе, а по железу, связанный с АЦП mega8.
Какие лучше индуктивности применять для отфильровки питания (которое к AVCC идет)?
Картинки приветствуются!
Вот то, что у меня есть на данный момент:




Также есть вариант с намоткой провода на резистор.
Вопрос не по программе, а по железу, связанный с АЦП mega8.
Какие лучше индуктивности применять для отфильровки питания (которое к AVCC идет)?
Картинки приветствуются!
Вот то, что у меня есть на данный момент:
Спойлер






- Сообщения: 261
- Зарегистрирован: Ср авг 01, 2012 10:45:42
Добрый день!
Так и не могу я добиться чтоб на АЦП у меня не прыгали показания.
Можете посмотреть, подсказать может я зря стараюсь.
Сигнал с датчика подается на АЦП с частотой 1Гц.
Так и не могу я добиться чтоб на АЦП у меня не прыгали показания.
Можете посмотреть, подсказать может я зря стараюсь.
Сигнал с датчика подается на АЦП с частотой 1Гц.
- Вложения
-
- Проект.rar
- (18.02 КБ) 156 скачиваний
Вы ничего не сказали, а ЧТО ВЫ СДЕЛАЛИ для этого.kk042fjv писал(а):Добрый день!
Так и не могу я добиться...
Кроме того, приводить проект в Протеусе с хексом как то бессмысленно...
Нужен исходник и схема в обычном виде. Далеко не все держат протеус на своем компьютере...
- Сообщения: 261
- Зарегистрирован: Ср авг 01, 2012 10:45:42
Код: Выделить всё
#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);
};
}С датчика поступает сигнал, допустим 5В с частотой 1Гц.
естественно на экране будет показывать
Код: Выделить всё
50505050505050Я не совсем понимаю, в чём проблема.
АЦП снимает некоторое "мгновенное" значение, оно же на дисплее и отображается. Приблизительно раз в (20мс + время замера)
Чтобы видеть какое-то среднее значение, можно, например, завести массив из нескольких элементов. Сколько нужно элементов - зависит от минимальной частоты сигнала и количестве выборок. Хорошо бы перекрыть хотя бы пару периодов сигнала.
В массив поочерёдно заносите показания ADC, после заполнения последнего элемента возвращаетесь в первый. Потом уже усредняете и переводите в double с отображением на экран.
Заполнение массива хорошо бы делать по прерыванию от таймера, а не в основном цикле программы, который пусть только берёт готовые результаты.
АЦП снимает некоторое "мгновенное" значение, оно же на дисплее и отображается. Приблизительно раз в (20мс + время замера)
Чтобы видеть какое-то среднее значение, можно, например, завести массив из нескольких элементов. Сколько нужно элементов - зависит от минимальной частоты сигнала и количестве выборок. Хорошо бы перекрыть хотя бы пару периодов сигнала.
В массив поочерёдно заносите показания ADC, после заполнения последнего элемента возвращаетесь в первый. Потом уже усредняете и переводите в double с отображением на экран.
Заполнение массива хорошо бы делать по прерыванию от таймера, а не в основном цикле программы, который пусть только берёт готовые результаты.
А как еще можно усреднять, если например используется 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]; усредненные значения по каждому каналу
}
}
Усреднение в скользящем окне называется фильтром КИХ (с конечной импульсной характеристикой).
А можно усреднять (фильтровать) с помощью рекурсивного фильтра (БИХ - с бесконечной импульсной характеристикой). Этот вариант не требует массива, но требует расчета УСТОЙЧИВОГО фильтра).
Подробности, полагаю, Вы найдете самостоятельно...
А можно усреднять (фильтровать) с помощью рекурсивного фильтра (БИХ - с бесконечной импульсной характеристикой). Этот вариант не требует массива, но требует расчета УСТОЙЧИВОГО фильтра).
Подробности, полагаю, Вы найдете самостоятельно...
- Сообщения: 261
- Зарегистрирован: Ср авг 01, 2012 10:45:42
Добрый день!
Возник вопрос, как сделать, чтобы на жк когда напряжение (для примера) меньше 1В загоралась надпись "ЗАРЯДКА", когда напряжение поднимается (больше 1В) загоралась надпись "ок"
Вот пример кода:
у меня получается: горит "ок", как напряжение опускается меньше 1В загорается "ЗАРЯДКА", как увеличивается больше 1В загорается "окРЯДКА".
Как нужно правильно сделать?
Возник вопрос, как сделать, чтобы на жк когда напряжение (для примера) меньше 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);Как нужно правильно сделать?
- Сообщения: 261
- Зарегистрирован: Ср авг 01, 2012 10:45:42
Так и сделал.КРАМ писал(а):добавить пробелы к "ОК" так, чтобы суммарная длина ОК и ЗАРЯДКА совпадала.


