|
Усреднение вычислений АЦП |
|
|
|
Jan 10 2009, 05:35
|
Участник

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366

|
Подскажите хороший алгоритм для усреднения вычислений АЦП,я сделал так- Код // Read the 8 most significant bits // of the AD conversion result unsigned char 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 ADCH;}
while (1){....
ta++;if(ta==21)ta=0; tempADC=(read_adc(0)/5); tempADC1=tempADC+(read_adc(0)/5); if(ta==20)temp=tempADC1/2;tempADC=0;tempADC1=0; т.е. показывать на индикаторе среднее число из 20 вычислений,но все равно не так сглаженно получается.
|
|
|
|
3 страниц
1 2 3 >
|
 |
Ответов
(1 - 42)
|
Jan 10 2009, 05:57
|
Частый гость
 
Группа: Свой
Сообщений: 165
Регистрация: 13-05-06
Из: Камышин
Пользователь №: 17 067

|
Конечно не сглаженно, потому что Вы сняв двадцать значений стираете и начинаете набирать по новой. И кстати складываете Вы двадцать значений, а делите только на два. Если это такая фишка, чтобы результат в итоге умножить на два, тогда понятно. И деление на 5 я провожу в самом конце, всего один раз над переменной temp. Это сделает более точными вычисления.
Поэтому у Вас есть усреднение не скользящее, а среднее на каждые отдельно взятые двадцать значений. Простой способ сделать скользящее, это вычитать самое раннее и прибавлять последнее. Но при этом обязательно накопится ошибка вычислений. Продвинутый способ, как писал в другой ветке resident, это хранить все двадцать последних значений в массиве, и делать среднее по нему, примерно вот так: unsigned char tempData[20]; unsigned char ta=0; unsigned char i; unsigend short temp;
while (1){....
ta++;if(ta==20) ta=0; tempData[ta]=read_adc(0);// деление на 5 перенесено в расчет temp
temp = 0; for(i=0;i<20;i++){ temp += tempData[i]; } temp =temp/100; // 20*5
}
В этом случае, переменная temp будет содержать усреднение последних снятых двадцати значений, и обновляться будет при каждом чтении АЦП.
Сообщение отредактировал rvk - Jan 10 2009, 06:04
|
|
|
|
|
Jan 10 2009, 06:12
|
Участник

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366

|
Цитата И кстати складываете Вы двадцать значений, а делите только на два -по другому почему-то не получилось-показания выходят из диапазона.Ваш пример попробую,но for не желательно(может даже не допустимо),т.к. этот алгоритм находится внутри программного генератора,for даст задержку и повлияет на частоту генератора,но попробую и отпишусь,что получилось.
|
|
|
|
|
Jan 10 2009, 10:52
|
Участник

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366

|
С последним способом у меня не получилось-цифры скачут, а вот так-тоже интересно- Код ta++;if(ta==100) ta=0; tempData[ta]=read_adc(0); tempADC=0; for(a=0;a<100;a++){ tempADC+=tempData[a];} temp=tempADC/500; -как нагрузку подключаю-цифры на индикаторе пробегают от нуля до нормального значения,после отключения-обратно до нуля.
|
|
|
|
|
Jan 10 2009, 18:33
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(rvk @ Jan 10 2009, 08:57)  Продвинутый способ, как писал в другой ветке resident, это хранить все двадцать последних значений в массиве, и делать среднее по нему, примерно вот так: ........... Для усреднения по последним 20 значениям совсем необязательно хранить последние 20 значений в массиве, зачем расходовать память и ресурсы процессора, чтобы их каждый раз суммировать. А если нужно будет усреднять по 10000 значений ? Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения: Код T_average = T_average + (T_current - T_average) / 20.0 где T_average - среднее значение на данный момент времени, T_current - мгновенное измеренное значение, снятое, например, с АЦП в текущий момент времени. Этот пример НЧ-фильтра измерений практически эквивалентен усреднению последних 20 значений. Однако здесь число усреднений (20) необязательно может быть целым значением. Вообще говоря, в этой формуле число 20.0 является постоянной времени фильтра, которая вкупе с периодом измерений определяет скорость реакции (инертность) этого фильтра на резкие изменения входных измеряемых величин.
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Jan 10 2009, 18:55
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Demeny @ Jan 10 2009, 23:33)  Этот пример НЧ-фильтра измерений практически эквивалентен усреднению последних 20 значений. Вообще-то это пример фильтра EMA (экспоненциальное скользящее среднее), а не SMA (простое скользящее среднее). В общем виде EMA выглядит как рекурсия Y(i+1)=Y(i)+(X(i)–Y(i))*K, где K=2/(N+1), Y(i+1) выходное значение фильтра, Y(i) - предыдущее выходное значение фильтра, X(i) - значение текущего отсчета. N имеет тот же смысл, что и "тау" RC-цепочки. Отличие EMA от SMA еще в том, что EMA это фильтр с БИХ, а SMA - с КИХ.
|
|
|
|
|
Jan 10 2009, 19:02
|
Частый гость
 
