Messen Sie die Audiofrequenz über die Pulsdauer
Hallo liebe StackExchange-Benutzer,
Ich möchte einen Infrarot-Blaster entwickeln, den ich an die Kopfhörerbuchse eines Telefons oder einer Konsole Ihrer Wahl anschließen kann. Um zu kommunizieren, mit welcher Frequenz die Diode moduliert werden soll und wie lange Impulse gesendet werden sollen, generiere ich einfach Töne, die der Arduino lesen und interpretieren kann.
Mein Problem ist, dass ich die Frequenz damit nicht zuverlässig messen kann. Um die Frequenz zu messen, benutze ich einen Analogkomparator-Interrupt und einen Trigger bei fallender Flanke, um immer den Interrupt zu erhalten, nachdem ein voller Impuls aufgetreten ist. Ich habe bei jedem Interrupt micros () aufgerufen, um die Pulsdauer zu ermitteln, aber es war zu ungenau. Dann habe ich versucht, Timer0 mit einem Prescaler von 8 zu verwenden, um die Frequenz zu messen, aber es zählt nur bis 255.
Jetzt habe ich wegen des größeren TCNT-Registers auf Timer1 umgestellt. Aus irgendeinem Grund läuft es jedoch bereits bei 255 über, obwohl ich die OCR1-Register nicht eingestellt habe.
Dies ist mein aktueller Code. Bitte beachten Sie, dass die tatsächliche Frequenzberechnung in der Hauptschleife erfolgt, um die im ISR gewonnenen Messungen nicht zu beeinträchtigen.
const int setFrequencySound = 1000;
const int receiveDataSound = 2000;
const int receiveDataEndSound = 3000;
const int readyMode = 0;
const int setFrequencyMode = 1;
const int receiveDataMode = 2;
const int sendDataMode = 3;
volatile unsigned long timeBefore;
volatile unsigned long timeNow;
volatile unsigned long delta;
volatile float audioFrequencyBefore;
volatile float audioFrequency;
volatile float irFrequency;
volatile int irData[100];
volatile int irDataIndex;
volatile int mode = 0;
ISR (ANALOG_COMP_vect)
{
timeNow = TCNT1;
Serial.print (timeNow);
Serial.println (" TCNT1");
//TCNT1 = 0; //reset timer register
}
void setup ()
{
Serial.begin (115200);
Serial.println ("Started.");
ADCSRB = 0; // (Disable) ACME: Analog Comparator Multiplexer Enable
ACSR = bit (ACI) // (Clear) Analog Comparator Interrupt Flag
| bit (ACIE) // Analog Comparator Interrupt Enable
| bit (ACIS1); // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on falling edge)
TCCR1B |= (0 << CS12) | (1 << CS11) | (0 >> CS10); //Activate timer and set prescaler to 8
TCNT1 = 0; //Reset timer register
} // end of setup
void loop ()
{
//Calculate frequency by using the period duration
//determined through the timestamps captured in
//ISR method.
//delta = timeNow - timeBefore;
audioFrequency = 1000000000.0 / (float(timeNow) * 2.0);
if (millis() % 1000 == 0)
{
Serial.print (audioFrequency, 5);
Serial.print (" Hz, ");
Serial.print (timeNow);
Serial.println (" TCNT0");
}
//Do not process the same frequency multiple times
if (audioFrequency != audioFrequencyBefore)
{
switch (mode)
{
case readyMode:
if (audioFrequency == setFrequencySound)
{
mode = 1;
Serial.println ("Start setting IR frequency.");
}
else if (audioFrequency == receiveDataSound)
{
mode = 2;
Serial.println ("Start receiving IR data.");
}
break;
case setFrequencyMode:
irFrequency = audioFrequency + 25000;
Serial.print ("IR frequency set to ");
Serial.print (irFrequency, 10);
Serial.println (" Hz.");
mode = readyMode;
break;
case receiveDataMode:
if (audioFrequency == receiveDataEndSound)
{
Serial.println ("Stop receiving IR data.");
mode = readyMode;
irDataIndex = 0;
}
else
{
irData[irDataIndex] = audioFrequency;
irDataIndex++;
Serial.print ("IR pulse received: ");
Serial.print (irData[irDataIndex]);
Serial.println (" microsecs.");
}
break;
case sendDataMode:
Serial.println ("Sending IR data...");
break;
}
}
audioFrequencyBefore = audioFrequency;
} // end of loop
Antworten
Timer 1 läuft bei 255 über, da er vom Arduino-Kern so konfiguriert wurde, da er 8-Bit-PWM bereitstellen soll. Wenn Sie den Timer im normalen Modus verwenden möchten, sollten Sie die Initialisierung des Arduino rückgängig machen, indem Sie ihn TCCR1Aauf Null setzen.
Zwei Kommentare zum ISR:
ISR (ANALOG_COMP_vect)
{
timeNow = TCNT1;
Serial.print (timeNow);
Serial.println (" TCNT1");
//TCNT1 = 0; //reset timer register
}
Erstens sollten Sie, wie bereits in einem früheren Kommentar erwähnt, nicht innerhalb eines ISR drucken. Wenn Sie ein konsistentes Timing wünschen, sollten Sie den Timer niemals zurücksetzen. Lassen Sie es einfach frei laufen und verwenden Sie Zeitunterschiede, um die Signalperiode zu erhalten. Machen Sie sich keine Sorgen über das Überlaufen des Timers: Wenn Sie die Berechnung mit vorzeichenlosen 16-Bit-Ganzzahlen ( uint16_t) durchführen, sind Sie immun gegen Überläufe. Wenn Sie den Timer zurücksetzen, verlieren Sie wahrscheinlich einige Ticks, da Sie ihn nicht lesen und im selben Taktzyklus zurücksetzen können.
Zuletzt empfehle ich Ihnen, sich über die Funktion „Eingabeerfassung“ von Timer 1 zu informieren. Dies ist eine Funktion des Timers, mit der genau das Problem gelöst werden kann, das Sie hier haben: Der Timer zeichnet automatisch einen Zeitstempel für ein externes Ereignis auf. Sie können dies durch eine steigende oder fallende Flanke am Eingangspin 8 oder durch den analogen Komparator auslösen lassen, wenn Sie dies bevorzugen. Diese Methode gibt Ihnen keinen Jitter, der mit Code nicht erreicht werden kann, selbst wenn er innerhalb eines Interrupts ausgeführt wird.
Bearbeiten : Beantwortung einer zusätzlichen Frage im Kommentar.
Könnte ich das Audiokabel [...] direkt an Pin 8 anschließen und die Eingangserfassungsfunktion verwenden?
Dies hängt von der Form des Signals ab, das aus diesem Kabel kommt. Zunächst müssen Sie sicherstellen, dass das Signal immer zwischen den elektrischen Potentialen von GND und Vcc (dh 0 - 5 V) bleibt, da Sie sonst das Arduino beschädigen können.
Beachten Sie, dass das Signal, wenn es von einer hochohmigen Quelle stammt, sicher etwas über diesen Bereich hinausgehen kann, da alle Eingänge des Arduino durch Dioden geschützt sind. Sie müssen nur den Strom in den Dioden auf weniger als 1 mA begrenzen. Mit anderen Worten, Sie können für jedes kΩ Ausgangsimpedanz den (unbelasteten) akzeptablen Spannungsbereich auf jeder Seite um 1 V erweitern. Wenn die Quelle eine niedrige Impedanz hat, können Sie sie hochohmig machen, indem Sie einfach einen 10-kΩ-Widerstand in Reihe schalten.
Der zweite Punkt ist, dass Sie sicherstellen müssen, dass das Signal eine ausreichend große Spannungsspanne hat, damit der digitale Eingang zwischen LOW und HIGH umschaltet. Laut Datenblatt des ATmega328P liegen die Eingangsschwellen bei einer Stromversorgung mit 5 V typischerweise bei 2,1 V für das Schalten auf LOW und 2,6 V für das Schalten auf HIGH. Der Unterschied zwischen diesen Schwellenwerten führt zu einer gewissen Hysterese, die zur Unterdrückung des Rauschens beitragen kann. Beachten Sie, dass dies typische Werte sind. Wenn Sie eine garantierte Umschaltung wünschen , sollte das Signal unter 1,5 V fallen, um einen garantierten NIEDRIGEN Messwert zu erhalten (dies ist V IL ), und über 3 V, um einen garantierten HIGH-Messwert (V IH ) zu erhalten.
Beachten Sie auch, dass Sie den analogen Komparator als Auslöser für die Eingangserfassung verwenden können, wenn die Eingangsschwellen des digitalen Pins nicht Ihren Anforderungen entsprechen.