Taula de continguts:

1024 mostres d'analitzador d'espectre FFT mitjançant un Atmega1284: 9 passos
1024 mostres d'analitzador d'espectre FFT mitjançant un Atmega1284: 9 passos

Vídeo: 1024 mostres d'analitzador d'espectre FFT mitjançant un Atmega1284: 9 passos

Vídeo: 1024 mostres d'analitzador d'espectre FFT mitjançant un Atmega1284: 9 passos
Vídeo: Wolds thinnest Real Time Spectrum Analyzer | Flexible/Bendable AMOLED Display & Aaronia SPECTRAN V6 2024, De novembre
Anonim
1024 mostres d'analitzador d'espectre FFT mitjançant un Atmega1284
1024 mostres d'analitzador d'espectre FFT mitjançant un Atmega1284
1024 mostres d'analitzador d'espectre FFT mitjançant un Atmega1284
1024 mostres d'analitzador d'espectre FFT mitjançant un Atmega1284

Aquest tutorial relativament senzill (tenint en compte la complexitat d’aquest tema) us mostrarà com podeu fer un analitzador d’espectre de mostres de 1024 molt senzill mitjançant una placa tipus Arduino (1284 Narrow) i el traçador en sèrie. Farà qualsevol tipus de placa compatible amb Arduino, però com més memòria RAM tingui, obtindreu la millor resolució de freqüència. Necessitarà més de 8 KB de RAM per calcular el FFT amb 1024 mostres.

L'anàlisi de l'espectre s'utilitza per determinar els components de freqüència principals d'un senyal. Molts sons (com els produïts per un instrument musical) es componen d'una freqüència fonamental i d'alguns harmònics que tenen una freqüència que és múltiple enter de la freqüència fonamental. L'analitzador d'espectre us mostrarà tots aquests components espectrals.

Us recomanem que utilitzeu aquesta configuració com a comptador de freqüències o per comprovar qualsevol tipus de senyal que sospiteu que provoca soroll al circuit electrònic.

Ens centrarem aquí en la part del programari. Si voleu crear un circuit permanent per a una aplicació específica, haureu d'amplificar i filtrar el senyal. Aquest condicionament previ depèn totalment del senyal que vulgueu estudiar, en funció de la seva amplitud, impedància, freqüència màxima, etc.

Pas 1: Instal·lació de la biblioteca

Utilitzarem la biblioteca ArduinoFFT escrita per Enrique Condes. Com que volem estalviar memòria RAM el màxim possible, utilitzarem la branca de desenvolupament d’aquest dipòsit que permet utilitzar el tipus de dades flotant (en lloc de doble) per emmagatzemar les dades mostrejades i calculades. Per tant, hem d’instal·lar-lo manualment. No us preocupeu, només heu de descarregar l’arxiu i descomprimir-lo a la carpeta de la biblioteca Arduino (per exemple, a la configuració predeterminada del Windows 10: C: / Users / _el vostre_usuari_nom_ / Documents / Arduino / libraries)

Podeu comprovar que la biblioteca està instal·lada correctament compilant un dels exemples proporcionats, com ara "FFT_01.ino".

Pas 2: Conceptes de transformada de Fourier i FFT

Advertiment: si no suporteu veure cap notació matemàtica, potser voldreu passar al pas 3. De tota manera, si no ho obteniu tot, només cal considerar la conclusió al final de la secció.

L’espectre de freqüències s’obté a través d’un algoritme de transformada ràpida de Fourier. FFT és una implementació digital que s’acosta al concepte matemàtic de la transformada de Fourier. Sota aquest concepte, un cop obtingueu l’evolució d’un senyal seguint un eix temporal, podeu conèixer-ne la representació en un domini de freqüència, compost per valors complexos (reals + imaginaris). El concepte és recíproc, de manera que quan coneixeu la representació de dominis de freqüència podeu transformar-la al domini temporal i recuperar el senyal exactament com abans de la transformació.

