Arduino frekvens identifiering (4 / 4 steg)
Steg 4: Generaliserad pitch upptäckt
I grund och botten var vad jag gjorde att välja en spänning som jag alltid visste skulle vara i gränserna för min våg (2.5V). Sedan tittade jag på varje gång vågen passerat denna nivå med en positiv lutning, låt oss kalla dessa "tröskel händelser". Om detta hänt flera gånger i en cykel valde jag tröskel händelsen med största lutningen vara i början av min cykel. Liknar det sista steget, jag använde en variabel som kallas "tiden" (ökas med en hastighet av 38,5 kHz) att mäta tiden mellan tröskel händelser och lagras detta är en matris kallas timer []. Jag spelade också in lutningen vid varje tröskelvärde händelserna i en matris kallas lutning []. Då jag jämförde element av timer [] och [] lutning att räkna ut var det var en match. När en matchning hittades, jag la upp delarna av timer [] att bestämma varaktighet av cykeln och skickade detta värde till en global variabel kallas "period." Sedan i funktionen huvudsakliga loop() (alla steg jag har bara beskrivit hända i rutinen ADC avbrott) använde jag värdet av perioden att beräkna frekvensen och skriva ut den. Jag bör också tillägga att jag sätter en variabel i den kod som kallas "noMatch" som hjälpte mig att besluta att det hade varit för länge sedan jag hade en bra match och att jag bara ska spela in delar av timer [] och [] lutning.
När du skriver detta tänkte jag på en massa möjliga scenarier som kan bryta algoritmen. Den svåraste våg i mitt sinne är en som passerar den 2.5V tröskeln många gånger i en cykel på liknande sluttar och fördelas längs cykel på samma sätt. Jag du har en våg som denna, bör du hålla slopeTol mycket låg (0-3) och du kanske tycker att sänka timerTol (till 5 kanske) hjälper spåra vågen korrekt. Också, om du vill mäta vågor med mycket branta sluttningar (som pulsvåger) bör du ange värdet för slopeTol upp till 100 eller ännu hela vägen upp till 256 att spåra dem bättre.
Generellt denna del av koden verkar mycket väl hantera massor av former, kan du se några av mina resultat i bilderna ovan. Den inkommande signalen visas i gult och tröskel händelsen som Arduino är spårning indikeras av en puls av stift 12 (blå).
generaliserad wave freq upptäckten med 38,5 kHz samplingsfrekvens och avbryter
av Amanda Ghassaei
Sept 2012
/*
* Detta program är fri programvara; Du kan vidaredistribuera det och/eller ändra
* det enligt villkoren i GNU General Public License som offentliggjorts av
* den Free Software Foundation; antingen version 3 av licensen, eller
* (på ditt alternativ) någon senare version.
*
*/
Clipping indicator variabler
booleska klippning = 0;
data storage variabler
byte newData = 0;
byte prevData = 0;
unsigned int tid = 0; //keeps tid och skickar vales att lagra i [timer] ibland
int timer [10]; //sstorage för tidpunkten för händelser
int lutning [10]. //storage fro slutta av händelser
unsigned int totalTimer; //used att beräkna period
unsigned int period; //storage under för wave
byte index = 0; //current storage index
float frekvens; //storage för frekvens beräkningar
int maxSlope = 0; //used att beräkna max lutning som trigger punkt
int newSlope; //storage för inkommande lutning data
variabler för beslutat om du har en match
byte noMatch = 0; //counts hur många icke-matcher du har fått för att återställa variabler om det har gått för långt
byte slopeTol = 3; //slope tolerans - justera detta om du behöver
int timerTol = 10; //timer tolerans - justera detta om du behöver
void setup() {
Serial.BEGIN(9600);
pinMode (13, OUTPUT); //led indikator pin
pinMode (12, OUTPUT); //output pin
(CLI); //diable avbrott
Ställ in kontinuerlig provtagning av analoga pin 0 vid 38,5 kHz
Rensa ADCSRA och ADCSRB register
ADCSRA = 0;
ADCSRB = 0;
ADMUX | = (1 << REFS0); ange referens spänning
ADMUX | = (1 << ADLAR); Vänsterjustera ADC värde - så vi kan läsa högst 8 bitar från ADCH register endast
ADCSRA | = (1 << ADPS2) | (1 << ADPS0); ställa in ADC klockan med 32 prescaler - 16mHz/32 = 500 kHz
ADCSRA | = (1 << ADATE); enabble automatisk utlösare
ADCSRA | = (1 << Ebba Grön); Aktivera avbrott när mätningen är klar
ADCSRA | = (1 << ADEN); Aktivera ADC
ADCSRA | = (1 << ADSC); Starta ADC mätningar
SEI (); //enable avbrott
}
ISR(ADC_vect) {//when nya ADC värdet redo
PORTB & = B11101111; //set stift 12 låg
prevData = newData; //store tidigare värde
newData = ADCH; //get värde från A0
om (prevData < 127 & & newData > = 127) {//if ökar och passage mittpunkten
newSlope = newData - prevData; //calculate lutning
om (abs(newSlope-maxSlope) < slopeTol) {//if sluttningar är ==
spela in nya data och återställa tiden
sluttningen [index] = newSlope;
timer [index] = tid;
tid = 0;
om (index == 0) {//new max lutning bara reset
PORTB | = B00010000; //set stift 12 hög
noMatch = 0;
index ++; //increment index
}
annars om (abs(timer[0]-timer[index]) < timerTol & & abs(slope[0]-newSlope) < slopeTol) {//if timer varaktighet och backarna match
summa timer-värden
totalTimer = 0;
för (byte jag = 0; jag < index; i ++) {
totalTimer += timer [i];
}
period = totalTimer; //set perioden
återställa nya noll indexvärden att jämföra med
timer [0] = timer [index];
sluttningen [0] = lutningen [index];
index = 1; //set index 1
PORTB | = B00010000; //set stift 12 hög
noMatch = 0;
}
annat {//crossing mittpunkt men inte matchen
index ++; //increment index
om (index > 9) {
Reset();
}
}
}
annars om (newSlope > maxSlope) {//if nya lutning är mycket större än max lutning
maxSlope = newSlope;
tid = 0; //reset klocka
noMatch = 0;
index = 0; //reset index
}
annat {//slope inte brant nog
noMatch ++; //increment ingen match counter
om (noMatch > 9) {
Reset();
}
}
}
om (newData == 0 || newData == 1023) {//if klippning
PORTB | = B00100000; //set stift 13 hög-sväng på klippning indikator ledde
klippningen = 1; //currently klippning
}
tid ++; //increment timer i takt på 38,5 kHz
}
void reset() {//clea ut vissa variabler
index = 0; //reset index
noMatch = 0; //reset matcha couner
maxSlope = 0; //reset lutning
}
void checkClipping() {//manage klippning indikator LED
om (klippning) {//if för närvarande klippning
PORTB & = B11011111; //turn av klippning indikator ledde
klippningen = 0;
}
}
void loop() {
checkClipping();
frekvens = 38462/float (period); //calculate frekvens timer kurs/period
skriva ut resultaten
Serial.Print(Frequency);
Serial.println ("hz");
fördröjning (100); //feel gratis att ta bort detta om du vill
göra andra saker här
}
Jag lade också till en bit kod att stoppa beräkna och skriva ut frekvensen data när amplituden av vågen understiger en viss nivå. (Om det finns liten eller ingen signal då koden ovan ibland spottar ut en massa skräp). Här är det:
generaliserad wave freq upptäckten med 38,5 kHz samplingsfrekvens och avbryter
av Amanda Ghassaei
Sept 2012
/*
* Detta program är fri programvara; Du kan vidaredistribuera det och/eller ändra
* det enligt villkoren i GNU General Public License som offentliggjorts av
* den Free Software Foundation; antingen version 3 av licensen, eller
* (på ditt alternativ) någon senare version.
*
*/
Clipping indicator variabler
booleska klippning = 0;
data storage variabler
byte newData = 0;
byte prevData = 0;
unsigned int tid = 0; //keeps tid och skickar vales att lagra i [timer] ibland
int timer [10]; //sstorage för tidpunkten för händelser
int lutning [10]; //storage för lutning av händelser
unsigned int totalTimer; //used att beräkna period
unsigned int period; //storage under för wave
byte index = 0; //current storage index
float frekvens; //storage för frekvens beräkningar
int maxSlope = 0; //used att beräkna max lutning som trigger punkt
int newSlope; //storage för inkommande lutning data
variabler för beslutat om du har en match
byte noMatch = 0; //counts hur många icke-matcher du har fått för att återställa variabler om det har gått för långt
byte slopeTol = 3; //slope tolerans - justera detta om du behöver
int timerTol = 10; //timer tolerans - justera detta om du behöver
variabler för amp upptäckt
unsigned int ampTimer = 0;
byte maxAmp = 0;
byte checkMaxAmp;
byte ampThreshold = 30. //raise om du har en mycket högljudd signal
void setup() {
Serial.BEGIN(9600);
pinMode (13, OUTPUT); //led indikator pin
pinMode (12, OUTPUT); //output pin
(CLI); //diable avbrott
Ställ in kontinuerlig provtagning av analoga pin 0 vid 38,5 kHz
Rensa ADCSRA och ADCSRB register
ADCSRA = 0;
ADCSRB = 0;
ADMUX | = (1 << REFS0); ange referens spänning
ADMUX | = (1 << ADLAR); Vänsterjustera ADC värde - så vi kan läsa högst 8 bitar från ADCH register endast
ADCSRA | = (1 << ADPS2) | (1 << ADPS0); ställa in ADC klockan med 32 prescaler - 16mHz/32 = 500 kHz
ADCSRA | = (1 << ADATE); enabble automatisk utlösare
ADCSRA | = (1 << Ebba Grön); Aktivera avbrott när mätningen är klar
ADCSRA | = (1 << ADEN); Aktivera ADC
ADCSRA | = (1 << ADSC); Starta ADC mätningar
SEI (); //enable avbrott
}
ISR(ADC_vect) {//when nya ADC värdet redo
PORTB & = B11101111; //set stift 12 låg
prevData = newData; //store tidigare värde
newData = ADCH; //get värde från A0
om (prevData < 127 & & newData > = 127) {//if ökar och passage mittpunkten
newSlope = newData - prevData; //calculate lutning
om (abs(newSlope-maxSlope) < slopeTol) {//if sluttningar är ==
spela in nya data och återställa tiden
sluttningen [index] = newSlope;
timer [index] = tid;
tid = 0;
om (index == 0) {//new max lutning bara reset
PORTB | = B00010000; //set stift 12 hög
noMatch = 0;
index ++; //increment index
}
annars om (abs(timer[0]-timer[index]) < timerTol & & abs(slope[0]-newSlope) < slopeTol) {//if timer varaktighet och backarna match
summa timer-värden
totalTimer = 0;
för (byte jag = 0; jag < index; i ++) {
totalTimer += timer [i];
}
period = totalTimer; //set perioden
återställa nya noll indexvärden att jämföra med
timer [0] = timer [index];
sluttningen [0] = lutningen [index];
index = 1; //set index 1
PORTB | = B00010000; //set stift 12 hög
noMatch = 0;
}
annat {//crossing mittpunkt men inte matchen
index ++; //increment index
om (index > 9) {
Reset();
}
}
}
annars om (newSlope > maxSlope) {//if nya lutning är mycket större än max lutning
maxSlope = newSlope;
tid = 0; //reset klocka
noMatch = 0;
index = 0; //reset index
}
annat {//slope inte brant nog
noMatch ++; //increment ingen match counter
om (noMatch > 9) {
Reset();
}
}
}
om (newData == 0 || newData == 1023) {//if klippning
PORTB | = B00100000; //set stift 13 hög-sväng på klippning indikator ledde
klippningen = 1; //currently klippning
}
tid ++; //increment timer i takt på 38,5 kHz
ampTimer ++; //increment amplitud timer
om (abs(127-ADCH) > maxAmp) {
maxAmp = abs(127-ADCH);
}
om (ampTimer == 1000) {
ampTimer = 0;
checkMaxAmp = maxAmp;
maxAmp = 0;
}
}
void reset() {//clea ut vissa variabler
index = 0; //reset index
noMatch = 0; //reset matcha couner
maxSlope = 0; //reset lutning
}
void checkClipping() {//manage klippning indikator LED
om (klippning) {//if för närvarande klippning
PORTB & = B11011111; //turn av klippning indikator ledde
klippningen = 0;
}
}
void loop() {
checkClipping();
om (checkMaxAmp > ampThreshold) {
frekvens = 38462/float (period); //calculate frekvens timer kurs/period
skriva ut resultaten
Serial.Print(Frequency);
Serial.println ("hz");
}
fördröjning (100); //delete detta om du vill
göra andra saker här
}