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 .