Però, què farem amb aquest conjunt de valors complexos calculats en el domini temporal? Bé, la major part es deixarà als enginyers. Per a nosaltres anomenarem un altre algorisme que transformarà aquests valors complexos en dades de densitat espectral: és a dir, un valor de magnitud (= intensitat) associat a cada banda de freqüència. El nombre de bandes de freqüència serà el mateix que el nombre de mostres.

Segur que coneixeu el concepte d’equalitzador, com aquest Tornar als anys vuitanta amb l’equalitzador gràfic. Doncs obtindrem el mateix tipus de resultats, però amb 1024 bandes en lloc de 16 i molta més resolució d’intensitat. Quan l'equalitzador proporciona una visió global de la música, la fina anàlisi espectral permet calcular amb precisió la intensitat de cadascuna de les 1024 bandes.

Un concepte perfecte, però:

  1. Com que el FFT és una versió digitalitzada de la transformada de Fourier, s'aproxima al senyal digital i perd certa informació. Per tant, en sentit estricte, el resultat del FFT si es transforma de nou amb un algorisme FFT invertit no donaria exactament el senyal original.
  2. També la teoria considera un senyal que no és finit, però que és un senyal constant i durador. Com que el digitalitzarem només durant un període de temps determinat (és a dir, mostres), s’introduiran alguns errors més.
  3. Finalment, la resolució de la conversió analògica a digital afectarà la qualitat dels valors calculats.

En la pràctica

1) La freqüència de mostreig (fs assenyalat)

Mostrem un senyal, és a dir, mesurem la seva amplitud, cada 1 / fs segons. fs és la freqüència de mostreig. Per exemple, si prenem mostres a 8 KHz, l'ADC (convertidor analògic a digital) que hi ha a bord del xip proporcionarà una mesura cada 1/8000 de segons.

2) El nombre de mostres (anotades N o mostres al codi)

Com que necessitem obtenir tots els valors abans d'executar el FFT, haurem d'emmagatzemar-los i, per tant, limitarem el nombre de mostres. L’algorisme FFT necessita una sèrie de mostres que és una potència de 2. Com més mostres tinguem millor, però necessita molta memòria, tant més necessitarem per emmagatzemar les dades transformades, que són valors complexos. La biblioteca Arduino FFT estalvia una mica d’espai mitjançant l’ús

  • Una matriu anomenada "vReal" per emmagatzemar les dades mostrejades i després la part real de les dades transformades
  • Una matriu anomenada "vImag" per emmagatzemar la part imaginària de les dades transformades

La quantitat necessària de RAM és igual a 2 (matrius) * 32 (bits) * N (mostres).

Així doncs, al nostre Atmega1284 que té una bona memòria RAM de 16 KB, emmagatzemarem un màxim de valors N = 16000 * 8/64 = 2000. Com que el nombre de valors ha de ser una potència de 2, emmagatzemarem un màxim de 1024 valors.

3) La resolució de freqüència

El FFT calcularà valors per a tantes bandes de freqüència com el nombre de mostres. Aquestes bandes abastaran des de 0 HZ fins a la freqüència de mostreig (fs). Per tant, la resolució de freqüència és:

Fresolution = fs / N

La resolució és millor quan és més baixa. Per tant, per a una millor resolució (menor) volem:

  • més mostres i / o
  • un fs inferior

Però …

4) Fs mínim

Com que volem veure moltes freqüències, algunes d'elles molt superiors a la "freqüència fonamental", no podem establir fs massa baixes. De fet, hi ha el teorema de mostreig de Nyquist – Shannon que ens obliga a tenir una freqüència de mostreig molt superior al doble de la freqüència màxima que ens agradaria provar.

Per exemple, si ens agradaria analitzar tot l’espectre des de 0 Hz fins a 15 KHz, que és aproximadament la freqüència màxima que la majoria dels humans poden escoltar de manera diferent, hem d’establir la freqüència de mostreig a 30 KHz. De fet, els electrònics solen establir-la en 2,5 (o fins i tot en 2,52) * la freqüència màxima. En aquest exemple, seria de 2,5 * 15 KHz = 37,5 KHz. Les freqüències de mostreig habituals en àudio professional són de 44,1 KHz (enregistrament de CD d’àudio), 48 KHz i més.

