' reziproke Frequenzmessung mit ATmega48/88 und BASCOM ' typische Daten: ' Eingangsfrequenz ca. 0,005 Hz - 900 kHz ohne Vorteiler, mit Vorteiler 74VHC4040 - 200 MHz ' mit 6-stelliger Ausgabe ' Messrate ca. 3 Messungen/s als Frequenz, Periode oder Drehzahl ' Die Beschreibung der Funktion und weitere Programme (allerdings in C) ' finden sich hier: http://www.mino-elektronik.de/fmeter/fm_software.htm ' und das Schaltbild dazu: http://www.mino-elektronik.de/download/AVR_FMETER.png ' 2015-03-05 ' Alle Angaben ohne Gewähr ' www.mino-elektronik.de ' Die Interruptroutinen sind komplett in Assembler geschrieben und bezüglich der Laufzeit optimiert. ' für Bezeichner Frequenz/Periode Dim Dim_string(15) As String * 6 Dim I As Byte $crystal = 20000000 ' 20 MHz $baud = 19200 $swstack = 100 ' nicht zu knapp bemessen $hwstack = 100 ' dto. Const F_clock = 20000000 Const T1_ovfl_pro_sekunde = 305 ' Vorteiler 74HC393 %256 oder 74VHC4040 %1024 einstellen Const Vorteiler_faktor = 1024 ' stellen um 1 zu niedrig angeben Const Stellen = 6 Const Minimale_messzeit = 102 ' ca. 3 Messungen/s Const Maximale_messzeit = 61000 ' ca. 200 Sekunden Const Teiler_100ms = 30 ' alle 100 ms Const Messen = 0 Const Auslesen = 1 Const Auswerten = 2 ' Zur Anwahl der Ergebnisses Const Zeige_f = 0 Const Zeige_p = 1 Const Zeige_upm = 2 Dim Frequenz As Single ' float-Ergebnis Dim Ergebnis_str As String * 20 ' als String Dim Sync_flag As Byte , Temp As Byte , Vorteiler_aktiv As Byte , Teiler As Byte Dim Messwert_vorhanden As Byte , Mess_status As Byte , Led_cnt As Byte Dim Zeitpunkt As Dword , Messdauer As Word , Ueberlauf As Word ' Zeitmessung Dim Start_ereignis As Dword , End_ereignis As Dword , Start_zeit As Dword Dim End_zeit As Dword , Mess_zeit As Dword , Mess_ereignisse As Dword Dim Eingangsimpulse As Dword ' Zaehler für die Eingangsimpulse ' zur Ausgabe eines fomatierten Meßwertes Declare Sub Frequenz_ausgeben(byval X As Single , Byval Ausgabe_wert As Byte) ' Hier beginnt das Hauptprogramm mit der Initialisierung von Daten und Timer-Interrupts ' init der Dim_Strings, liegen ganz am Ende Restore Dta ' auf ersten Wert zurücksetzen For I = 0 To 14 Read Dim_string(i) ' und dann Wert für Wert einlesen Next ' Timer1 aktivieren Config Timer1 = Counter , Capture_edge = Falling , Prescale = 1 ' freilaufend mit fallender Flanke an ICP1 Enable Timer1 On Timer1 Timer1_int Nosave ' für Timer1 Überläufe Enable Icp1 On Icp1 Timer1_capt_int Nosave ' Zählung der Eingangsimpulse und Zeitmessung ' abschließend Interrupts freigeben Enable Interrupts Config Lcdpin = Pin , Db4 = Portb.2 , Db5 = Portb.3 , Db6 = Portb.4 , Db7 = Portb.5 , E = Portc.1 , Rs = Portc.0 Config Lcd = 16 * 2 'configure lcd screen Cursor Off Cls Ddrd.5 = 1 ' für blinkende LED Acsr.2 = 1 ' analog Komparator verwenden Tccr0b = 7 ' T0 zaehlt Impulse des Vorteilers Vorteiler_aktiv = 0 ' zunächst direkt messen Messwert_vorhanden = 0 ' erstes Ergebnis verwerfen Locate 1 , 1 ' Grundanzeige LCD Lcd "F:" Locate 2 , 1 Lcd "P:" ' Hauptprogramm zum messen der Frequenz ' Auswertung erfolgt, wenn minimale_messzeit erreicht ist Do If Sync_flag = 1 Then ' alle 0,1sek. Frequenz an T0 bewerten Sync_flag = 0 ' wieder loeschen Temp = Tcnt0 ' T0 lesen If Tifr0.0 = 1 Then ' bei Ueberlauf Tifr0.0 = 1 ' flag loeschen Temp = 255 ' und max. Wert nehmen End If Tcnt0 = 0 ' Messung am Vorteiler neu starten If Vorteiler_aktiv = 1 Then ' If Temp < 3 Then ' Vorteiler wieder abschalten Acsr.2 = 1 ' analog Komparator verwenden Mess_status = Auslesen ' aktuelle Ergebnisse lesen Messwert_vorhanden = 0 ' aber nicht anzeigen Vorteiler_aktiv = 0 ' wieder auf ANA-COMP umschalten ' Cursor Off ' zur Kontrolle End If Elseif Temp > 5 Then ' Vorteiler zuschalten Vorteiler_aktiv = 1 ' Cursor On ' zur Kontrolle Acsr.2 = 0 ' ICP aktivieren Mess_status = Auslesen ' Aktuelle Ergebnisse Lesen Messwert_vorhanden = 0 ' aber nicht anzeigen End If End If Timsk1.0 = 0 ' t1-overflow interrupt sperren If Mess_status = Messen And Messdauer >= Minimale_messzeit Then Mess_status = Auslesen ' Auswertung starten Messdauer = 0 ' wieder Messzeit abwarten End If If Messdauer > Maximale_messzeit Then ' timeout Mess_status = Auslesen ' aktuelle Messung abschließen Messwert_vorhanden = 0 ' aber nicht auswerten End If Timsk1.0 = 1 ' t1-overflow interrupt freigeben If Mess_status = Auswerten Then ' minimale Meßzeit abgelaufen, dann auswerten End_zeit = Zeitpunkt Mess_zeit = End_zeit - Start_zeit ' Zeit-Differenz bilden Start_zeit = End_zeit ' neue startzeit merken Mess_ereignisse = End_ereignis - Start_ereignis ' Impuls-Differenz Start_ereignis = End_ereignis ' fuers naechste Intervall Mess_status = Messen ' wieder warten If Messwert_vorhanden = 1 Then ' gueltige Messung Led_cnt = 31 ' ca. 0,1 s Frequenz = Mess_ereignisse Frequenz = Frequenz / Mess_zeit ' Frequenz Berechnen Frequenz = Frequenz * F_clock If Vorteiler_aktiv = 1 Then Frequenz = Frequenz * Vorteiler_faktor End If Call Frequenz_ausgeben(frequenz , Zeige_f) ' Frequenz wandeln Locate 1 , 5 Lcd Ergebnis_str Print Ergebnis_str ' Beispiel: Ausgabe per ser. Schnittstelle Call Frequenz_ausgeben(frequenz , Zeige_p) ' Periode wandeln ' Call Frequenz_ausgeben(frequenz , Zeige_upm) ' Drehzahl wandeln Locate 2 , 5 ' und anzeigen Lcd Ergebnis_str Else Messwert_vorhanden = 1 ' falls gesperrt wieder aufheben End If End If Loop ' endlosschleife ' der übergebene Frequenzwert "x" wird in eine Zeichenkette mit Dez.-Punkt und Dimension gewandelt ' mit "ausgabe_wert" wird zuvor der Kehrwert gebildet und die Periodendauer ermittelt ' Das Ergebnis findet sich in "Ergebnis_str" und kann beliebig ausgegeben werden Sub Frequenz_ausgeben(byval X As Single , Byval Ausgabe_wert As Byte) 'Wandlung von Frequenz oder Periode Local I As Byte , J As Byte , Dez_punkt As Byte , Dimension As Byte Dez_punkt = 1 ' Vorgabe für "1.xxxxx" Dimension = 3 ' vorgabe für "Hz" If X < 0.005 Then ' 5 mHz ist Untergrenze X = 0.0 ' falls zu tiefe Frequenz "0.0 Hz" ausgeben Else If Ausgabe_wert = Zeige_p Then ' falls Periode gewandelt werden soll X = 1 / X ' Kehrwert Bilden Dimension = 5 ' und in Sekunden als Dimension Elseif Ausgabe_wert = Zeige_upm Then X = X * 60 Dimension = 13 End If While X < 1.0 X = X * 1000.0 Incr Dimension ' in den Hz-Bereich bringen Wend While X >= 1000.0 X = X * 0.001 Decr Dimension Wend While X >= 10.0 X = X * 0.1 Incr Dez_punkt Wend X = X + 5e-6 ' runden If X >= 10.0 Then ' Ueberlauf bei Rundung X = X * 0.1 Incr Dez_punkt If Dez_punkt > 3 Then ' Ueberlauf der Dimension Decr Dimension Dez_punkt = 1 End If End If End If Ergebnis_str = "" For I = 1 To Stellen ' ab hier Datenausgabe J = X ' nur eine Dezimalstelle Ergebnis_str = Ergebnis_str + Str(j) ' ans Ergebnis anhängen If I = Dez_punkt Then Ergebnis_str = Ergebnis_str + "." ' Dez-Punkt einfügen End If X = X - J X = X * 10 Next Ergebnis_str = Ergebnis_str + Dim_string(dimension) ' und Dimension dazu End Sub ' bei jedem Timer1 Überlauf die Überläufe zählen und die grobe Messdauer erhöhen Timer1_int: ' bei jedem Überlauf von Timer1 $asm push r16 push r17 in r17, SREG lds r16, {ueberlauf} ' ueberlauf = ueberlauf + 1 subi r16,-1 sts {ueberlauf}, r16 lds r16, {ueberlauf+1} sbci r16,-1 sts {ueberlauf+1}, r16 sei ' Interrupt wieder zulassen lds r16, {messdauer} ' messdauer = messdauer + 1 subi r16,-1 sts {messdauer}, r16 lds r16, {messdauer+1} sbci r16,-1 sts {messdauer+1}, r16 lds r16, {led_cnt} ' LED zur Anzeige einer fertigen Messung tst r16 breq led_ist_aus dec r16 sts {led_cnt}, r16 sbi PORTD,5 ' für 100 ms einschalten rjmp weiter_t1 Led_ist_aus: Cbi Portd , 5 ' und wieder aus Weiter_t1: lds r16, {teiler} ' sync-flag alle 100 ms setzen inc r16 sts {teiler}, r16 brne t1_ovfl_fertig ldi r16, 256-teiler_100ms sts {teiler}, r16 ldi r16, 1 ' zur groben Messung für Vorteiler sts {sync_flag}, r16 T1_ovfl_fertig: Out Sreg , R17 pop r17 pop r16 $end Asm Return ' Hier werden die Eingangsimpulse und ihr genauer Zeitpunkt erfasst Timer1_capt_int: $asm Out Gpior0 , R16 Out Gpior1 , R0 in r0, SREG lds r16, {Eingangsimpulse} ' Eingangsimpulse zählen inc r16 sts {Eingangsimpulse}, r16 brne T1_capt1 lds r16, {Eingangsimpulse+1} inc r16 sts {Eingangsimpulse+1}, r16 brne T1_capt1 lds r16, {Eingangsimpulse+2} inc r16 sts {Eingangsimpulse+2}, r16 brne T1_capt1 lds r16, {Eingangsimpulse+3} inc r16 sts {Eingangsimpulse+3}, r16 T1_capt1: lds r16, {mess_status} cpi r16, auslesen Brne Nicht_auswerten ' nur zählen, nicht noch nicht auswerten ldi r16, auswerten sts {mess_status}, r16 ' mess_status auf auswerten setzen lds r16, {ueberlauf} ' den genauen Zeitpunkt aus Anzahl der Überläufe * 0x1000 sts {zeitpunkt+2}, r16 lds r16, {ueberlauf+1} sts {zeitpunkt+3}, r16 lds r16, icr1l ' und dem Capture-Register zusammensetzen sts {zeitpunkt}, r16 lds r16, icr1h sts {zeitpunkt+1}, r16 tst r16 Brmi Kein_ueberlauf ' wenn ICR1H >= 0x80 kann T1 nicht übergelaufen sein in r16,TIFR1 sbrs r16,0 ' wenn cap-reg >= 0 && timer1 OVF = 1: rjmp kein_ueberlauf ' kein Überlauf registriert lds r16,{zeitpunkt+2} ' anderfalls ueberlauf berücksichtigen !!! subi r16,-1 sts {zeitpunkt+2}, r16 lds r16,{zeitpunkt+3} sbci r16,-1 sts {zeitpunkt+3}, r16 Kein_ueberlauf: lds r16, {eingangsimpulse} ' Zur Berechnung umkopieren sts {end_ereignis}, r16 lds r16, {eingangsimpulse+1} sts {end_ereignis+1}, r16 lds r16, {eingangsimpulse+2} sts {end_ereignis+2}, r16 lds r16, {eingangsimpulse+3} sts {end_ereignis+3}, r16 Nicht_auswerten: sbis TIFR1, 5 ' neuer Impulse am ICP1-Eingang rjmp T1_CAPT_FERTIG lds r16, {Eingangsimpulse} ' dann gleich zählen inc r16 sts {Eingangsimpulse}, r16 brne T1_capt2 lds r16, {Eingangsimpulse+1} inc r16 sts {Eingangsimpulse+1}, r16 brne T1_capt2 lds r16, {Eingangsimpulse+2} inc r16 sts {Eingangsimpulse+2}, r16 brne T1_capt2 lds r16, {Eingangsimpulse+3} inc r16 sts {Eingangsimpulse+3}, r16 T1_capt2: sbi TIFR1, 5 ' und ICP1-flag abschliessend löschen T1_capt_fertig: Out Sreg , R0 in r0, GPIOR1 in r16, GPIOR0 $end Asm Return End Dta: Data " GHz" , " MHz" , " kHz" , " Hz " , " mHz" , " sek" , " ms " , " us " , " ns " , " ps " , " Grpm" , " Mrpm" , " krpm" , " rpm" , " mrpm"