АЦП. Разбор полётов
- КРАМ
- Друг Кота
- Сообщения: 25121
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: АЦП. Разбор полётов
глобальные переменныe - объявляем до main()
char i
int Buffer[8]
Начальная инициализация указателя на массив в начале main():
i=0
В обработчике прерываний от АЦП:
int ADCres
ADCres = младший регистр АЦП + старший регистр АЦП*256
Buffer[(i++)&0x07] = ADCres
Среднее массива можно вычислять либо тут же, либо в main() - это зависит от временных ограничений программы.
Только не вздумайте применять i для вычисления среднего, объявите для этого ЛОКАЛЬНУЮ переменную той же размерности char.
Комментарии.
Сначала объявляем массив как глобальный int, а указатель на него как глобальный char
Потом указатель инициализируем начальным значением и запускаем цепочку таймер-АЦП. Тут я немного не в курсе какой таймер может запускать АЦП по своему переполнению в AVR. Но это несущественно - можно взглянуть в даташите или кто подскажет на лету.
В прерывании по АЦП сначала формируем 16-разрядное значение из двух отдельных байт результата преобразования.
Затем вписываем этот результат в массив по текущему значению указателя (адреса массива). После этого инкрементируем указатель и накладываем на него маску 0х07, тем самым ограничивая размер указателя от 0 до 7.
В приведенном примере использован массив из 8 элементов.
Все! Массив готов для вычисления среднего.
Таким образом мы записываем значения преобразования в массив по КРУГУ. В массиве всегда имеется последние 8 измерений. Правда они неупорядочены, поскольку граница начала новых данных все время скользит по кругу, но для прямоугольного окна это и не требуется, поскольку мы просто суммируем все значения для нахождения среднего.
ЗЫ. Решил не рисовать. Проще будет метапрограмма
char i
int Buffer[8]
Начальная инициализация указателя на массив в начале main():
i=0
В обработчике прерываний от АЦП:
int ADCres
ADCres = младший регистр АЦП + старший регистр АЦП*256
Buffer[(i++)&0x07] = ADCres
Среднее массива можно вычислять либо тут же, либо в main() - это зависит от временных ограничений программы.
Только не вздумайте применять i для вычисления среднего, объявите для этого ЛОКАЛЬНУЮ переменную той же размерности char.
Комментарии.
Сначала объявляем массив как глобальный int, а указатель на него как глобальный char
Потом указатель инициализируем начальным значением и запускаем цепочку таймер-АЦП. Тут я немного не в курсе какой таймер может запускать АЦП по своему переполнению в AVR. Но это несущественно - можно взглянуть в даташите или кто подскажет на лету.
В прерывании по АЦП сначала формируем 16-разрядное значение из двух отдельных байт результата преобразования.
Затем вписываем этот результат в массив по текущему значению указателя (адреса массива). После этого инкрементируем указатель и накладываем на него маску 0х07, тем самым ограничивая размер указателя от 0 до 7.
В приведенном примере использован массив из 8 элементов.
Все! Массив готов для вычисления среднего.
Таким образом мы записываем значения преобразования в массив по КРУГУ. В массиве всегда имеется последние 8 измерений. Правда они неупорядочены, поскольку граница начала новых данных все время скользит по кругу, но для прямоугольного окна это и не требуется, поскольку мы просто суммируем все значения для нахождения среднего.
ЗЫ. Решил не рисовать. Проще будет метапрограмма
- просто КОТ
- Друг Кота
- Сообщения: 12364
- Зарегистрирован: Пт дек 17, 2010 15:07:50
- Откуда: Крымский Федеральный Округ
- Контактная информация:
Re: АЦП. Разбор полётов
Так, получается, что в буфере сумма из 8 измерений. Тогда мне надо или мои требования умножить на 8, или буфер делить. Пожалуй заранее умножу пункты на 8. . .
Кстати, а можно запустит АЦП не таймером?! А просто прописав в основной программе. От него прерывания, а потом продолжается основная программа, в которой идёт обработка прерываний!
Типа так:
Кстати, а можно запустит АЦП не таймером?! А просто прописав в основной программе. От него прерывания, а потом продолжается основная программа, в которой идёт обработка прерываний!
Типа так:
Спойлер
Код: Выделить всё
#define F_CPU 4800000 // Инициализация
#include <io.h>
#include <delay.h>
char i
int Buffer[8]
int sum = 0;
interrupt[10] void ADCInt1(void) // Прерывания от АЦП
int ADCres
ADCres = ADCH*256 + ADCL;
sum = sum - Buffer[i] + ADCres;
Buffer[(i++)&0x07] = ADCres
void main (void)
{
i=0
DDRB=0b011011;
PORTB=0b10010;
ADMUX=00000001;
#asm("sei");
{
ADCSRA=11000000; //Запускаем Аналого-Цифровой преобразователь
if (ADCres>5600) // Если насчитал больше 700, зажечь Синий светик
{
PORTB=000001;
delay_ms(5000);
PORTB=000000;
}
else if (ADCres>5200) // Если более 650, то Зелёный
{
PORTB=000010;
delay_ms(2000);
PORTB=000000;
}
else if (ADCres>4800) // Если хоть более 600, то Жёлтый
{
PORTB=010000;
delay_ms(2000);
PORTB=000000;
}
else if (ADCres<4400) // А если менее 550, то Красный!
{
PORTB=001000;
delay_ms(2000);
PORTB=000000;
}
}
}
Последний раз редактировалось просто КОТ Чт авг 16, 2012 19:52:11, всего редактировалось 4 раза.
Re: АЦП. Разбор полётов
Поддерживаю способ усреднения показаний АЦП с помощью скользящего среднего, как писал Kramer. Сам такими часто пользуюсь, эффект от них колосальный. В плане реализации, для подсчета суммы элементов в буфере при занесении в него нового числа можно добавить к старой сумме это новое число (текущее значение АЦП) и вычесть из результата самое старое число в буфере. Это самое статое число имеет индекс i до занесения в буфер нового числа.
Конкретно, в мета-программе Krama-а вместо операци
Buffer[(i++)&0x07] = ADCres
можно сделать так:
sum = sum - Buffer[i] + ADCres;
Buffer[(++i)&0x007] = ADCres;
Переменную sum можно об'явить как глобальную там-же где об'явлен сам буфер и инициализировать нулем:
int sum = 0;
Конкретно, в мета-программе Krama-а вместо операци
Buffer[(i++)&0x07] = ADCres
можно сделать так:
sum = sum - Buffer[i] + ADCres;
Buffer[(++i)&0x007] = ADCres;
Переменную sum можно об'явить как глобальную там-же где об'явлен сам буфер и инициализировать нулем:
int sum = 0;
- просто КОТ
- Друг Кота
- Сообщения: 12364
- Зарегистрирован: Пт дек 17, 2010 15:07:50
- Откуда: Крымский Федеральный Округ
- Контактная информация:
Re: АЦП. Разбор полётов
Уважаемый КРАМ! Честно говоря, ваше утверждение вызывает у меня большие сомнения. И чтобы не считать Ваше утверждение голословным выложите на форум hex. файл чтобы желающие могли протестировать Вашу программу. Когда я выложил на форум программу “Чтение АЦП”, у нашего коллеги Совесть закрались сомнения в ее работоспособности (и это его законное право) и я убедил его выложив результат работы программы. Так и мне хотелось бы, чтобы Вы убедили меня и наших коллег в правомерности Ваших утверждений реальным примером.КРАМ писал(а):Конденсатор на входе сам по себе никаким внятным фильтром не будет.
Опять же ЗАЧЕМ городить даже единственный конденсатор, когда программная реализация ВООБЩЕ НЕ ТРЕБУЕТ никаких схемотехнических затрат?
На каждого Моцарта есть свой Сальери.
- КРАМ
- Друг Кота
- Сообщения: 25121
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: АЦП. Разбор полётов
просто КОТ писал(а):Так, получается, что в буфере сумма из 8 измерений. Тогда мне надо или мои требования умножить на 8, или буфер делить.
В буфере не сумма, а 8 элементов массива. После суммирования результат нужно поделить на 8 путем сдвига вправо на 3. Причем по хорошему перед последним сдвигом нужно проверить младший разряд промежуточного результата и если он =1, то после 3-его сдвига нужно к результату прибавить 1. Это будет корректным округлением.
- просто КОТ
- Друг Кота
- Сообщения: 12364
- Зарегистрирован: Пт дек 17, 2010 15:07:50
- Откуда: Крымский Федеральный Округ
- Контактная информация:
Re: АЦП. Разбор полётов
Извиняюсь -- не так сказал. Не буфер, а регистр ADCres!
- КРАМ
- Друг Кота
- Сообщения: 25121
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: АЦП. Разбор полётов
siamds писал(а):Уважаемый КРАМ! Честно говоря, ваше утверждение вызывает у меня большие сомнения. И чтобы не считать Ваше утверждение голословным выложите на форум hex. файл чтобы желающие могли протестировать Вашу программу. Когда я выложил на форум программу “Чтение АЦП”, у нашего коллеги Совесть закрались сомнения в ее работоспособности (и это его законное право) и я убедил его выложив результат работы программы. Так и мне хотелось бы, чтобы Вы убедили меня и наших коллег в правомерности Ваших утверждений реальным примером.
Я даже не знаю как это комментировать...
Для того, чтобы что либо проверить, нужно собрать устройство целиком...
Опять же требуется некая программа для сравнения...
И зачем Вам HEX?
Вообще то ваши сомнения легко разрешимы гораздо более продуктивным путем.
Путем изучения теории цифровой фильтрации.
ЗЫ. Кстати, я как то давно на Паяльнике статейку накропал про термометр на 18В20 и, если мне не изменяет память, там я скользящее среднее как раз и считаю, чтобы показания были "резиновыми" и не дрожали. А тут давал ссылку.
Но проще Вам самим сходить на Паяльник и посмотреть и сам проект и исходный код к нему.
А вообще то вопросы фильтрации - это моя профессия.
Последний раз редактировалось КРАМ Чт авг 16, 2012 19:41:21, всего редактировалось 1 раз.
- просто КОТ
- Друг Кота
- Сообщения: 12364
- Зарегистрирован: Пт дек 17, 2010 15:07:50
- Откуда: Крымский Федеральный Округ
- Контактная информация:
Re: АЦП. Разбор полётов
Ну так скажите мне -- правильно я "буфер" вписал в код?!
- КРАМ
- Друг Кота
- Сообщения: 25121
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: АЦП. Разбор полётов
В буфер правильно, а результат суммирования далее почему то проигнорировали и вместо сдвига и округления суммы (саму сумму при этом трогать нельзя, поскольку она у Вас текущая) зачем то используете последний результат преобразования...
Ну и нет кода для запуска таймера-АЦП.
Как будет вообще стартовать преобразование?
Ну и нет кода для запуска таймера-АЦП.
Как будет вообще стартовать преобразование?
- просто КОТ
- Друг Кота
- Сообщения: 12364
- Зарегистрирован: Пт дек 17, 2010 15:07:50
- Откуда: Крымский Федеральный Округ
- Контактная информация:
Re: АЦП. Разбор полётов
Код: Выделить всё
ADCSRA=11000000; //Запускаем Аналогово-Цифровой преобразовательВот то должно АЦП запускать. Я попытался его без таймера сделать. Судя по всему не правильно. . .
- КРАМ
- Друг Кота
- Сообщения: 25121
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: АЦП. Разбор полётов
Так тоже можно, только в main() должен быть БЕСКОНЕЧНЫЙ ЦИКЛ, где в КАЖДОМ цикле будет происходить запуск АЦП и обработка результата с выводом на светодиоды. Тогда и накопление будет в прерывании и программа будет работать непрерывно. А так она у Вас уходит в никуда...
Нет так называемого суперлупа - т.е. главного цикла.
Нет так называемого суперлупа - т.е. главного цикла.
- Аlex
- Модератор
- Сообщения: 4614
- Зарегистрирован: Чт мар 18, 2010 23:09:57
- Откуда: Планета Земля
- Контактная информация:
Re: АЦП. Разбор полётов
просто КОТ, как то примерно так:
Код: Выделить всё
volatile unsigned int ADC_Buf[8];
volatile unsigned int ADC_result;
/*****************************************************/
ОП_Прерывания_От_ADC(){
static unsigned char cnt_buf=0;
ADC_Buf[cnt_buf] = (unsigned int)ADCH<<8 | ADCL;
if(++cnt_buf>=8) cnt_buf=0;
перезапускаем_преобразование();
}
/*****************************************************/
/*****************************************************/
void main(void){
long adc_summ;
unsigned char i;
for(i=0;i<8;i++) ADC_Buf[i]=0;
..................
..................
adc_summ=0;
for(i=0;i<8;i++) adc_summ+=ADC_Buf[i];
adc_summ >>=8;
ADC_result = (unsigned int)adc_summ;
.....................
.....................
}
/*****************************************************/
Re: АЦП. Разбор полётов
Опять полностью согласен с Kramerom по поводу округления.
ПростоКОТ - у Вас отсутствуют скобки {} вокруг тела прерывания.
А сравнивать с пунктами надо не ADCres а sum.
ПростоКОТ - у Вас отсутствуют скобки {} вокруг тела прерывания.
А сравнивать с пунктами надо не ADCres а sum.
- КРАМ
- Друг Кота
- Сообщения: 25121
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: АЦП. Разбор полётов
Ну вооот, пришел Алекс и все на Си грамотно написал, а то я все больше на АСМе...
Только я не понял про 8 сдвигов после суммирования...
Только я не понял про 8 сдвигов после суммирования...
- просто КОТ
- Друг Кота
- Сообщения: 12364
- Зарегистрирован: Пт дек 17, 2010 15:07:50
- Откуда: Крымский Федеральный Округ
- Контактная информация:
- Аlex
- Модератор
- Сообщения: 4614
- Зарегистрирован: Чт мар 18, 2010 23:09:57
- Откуда: Планета Земля
- Контактная информация:
Re: АЦП. Разбор полётов
Тьфу, блин, лохонулся...КРАМ писал(а):Только я не понял про 8 сдвигов после суммирования...
Код: Выделить всё
adc_summ >>=3;
Код: Выделить всё
adc_summ >>=8;- Аlex
- Модератор
- Сообщения: 4614
- Зарегистрирован: Чт мар 18, 2010 23:09:57
- Откуда: Планета Земля
- Контактная информация:
Re: АЦП. Разбор полётов
За место cnt_buf можно держать указатель (unsigned int*) и, инкрементируя его, сравнивать с адресом последней ячейки массива и сбрасывать на адрес нулевой ячейки. И работать напрямую через указатель. Будет, скорее всего, оптимальнее.
И ещё, во время обработки массива в майне, необходимо запретить прерывания (можно только для ADC) для атомарного доступа к массиву.
И ещё, во время обработки массива в майне, необходимо запретить прерывания (можно только для ADC) для атомарного доступа к массиву.
- КРАМ
- Друг Кота
- Сообщения: 25121
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: АЦП. Разбор полётов
Алекс, ты его так совсем запутаешь...
Ему бы с обычным индексом управиться...
Ему бы с обычным индексом управиться...
- Аlex
- Модератор
- Сообщения: 4614
- Зарегистрирован: Чт мар 18, 2010 23:09:57
- Откуда: Планета Земля
- Контактная информация:
Re: АЦП. Разбор полётов
Ну я и привёл простенький пример для простоты понимания 