Группа: Свой
Сообщений: 165
Регистрация: 13-05-06
Из: Камышин
Пользователь №: 17 067

|
Цитата(Demeny @ Jan 10 2009, 21:33)  Вот простой способ получать усреднённое значение каждый раз при получении текущего измеренного значения: Код T_average = T_average + (T_current - T_average) / 20.0 где T_average - среднее значение на данный момент времени, T_current - мгновенное измеренное значение, снятое, например, с АЦП в текущий момент времени. Вот пример в числах есть 10 чисел. Берем скользящее среднее 5 значений: 11, 9, 11, 9, 10, 12, 11, 7, 8, 5, среднее____ 10, 10.4, 10.52, 9.816, 9.4528, 8.56224 Это по Вашей формуле А это по массиву из последних набранных пяти значений: 11, 9, 11, 9, 10, 12, 11, 7, 8, 5 среднее____ 10, 10.2, 10.6, 9.8, 9.6, 8.6 Поэтому эти формулы какие угодно, только не взаимозаменяемые. И Ваша формула не проще, она просто другая.
Сообщение отредактировал rvk - Jan 10 2009, 19:08
|
|
|
|
|
Jan 10 2009, 19:42
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(rezident @ Jan 10 2009, 21:55)  Вообще-то это пример фильтра EMA (экспоненциальное скользящее среднее), а не SMA (простое скользящее среднее). В общем виде EMA выглядит как рекурсия Y(i+1)=Y(i)+(X(i)–Y(i))*K, где K=2/(N+1), Y(i+1) выходное значение фильтра, Y(i) - предыдущее выходное значение фильтра, X(i) - значение текущего отсчета. N имеет тот же смысл, что и "тау" RC-цепочки. Отличие EMA от SMA еще в том, что EMA это фильтр с БИХ, а SMA - с КИХ. Всё это верно, я и не сомневался в Вашей компетентности в этом вопросе. Автор топика просил алгоритм усреднения значений АЦП, поэтому я предложил практическую формулу, не вдаваясь в теорию фильтров, импульсные характеристики, оператор Лапласа и т. п. Эта формула неоднократно мною проверена и прекрасно работает, например, в системах управления газотурбинными двигателями. Конечно, численно она отличается от простого усреднения, но практически ведёт себя точно также и позволяет избежать хранения и суммирования последних N измерений, что особенно актуально для систем на маломощных вычислителях с ограниченными ресурсами (AVR, PIC).
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Jan 11 2009, 07:46
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(Wantcan @ Jan 10 2009, 08:35)  Подскажите хороший алгоритм для усреднения вычислений АЦП Если показания АЦП дрожат, то нужно думать не о том, как их отфильтровать, а о том, как устранить источник помех. А для этого нужно совсем немного, как-то: - разводить земли и питание широкими проводниками, желательно чтобы эти проводники не прыгали с одного слоя на другой, а если нужно, то переход делать через большие переходные отверстия. - подводить ко всем 8 ногам контроллера, а для опорного желательно дополнительный RC-фильтр - в непосредственной близости от него должно быть хотя бы две пары конденсаторов по питанию (электролит+керамика) - если в схеме есть сильнопотребляющие элементы (светодиоды, например), то землю от них до источника вести отдельным проводником, чтобы падение напряжения от скачков тока не вызывало скачков напряжения на земле - и т.д. Я бы еще понял, если бы АЦП было 14-16 разрядным, но я не представляю, как надо изуродовать схему, чтобы дрожало 10-разрядное АЦП - чтобы там получить стабильные значения не требуется особо напрягаться.
|
|
|
|
|
Jan 11 2009, 07:46
|

