Перейти к содержанию
    

Цифровой Фильтр на ATmega

Добрый день, давно не заходил с проблемами. КТо может помочь куском кода к цифровому фильтру для Codevision. Даже с чего начать не знаю, куда не сунусь везде только формулы да теория. Хочу попробовать отфильтровать сигнал снятый с ацп Atmegи, и преобразовать на выход с помощью ШИМ и RC цепи

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Радиолюбительская конструкция.

Изменено пользователем Genadi Zawidowski

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

куда не сунусь везде только формулы да теория.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Боюсь, что меня сейчас поколотят, но попробую облегчить вам жизнь. :)

1) Берём прогу расчёта фильтров, которую я приложил.

2) Рисуем фильтр с нужными параметрами.

3) Садимся и вручную округляем коэффициенты до ближайшего двоичного (если непонятно как, то спрашивайте - поясню)

4) Переписываем (на бумаге) формулу каждого звена ч/з сдвиги

5) объединяем сдвиги и получаем общую прогу.

6) Я обычно, проверяю АЧХ полученной проги на IBM, чтобы исключить ошибки.

 

Правда такова, что фильтрация фильтром 2-4 порядка, часто бывает лаконичнее, эфективнее, красивее и, однозначно менее затратно по ресурсам чем скользящее усреднение.

 

Так, например, фильтр Y(i) = X(i) + 0.875Y(i-1) - X(i-1) прекрасно убирает постоянную составляющую. Иногда использовал такое свойство фильтра, как коэффициент передачи. Например требуется ослабить сигнал, либо наоборот его усилить.

 

Конечно, если более-менее сложная задача, то я сначала полностью моделирую процесс и обязательно его визуализирую. На всех этапах. Далее отрабатываю все формулы на модели. Потом уже переношу всё это на однокристалку. Для меня так проще.

Могу чего-нибудь на простом примере показать.

ciirf1.zip

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1) Берём прогу расчёта фильтров, которую я приложил.

А для КИХ есть что-то подобное?

БИХ не всегда подходит из-за длинных "хвостов"

Хотя при том же порядке фильтрует намного лучше.

 

У меня дома валяется что-то подобное, но вся проблема в вычислении коэффициентов передачи.

Неплохо было бы автоматизировать

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неплохо было бы автоматизировать

Тоже вот подумываю.... Тока всё чё то руки не доходят... :laughing:

 

В той проге, что я выложил можно править коэффициенты и смотреть результирующий график. Это очень удобно. Иногда расчётное значение весьма сложное получается, сначала пробуешь обрезать лишнее и смотришь на результат. :)

Есть конечно и QED, но как то не сложилось ... :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Могу чего-нибудь на простом примере показать.

Да покажите пример пожалуйста. Например приведенный Вами же фильтр который убирает постоянку.

Думаю топикстартеру будет полезно, да и многим другим тоже(в том числе и мне :rolleyes: ).

Заранее благодарю.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А для КИХ есть что-то подобное?

А почему бы не использовать Matlab?

Там есть и КИХ и БИХ. Плюс есть ФЧХ и картинка с полюсами! Плюс всё можно качественно промоделировать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А почему бы не использовать Matlab?
Может потому, что он стоит немалых для любителя денег?

Есть бесплатная альтернатива - SciLab, но в нем нет таких красивых и удобных надстроек.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Много лет назад экспериментировал на ATmega16 со звуковыми частотами. То же АЦП-цифровой фильтр-ШИМ-RC. Строил НЧ и ВЧ фильтры с БИХ, с аккумулятором сумм до 32-бит, а также коэффициентами до 16-бит. Фильтры в итоге всегда самовозбуждались. Тема по прежнему актуальна.

Кто либо построил и реально использовал такие фильтры, есть ли положительные результаты, чем рассчитывали коэффициенты?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да покажите пример пожалуйста....

Я очень сильно упрощу. Те кто владеют теорией - пусть лучше не читают. :) Основная цель будет - показать, что всё не так уж страшно. :) Кто хочет глубже - без учебников не обойтись. Я не буду касаться таких свойств фильтров как фазовые искажения, импульсная х-ка и т.п. Только частотная и амплитудная.

 

