Съдържание:

1024 проби анализатор на FFT спектър, използващ Atmega1284: 9 стъпки
1024 проби анализатор на FFT спектър, използващ Atmega1284: 9 стъпки

Видео: 1024 проби анализатор на FFT спектър, използващ Atmega1284: 9 стъпки

Видео: 1024 проби анализатор на FFT спектър, използващ Atmega1284: 9 стъпки
Видео: USB анализатор (24M 8CH logic analyzer) 2024, Юли
Anonim
1024 проби анализатор на FFT спектър, използващ Atmega1284
1024 проби анализатор на FFT спектър, използващ Atmega1284
1024 проби анализатор на FFT спектър, използващ Atmega1284
1024 проби анализатор на FFT спектър, използващ Atmega1284

Този сравнително лесен урок (като се има предвид сложността на този предмет) ще ви покаже как можете да направите много прост анализатор на спектър от 1024 проби, като използвате дъска тип Arduino (1284 тесен) и серийния плотер. Всеки вид съвместима с Arduino платка ще свърши работа, но колкото повече RAM има, толкова по -добра честотна резолюция ще получите. Ще са необходими повече от 8 KB RAM, за да се изчисли FFT с 1024 проби.

Анализът на спектъра се използва за определяне на основните честотни компоненти на сигнала. Много звуци (като тези, произведени от музикален инструмент) са съставени от основна честота и някои хармоници, които имат честота, която е цяло число, кратно на основната честота. Спектърният анализатор ще ви покаже всички тези спектрални компоненти.

Може да искате да използвате тази настройка като честотен брояч или да проверите всякакъв вид сигнали, за които подозирате, че внасят някакъв шум във вашата електронна верига.

Тук ще се съсредоточим върху софтуерната част. Ако искате да направите постоянна верига за конкретно приложение, ще трябва да усилите и филтрирате сигнала. Това предварително кондициониране зависи изцяло от сигнала, който искате да изучавате, в зависимост от неговата амплитуда, импеданс, максимална честота и т.н … Можете да проверите

Стъпка 1: Инсталиране на библиотеката

Ще използваме библиотеката ArduinoFFT, написана от Енрике Кондес. Тъй като искаме да спестим възможно най -много RAM, ще използваме клона за развитие на това хранилище, който позволява да се използва типът на данните с плаващ (вместо двоен) за съхраняване на извадените и изчислени данни. Така че трябва да го инсталираме ръчно. Не се притеснявайте, просто изтеглете архива и го декомпресирайте в папката на вашата библиотека Arduino (например в конфигурацията по подразбиране на Windows 10: C: / Users / _your_user_name_ / Documents / Arduino / libraries)

Можете да проверите дали библиотеката е правилно инсталирана, като компилирате един от предоставените примери, като „FFT_01.ino“.

Стъпка 2: Концепции за преобразуване на Фурие и FFT

Предупреждение: ако не можете да понасяте някаква математическа нотация, може да искате да преминете към Стъпка 3. Както и да е, ако не получите всичко, просто помислете за заключението в края на раздела.

Честотният спектър се получава чрез алгоритъм за бързо преобразуване на Фурие. FFT е цифрова реализация, която приближава математическата концепция на преобразуването на Фурие. Съгласно тази концепция, след като получите еволюцията на сигнал след времева ос, можете да знаете неговото представяне в честотна област, съставена от сложни (реални + въображаеми) стойности. Концепцията е взаимна, така че когато знаете представянето на честотната област, можете да я трансформирате обратно във времевата област и да получите обратно сигнала точно както преди трансформацията.

Но какво ще правим с този набор от изчислени комплексни стойности във времевата област? Е, повечето от тях ще бъдат оставени на инженерите. За нас ще извикаме друг алгоритъм, който ще преобразува тези комплексни стойности в данни за спектралната плътност: това е величина (= интензитет), свързана с всяка честотна лента. Броят на честотната лента ще бъде същият като броя на пробите.

