PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : hier ein code für niedrige frequenzmessung mit icp



kolisson
16.09.2009, 01:13
hier einmal ein quelltext für die messung von niedrigen frequenzen
durch periodenmessung mit hilfe der input-capture funktion.

viel spass
klaus



' 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

Waldichecker
16.09.2009, 18:08
Hey guter Quellcode!

Besserwessi
16.09.2009, 18:39
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.

kolisson
16.09.2009, 19:03
@Besserwessi

danke für den tipp.
den artikel hatte ich noch garnicht gefunden und gelesen.

mal sehen, was sich machen lässt.

gruss klaus

kolisson
05.11.2009, 18:19
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



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

Besserwessi
05.11.2009, 19:12
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.

kolisson
05.11.2009, 20:19
ach besserwessi,

was bist du denn für einer ? und ich dachte ich kenn dich schon etwas!
aber irgendwie überaschst du mich immer wieder.


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 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.

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.

Besserwessi
05.11.2009, 20:38
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.

kolisson
05.11.2009, 21:28
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

Besserwessi
05.11.2009, 22:46
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.

kolisson
05.11.2009, 23:23
danke für die antwort...
hat mich richtig fröhlich gemacht, da ich nun weiss, dass du kein NASA-projekt bist, was darauf abzielt mal so robotmässig als wissensdatenbank an einem forum teilzunehmen.

ich hatte wirklich schon vorgestellt, dich gäbe es garnicht.

und nun.. da es ja mit der mimik immer so schwer ist im web:

ein riesengrosses schmulzendes grinsen für dich von mir !!.

gruss klaus


restfrage: eigene projecte ? / homepage oder so ?