Представим себе, что у нас входной сигнал 1200 Гц. Мы его принимаем на АЦП AVR. В связи с тем, что АЦП AVR работает лишь с положительным сигналом, нам пришлось его пропустить ч/з разделительный конденсатор и выставить резисторами 0.5 от опорного напряжения (типичный случай). В результате мы получаем синус от 0 до 1.1V(опора) max, что составит 0-1023 в единицах АЦП. Но для дальнейших расчтётов нам лучше работать с сигналом имеющим знак. Можно, не мудрствуя лукаво вычесть 512 и получить сигнал +/- 512. Но, резистивный делитель может быть сыставлен не совсем точно (например 0.6V) и в результате мы получим сдвинутый сигнал и соответственно постоянную составляющую в нём. Плюс, допустим мы хотим зарезать сетевые помехи (50 и 100 Гц) и какие-то высокочастотные помехи (например 5кГц). Короче мы решили слегка фильтрануть сигнал для уменьшения воздействия внешних помех. Допустим амплитудная характеристика нам важна только относительная. Ну к примеру идёт манипуляция по принципу есть сигнал/нет сигнала. :) Таким образом надо его выделить и обработать пиковым детектором. :) Скажем частота может плавать незначительно.

 

Берём прогу, что я приложил в прошлом посте (спасибо автору) и строим полосовой фильтр. (clip1)

 

Теперь немного о дробной арифметике.

Все знают что если 1 поделить на 2 то получим 0.5. Если ещё раз разделить = 0.25. Что такое деление на 2 - сдвиг вправо на 1 разряд. Таким образом если себе представить дробную часть, то двочное число 1000000B = 0.5, 01000000B = 0.25, 01100000B = 0.25+0125 = 0.375. Из этого понятно, что умножение на дробные коэффициенты можно представить как сдвиги и сложения. Желательно подобрать такие коэффициенты, чтобы число этих операций было минимальным при допустимой погрешности вычислений. Понятно также, что если у нас сигнал 10 бит, то сдвиги на 8-9 разрядов практически не внесут значимых результатов. Надо также учитывать, что фильтр может искажать сигнал по амплитуде. Для этого желательно следить за амплитудным значением на графике АЧХ. Для простоты картины можно отталкиваться от того, что сумма коэффициентов должна быть равна 1. Если это не так, то результирующий сигнал увеличится либо уменьшится. Часто это бывает непринципиально или даже желательно.

 

Исходя из этого "подкорректируем" коэффициенты X фильтра и перерисуем график воспользовавшись программой. (clip2)

Поступим аналогично с Y. (clip3)

После построения графика - видим что размах полезного сигнала изменился в 1.3 раза. Можно, естественно, подобрать коэффициенты более точно, но для поставленной задачи нам это ни к чему.

 

Теперь перенесём это на бумагу.

Теперь поясню, для тех кто непонимает, что это за X, Y, i. :)

Надо понимать, что фильтр состоит из звеньев (в данном фильтре 1 звено). Звенья независимы. Выходные данные первого звена - входные второго. И так далее.

В данном контексте будем считать так:

X входные данные первого звена (сигнал с АЦП)

Y выходные данные первого звена (выход фильтра). Если у нас более одного звена, то Y это входные данные второго звена.

(i) текущее значение

(i-1) предыдущее значение (значение полученное в предыдущей выборке)

(i-2) значение полученное 2 выборки назад и т.д.

Я упрощу выражение и вместо X(i-1) буду писать X1 и так далее.

 

Итак X коэфф. фильтра будут выглядеть так:

0.0625*(X0+X4)-0.125*X2 = ((X0+X4)>>4)-(X2>>3)

 

Y коэф. будут выглядеть так

-(-1.875*Y1+2*Y2-1.0625*Y3+0.3125*Y4) = 1.875*Y1-2*Y2+1.0625*Y3-0.3125Y4 = 2*Y1-0.125*Y1-2*Y2+Y3+0.0625*Y3-0.3125*Y4

= (Y1<<1)-(Y1>>3)-(Y2<<1)+Y3+(Y3>>4)-(Y4>>2)-(Y4>>4)

 

Итого общая формула (1):

Y0 = ((X0+X4)>>4)-(X2>>3)+(Y1<<1)-(Y1>>3)-(Y2<<1)+Y3+(Y3>>4)-(Y4>>2)-(Y4>>4)

 

Для понимания дальнейшего я перепишу по другому первых 2 члена

((X0+X4)>>4)-(X2>>3) = ((X0+X4)>>1)-X2)>>3

По сути это вынос множителя за скобки и математически ничего не даёт. На самом деле это уменьшает объём вычислений и повышает их точность.

 

Давайте перепишем полученную формулу фильтра (1) с учётом последнего замечания, но не ввиде формулы, а в виде последовательности вычислений.

Для этого введём временную переменную Temp и будем выбирать равные коэф. сдвигов. Я в коментах буду указывать номер члена формулы (1) которые я взял.

В результате получим:

 

Temp = (X0+X4+Y3-Y4)>>1; // все члены со сдвигом 4 = 1,7,9

