Hey guter Quellcode!
hier einmal ein quelltext für die messung von niedrigen frequenzen
durch periodenmessung mit hilfe der input-capture funktion.
viel spass
klaus
Code:' Bascom Frequenzzähler für langsame Frequenzen: Zeit zwischen den Flanken zählen ' unter verwendung der input capture einheit ' dieser code basiert auf einem C quelltext von ' Autor: Karl heinz Buchegger (kbuchegg) (Moderator) ' Datum: 08.02.2007 22:28 ' Dateianhang : Freqcount.c ' aus dem Microcontroller.net ' umsetzung für Bascom durch kolisson 15.09.2009 $baud = 115200 ' ********************** hier noch deinen crystal rein $crystal = 14745600 $regfile = "m8def.dat" $hwstack = 32 $swstack = 10 $framesize = 40 ' ++++++++++++++++++++++++++++++++++ hier wird $crystal ausgelesen Dim Quartzfrequenz As Long Quartzfrequenz = _xtal ' ********************************************** hier den prescaler für timer1 definieren ' zulässig sind 1,8,64,256,1024 Const Prescalerwert = 1 ' in dieser version wird der overflow von T1 ausgewertet und mitgezählt ' daher kann man mit prescale = 1 d.h. volle auflösung fahren '******************** und den capturetimer configurieren ******************** Config Timer1 = Timer , Capture Edge = Falling , Prescale = 1 'Prescalerwert Start Timer1 ' **************************************************************************** ' *********** das zu messende digitalsignal wird am ICP1 eingang (hier PB0) eingespeist Dim Starttime As Word Dim Endtime As Word Dim Nroverflows As Byte Dim Ersteflanke As Bit Dim Updatedisplay As Bit Dim Taktejesekunde As Long Dim Periodendauer As Single Dim Frequenz As Single Dim Counterticks As Long Taktejesekunde = Quartzfrequenz / Prescalerwert Dim Maxoverflows As Long Maxoverflows = Taktejesekunde / 65535 '***** zwei werte zur verzögerung des seriellen ausgabe '***** sonst verschluckt sich das terminalprogramm '***** kleinerer wert = schnellerer output Dim Outputcounter As Byte Const Anzeigeinterval = 10 '*************************** Ersteflanke = 1 Updatedisplay = 0 '********************************* option eingangscomparator ' diese 2 zeilen sind optional zu verwenden ' da der icp1 eingang ja logigpegel benötigt 'besteht hier die möglichkeit anstelle des icp den ain1 eingang des comparators zu verwenden ' der eingang ist dann empfindlicher --- schwelle etwa 1,23 volt ' ' der icp1 eingang gilt dann nicht mehr als eingang und kann als normaler port verwendet werden Config Aci = On , Compare = On Acsr.6 = 1 'interne bandgap als ref '********************************* ende option eingangscomparator ' ******************* und die int routinen definieren On Ovf1 T1overflow Enable Ovf1 On Capture1 Captureint 'wenn pb0 auf low geht dann capture int Enable Capture1 Enable Interrupts ' ************* und los gehts Do If Nroverflows > Maxoverflows Then Print "freq kleiner 2 Hz" Nroverflows = 0 Updatedisplay = 0 Ersteflanke = 1 End If If Updatedisplay = 1 Then If Outputcounter = Anzeigeinterval Then 'Disable Capture1 'Disable Ovf1 Disable Interrupts ' // ' // Die Zeitdauer zwischen den Flanken bestimmen ' // Da EndTime und StartTime unsigned sind, braucht nicht ' // darauf Ruecksicht genommen werden, dass EndTime auch ' // kleiner als StartTime sein kann. Es kommt trotzdem ' // das richtige Ergebnis raus. ' // Allerdings muss die Anzahl der Overflows in der Messperiode ' // beruecksichtigt werden ' // ' // Die Zeitdauer wird als Anzahl der Taktzyklen zwischen den ' // beiden gemessenen Flanken berechnet ... Counterticks = Nroverflows * 65536 Counterticks = Counterticks + Endtime Counterticks = Counterticks - Starttime '// ... mit der bekannten Taktfrequenz ergibt sich dann die Signalfrequenz Periodendauer = Counterticks / Taktejesekunde Frequenz = 1 / Periodendauer ' // ' // Das Ergebnis fuer die Anzeige aufbereiten ... ' // ' // ... und ausgeben ' // Print " Counterticks= " ; Counterticks Print " Periodendauer= " ; Periodendauer Print " Frequenz= " ; Frequenz ' // ' // Das wars: Display ist wieder up to date '// die naechste Messung kann starten '// Outputcounter = 0 'Timer1 = 0 'Enable Capture1 'Enable Ovf1 Enable Interrupts End If Outputcounter = Outputcounter + 1 Counterticks = 0 Updatedisplay = 0 End If Loop End 'end program T1overflow: Nroverflows = Nroverflows + 1 'Print "timer1 overflow" Return Captureint: If Updatedisplay = 1 Then Return '// Das Display wurde mit den Ergebnissen der vorhergehenden '// Messung noch nicht upgedated. Die naechste Messung '// verzögern, bis die Start und EndTime Variablen wieder '// gefahrlos beschrieben werden koennen If Ersteflanke = 1 Then Starttime = Capture1 Nroverflows = 0 Ersteflanke = 0 ' / / Die Naechste Flanke Ist Das Ende Der Messung Else Endtime = Capture1 Updatedisplay = 1 ' / / Eine Vollständige Messung. Sie Kann Ausgewertet Werden Ersteflanke = 1 '/ / Bei Der Naechsten Flanke Beginnt Der Naechste Messzyklus End If Return
Hey guter Quellcode!
Der code hat noch eine kleine Schwäche: Wenn ICP und overflow interrupt fast gleichzeitig auftreten, kann es passieren das der ICP overflow wegen der höheren priorität zuerst aufgerufen wird, obwohl erst der overflow dran wäre. Das kann zu einigen seltenen Falschen Werten führen. Sollte etwa einer von 20000 Werten betroffen sein, wenn nicht längere Zeit der Interrupt gesperrt ist.
Wie man das berücksichtigt steht z.B. im Wiki unter:
http://www.rn-wissen.de/index.php/Timer/Counter_(Avr)#Input_Capture
Der code da ist in C aber leicht zu überragen.
@Besserwessi
danke für den tipp.
den artikel hatte ich noch garnicht gefunden und gelesen.
mal sehen, was sich machen lässt.
gruss klaus
hallo ihr lieben,
da ich mal wider etwas mit der messung von niedrigen frequenzen machen musste (wollte), ist noch ein verbesserter code entstanden, den ich hier posten möchte.
der einwnd von besserwessi über die int-überschneidung wurde berücksichtigt.
der hintergrung dieser neuen version ist die tatsache, dass ich ein zweikanaliges gerät benötigte. da der M8 aber nur eine icp-einheit hat und zudem die int-priorität für icp sehr niedrig ist, habe ich mich entschieden den int0 eingang für das zu messende signal zu nehmen und das entsprechde programm möglichst efficient zu gestalten.
im ende kam ein programm heraus, bei dem man wahlweise den int0 oder den int1 verwenden kann. beide int vectoren müssen auf die gleiche isr zeigen.
durch einfaches enablen und disablen des int1 oder int0 kann man dann den messkanal umschalten.
einen weiteren vorteil dieser programmierung sehe ich darin, dass die int-routine als (ein besseres wort fällt mir jetzt nicht ein) als Flip-Flop INT programmiert ist und das im ende noch als Inline assembler.
nach durchführung einer Messung, kann man in Ruhe seine calculationen oder LCd ausgaben machen, da die ganze messung bis zum nächsten aufruf stoppt und es laufen keine weiteren wartenden Ints mehr auf, die sich störend auswirken könnten.
auf die umrechnung des messwertes (in prozessortakten, je eingangsperiode) habe ich verzichtet, da die reziprokberechnung über single oder gar double für meine anwendung nicht nötig ist und den code nur unnötig verschmutzt.
gruss klaus
Code:hier also nun der liebevoll erzeugte code: Code: $baud = 256000 'set baudrate depending on cpu clockspeed $crystal = 32000000 'and your clockrate must be set too '********** no joke... my M8 is running on 32Mhz now without any problems ' but take care .. its dangerous because some parts of your cpu may fail at high clockrates '########## warning !!!!!!!!!!!!! ' this software is made for atmega 8 'Inside The Int0isr : The Register Tccr1b Is Directly Adressed . ' so .. if you use a different processor, check for register names and adresses. $regfile = "m8def.dat" $hwstack = 32 $swstack = 10 $framesize = 40 Config Portd.2 = Input 'int 0 Portd.2 = 1 Config Timer1 = Timer , Prescale = 1 'Tccr1b.6 = 0 Stop Timer1 'we stop the timer , because it will be activated 'inside the int0 routine Config Int0 = Falling 'must be falling for efficient programming On Int0 Int0isr Nosave 'nosave .. saves time and code Enable Int0 On Ovf1 T1overflow Nosave 'again nosave Enable Ovf1 'about nosave : if you use the int routines "called ORIGINAL BASCOM" please comment 'out the NOSAVE DIRECTIVE Enable Interrupts ' ************************************************************************************** ' *** hier kommen zwei spezialkonstukte, die rechenzeit sparen .. was zum nachdenken !! Dim Periodelow As Word Dim Periodehigh As Word Dim Pericount As Long At Periodelow Overlay Dim T1overlow As Byte Dim T1overhigh As Byte Dim T1overflows As Word At T1overlow Overlay ' ** ENDE von *** hier kommen zwei spezialkonstukte, die rechenzeit sparen .. was zum nachdenken !! ' ************************************************************************************** Dim Messzyklus As Byte 'fertig = 0 -- also eine ganze periode gemessen Messzyklus = 0 Dim Status As Byte ' 0=stop timer 1 = start timer Status = 0 Print "Starting " Wait 2 ' Do '************ do not change below this line, if you are not sure what you are doing If Messzyklus = 0 Then Periodelow = Timer1 Periodehigh = T1overflows '************ do not change above this line, if you are not sure what you are doing ' ##### your code below this line ##### Print "timer" ; Timer1 Print "ovr" ; T1overflows Print "pericount " ; Pericount Waitms 100 'das kann auch waitdays=100 sein.. gibt es ja nicht 'genausogut kann es entfallen, wenn man in der zeit genug anderes machen will ' #### your code above this line ##### '************ do not change below this line, if you are not sure what you are doing Pericount = 0 T1overflows = 0 Timer1 = 0 Messzyklus = 1 Status = 1 End If '************ do not change above this line, if you are not sure what you are doing Loop End 'Int0isr: 'ORIGINAL Bascom 'Tccr1b = Status 'If Status = 0 Then 'Messzyklus = Status 'End If 'Status = 0 'Return Int0isr: ' FASTER VERSION push r23 PUSH R24 ' since we are going to use R24 we better save it IN r24, SREG ' get content of SREG into R24 PUSH R24 ' we can save a register lds r24,{status} !out tccr1b,r24 cpi r24,0 brne exitthis sts {messzyklus},r24 Exitthis: cbr r24,1 sts {status},r24 POP R24 'get content of SREG !OUT SREG,r24 ' save into SREG POP R24 pop r23 ' get r24 back Return 'T1overflow: ' ORIGINAL Bascom 'T1overflows = T1overflows + 1 'Return T1overflow: ' FASTER VERSION push r23 PUSH R24 ' since we are going to use R24 we better save it IN r24, SREG ' get content of SREG into R24 PUSH R24 ' we can save a register lds r23,{t1overlow} lds r24,{t1overhigh} inc r23 brne kein_uebertrag inc r24 Kein_uebertrag: sts {t1overlow} , r23 sts {t1overhigh} , r24 POP R24 'get content of SREG !OUT SREG,r24 ' save into SREG POP R24 pop r23 ' get r24 back Return
So wie es Aussieht, ist das Problem mit einer möglichen Überschneidung der Interrupts nicht berücksichtigt, sondern einfach umgangen indem der Zähler angehalten wird. Das geht aber nur beim weniger genauen Weg über Int0, Int1 , nicht wenn man die ICP Funktion nutzt.
Es ist gar nicht so schlimm, dass der ISP interrupt eine so niedriege Priörität hat. Das Puffern macht ja die Hardware. Es muß nur sichergestellt werden, dass bis zur Interrupt-routine (ISR) keine weitere Flanke dazwischenkommt.
Wenn man mehr als einen ISP Eingang braucht, kann man bei vielen µCs den Multiplexer des AD wandlers nutzen, um den Analog comperator auf einen der AD Eingänge zu legen. Der analoge Comperator kann auch als Quelle für die ISP Funktion gewählt werden.
ach besserwessi,
was bist du denn für einer ? und ich dachte ich kenn dich schon etwas!
aber irgendwie überaschst du mich immer wieder.
es ist halt durch vermeidung berücksichtigt. das macht man doch auch in anderen lebenslagen so. wenn z.b. das Rauchen von Tabak problematisch ist, könnte man es vermeiden, sofern nicht andere umstände dagegen sprechen.So wie es Aussieht, ist das Problem mit einer möglichen Überschneidung der Interrupts nicht berücksichtigt, sondern einfach umgangen indem der Zähler angehalten wird. Das geht aber nur beim weniger genauen Weg über Int0, Int1 , nicht wenn man die ICP Funktion nutzt.
Das mit dem Admux ist natürlich wieder ein guter tipp, den ich noch nicht kannte, aber für meine sache ist das mit dem int0 und int1 schon optimal.
zwischen int0 und int1 wechseln dauert 2 takte und das in einer phase des progs , wo es angehalten ist und es nicht stört.
den vorteil der muxe verstehe da nicht wirklich und es wird keinen geben, da auch dies takte kostet und das schlimme an der idee mit dem icp sind ja dann die extra rechenoperationen, da der timer ja ständig weiterrennt und dann muss wieder ab und aufgerechnet werden.
also.
die kleinen seitentipps finde ich okay, aber diese dieses globale gezeter (mir auch jetzt kein schöneres wort ein ) naja.
die software funktioniert übrigens super mein lieber.
gruss klaus
________________ ein kleine ergänzung als edit __________________
ich habe mein prog ja auch nicht gepostet, um den nobelpreis zu bekommen, sondern um einmal etwas funktionierendes zu teilen und zum anderen um den interessierten leuetn hier mal ein wenig zu zeigen, was ein Inline-assembler bringen kann.
nicht umsonst habe ich ja die bascom-isr versionen nur auskommentiert und nicht gelöscht. jeder kann also nun diese wieder einkommentieren und dafür die inline versionen auskommentieren.
das programm funktioniert dann genauso (bis auf die maximale messfrequenz) aber man sieht sehr deutlich, wie der code durch den Inline-asm schrumpft.
es ist also nicht mehr , als ein funktionierendes beispiel.
Entschuldigung, die Kritik war nicht so gemeint. Klingt beim späteren lesen doch etwas hart. Die Anmerkung war hauptsächlich dafür, falls jemand nach anderes nach der berücksichtigung der Interruptkollision sucht, und sie da nicht so direkt findet.
Die Benutzung der ICP-Funktion ist hauptsächlich für relativ hohe Frequenzen sinnvoll, oder wenn man eine sehr hohe Auflösung braucht. Die Fehler sollte etwa 3 mal kleiner sein. In Einzelfällen (Stopsignal kommt wenn gerade der Überlauf Interrupt läuft) kann mit dem einfachen Interrupts noch ein größerer Fehler Auftreten. Wenn man die reine Basic Version ohne die ASM Beschleunigung nutzt könnte das schon recht stark stören.
Wenn es so reicht, dann ist ja auch gut.
na siehste mein lieber,
nun lächele ich schon wieder. obwohl ich kurz an VERMEIDUNG von beiträgen meinerseits dachte .
leider ist das im web immer schwierig, sich auszudrücken, da ja die mimik des persönlichen gespräches fehlt. darunter leiden ja nun alle FORENTYPEN.
dann ist ja alles okay soweit und es ist natürlich wichtig, dass du darauf hinweist. .. und eigentlich hatte ich auch nur geschrieben, dass dein einwand mit dem overlap-int berücksichtigt wurde und nicht, dass er so berücksichtigt wurde, wie du es gemacht hättest.
also .. geben wir den artikel bald wieder frei, für die, die low-freq messungen machen wollen.
eine frage am rande hätte ich schon noch, da sie mich schon länger bewegt:
da ich ja schon oft mehr oder weniger mit dir zu tun hatte und auch das roboternetz wie eine tägliche zeitung durchlese habe ich mich oft schon gefragt, was dein hintergrund eigentlich sein könnte. du hast viele informationen bereitgestellt zu verschiedensten themen und bist offenbar in vielen wissensbereichen zu hause oder zumindest gast.
ein projekt oder einen eigenen beitrag von dir habe ich noch nie bemerkt (das könnte mir ja entgangen sein).
nachdem meine letzte theorie war, dass du ein projekt der Nasa oder vom Fbi bist (persönlicher smiley) erlaube ich mir mal die frage , wie ich mir Dich so vorstellen kann ?
Ich hoffe du kannst mir helfen.
Gruss Klaus
Mein Hintergrund ist ein Physikstudium, und schon eine ganze Zeit (fast 30 Jahre) mit Elektronik/Computer als Hobby.
Als Phsiker sollte man halt die Fähigkeit haben sich schnell auch in neue Gebiete einzulesen.
Lesezeichen