Знающий
   
Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237

|
Цитата(rezident @ Jan 10 2009, 23:26)  Да как же они могут вести себя одинаково, если у них и ФЧХ и ПХ разные!? В первом посте автор топика обозначил задачу - "показывать на индикаторе среднее значение", для этой задачи различия КИХ и БИХ фильтров несущественны - оба являются инерционным звеном и справляются с поставленной задачей. Приведённая мной формула экспоненциального скользящего среднего позволяет обойтись без хранения массива измеренных значений и их пересуммирования на каждом цикле измерения. Может быть, автору окажется полезной статья "Выбор типа скользящих средних" , которую я прикрепил к сообщению. Кстати, в ней на 52 странице приведены импульсные характеристики различных типов усреднения. Сравнив графики б) и г) легко убедиться, что ИХ простого взвешенного усреднения и экспоненциального весьма схожи.
Прикрепленные файлы
50_55.pdf ( 945.56 килобайт )
Кол-во скачиваний: 437
--------------------
Сделано в Китае. Упаковано в России.
|
|
|
|
|
Jan 11 2009, 07:53
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(rezident @ Jan 10 2009, 21:55)  Вообще-то это пример фильтра EMA (экспоненциальное скользящее среднее), а не SMA (простое скользящее среднее). В общем виде EMA выглядит как рекурсия Y(i+1)=Y(i)+(X(i)–Y(i))*K, где K=2/(N+1), Y(i+1) выходное значение фильтра, Y(i) - предыдущее выходное значение фильтра, X(i) - значение текущего отсчета. N имеет тот же смысл, что и "тау" RC-цепочки. Смысл может и тот же, а насчет численного значения я сильно сомневаюсь. Чтобы получить фильтр с постоянной времени tau, коэффициент вычисляется по формуле K = 1 - exp(-T/tau), где T - период следования входных отсчетов.
|
|
|
|
|
Jan 11 2009, 09:03
|
Гуру
     
Группа: Почетный участник
Сообщений: 6 851
Регистрация: 25-08-08
Из: Запорожье
Пользователь №: 39 802

|
Цитата(777777 @ Jan 11 2009, 11:53)  Смысл может и тот же, а насчет численного значения я сильно сомневаюсь. Чтобы получить фильтр с постоянной времени tau, коэффициент вычисляется по формуле K = 1 - exp(-T/tau), где T - период следования входных отсчетов. Вот то-то и оно. Знания и вера входят в конфликт. Инженер, чтобы устранить мигание цифр на индикаторе, возьмет среднее арифметическое от 2-4 измерений. Или даже в АЦП что-нибудь из двух компонентов придумает. Программист, ничтоже сумняшеся, будет брать экспоненту. При сем потребуется мощный процессор, плавающая запятая, дополнительные килобайты памяти, цикл решения задачи вырастет... Но он же за это не платит.
|
|
|
|
|
Jan 11 2009, 11:28
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(Microwatt @ Jan 11 2009, 12:03)  Инженер, чтобы устранить мигание цифр на индикаторе, возьмет среднее арифметическое от 2-4 измерений. Или даже в АЦП что-нибудь из двух компонентов придумает. Программист, ничтоже сумняшеся, будет брать экспоненту. При сем потребуется мощный процессор, плавающая запятая, дополнительные килобайты памяти, цикл решения задачи вырастет... Но он же за это не платит. Во-первых, вашему "инженеру" для устранения мигания цифр нужно, как я уже писал здесь, не прикрывать свое дерьмо усреднением, а устранять его, потому что если он не в состоянии получить без помех 10 разрядов, то какой он нахрен инженер - самый дешевый китайский мультиметр дает точность в два раза большую. А во-вторых, если шум имеется в самом сигнале и его надо устранять, то имейте в виду, что цифровая фильтрация - наука несколько более сложная, нежели вычисление среднего арифметического. Для начала надо исследовать характер помех - они могут оказаться не шумовыми, а например, в сигнале могут наблюдаться скачки в одну сторну - такое часто встречается если помеха идет по земле от мощных потребителей. Тогда никаким усреднением вы эту помеху не устраните, а если устраните, то получите неправильный результат. Если помеха действительно представляет собой шумовой сигнал, то небходимо оценить ее спектр - если его частота сравнима со спектром полезного сигнала, то фильтровать ее также бесполезно. Наконец, если частота помехи существенно выше - нужно посмотреть насколько отличаются часоты сигнала и помехи и насколько отличаются их амплитуды и оценить необходимую крутизну характеристики фильтра для гарантированного подавления помехи и, следовательно, его порядок. Что же касается фильтра T_ave += (T_in - T_ave) / K то для него не требуется никакой плавающей точки - он работает c целыми переменными практически в таком виде как я здесь написал. Если же вы имеете в виду экспоненту для для вычисления K, то она вычисляется один раз на калькуляторе, а если она является степенью двойки, то не требуется даже деления, можно обойтись сдвигами. А дополнительные килобайты нужны как раз при вычислении среднего арифметического - в данном случае кроме текущего и предыдущего отсчетов хранить ничего не требуется.
|
|
|
|
|
Jan 11 2009, 16:59
|
Участник

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366