Temp += (-X2-Y1); // ... 3 = 2,4 Естественно лучше записать Temp -= X2+Y1;

Temp >>= 1;

Temp += (-Y4); // .... 2 = 8 Опять таки Temp -= Y4;

Temp >>= 2; // Поскольку у нас нет членов со сдвигом >>1

Temp += (Y1<<1)-(Y2<<1)+Y3;

 

Из получившейся проги видно что результирующий коэфф. действительно будет более 1 так как в последней строчки получим 1 а в предпоследней ещё 1/4, но это просто так, для проверки себя.

Я не буду сейчас писать кольцевой буфер для входных значений фильтра. Чтобы было понятней что я делаю. Кстати если используем ФНЧ или ФВЧ, то там вообще это без надобности. Тем не менее, я часто пишу кольцевой буфер с отладочными целями, чтобы в процессе отладки увидеть как работает фильтр и некоторые его звенья. После завершения отладки - могу выкинуть, либо сохранить.

 

Итак, будет выглядеть так:

 

X4=X3; // Новый сэмпл сдвигает значения

X3=X2;

X2=X1;

X1=X0;

X0=ADCH;

Temp = (X0+X4+Y3-Y4)>>1; // все члены со сдвигом 4 = 1,7,9

Temp += (-X2-Y1); // ... 3 = 2,4 Естественно лучше записать Temp -= X2+Y1;

Temp >>= 1;

Temp += (-Y4); // .... 2 = 8 Опять таки Temp -= Y4;

Temp >>= 2; // Поскольку у нас нет членов со сдвигом >>1

Temp += (Y1<<1)-(Y2<<1)+Y3;

Y4=Y3; // Новый сэмпл сдвигает значения

Y3=Y2;

Y2=Y1;

Y1=Y0;

Y0=Temp;

 

Ну вот и всё.

На последок хотелось бы отметить что имеет значение разрядность чисел. Бывает, при сложных фильтрах, где малые коэффициенты происходит потеря точности, которая сводит на нет весь результат. Особенно если пользовать 8 бит данные. Часто бывает, при пограничных значениях (близких к максимому или минимому) что происходит потеря точности из-за переполнений и в этом случае достаточно использовать повышенную разрядность только переменной Temp.

Но в этом смысле преимущество Си просто фантастическое.

Представим что я написал этот фильтр для разрядности АЦП 8 бит. Тогда объявление переменных следующее:

int8_t X0,X1,X2,X3,X4,Y1,Y2,Y3,Y4,Temp;

Представим, что в процессе работы/наладки, я пришёл к выводу, что мне надо повысить разрядность до 10 бит.

Исправления будут следующие:

1) В строке объявления переменных надо исправить uint8_t на uint16_t (2 символа)

2) в строке программы изменить ADCH на ADC (1 символ)

3) В строке инициализации АЦП убрать (1<<ADLAR) (ещё 10 символов)

Итого исправления 13 символов. Тот кто пишет на ассемблере может оценить объём работы необходимый для перевода такого фильтра с 8 на 16 бит.

 

 

 

Много лет назад экспериментировал на ATmega16 со звуковыми частотами. То же АЦП-цифровой фильтр-ШИМ-RC. Строил НЧ и ВЧ фильтры с БИХ, с аккумулятором сумм до 32-бит, а также коэффициентами до 16-бит. Фильтры в итоге всегда самовозбуждались. Тема по прежнему актуальна.

Кто либо построил и реально использовал такие фильтры, есть ли положительные результаты, чем рассчитывали коэффициенты?

Собственно цифровые фильтры хороши именно тем, что без разницы на чем они считаются. Mega16/ARM/PIC - при тех же коэффициентах и разрядностях работать будут одинаково. По моему очевидно, что ATMega не разрабатывалась для обработки звука. Для этого есть более удобные процы. Я работал со звуком только в рамках телефонной линии. При выборке 8кГц и верхней частоте 3.4кГц требуется, для удовлетворительного качества, АЦП разрядностью 14 бит. При этом для обработки двух потоков приходится греметь всеми костями ATMega на частоте 16МГц. Пробовал обеспечить вывод разными способами, в том числе и ШИМом. Понятно что 14 бит ШИМ на 8кГц даёт сотни МГц поэтому упрощали до 8 бит. Даже при 8 бит для получения приемлемого сигнала, требовалась выходная обвеска сопоставимая с размерами изделия. Причём настройка-регулировка была бы непростая.