Conclusió:

Els punts 1 a 4 donen lloc a: volem fer servir el màxim de mostres possible. En el nostre cas, amb un dispositiu RAM de 16 KB, considerarem 1024 mostres. Volem obtenir mostres a la freqüència de mostreig més baixa possible, sempre que sigui prou alta per analitzar la freqüència més alta que esperem en el nostre senyal (almenys 2,5 * aquesta freqüència).

Pas 3: Simulació d'un senyal

Simulació d'un senyal
Simulació d'un senyal

Per al nostre primer intent, modificarem lleugerament l’exemple de TFT_01.ino donat a la biblioteca per analitzar un senyal compost per

  • La freqüència fonamental, establerta a 440 Hz (musical A)
  • 3r harmònic a la meitat de la potència del fonamental ("-3 dB")
  • 5è harmònic a 1/4 de la potència del fonamental ("-6 dB)

Podeu veure a la imatge superior el senyal resultant. De fet, s’assembla molt a un senyal real que de vegades es pot veure en un oscil·loscopi (l’anomenaria "Batman") en el cas que hi hagi un retall d’un senyal sinusoïdal.

Pas 4: Anàlisi d'un senyal simulat: codificació

0) Incloeu la biblioteca

#include "arduinoFFT.h"

1) Definicions

A les seccions de declaracions, tenim

byte const adcPin = 0; // A0

const uint16_t samples = 1024; // Aquest valor SEMPRE ha de ser una potència de 2 const uint16_t samplingFrequency = 8000; // Afectarà el valor màxim del temporitzador a timer_setup () SYSCLOCK / 8 / sampling La freqüència ha de ser un nombre enter

Com que el senyal té un cinquè harmònic (freqüència d’aquest harmònic = 5 * 440 = 2200 Hz), hem d’establir la freqüència de mostreig per sobre de 2,5 * 2200 = 5500 Hz. Aquí vaig triar 8000 Hz.

També declarem les matrius on emmagatzemarem les dades en brut i calculades

float vReal [mostres];

float vImag [mostres];

2) Instanciació

Creem un objecte ArduinoFFT. La versió per a desenvolupadors d’ArduinoFFT utilitza una plantilla per poder utilitzar el tipus de dades flotant o el doble. Float (32 bits) és suficient pel que fa a la precisió general del nostre programa.

ArduinoFFT FFT = ArduinoFFT (vReal, vImag, samples, samplingFrequency);

3) Simulant el senyal emplenant la matriu vReal, en lloc de fer-lo amb valors ADC.

Al principi del bucle, omplim la matriu vReal amb:

cicles de flotació = (((samples) * signalFrequency) / samplingFrequency); // Nombre de cicles de senyal que llegirà el mostreig

for (uint16_t i = 0; i <samples; i ++) {vReal = float ((amplitude * (sin ((i * (TWO_PI * cycles)) / samples)))); / * Construir dades amb positiu i valors negatius * / vReal + = float ((amplitud * (sin ((3 * i * (TWO_PI * cicles)) / samples))) / 2.0); / * Construir dades amb valors positius i negatius * / vReal + = float ((amplitud * (sin ((5 * i * (TWO_PI * cicles)) / mostres))) / 4.0); / * Construir dades amb valors positius i negatius * / vImag = 0,0; // La part imaginària s'ha de posar a zero en cas de fer un bucle per evitar càlculs i desbordaments incorrectes}

Afegim una digitalització de l’ona fonamental i dels dos harmònics amb menys amplitud. Que inicialitzem la matriu imaginària amb zeros. Atès que aquesta matriu està poblada per l'algorisme FFT, hem d'esborrar-la de nou abans de cada nou càlcul.

4) Informàtica FFT

Després calculem el FFT i la densitat espectral

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

FFT.compute (FFTDirection:: Endavant); / * Compute FFT * / FFT.complexToMagnitude (); / * Calcular magnituds * /

