|
funktioniert
hier
die
Zählmethode?
Ich habe schon in einigen Beispielen Frequenzen mit Hilfe des Timers0 mit der
Zählmethode gemessen. Funktioniert das auch für niedrige
Frequenzen? Da eine multiplex angesteuerte
LED-Anzeige (die ja auch den Timer0
benutzt)
die Frequenzmessung nach der Zählmethode behindert, müsste
die
Anzeige während der
Frequenzmessung
abgeschaltet werden. Damit das nicht als störendes Flackern
sichtbar
wird, kann nur eine sehr kurze Messzeit (Zählzeit)
gewählt
werden. Dadurch ist die Messgenauigkeit nicht sehr hoch. Mit einer
Zählzeit von z.B. 10 ms erreicht man eine Messgenauigkeit von nur
100 Hz. (Messauflösung ist das Reziprok der Zählzeit.) Das
ist nicht akzeptabel. Längere Zählzeiten führen aber zum
deutlich sichtbaren Flackern des Displays. Damit scheidet die
Zählmethode hier aus
Messung
der
Periodendauer Alternativ kann man die Periodendauer des Eingangssignals messen, und daraus die Frequenz errechnen. Zur Messung der Periodendauer verwende ich das Capture-Modul des PIC16F628. Das Capturemodul behindert nicht die LED-Anzeige und verwendet nicht den Timer0. Messung und Anzeige können deshalb parallel erfolgen. Das Capturemodul besteht im Wesentlichen aus dem Timer1, der mit z.B. 625 kHz getaktet wird. Dieser Takt wird aus dem 20-MHz-PIC-Takt gewonnen, indem er erst durch 4 (Zyklustakt) und dann durch 8 (Vorteiler des Timer1) geteilt wird. Beim Eintreffen eines Pulses vom Eingang RB3 wird der Timer1 per Software auf 0 gesetzt. Dann zählt er mit 625 kHz. Alle 1,6 Mikrosekunden erhöht sich der Wert des Timers um 1. Beim Eintreffen des nächsten Pulses wird der Timer1 (via Capture Modul) ausgelesen. Durch das Setzen des Bits CCP1IF wird ein Interrupt ausgelöst, in dem der "gecapturete" 16-Bit-Zählwert (CCPR1H & CCPR1L) aus dem Captureregister ausgelesen wird, und nur für Berechnungen zur Verfügung steht. |
Frequenz
=
625000/Zählwert
Frequenz
[Hz] |
1 |
10 |
100 |
500 |
1000 |
9999 |
Zählwert |
625 000 = 0x 09 89 68 |
62 500 = 0x F4 24 |
6 250 = 0x 18 6A |
1 250 = 0x 04 E2 |
625 = 0x 02 71 |
62 = 0x 00 3E |
Messauflösung |
0,00016% |
0,0016% = 0,00016 Hz |
0,016% = 0,016 Hz |
0,08% = 0,4 Hz |
0,16% = 1,6 Hz |
1,6% = 160 Hz |
Anzeigeauflösung
1Hz
= |
100% |
10% |
1% |
0,2% |
0,1% |
0,01% |
untere
Frequenzgrenze
Bei Frequenzen unterhalb 9,54 Hz überschreitet das
Zählergebnis den Wert 65535 und wird somit länger als 16 Bit.
Der
Timer1 ist aber nur 16 Bit lang. Eine Softwareerweiterung des Timer1
auf 24 Bit wäre deshalb erforderlich, um Frequenzen unterhalb von
10 Hz
zu messen. Darauf verzichte ich. Der Zähler wird also erst ab ca.
10 Hz arbeiten.
Um aus diesem Messwert die Frequenz in Herz
zu berechnen, muss der Reziprokwert des Zählergebnisses
berechnet
werden, und dieser Wert mit 2500000 oder 10000000
multipliziert werden.
Also gilt:
Frequenz
[Hz] |
600 |
1000 |
3000 |
9999 |
Zählwert
1:1 |
1 041 = 0x 04 11 | 625 = 0x 02 71 | 208 = 0x 00 D0 |
62 = 0x 00 3E |
Zählwert
16:1 |
16 656= 0x 41 10 | 10 000 = 0x 27 10 | 3 328 = 0x 0D 00 |
992 = 0x 03 E0 |
Messauflösung
1:1 |
0,1% = 0,57 Hz | 0,16% = 1,6 Hz | 0,23% = 6,9 Hz |
1,6% = 160 Hz |
Messauflösung 4:1 | 0,025% = 0,14 Hz | 0,04% = 0,4 Hz | 0,06% = 1,7 Hz |
0,4% = 40 Hz |
Messauflösung 16:1 | 0,006% = 0,04 Hz | 0,01% = 0,1 Hz | 0,02% = 0,4 Hz |
0,1% = 10 Hz |
Die obige Tabelle zeigt die erzielbare Auflösung unter
Anwendung des Vorteilers. Der 4:1-Vorteiler garantiert 1 Hz
Auflösung nur bis etwa 1000 Hz, so dass sich gleich der
16:1-Vorteiler empfiehlt. Damit kann bis zu 3000 Hz eine Auflösung
von 1 Hz erreicht werden. Danach steigt die Auflösung auf bis zu
10 Hz am Messbereichsende an.
Ein besseres Ergebnis lässt sich durch einen höheren Takt
für den Timer 1 erreichen.Bei Nutzung eines 20-MHz-Quarzes stehen
für den Timer1 die Frequenzen 5 MHz, 2,5 MHz, 1,25 kHz
und 625 kHz zur Auswahl. Mit 5
MHz-Zähltakt wäre der
Auflösungsfehler bei
9999 Hz-Messfrequenz auf etwa 1,25 Hz verringert. Die Formeln zur
Frequenzberechnung sehen dann wie folgt aus:
Frequenz
[Hz] |
800 |
1000 |
3000 |
9999 |
Zählwert
1:1 |
6 250 = 0x 18 6A | 5 000 = 0x 13 88 | 1 666 = 0x 06 82 |
500 = 0x 01 F4 |
Zählwert
4:1 |
25 000 = 0x 61 A8 | 20 000 = 0x 4E 20 | 6 666 = 0x 1A 0A |
2 000 = 0x 07 D0 |
Zählwert
16:1 |
100 0000 = 0x 01 86 A0 | 80 000 = 0x 01 38 80 | 26 656= 0x 68 20 |
8 000 = 0x 1F 40 |
Messauflösung
1:1 |
0,02% = 0,13 Hz | 0,02% = 0,2 Hz | 0,06% = 1,8 Hz |
0,2% = 20 Hz |
Messauflösung 4:1 | 0,005% = 0,03 Hz | 0,005% = 0,05 Hz | 0,015% = 0,45 Hz |
0,05% = 5 Hz |
Messauflösung 16:1 | 0,001% = 0,01 Hz | 0,001% = 0,013 Hz | 0,004% = 0,11 Hz |
0,013% = 1,25 Hz |
Frequenzbereich |
CCP-Vorteiler |
Timer1-Takt |
Berechnung |
10 ... 812 Hz |
1:1 |
625 kHz |
Frequenz = 625000 / Zählwert |
813 ...4882 Hz |
4:1 |
5 MHz |
Frequenz = 20 000 000 / Zählwert |
4882 ...9999 Hz |
16:1 |
5 MHz |
Frequenz = 80 000 000 / Zählwert |
Durch eine Verzögerung wird erreicht, dass maximal 4 Messungen
pro
Sekunde durchgeführt werden, damit das Display nicht zu unruhig
wird.Diese Verzögerung ist an die Multiplexfrequenz des
LED-Displays gekoppelt.
.... ; Includedatei für den 16F628 einbinden #include <P16f628.INC> #INCLUDE <MATH16.INC> ; PIC16 math library definitions, belegt 0x20 bis 0x4B ... ... #INCLUDE <FXD24.A16> ... |
Die Bibliotheksfunktion verwendet eine ganze Reihe von
Rechenregistern (siehe MATH16.INC) die im RAM des 16F628 den Bereich
von 0x20 bis 0x4B belegen. Dieser Bereich bleibt also für den Rest
der Software tabu. Erst ab 0x4C kann ich eigene Variablen definieren.
Bevor ich nun eine Division durchführe, kopiere ich meine Zahlen
in die richtigen Rechenregister des Routine (AARGB3...0 und
BARGB2...0),
und rufe die Routine auf. Das Ergebns steht dann in AARGB3...1.
;************************************************************** ; 32/24 bit Division f:= f / xw ; aus der Microchip-Bibliothek AN617 ; AARG := AARG / BARG ; Microchip numeriert die Bytes anders herum als ich: aufpassen! ; A0-A1-A2-A3 B0-B1-B2 ; f3-f2-f1-f0 x2-x2-x0 Div24 MOVFW f0 MOVWF AARGB3 MOVFW f1 MOVWF AARGB2 MOVFW f2 MOVWF AARGB1 MOVFW f3 MOVWF AARGB0 MOVFW xw0 MOVWF BARGB2 MOVFW xw1 MOVWF BARGB1 MOVFW xw2 MOVWF BARGB0 CALL FXD3224S MOVFW AARGB3 MOVWF f0 MOVFW AARGB2 MOVWF f1 MOVFW AARGB1 MOVWF f2 return |
Wenn man von vornherein bei der Programmentwicklung beabsichtig,
eine Bibliotheksfunktion zu nutzen, dann kann man auch gleich die
Register der Funktion in der eigenen Software nutzen. Ich habe aber die
Biblitheksfunktion zu einem recht späten Zeitpunkt in das Programm
hineingebaut, um die Berechnung zu beschleunigen. Dadurch musste ich
diese umfangreichen Kopierereien in der Routine Div24 verwenden. Nun
ja, im Ergebnis habe ich eine schnelle Divisionsroutine.
... #define Ziffer0_aus bsf PORTA, 3 ; Ziffer0 aus #define Ziffer0_an bcf PORTA, 3 ; Ziffer0 an ... |
Nach dieser
Definition können Ziffer0_aus
und Ziffer0_an im Quelltext
verwendet werden, und der Assembler setzt selbsttätig dafür
die richtigen bcf- oder bsf-Befehle ein. Das verbesser die Lesbarkeit
des Codes.
... #define Haleluja ... #ifdef Haleluja ; hier kann Programmcode stehen #endif ... |
Es gibt auch noch
eine #else-Direktive. Mit der
sich eine #ifdef-#else-#endif
-Konstruktion bauen lässt, die zwei
alternative Programmblöcke enthält. Ja nachdem ob der hinter
#ifdef stehende Bezeicher
bekannt ist, wird der Programmblock zwischen
#ifdef und #else oder der Programmblock
zwischen #else und #endif
verwendet.
Das Folgende Beispiel ist ein Ausschnitt aus dem
Frequenzzählerprogramm. Es definiert 8 Makros. Je nachdem, ob der
Bezeichner "Treiber" definiert
ist, werden die 8 Makros aber
unterschiedlich definiert.
... #define Treiber ; Treibertransistor vorhanden ... ; Makros #ifdef Treiber ; das sind die Makros fuer ein LED-Display mit Treibertransistoren an den Anoden #define Ziffer0_aus bsf PORTA, 3 ; Ziffer0 aus #define Ziffer1_aus bsf PORTA, 2 ; Ziffer1 aus #define Ziffer2_aus bsf PORTA, 1 ; Ziffer2 aus #define Ziffer3_aus bsf PORTA, 0 ; Ziffer3 aus #define Ziffer0_an bcf PORTA, 3 ; Ziffer0 an #define Ziffer1_an bcf PORTA, 2 ; Ziffer1 an #define Ziffer2_an bcf PORTA, 1 ; Ziffer2 an #define Ziffer3_an bcf PORTA, 0 ; Ziffer3 an #else ; das sind die Makros fuer ein LED-Display mit direkter Ansteuerung der Anoden ohne Treiber #define Ziffer0_aus bcf PORTA, 3 ; Ziffer0 aus #define Ziffer1_aus bcf PORTA, 2 ; Ziffer1 aus #define Ziffer2_aus bcf PORTA, 1 ; Ziffer2 aus #define Ziffer3_aus bcf PORTA, 0 ; Ziffer3 aus #define Ziffer0_an bsf PORTA, 3 ; Ziffer0 an #define Ziffer1_an bsf PORTA, 2 ; Ziffer1 an #define Ziffer2_an bsf PORTA, 1 ; Ziffer2 an #define Ziffer3_an bsf PORTA, 0 ; Ziffer3 an #endif ... |
Eine einfache (wenn auch nicht sehr elegante) Lösung des Problems ist der Einsatz des Watchdogtimers (WDT). Dieser wird in der Konfiguration aktiviert, und er würde immer nach ca. 18ms einen Reset des PICs auslösen, wenn er nicht regelmäßig durch den Befehl clrwdt zurückgesetzt werden würde. Das Zurücksetzen erfolgt immer dann, wenn durch den Timer0 die Displaymultiplexroutine aufgerufen wird. Da dieser Aufruf normalerweise alle 3,3ms erfolgt, bekommt der WDT also normalerweise auch nichts zu tun. Nur wenn extrem hohe Eingangsfrequenzen das Multiplexen blockieren, wird der PIC neu gestartet, was im Display ein '----' oder '____' erzeugt.
; Anzeigeschleife disploop btfss INTCON, T0IF goto disploop clrwdt ; watchdog zuruecksetzen bevor 18 ms vorbei sind (7..33 ms) bcf INTCON, T0IF call Display goto disploop |
Autor: sprut
erstellt: 15.12.2009
letzte Änderung: 25.03.2010