Короче из всех вариантов этот был самым отвратительным. Существуют внешние цапы, в том числе звуковые, в том числе с нелинейной характеристикой и встроенными цифровыми фильтрами. Стоят - копейки. С фильтрами проблем не было. Правда, как я говорил обработка была весьма топорная.

post-11521-1253215209_thumb.jpg

post-11521-1253215233_thumb.jpg

post-11521-1253215250_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

...Основная цель будет - показать, что всё не так уж страшно...

Спасибо за пример. Так красиво все расписали, что решил руками потрогать. То ли что не так, то ли руки неровные.. :)

По-порядку. Система такая: несколько сигналов (16 штук, от 300 до 1800 Гц, амплитуда каждого 0,45В) через сумматор на ОУ с питанием -+15В (плюс добавляется постоянка 7,5В и инвертируется вторым ОУ, им же приводится к диапазону 0-5В вся сумма сигналов). Оцифровку ведет мега48 с частотой 8кГц, ЦАП 8 бит на 2R-R матрице.

Схема:

post-29581-1253305965_thumb.png

Вкратце работает так: каждые 125мкс переполняется таймер, за ним автотриггером срабатывает запуск преобразования. По окончании преобразования в прерывании ставится флаг, по которому в основном цикле и работаем. Обновлять ЦАП можно и в прерывании, но джиттера нет и в основном цикле по флагу.

Взял Вашу реализацию фильтра без переделок, благо частоты близкие...

Так вот, если по флагу делать ЦАП без преобразований [PORTD = (unsigned char) ((X0+512)/4);] то получается:

post-29581-1253306276_thumb.jpg

Т.е. все нормально. Даже амплитуды все совпадают. Это при любом количестве сигналов (фильтр при этом продолжает просчитываться)...

Далее переходим к использованию фильтра

Код:

volatile unsigned char fl_adc_result;
int X0,X1,X2,X3,X4,Y0,Y1,Y2,Y3,Y4,Temp;
...
if(fl_adc_result) 
        { PORTB|=(1<<0); //debug time
            
            X4=X3; // Новый сэмпл сдвигает значения
            X3=X2;
            X2=X1;
            X1=X0;
            X0=ADCW-512;
            /*
            Temp = (X0+X4+Y3-Y4)>>1; // все члены со сдвигом 4 = 1,7,9
            Temp += (-X2-Y1); // ... 3 = 2,4 Естественно лучше записать Temp -= X2+Y1;
            Temp >>= 1;
            Temp += (-Y4); // .... 2 = 8 Опять таки Temp -= Y4;
            Temp >>= 2; // Поскольку у нас нет членов со сдвигом >>1
            Temp += (Y1<<1)-(Y2<<1)+Y3;
                        */
            Temp = (X0+X4+Y3-Y4)/2; // все члены со сдвигом 4 = 1,7,9
            Temp -= X2+Y1; // ... 3 = 2,4 Естественно лучше записать Temp -= X2+Y1;
            Temp /= 2;
            Temp -= Y4; // .... 2 = 8 Опять таки Temp -= Y4;
            Temp /= 4; // Поскольку у нас нет членов со сдвигом >>1
            Temp += (Y1-Y2)*2+Y3;

            Y4=Y3; // Новый сэмпл сдвигает значения
            Y3=Y2;
            Y2=Y1;
            Y1=Y0;
            Y0=Temp;            
            
            //PORTD = (unsigned char) ((X0+512)/4); //без преобразования            
            
            PORTD= (unsigned char) ((Y0+512)/4); 
            fl_adc_result=0;
            
        PORTB&=~(1<<0); //debug

        }

При этом, получается: когда сигналов нет, на выходе фильтра постоянка 2,5В. Как и на входе, т.е. вроде правильно. Но при подаче любого сигнала на вход, а также любой их комбинации, фильтр начинает "колбасить":

post-29581-1253307048_thumb.jpg

Это продолжается и после отключения сигналов, т.е. Xi и/или Yi не сходятся :(

Первая мысль была что компилятор (AVRGCC 4.3.2 Binutils 2.19 avr-libc 1.6.6, в протеусе не дебажится у меня) некорректно реализует сдвиги signed чисел. Потому сделал явно делением. Умный к0мпилятор и сам сдвиги сделает. Та же картина... Обрезание результата АЦП до 8-ми бит (дабы избежать переполнения) картины не меняет...

Что может быть не так в консерватории?...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Скорее всего причина вот тут:

    X0=ADCW-512;

Попробуйте заменить на

    X0=(int)ADCW-512;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Попробуйте заменить на

    X0=(int)ADCW-512;

Картина та же...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тогда наоборот, замените в объявлении Xi, Yi и Temp int на unsigned int.

Ну и не вычитайте 512, конечно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...