Със сигурност сте запознати с концепцията за еквалайзер, като тази Назад към 80 -те години на миналия век с графичния еквалайзер. Е, ще получим същия вид резултати, но с 1024 ленти вместо 16 и много по -висока разделителна способност. Когато еквалайзерът дава глобален изглед на музиката, финият спектрален анализ позволява точно да се изчисли интензитетът на всяка от 1024 ленти.

Перфектна концепция, но:

  1. Тъй като FFT е дигитализирана версия на преобразуването на Фурие, тя приближава цифровия сигнал и губи известна информация. Така че, строго погледнато, резултатът от FFT, ако се трансформира обратно с обърнат FFT алгоритъм, няма да даде точно оригиналния сигнал.
  2. Също така теорията разглежда сигнал, който не е краен, но това е вечно постоянен сигнал. Тъй като ще го цифровизираме само за определен период от време (т.е. проби), ще бъдат въведени още някои грешки.
  3. И накрая, разделителната способност на аналогово -цифровото преобразуване ще повлияе на качеството на изчислените стойности.

На практика

1) Честотата на вземане на проби (отбелязана fs)

Ще вземаме проби от сигнал, т.е.измерваме амплитудата му на всеки 1/fs секунди. fs е честотата на вземане на проби. Например, ако правим проба при 8 KHz, ADC (аналогово -цифров преобразувател), който е на борда на чипа, ще осигурява измерване на всеки 1/8000 секунди.

2) Броят на пробите (отбелязани N или проби в кода)

Тъй като трябва да получим всички стойности, преди да стартираме FFT, ще трябва да ги съхраним и така ще ограничим броя на пробите. Алгоритъмът FFT се нуждае от няколко проби с мощност 2. Колкото повече проби имаме, толкова по -добре, но отнема много памет, още повече, че ще трябва да съхраняваме и трансформираните данни, които са комплексни стойности. Библиотеката Arduino FFT спестява малко място чрез използване

  • Един масив с име "vReal" за съхраняване на извадените данни и след това реалната част от трансформираните данни
  • Един масив с име "vImag" за съхраняване на въображаемата част от трансформираните данни

Необходимото количество RAM е равно на 2 (масиви) * 32 (бита) * N (проби).

Така че в нашия Atmega1284, който има хубави 16 KB RAM, ще съхраняваме максимум N = 16000*8 /64 = 2000 стойности. Тъй като броят на стойностите трябва да бъде степен 2, ще съхраняваме максимум 1024 стойности.

3) Честотната разделителна способност

БПФ ще изчислява стойности за толкова честотни ленти, колкото броя на пробите. Тези ленти ще обхващат от 0 HZ до честотата на дискретизация (fs). Следователно, честотната резолюция е:

Fresolution = fs / N

Разделителната способност е по -добра, когато е по -ниска. Така че за по -добра разделителна способност (по -ниска) искаме:

  • повече проби и/или
  • по -ниска fs

Но…

4) Минимално fs

Тъй като искаме да видим много честоти, някои от които са много по -високи от "основната честота", не можем да зададем fs твърде ниско. Всъщност съществува теоремата за вземане на проби от Найкуист -Шанън, която ни принуждава да имаме честота на вземане на проби доста над два пъти максималната честота, която бихме искали да тестваме.

Например, ако искаме да анализираме целия спектър от 0 Hz, да речем 15 KHz, което е приблизително максималната честота, която повечето хора могат да чуят отчетливо, трябва да зададем честотата на дискретизация на 30 KHz. Всъщност електрониката често го настройва на 2,5 (или дори 2,52) * максималната честота. В този пример това би било 2,5 * 15 KHz = 37,5 KHz. Обичайните честоти на дискретизация в професионалното аудио са 44,1 KHz (запис на аудио CD), 48 KHz и повече.

Заключение:

Точки 1 до 4 водят до: искаме да използваме възможно най -много проби. В нашия случай с 16 KB RAM устройство ще разгледаме 1024 проби. Искаме да вземаме проби при възможно най -ниската честота на вземане на проби, стига да е достатъчно висока, за да анализираме най -високата честота, която очакваме в нашия сигнал (поне 2,5 * тази честота, поне).

Стъпка 3: Симулиране на сигнал

Симулиране на сигнал
Симулиране на сигнал

За първия ни опит ще променим леко примера TFT_01.ino, даден в библиотеката, за да анализираме сигнал, съставен от

  • Основната честота, зададена на 440 Hz (музикален A)
  • 3-ти хармоник при половината мощност на фундамента ("-3 dB")
  • 5-та хармоника при 1/4 от мощността на фундамента ("-6 dB)

Можете да видите на снимката над получения сигнал. Наистина много прилича на реален сигнал, който понякога може да се види на осцилоскоп (бих го нарекъл "Батман") в ситуация, когато има изрязване на синусоидален сигнал.

Стъпка 4: Анализ на симулиран сигнал - кодиране

0) Включете библиотеката

#include "arduinoFFT.h"

1) Определения

В разделите за декларации имаме

const байт adcPin = 0; // A0

const uint16_t проби = 1024; // Тази стойност ВИНАГИ трябва да бъде степен на 2 const uint16_t samplingFrequency = 8000; // Ще повлияе на максималната стойност на таймера в timer_setup () SYSCLOCK/8/samplingFrequency трябва да бъде цяло число

Тъй като сигналът има 5 -та хармоника (честотата на тази хармоника = 5 * 440 = 2200 Hz), трябва да зададем честотата на дискретизация над 2,5 * 2200 = 5500 Hz. Тук избрах 8000 Hz.

Декларираме и масивите, където ще съхраняваме необработените и изчислени данни

плаващ vReal [проби];

поплавък vImag [проби];

2) Инстанция

Създаваме ArduinoFFT обект. Dev версията на ArduinoFFT използва шаблон, така че можем да използваме или плаващия, или двойния тип данни. Float (32 бита) е достатъчно по отношение на цялостната прецизност на нашата програма.

ArduinoFFT FFT = ArduinoFFT (vReal, vImag, проби, samplingFrequency);

3) Симулиране на сигнала чрез попълване на масива vReal, вместо да се попълва с ADC стойности.

В началото на цикъла попълваме масива vReal с:

плаващи цикли = (((проби) * signalFrequency) / samplingFrequency); // Брой цикли на сигнала, които пробата ще прочете

for (uint16_t i = 0; i <проби; i ++) {vReal = float ((амплитуда * (sin ((i * (TWO_PI * цикли))) / проби)))); / * Изграждане на данни с положителни и отрицателни стойности */ vReal += float ((амплитуда * (sin ((3 * i * (TWO_PI * цикъла))/ проби)))/ 2.0);/ * Изграждане на данни с положителни и отрицателни стойности */ vReal += float ((амплитуда * (sin ((5 * i * (TWO_PI * цикъла)) / проби)))) / 4.0); / * Изграждане на данни с положителни и отрицателни стойности * / vImag = 0.0; // Въображаемата част трябва да бъде нулирана в случай на цикъл, за да се избегнат грешни изчисления и преливания}

Добавяме дигитализация на фундаменталната вълна и двата хармоника с по -малка амплитуда. След това инициализираме въображаемия масив с нули. Тъй като този масив е попълнен от алгоритъма FFT, трябва да го изчистваме отново преди всяко ново изчисление.

4) FFT изчисления

След това изчисляваме FFT и спектралната плътност

FFT.windowing (FFTWindow:: Hamming, FFTDirection:: Forward);

FFT.compute (FFTDirection:: Forward); / * Изчислете FFT */ FFT.complexToMagnitude (); / * Изчисляване на величини */