L'operació FFT.windowing (…) modifica les dades brutes perquè executem el FFT en un nombre limitat de mostres. La primera i l'última mostra mostren una discontinuïtat (no hi ha "res" en un dels seus costats). Aquesta és una font d'error. L'operació de "finestra" tendeix a reduir aquest error.

FFT.compute (…) amb la direcció "Endavant" calcula la transformació del domini del temps al domini de la freqüència.

A continuació, calculem els valors de magnitud (és a dir, intensitat) de cadascuna de les bandes de freqüència. Ara la matriu vReal està plena de valors de magnituds.

5) Dibuix de traçador en sèrie

Imprimim els valors al traçador de sèrie trucant a la funció printVector (…)

PrintVector (vReal, (samples >> 1), SCL_FREQUENCY);

Aquesta és una funció genèrica que permet imprimir dades amb un eix de temps o un eix de freqüència.

També imprimim la freqüència de la banda que té el valor de magnitud més alt

float x = FFT.majorPeak ();

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

Pas 5: anàlisi d'un senyal simulat: resultats

Anàlisi d'un senyal simulat - Resultats
Anàlisi d'un senyal simulat - Resultats

Veiem 3 pics corresponents a la freqüència fonamental (f0), el 3r i el 5è harmònics, amb la meitat i 1/4 de la magnitud f0, tal com s’esperava. Podem llegir a la part superior de la finestra f0 = 440,430114 Hz. Aquest valor no és exactament de 440 Hz, a causa de tots els motius explicats anteriorment, però és molt proper al valor real. Realment no era necessari mostrar tants decimals insignificants.

Pas 6: anàlisi d'un senyal real: cablejat de l'ADC

Anàlisi d'un senyal real: cablejat de l'ADC
Anàlisi d'un senyal real: cablejat de l'ADC

Com que sabem com procedir en teoria, ens agradaria analitzar un senyal real.

El cablejat és molt senzill. Connecteu els terrenys i la línia de senyal al pin A0 de la vostra placa mitjançant una resistència de sèrie amb un valor d’1 KOhm a 10 KOhm.

Aquesta resistència de sèrie protegirà l'entrada analògica i evitarà sonar. Ha d’ésser el més alt possible per evitar sonar i el més baix possible per proporcionar el corrent suficient per carregar l’ADC ràpidament. Consulteu el full de dades de l’MCU per conèixer la impedància esperada del senyal connectat a l’entrada ADC.

Per a aquesta demostració he utilitzat un generador de funcions per alimentar un senyal sinusoidal de freqüència 440 Hz i amplitud al voltant de 5 volts (el millor és que l'amplitud estigui entre 3 i 5 volts, de manera que l'ADC s'utilitza a prop de tota la escala), mitjançant una resistència d'1,2 KOhm.

Pas 7: Anàlisi d'un senyal real: codificació

0) Incloeu la biblioteca

#include "arduinoFFT.h"

1) Declaracions i instanciació

A la secció de declaració definim l'entrada d'ADC (A0), el nombre de mostres i la freqüència de mostreig, com a l'exemple anterior.

byte const adcPin = 0; // A0

const uint16_t samples = 1024; // Aquest valor SEMPRE ha de ser una potència de 2 const uint16_t samplingFrequency = 8000; // Afectarà el valor màxim del temporitzador a timer_setup () SYSCLOCK / 8 / sampling La freqüència ha de ser un nombre enter

Creem l’objecte ArduinoFFT

ArduinoFFT FFT = ArduinoFFT (vReal, vImag, samples, samplingFrequency);

2) Configuració de temporitzador i ADC

Establim el temporitzador 1 perquè cicle a la freqüència de mostreig (8 KHz) i provoca una interrupció en comparar la sortida.

void timer_setup () {

// restableix el temporitzador 1 TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; TCCR1B = bit (CS11) | bit (WGM12); // CTC, prescalador de 8 TIMSK1 = bit (OCIE1B); OCR1A = ((16000000/8) / freqüència de mostreig) -1; }

I configureu l'ADC perquè ho faci

  • Utilitza A0 com a entrada
  • S'activa automàticament a cada sortida de 1 temporitzador per comparar la coincidència B.
  • Genera una interrupció quan es completa la conversió

El rellotge ADC s’estableix a 1 MHz, mitjançant la preescalificació del rellotge del sistema (16 MHz) per 16. Com que cada conversió dura aproximadament 13 rellotges a escala completa, es poden aconseguir conversions a una freqüència de 1/13 = 0,076 MHz = 76 KHz. La freqüència de mostreig ha de ser significativament inferior a 76 KHz per permetre a l’ADC tenir el temps de mostrejar les dades. (hem triat fs = 8 KHz).

void adc_setup () {

ADCSRA = bit (ADEN) | bit (ADIE) | bit (ADIF); // activeu ADC, voleu interrompre en finalitzar ADCSRA | = bit (ADPS2); // Preescalador de 16 ADMUX = bit (REFS0) | (adcPin & 7); // configuració de l'entrada ADC ADCSRB = bit (ADTS0) | bit (ADTS2); // Temporitzador / comptador1 Compareu la font d’activació de la coincidència B ADCSRA | = bit (ADATE); // activar l'activació automàtica}

Declarem el gestor d'interrupcions que es cridarà després de cada conversió ADC per emmagatzemar les dades convertides a la matriu vReal i esborrar la interrupció

// ADC completa ISR

ISR (ADC_vect) {vReal [resultNumber ++] = ADC; if (resultNumber == mostres) {ADCSRA = 0; // desactiva ADC}} EMPTY_INTERRUPT (TIMER1_COMPB_vect);

Podeu tenir una explicació exhaustiva sobre la conversió ADC a l’Arduino (analogRead).

3) Configuració

A la funció de configuració esborrem la taula de dades imaginàries i cridem al temporitzador i a les funcions de configuració ADC

zeroI (); // una funció que defineix a 0 totes les dades imaginàries, explicades a la secció anterior

timer_setup (); adc_setup ();

3) Bucle

FFT.dcRemoval (); // Traieu el component de corrent continu d’aquest senyal ja que l’ADC fa referència a terra

FFT.windowing (FFTWindow:: Hamming, FFTDirection:: Endavant); // Pesar dades FFT.compute (FFTDirection:: Endavant); // Calculeu FFT FFT.complexToMagnitude (); // Calculeu magnituds // imprimint l'espectre i la freqüència fonamental f0 PrintVector (vReal, (samples >> 1), SCL_FREQUENCY); float x = FFT.majorPeak (); Serial.print ("f0 ="); Serial.print (x, 6); Serial.println ("Hz");

Eliminem el component de corrent continu perquè l'ADC fa referència a terra i el senyal es centra al voltant de 2,5 volts aproximadament.

A continuació, calculem les dades tal com s’explica a l’exemple anterior.

Pas 8: Anàlisi d'un senyal real: resultats

Anàlisi d'un senyal real - Resultats
Anàlisi d'un senyal real - Resultats

De fet, només veiem una freqüència en aquest senyal senzill. La freqüència fonamental calculada és de 440,118194 Hz. Aquí també el valor és una aproximació molt propera de la freqüència real.

Pas 9: Què passa amb un senyal sinusoïdal retallat?

Què passa amb un senyal sinusoïdal retallat?
Què passa amb un senyal sinusoïdal retallat?

Ara permet excedir una mica l'ADC augmentant l'amplitud del senyal per sobre de 5 volts, de manera que es retalla. No premeu massa per no destruir l'entrada ADC.

Podem veure que apareixen alguns harmònics. Retallar el senyal crea components d'alta freqüència.

Heu vist els fonaments de l’anàlisi FFT en una placa Arduino. Ara podeu provar de canviar la freqüència de mostreig, el nombre de mostres i el paràmetre de finestra. La biblioteca també afegeix alguns paràmetres per calcular el FFT més ràpidament amb menys precisió. Notareu que si configureu la freqüència de mostreig massa baixa, les magnituds calculades apareixeran totalment errònies a causa del plegament espectral.

Recomanat: