Ich muss die Frequenz der Rechteckwelle messen, die zwischen 0 und 1 MHz variieren kann und eine Auflösung von 0,25 Hz hat.
Ich habe mich noch nicht für einen Controller entschieden, aber es wird höchstwahrscheinlich einer der 20-Pin-Attiny sein.
Normalerweise würde ich Signale mit niedrigerer Frequenz messen, indem ich zwei Timer verwende, von denen einer im Timer-Erfassungsmodus konfiguriert ist, um beispielsweise die ansteigenden Flanken des externen Signals zu unterbrechen, und ein anderer Timer, der so eingestellt ist, dass er jede Sekunde unterbricht, daher der frühere Timer-Zählerregisterwert nach 1 Sekunde wäre gleich der Frequenz des Signals.
Diese Methode funktioniert jedoch offensichtlich nicht für die Erfassung von Signalen zwischen 0 und 1 MHz mit einer Auflösung von 0,25 Hz. Ich würde einen 22-Bit-Zähler benötigen (AFAIK 8-Bit-Mikros haben nur 8/16-Bit-Zähler).
Eine Idee, die ich hatte, war, das Signal vor dem Anlegen an das Mikro zu teilen, aber dies wäre unpraktisch, da das Signal durch 61 geteilt werden müsste, daher könnte die Frequenz nur alle 61 Sekunden aktualisiert werden, wo ich es alle paar Sekunden haben möchte .
Gibt es eine andere Methode, mit der die Frequenz etwa alle 4 Sekunden aktualisiert werden kann?
Aktualisieren:
Die einfachste Lösung besteht darin, einen externen Interrupt oder eine Timer-Erfassung zu verwenden, um die ansteigende Flanke des Signals zu unterbrechen und das isr
Inkrement als Variable vom Typ zu verwenden long int
. Lesen Sie die Variable alle 4 Sekunden ab (um Frequenzen bis zu 0,25 Hz zu messen).
Update 2:
Wie JustJeff hervorhob, kann eine 8-Bit-MCU nicht mit einem 1-MHz-Signal mithalten, sodass eine Unterbrechung bei jeder ansteigenden Flanke und ein Inkrementieren einer long int
...
Ich habe die von timororr vorgeschlagene Methode gewählt. Sobald ich mit der Implementierung fertig bin, werde ich zurückschicken und die Ergebnisse teilen. Vielen Dank an alle für Ihre Vorschläge.
Fortschrittsbericht:
Ich habe begonnen, einige der hier vorgestellten Ideen zu testen. Zuerst habe ich den Code von vicatcu ausprobiert. Es gab ein offensichtliches Problem, dass TCNT1 nach der Berechnung der Frequenz nicht behoben wurde - keine große Sache ...
Dann bemerkte ich beim Debuggen des Codes, dass etwa alle 2 bis 7 Mal, wenn die Frequenz berechnet wurde, die Überlaufzahl von Timer 1 (der Timer, der zum Zählen externer Ereignisse konfiguriert ist) um zwei kurz war. Ich habe dies auf die Latenz des Timer 0 ISR zurückgeführt und beschlossen, den if-Anweisungsblock vom ISR in den Hauptblock zu verschieben (siehe Snippet unten) und einfach ein Flag im ISR zu setzen. Einige Fehlerbehebungen haben gezeigt, dass die erste Messung in Ordnung ist, aber bei jedem weiteren Ablesen ist die Überlaufzahl von Timer 1 um 2 überschritten. Was ich nicht erklären kann. Ich hätte erwartet, dass sie nicht über ... liegt.
int main()
{
while(1)
{
if(global_task_timer_ms > 0 && (T0_overflow == 1))
{
global_task_timer_ms--;
T0_overflow = 0;
}
.....
}
}
Als nächstes beschloss ich, den Timrorrs-Vorschlag umzusetzen. Um das erforderliche Intervall (von ca. 15 ms zwischen jedem timer_isr-Interrupt) zu erzeugen, müsste ich die beiden 8-Bit-Timer kaskadieren, da der einzige 16-Bit-Timer auf dem Atmega16 zum Erfassen der ansteigenden Flanken des externen Signals verwendet wird.
Ich dachte, diese Lösung würde funktionieren und viel effizienter sein, da der größte Teil des Overheads auf die Timer verlagert wird und nur noch eine kurze ISR für die CPU übrig ist. Es war jedoch nicht so genau wie ich gehofft hatte, die Messungen wurden um ca. 70 Hz hin und her verschoben, was mir bei hohen Frequenzen nichts ausmachen würde, aber bei niedrigeren Frequenzen definitiv nicht akzeptabel ist. Ich habe nicht viel Zeit damit verbracht, das Problem zu analysieren, aber ich vermute, dass die Timer-Kaskadenanordnung nicht so genau ist, da ich eine ähnliche Anordnung wie der Timrorrs-Vorschlag auf einem weitaus langsameren 8051-Controller implementiert habe, der 2 16-Bit-Timer hatte und die Ergebnisse ziemlich genau waren.
Ich bin jetzt zu Vicatcus Vorschlag zurückgekehrt, aber ich habe die Frequenzberechnung in den Timer 0 isr verschoben (siehe Ausschnitt unten ). Dieser Code hat konsistente und einigermaßen genaue Messungen erzeugt. Mit ein wenig Kalibaration sollte die Genauigkeit ungefähr +/- 10 Hz betragen.
ISR(TIMER0_OVF_vect)
{
TCNT0 = TIMER0_PRELOAD; //Reload timer for 1KHz overflow rate
if(task_timer_ms > 0)
{
task_timer_ms--;
}
else
{
frequency_hz = 1.0 * TCNT1;
TCNT1 = 0;
frequency_hz += global_num_overflows * 65536.0;
global_num_overflows = 0;
frequency_hz /= (TASK_PERIOD_MS / 1000.0);
task_timer_ms = TASK_PERIOD_MS;
}
}
Wenn jemand andere Vorschläge hat, bin ich offen für sie, aber ich muss lieber keine Bereiche verwenden ... Ich bin auch nicht mehr darauf bedacht, eine Auflösung von 0,25% zu erreichen, es scheint nicht viel Sinn mit der Genauigkeit zu haben, die ich im Moment habe .
Antworten:
Wenn möglich, würde ich vorschlagen, einen Mikrocontroller auszuwählen, der eine Zähleroperation unter Verwendung der Timereingänge unterstützt. Anstatt einen Zähler innerhalb eines ISR manuell zu erhöhen (was bei hohen Frequenzen schnell dazu führt, dass die Mikrocontroller-Aktivität gesättigt wird), können die Hardware die Zählung durchführen. An diesem Punkt muss Ihr Code einfach auf Ihren periodischen Interrupt warten und dann die Häufigkeit berechnen.
Um den Bereich zu erweitern und den Frequenzzähler allgemeiner zu gestalten (wodurch die Notwendigkeit mehrerer Bereiche auf Kosten von etwas mehr Arbeit für die MCU entfällt), können Sie die folgende Technik verwenden.
Wählen Sie eine periodische Interruptrate, die Messgenauigkeit bei der höchsten Eingangsfrequenz ermöglicht. Dies sollte Ihre Zählergröße berücksichtigen (Sie müssen die Timer-Periode so auswählen, dass der Timer-Zähler bei der maximalen Eingangsfrequenz nicht überläuft). In diesem Beispiel gehe ich davon aus, dass der Eingangszählerwert aus der Variablen "timer_input_ctr" gelesen werden kann.
Fügen Sie eine Variable zum Zählen periodischer Interrupts hinzu (sollte beim Start auf 0 initialisiert werden). In diesem Beispiel werde ich diese Variable als "isr_count" bezeichnen. Die Unterbrechungsperiode ist in der Konstanten "isr_period" enthalten.
Ihr periodischer Interrupt sollte wie folgt implementiert werden: (C-Pseudocode):
Offensichtlich basiert dieses grobe Beispiel auf einer Gleitkomma-Mathematik, die möglicherweise nicht mit Low-End-Mikrocontrollern kompatibel ist. Es gibt Techniken, um dies zu überwinden, aber sie liegen außerhalb des Rahmens dieser Antwort.
quelle
Möglicherweise möchten Sie zwei (oder mehr) Bereiche verwenden. Die Probleme bei der Erfassung sehr niedriger Frequenzen unterscheiden sich etwas von den Problemen bei den höheren. Wie Sie bereits bemerkt haben, treten am oberen Ende Ihres Bereichs Probleme mit dem Gegenüberlauf auf.
Bedenken Sie jedoch, dass Ihre Genauigkeit am unteren Ende Ihres Bereichs darunter leidet, dass nicht genügend Zählungen im Register vorhanden sind. Sie sind sich nicht sicher, ob Sie wirklich zwischen 0,25 Hz und 0,5 Hz unterscheiden möchten, aber wenn Sie dies tun, müssen Sie tatsächlich vier Sekunden zählen, um dies zu tun.
Wenn Sie eine flache Auflösung von 0,25 Hz angeben, die streng interpretiert wird, können Sie 500.000,00 Hz von 500.000,25 Hz unterscheiden, was ein ziemlich hohes Maß an Präzision darstellt.
Aus diesen Gründen könnte das Entwerfen für unterschiedliche Bereiche das Problem der Zählergröße verringern. Wenn Sie beispielsweise für das untere Ende zufällig Zahlen ziehen, z. B. 0 bis 100 Hz, für Intervalle von 10 Sekunden zählen, erhalten Sie eine Auflösung von 0,1 Hz, und Ihr Zähler muss nur auf 1000 steigen, nicht einmal auf 10 Bit. Zählen Sie dann von 100 Hz bis 10 kHz für Intervalle von 1 Sekunde. Sie erhalten nur eine Auflösung von 1 Hz, aber Ihr Zähler muss nur bis zu 10.000 laufen, die noch kleiner als 16 Bit sind. Der obere Bereich von 10 kHz bis 1 MHz könnte nur 0,01 Sekunden lang zählen, und die maximale Anzahl würde immer noch nur 10.000 betragen, und obwohl Ihre Auflösung 100 Hz betragen würde, wäre dies eine angemessene Genauigkeit.
quelle
Sie können einen Hardware- und einen Software-Zähler mischen, indem Sie die Überläufe des Hardware-Zählers in einem ISR zählen.
Das Zählen jeder Flanke des Signals in einem ISR ist für ein 1-MHz-Signal zu langsam. Ich denke, Sie könnten auf diese Weise bis zu 50 kHz erzeugen.
quelle
Anstatt einen 1-Sekunden-Zähler zu erstellen, machen Sie ihn zu einem 0,1-Sekunden-Zähler und multiplizieren Sie den Zähler mit 10?
Wenn es nur darum geht, die Zählernummer zu speichern, können Sie dann keinen zusätzlichen Code verwenden, um zu verfolgen, wann der Zähler überläuft, und an einen anderen Speicherort schreiben, um die Zählung zu führen?
quelle
Können Sie nicht einfach die Eingangserfassung und die Überlaufinterrupts eines 16-Bit-Timers (plus eine Variable) verwenden, um die Messung durchzuführen? So würde ich es mit dem ATTiny24A mit AVR-GCC machen (ungetestet und möglicherweise fehlerhaft natürlich):
... jedenfalls kompiliert es :)
BEARBEITEN Ich habe mir die von meinem Code ausgegebene LSS-Datei angesehen, und der generierte Code enthält zu viele Anweisungen, um bei 1 MHz mit einem 8-MHz-Takt nicht über sich selbst zu stolpern ... Selbst das einfache Inkrementieren um eine Zeile im TIM1_OVF_vect generiert 19 Anweisungen! Um 1-MHz-Ereignisse zu verarbeiten, müssten Sie auf jeden Fall optimieren, wahrscheinlich einige Dinge registrieren (wahrscheinlich num_overflows und capture_value_ticks), den Inline-Assembler verwenden (wichtige Dinge aus der lss-Datei stehlen) und die Verarbeitung aus den Interrupts in die Hauptdatei verschieben Schleife wo immer möglich.
quelle
Das Posten dieses Codes als Alternative gemäß dem Vorschlag von @ timrorr zu meinem vorherigen Beitrag. Dies wird für den ATTiny24A unter Verwendung des c99-Sprachstandards kompiliert, aber ich habe es darüber hinaus in keiner Weise getestet.
Dies ist eine nette kleine Nutzung der Hardwarefunktionen des Timer1 und spart eine Menge Verarbeitungszyklen im Vergleich zu meinem ursprünglichen Beitrag.
quelle
Mit Vorskalierern kann sogar eine GHz-Messung erreicht werden. Dies ist ein einfacher 40-MHz-Frequenzmesser mit ATMEL AVR AT90S2313: http://www.myplace.nu/avr/countermeasures/index.htm
Hier sind einige andere ähnliche Projekte:
http://www.ikalogic.com/freq_meter_2.php
http://www.saturn.dti.ne.jp/~khr3887/lfcd_e.html
http://www.circuitlake.com/rs232-frequency-meter-and-pulse-generator.html
http://www.ulrichradig.de/home/index.php/avr/frequenzcounter
http://www.triplespark.net/elec/analysis/FreqCnt/
http://www.cappels.org/dproj/30MHzfmeter/30MhzFmtr.html
http://www.qsl.net/pa3ckr/bascom%20and%20avr/rfcounter/index.html
http://www.sump.org/projects/counter
http://digilander.libero.it/alfred73/eprojects.htm#1300%20Mhz%20Frequencymeter%20with%20prescaler
quelle