Операцията FFT.windowing (…) променя необработените данни, защото изпълняваме FFT върху ограничен брой проби. Първата и последната проба представляват прекъсване (няма "нищо" от едната им страна). Това е източник на грешка. Операцията "прозорец" има тенденция да намали тази грешка.

FFT.compute (…) с посока "Напред" изчислява трансформацията от времевата област към честотната област.

След това изчисляваме стойностите на величината (т.е. интензивността) за всяка от честотните ленти. Масивът vReal вече е изпълнен със стойности на величините.

5) Чертеж на сериен плотер

Нека отпечатаме стойностите на серийния плотер, като извикаме функцията printVector (…)

PrintVector (vReal, (мостри >> 1), SCL_FREQUENCY);

Това е обща функция, която позволява отпечатване на данни с времева ос или честотна ос.

Отпечатваме и честотата на лентата, която има най -високата стойност на величината

float x = FFT.majorPeak ();

Serial.print ("f0 ="); Serial.print (x, 6); Serial.println ("Hz");

Стъпка 5: Анализ на симулиран сигнал - резултати

Анализ на симулиран сигнал - резултати
Анализ на симулиран сигнал - резултати

Виждаме 3 скока, съответстващи на основната честота (f0), 3 -та и 5 -та хармоника, с половината и 1/4 от величината f0, както се очаква. Можем да прочетем в горната част на прозореца f0 = 440.430114 Hz. Тази стойност не е точно 440 Hz поради всички причини, обяснени по -горе, но е много близка до реалната стойност. Всъщност не беше необходимо да се показват толкова незначителни десетични знаци.

Стъпка 6: Анализ на реален сигнал - Окабеляване на ADC

Анализ на реален сигнал - Окабеляване на ADC
Анализ на реален сигнал - Окабеляване на ADC

Тъй като знаем как да действаме на теория, бихме искали да анализираме реален сигнал.

Окабеляването е много просто. Свържете заземяването заедно и сигналната линия към щифта A0 на вашата платка чрез последователен резистор със стойност от 1 KOhm до 10 KOhm.

Този сериен резистор ще защити аналоговия вход и ще избегне звънене. Тя трябва да бъде възможно най -висока, за да се избегне звънене, и възможно най -ниска, за да осигури достатъчно ток за бързо зареждане на ADC. Вижте листа с данни за MCU, за да знаете очаквания импеданс на сигнала, свързан към входа на ADC.

За тази демонстрация използвах генератор на функции за подаване на синусоидален сигнал с честота 440 Hz и амплитуда около 5 волта (най -добре е, ако амплитудата е между 3 и 5 волта, така че ADC да се използва почти в пълен мащаб), чрез резистор 1.2 KOhm.

Стъпка 7: Анализ на реален сигнал - кодиране

0) Включете библиотеката

#include "arduinoFFT.h"

1) Декларации и инстанции

В раздела за декларация дефинираме ADC входа (A0), броя на пробите и честотата на дискретизация, както в предишния пример.

const байт adcPin = 0; // A0

const uint16_t проби = 1024; // Тази стойност ВИНАГИ трябва да бъде степен на 2 const uint16_t samplingFrequency = 8000; // Ще повлияе на максималната стойност на таймера в timer_setup () SYSCLOCK/8/samplingFrequency трябва да бъде цяло число

Създаваме обект ArduinoFFT

ArduinoFFT FFT = ArduinoFFT (vReal, vImag, проби, samplingFrequency);

2) Настройка на таймер и ADC

Задаваме таймер 1, така че той да циклира при честотата на дискретизация (8 KHz) и повдига прекъсване при сравнение на изхода.