|
Спасибо за все ответы,на этой неделе не будет времени этим заниматься,но проблему обязательно надо будет решить.Измерить и показать нужно примерный ток устройства,точность совсем не нужна,стоит в устройстве как допольнительная функция,да-помехи по питанию есть,надо будет и с ними побороться тоже,но усреднение тоже нужно.
|
|
|
|
|
Jan 11 2009, 17:04
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Demeny @ Jan 11 2009, 12:46)  Кстати, в ней на 52 странице приведены импульсные характеристики различных типов усреднения. Сравнив графики б) и г) легко убедиться, что ИХ простого взвешенного усреднения и экспоненциального весьма схожи. Действительно схожи. Видимо я что-то перепутал или подзабыл  Цитата(777777 @ Jan 11 2009, 12:53)  Смысл может и тот же, а насчет численного значения я сильно сомневаюсь. Чтобы получить фильтр с постоянной времени tau, коэффициент вычисляется по формуле K = 1 - exp(-T/tau), где T - период следования входных отсчетов. А где это вы увидели, что я утверждал про идентичность численных значений?  Для EMA через количество отсчетов равное N величина выходного сигнала сравняется со величиной входного единичного импульсного сигнала. Для настоящей RC-цепочки, если на вход подан единичный импульсный сигнал, то через промежуток времени равный τ=RC величина сигнала на выходе фильтра увеличиться в e раз. Да, расчет для RC не очень простой, но существует радиолюбительские правила "пяти тау" и "двадцати тау". Первое гласит, что "за время равное τ=5*RC напряжение на конденсаторе RC-цепочки достигнет (с приемлимой точностью) 99% от входного". И соответственно для правила "двадцати тау" то же самое, но для τ=20*RC и отношение - 99,9%.
|
|
|
|
|
Jan 12 2009, 12:44
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(rezident @ Jan 11 2009, 20:04)  Действительно схожи. Видимо я что-то перепутал или подзабыл  Экспонента и прямая?.. Да, схожи, но... при наличии достаточного воображения. Или после 300...500 граммов. Цитата(Tanya @ Jan 12 2009, 11:02)  Цитата Для настоящей RC-цепочки, если на вход подан единичный импульсный сигнал, то через промежуток времени равный τ=RC величина сигнала на выходе фильтра увеличиться в e раз. Удивительные вещи Вы пишите... Праздники не проходят бесследно? Да ладно вам придираться, все поняли о чем речь. Это если на входе единичный сигнал упал в 0, то на выходе он уменьшится в e раз через время tau=RC. На выходе этого фильтра, тоже.
|
|
|
|
|
Jan 21 2009, 16:05
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(Папа Карло @ Jan 21 2009, 18:16)  Возник вопрос по формуле T_average = T_average + (T_current - T_average) / 20.0.
Если я правильно понял, то первое значение лучше сразу занести в переменную T_average. А если не записать, то показания будут нарастать в зависимости от кол-ва отсчётов ? Да, лучше инициализировать не нулем, а первым считанным значением, тогда фильтру не придется долго выходить на установившееся значение. А формулу лучше представить в сишном виде, даже если будешь реализовывать ее на ассемблере, легче для понимания: T_average += (T_current - T_average) / 20.0. То есть из считанного значения вычитаем текущее, делим на коэффициент и прибавляем к текущему. Плавающая точка здесь ни к чему, в целых числах тоже можно считать, правда там есть свои тонкости.
|
|
|
|
|
Feb 12 2009, 17:53
|
Участник

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366

|
haker_fox, прошу тнуть пальцем в то место форума,о котором вы упомянули,т.к. для меня все еще актуальна эта тема..
|
|
|
|
|
Feb 13 2009, 07:09
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(777777 @ Jan 21 2009, 19:05)  T_average += (T_current - T_average) / 20.0.
То есть из считанного значения вычитаем текущее, делим на коэффициент и прибавляем к текущему. Плавающая точка здесь ни к чему, в целых числах тоже можно считать, правда там есть свои тонкости. Вот, именно - тонкости! Представьте, что разность между считанным и текущим значениями - меньше двадцати. Что получим? По этой же причине у автора вопроса и прыгают значения: в первом топике он привёл код, якобы усредняющий из двадцати значений... Имхо, сумма подсчитывается из значений измерений делённых на 5 - так усреднять, мягко говоря, не корректно.
|
|
|
|
|
Feb 13 2009, 11:40
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(blackfin @ Feb 13 2009, 09:26)  Так ведь, "усреднить помехи" по сути и означает отфильтровать их, т.е. - устранить.. Или нет? Да, но только устраняют их обычно на входе АЦП, в аналоговом виде, так чтобы АЦП выдавал устойчивый код, который не пришлось бы фильтровать. И не путем подключения RC-цепочки, а путем выяснения их природы и устранения того, что их вызывает. Например, распространенная ошибка - обратный ток от светодиодных индикаторов идет по той же земле, на которой висит АЦП. Падение тока на нем вызывает скачки на земле. Эти помехи вы не устраните усреднением, потому что все они "в одну сторону", так что даже усреднив их вы получите неправильный результат.
|
|
|
|
|
Feb 13 2009, 11:55
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(Палыч @ Feb 13 2009, 10:09)  Вот, именно - тонкости! Представьте, что разность между считанным и текущим значениями - меньше двадцати. Что получим? Получим, что результат никогда не достигнет входного значения. Но ведь и реальная, математическая экспонента никогда его не достигает! Вопрос лишь в необходимой точности. Если нужна большая - результат храним в long, а используем в качестве результата старшую половину - то есть как бы число с фиксированной точкой, в котором младшие 16 разрядов дробные. Цитата(Палыч @ Feb 13 2009, 10:09)  По этой же причине у автора вопроса и прыгают значения: в первом топике он привёл код, якобы усредняющий из двадцати значений... Имхо, сумма подсчитывается из значений измерений делённых на 5 - так усреднять, мягко говоря, не корректно. Почему? Просто результат будет в 4 раза больше. Цитата(Палыч @ Feb 13 2009, 10:09)  По этой же причине у автора вопроса и прыгают значения: в первом топике он привёл код, якобы усредняющий из двадцати значений... Имхо, сумма подсчитывается из значений измерений делённых на 5 - так усреднять, мягко говоря, не корректно. Пардон, не доглядел - конечно же надо сначала суммировать 20 значений, а результат делить на 5. А еще лучше - использовать степень двойки.
Сообщение отредактировал 777777 - Feb 13 2009, 12:00
|
|
|
|
|
Feb 13 2009, 12:59
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(777777 @ Feb 13 2009, 14:55)  Получим, что результат никогда не достигнет входного значения. Но ведь и реальная, математическая экспонента никогда его не достигает! Вопрос лишь в необходимой точности. Математическая экспанента хотя бы стремиться к нему. В Вашем же случае мы будем иметь постоянную ошибку. Результат этого - всё тот же - отбрасывание остатка при делении, который тем больше, чем больше делитель (это - ведь, наверное, и есть те тонкости, о которых Вы говорили выше). Тогда, уж лучше что-нибудь такое: Инициализация переменной T_average_tmp = T_current * N ................... T_average_tmp += (T_current - T_average) T_average= T_average_tmp / N Последнюю строчку, может быть, записать даже так: T_average= (T_average_tmp + N/2) / N
|
|
|
|
|
Feb 14 2009, 11:18
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(Палыч @ Feb 13 2009, 15:59)  Математическая экспанента хотя бы стремиться к нему. В Вашем же случае мы будем иметь постоянную ошибку. Что значит "постоянную ошибку"? Разумеется, экспонента будет отличаться от реальной из-за округлений, это неизбежно при цифровых вычислениях, вопрос лишь в необходимой точности. Даже если вы примените float, все равно ошибка будет, так как float тоже дискретно. Но задача ведь не в том, чтобы имитировать экспоненту, а в том, чтобы сгладить сигнал. Для этого вычисления в целых вполне достаточно. Цитата(Палыч @ Feb 13 2009, 15:59)  Результат этого - всё тот же - отбрасывание остатка при делении, который тем больше, чем больше делитель (это - ведь, наверное, и есть те тонкости, о которых Вы говорили выше). Тогда, уж лучше что-нибудь такое: Инициализация переменной T_average_tmp = T_current * N ................... T_average_tmp += (T_current - T_average) T_average= T_average_tmp / N Тогда уж лучше что-нибудь такое: Код static long Adc0, AdcF;
Adc0 = (long)ADC << 16; AdcF += (Adc0 - AdcF)>>3; // или разделить на нужное число, если оно не степень двойки int Result = AdcF >> 16; Правда, gnu-тый компилятор не очень эффективно кодирует сдвиги (не знаю, может IAR поумнее), поэтому этот участок лучше написать на ассемблере. Смысл в том, что в старших 16 битах AdcF хранится фильтрованное значение, а в младших - его дробная часть, т.е. оно представляет собой число с фиксированной точкой. При кодировании на ассемблере можно даже подойти к делению (сдвигу) более творчески: если в отбрасываемых битах старший 1, то к оставшимся прибавляем 1, а если 0 - то не прибавляем, т.е. выполняем деление с округлением до целого. Тогда не будет ошибки с отбрасыванием, которой ты так боишься.
|
|
|
|
|
Feb 14 2009, 15:07
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(777777 @ Feb 14 2009, 13:18)  Тогда уж лучше что-нибудь такое: Код static long Adc0, AdcF;
Adc0 = (long)ADC << 16; AdcF += (Adc0 - AdcF)>>3; // или разделить на нужное число, если оно не степень двойки int Result = AdcF >> 16; ... Смысл в том, что в старших 16 битах AdcF хранится фильтрованное значение, а в младших - его дробная часть, т.е. оно представляет собой число с фиксированной точкой. +1 Давно делаю похожим образом, часто запас разрядов в аккумуляторе делаю равным числу сдвигов для деления на коффициент фильтра (он степень двойки). Т.е. для беззнаковых 10-битных отсчётов и "рециркуляции" 7/8, т.е. весе очередного отсчёта 1/8 будет так: Код uint16_t acc; void update(uint16_t val) { acc = acc - ((acc + 4) >> 3) + val; }
uint16_t get_result() { return (acc + 4) >> 3; } Собственно, с не-степенью двойки будет аналогично, просто Код (acc+4)>>3 езде заменится на Код (acc+DIVIDER/2)/DIVIDER , но я не вижу в этом особого смысла. Если задача так чувствительна к тому, что новый отсчёт будет входить не с 1/8 или 1/16, а имено с 1/10 или 1/13, то скорее всего там и фильтр нужен более высокого порядка.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Mar 2 2009, 16:57
|
Участник

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366

|
Пожалуйста,помогите переделать найденный на этом форуме программный ФНЧ под мои условия,сам не могу разобраться. Код int nStepFilter( int val) { #define shift (4) // n = 2^shift #define lsb ((1 << (shift)) >> 1)
static int x = 0; int av = (x + lsb) >> shift;
x += val - av;
return av; #undef lsb #undef shift }
|
|
|
|
|
Mar 16 2009, 11:10
|
Участник

Группа: Участник
Сообщений: 27
Регистрация: 11-12-08
Из: Earth
Пользователь №: 42 366

|
Остановился на таком варианте Код ta++; if(ta==20){ta=0;temp=tempADC/100;} if(ta==0)tempADC=0; tempADC+=read_adc(0);
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|