void timer_setup () {

// нулиране на Таймер 1 TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; TCCR1B = бит (CS11) | бит (WGM12); // CTC, прескалиране на 8 TIMSK1 = бит (OCIE1B); OCR1A = ((16000000 /8) / samplingFrequency) -1; }

И настройте ADC така

  • Използва A0 като вход
  • Задейства автоматично на всеки таймер 1 изход за сравнение на съвпадение B
  • Генерира прекъсване, когато преобразуването приключи

ADC часовникът е настроен на 1 MHz, чрез предварително мащабиране на системния часовник (16 MHz) на 16. Тъй като всяко преобразуване отнема приблизително 13 часа в пълен мащаб, преобразуванията могат да бъдат постигнати на честота 1/13 = 0,076 MHz = 76 KHz. Честотата на вземане на проби трябва да бъде значително по -ниска от 76 KHz, за да позволи на ADC да има време да вземе проби от данните. (избрахме fs = 8 KHz).

void adc_setup () {

ADCSRA = бит (ADEN) | бит (ADIE) | бит (ADIF); // включва ADC, искам прекъсване при завършване ADCSRA | = бит (ADPS2); // Предсказващо устройство от 16 ADMUX = бит (REFS0) | (adcPin & 7); // настройка на ADC входа ADCSRB = бит (ADTS0) | бит (ADTS2); // Таймер/Брояч1 Сравни източник на задействане на Match B ADCSRA | = бит (ADATE); // включете автоматичното задействане}

Декларираме манипулатора на прекъсване, който ще бъде извикан след всяко преобразуване на ADC, за да съхранява преобразуваните данни в масива vReal и изчистване на прекъсването

// ADC пълен ISR

ISR (ADC_vect) {vReal [resultNumber ++] = ADC; if (resultNumber == проби) {ADCSRA = 0; // изключете ADC}} EMPTY_INTERRUPT (TIMER1_COMPB_vect);

Можете да имате изчерпателно обяснение за преобразуването на ADC на Arduino (analogRead).

3) Настройка

Във функцията за настройка изчистваме въображаемата таблица с данни и извикваме функциите за настройка на таймера и ADC

нулаI (); // функция, която задава на 0 всички въображаеми данни - обяснено в предишния раздел

timer_setup (); adc_setup ();

3) Цикъл

FFT.dcRemoval (); // Премахнете DC компонента на този сигнал, тъй като ADC е посочен към маса

FFT.windowing (FFTWindow:: Hamming, FFTDirection:: Forward); // Претегляне на данни FFT.compute (FFTDirection:: Forward); // Изчисляване на FFT FFT.complexToMagnitude (); // Изчисляване на величини // отпечатване на спектъра и основната честота f0 PrintVector (vReal, (мостри >> 1), SCL_FREQUENCY); float x = FFT.majorPeak (); Serial.print ("f0 ="); Serial.print (x, 6); Serial.println ("Hz");

Премахваме постояннотоковия компонент, тъй като ADC е свързан към земята и сигналът е центриран около 2,5 волта приблизително.

След това изчисляваме данните, както е обяснено в предишния пример.

Стъпка 8: Анализ на реален сигнал - резултати

Анализ на реален сигнал - резултати
Анализ на реален сигнал - резултати

Всъщност виждаме само една честота в този прост сигнал. Изчислената основна честота е 440.118194 Hz. Тук отново стойността е много близко приближение на реалната честота.

Стъпка 9: Какво ще кажете за отрязан синусоидален сигнал?

Какво ще кажете за отрязан синусоидален сигнал?
Какво ще кажете за отрязан синусоидален сигнал?

Сега нека да презаредим малко ADC, като увеличим амплитудата на сигнала над 5 волта, така че той е подрязан. Не натискайте прекалено много, за да не унищожите ADC входа!

Можем да видим появата на някои хармоници. Изрязването на сигнала създава високочестотни компоненти.

Виждали сте основите на анализа на FFT на дъска на Arduino. Сега можете да опитате да промените честотата на вземане на проби, броя на пробите и параметъра за прозорец. Библиотеката също така добавя някои параметри за по -бързо изчисляване на FFT с по -малка прецизност. Ще забележите, че ако зададете честотата на вземане на проби твърде ниска, изчислените величини ще изглеждат напълно погрешни поради спектралното сгъване.

Препоръчано: