PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Frequenzmessen



Tryan
16.09.2008, 19:38
Hi, ich weiß diese Thema wurde schon sehr oft durchgekaut, aber je mehr Beiträge und Tutorials ich lese desto mehr blick ich nicht mehr durch...
Daher hoffe ich das ihr mir weiter Helfen könnt, ohne gleich ein würg-Reflex zu bekommen, wenn ihr den Titel lest. ;)

So nun zu meinen Anliegen.
Ich möchte gerne eine Frequenz (Rechteck) zwischen 1Hz und ca 1khz messen. Als Controller hab ich den Atmega16 mir ausgesucht.
Im Grunde soll das Programm so Funktionieren, das ab Frequenz X LED1 leuchtet, ab Frequenz Y LED2 usw. bis LED8 erreicht ist.

Als erstes habe ich versucht eine LED zu Toggeln wenn der Counter überläuft (siehe Code) jedoch funktioniert das schon nicht...



'Frequenzmessen

'---------------------------------
'Deklaration
'---------------------------------

$regfile = "m16DEF.DAT"
$crystal = 16000000


'---------------------------------
'Initialiserung:
'---------------------------------

Config Porta = Input
Config Portb = Output
Config Portc = Output

Config Portd = Input
Portd.6 = 1

Dim Frequenz As Word 'Frequenz als Word festelegen

Config Timer1 = Counter , Edge = Falling , Capture Edge = Falling , Noise Cancel = 1 , Prescale = 1 'Timer als Counter Festlegen

Enable Int0 'Interrupt Aktivieren
Enable Timer1 'Timer einschalten

Enable Interrupts ' Interrupts aktiv
On Timer1 On_timer1


'---------------------------------
'Hauptprogramm:
'---------------------------------
Portb.7 = 1 'Kontroll LED


Do

Loop

'---------------------------------
'Interrupt
'---------------------------------
On_timer1:
Toggle Portb.0
Return

End


Und keine Angst ich möchte von keinem alles Vorgekaut haben, nur ein paar Tipps und Ideen auf die ich alleine nicht komme.

Vielen Dank für eure Hilfe

Mfg Tryan

oberallgeier
16.09.2008, 19:46
Dumm, dass Du nach Bascom fragst. Ich mache das so, dass ich das Signal (muss für einen Controller einen TTL-Pegel haben) auf den externen Interrupt lege, beispielsweise auf steigende Flanke triggern, und dann die Zeit bis zur nächsten steigenden Flanke messe. Alles Andere ist danach ja einfach :). Beim M16 liegt der extInt0 glaub ich auf Pin16=PD2. Andere externe Interrupts sind dafür sicher auch geeignet - sieh mal im Datenblatt nach.

Vielleicht hilft Dir das.

Tryan
16.09.2008, 20:11
@ oberallgeier
Danke für die schnelle Hilfe-Stellung!
Ich hab mich für Bascom entschieden, weil wir in der Schule eine kleine Einführung in das Programm hatten,
sodass ich ein wenig Vorkenntnis habe (Drücke Taster1 = Leuchte LED1, naja ein wenig mehr ist es schon ;))

Ich wollte zum Messen Portd.6 nehmen das ist der "ICP-Pin"
Mit ihm sollte es am einfachsten gehen. Das mit dem Flanke zu Flanke messen ist eine sehr gut Idee.
Also müsste ich Messen wieviele "Takte" zwischen Flanke1 und Flanke2 abgelaufen sind. Ist das so richtig?

Mfg Tryan

Besserwessi
16.09.2008, 20:56
Die Takte zwischden den Flanken zu messen sollte bei den niedriegen Frequenzen das Beste sein. Bei nur 1 kHz Frequenz hat man reichlich Zeit, auch in Basic. Ich weiss nicht ob BASCOM für die ICP Funktion eine extra Unterstützung hat, vermutlich nicht, aber es geht auch ohne. Ein Beispiel in C ist im Wiki-Bereich unter timer:
https://www.roboternetz.de/wissen/index.php/Timer/Counter_(Avr)#Input_Capture

Wenn man etwas sucht, vielleicht findet sich ja auch ein ICP Beispiel in Basic. Die Umsetzung von C nach Basic sollte ziehmlich 1:1 möglich sein. Allerdings sind Interrupts auch nicht gerade das einfachste und etwas compilerspezifisch.

oberallgeier
16.09.2008, 23:02
Hi Tryan, hallo Besserwessi,

Tryan, meine Bemerkung mit "Dumm ... BASCOM" ... war natürlich von mir dumm ausgedrückt. Ich meinte damit blos, dass ich von Bascom nix verstehe. Ich hoffe nur, dass Du nichts anderes rausgehört hast.

Die Frequenz von 1 Hz zu zählen mit den Takten des ICP kommt mir schwierig vor:

Input Capture
... Die typische Anwendung ist die Messung von kurze Zeiten ...und weiter

Input Capture
... Etwas komplizierter wird es wenn die 16 Bit Auflösung nicht mehr ausreicht ... wesentliche Schwierigkeit ... Fall ... berücksichtigen, dass ein Überlauf Interrupt und der ICP Interrrupt fast gleichzeitig ausgelöst werden ...
Und dieses Nicht-Ausreichen bei etwas längeren Zeiten hatte mich abgeschreckt. Schliesslich zählst Du z.B. bei einem 4MHz-Quarz und einer Frequenz von 1 Hz bis 4 Millionen hoch . . . . . . Ich arbeite bevorzugt mit einem mega168 bei 20 MHz. Daher hatte ich mir für meine Zeitmessungen (in C) einen Timer geschrieben, der einen 50 µs-Interrupt auslöst und in der ISR eine Variable hochzählt. Die zeigt bei 20 000 eben genau eine Sekunde und kann bis über 60 000 gehen, da ich eine 16bit-unsigned-integer verwende. Ich kann also Zeiten bis über drei Sekunden ohne Überlaufprobleme messen. Diese Auflösung würde bei Deiner Einteilung der drei Größenordnungen 1 ... 1000 Hz doch reichen. Ich lese bei mir beim externen Interrupt die aktuelle Zeit aus. Du könntest so z.B. in der ISR des externen Interrupts die Variable in einen Wert für das Nicht-Interrupt-Modul sichern, danach die Zählvariable auf Null setzen und damit eine neue Zeitmessung starten. Im "Nicht-Interrupt-Modul" - also main oder einem Unterprogramm - wird dann der gesicherte Wert in eine Frequenz umgerechnet und die LED geschaltet. Sieh das bitte aber nur als Vorschlag, ich bin wirklich nicht firm im Frequenzmessen. (Anmerkung: der informative und praktische RN-Wissen-Abschnitt stammt ja von Besserwessi - ich habs nur gelesen :) ).

Tryan
17.09.2008, 10:36
@ oberallgeier
Nein ist alles klar, hab mir schon so etwas gedacht ;)

Meine Ursprünglich Idee war es so zu arbeiten wie es hier beschrieben ist:
http://avrhelp.mcselec.com/config_timer1.htm

Also wenn die erste Flanke (von 0V auf 5V) aus dem ICP - PORTD.6 ankommt startet der Timer1/Counter1.
Wenn die zweite Flanke (von 0V auf 5V) auf dem ICP - PORTD.6 ankommt stoppt der Timer1/Counter1.
Somit würden ich jeden zweiten Takt messen.

Der Zahlenwert der zwischen Flanke1 und Flanke2 aufgezählt wurde, wird gespeichert und so umgerechnet, sodass ich die Frequenz bekomme.

D.H. je höher die Frequenz wird desto kleiner der Zahlenwert zwischen Flanke1 und Flanke2.

Ist meine Idee im Grunde so in Ordnung? Oder ist da ein Denkfehler?

Mfg Tryan

Besserwessi
17.09.2008, 20:47
@tryan: Die Idee den Timer beim Interrupts zu starten ist durchausmöglich. Bei den eher kleinen Frequenzen geht das auch noch ganz gut, denn die mögliche Verzögerung beim Auslösen des Interrupts ist meistens unter 1 µs. Für höhere Frequenzen oder maximale Auflösung ist es aber besser das Starten auch über ICP zu machen. Sonst kann man den ICP Pin natürlich auch wie einen externen Interrupt nutzen.

@oberallgeier: Die ICP Funktion ist zwar Hauptsächlich für kurze Zeiten wichtig, geht aber natürlich auch für längere Zeiten. Wenn man die hohe Auflösung nicht braucht kann man ja den Prescaler nehmen. Dann ist die Periodenmessung mit ICP wirklich sehr einfach. Wenn man erst mal weiss wie es geht, ist auch die Erweiterung der Auflösung auf 32 Bit kein Problem, dann sind selbst bei 20 MHz Timer-Takt Zeiten bis etwa 200s möglich.

Tryan
18.09.2008, 20:49
Nabend, ich glaube ich bin ein wenig voran gekommen. Ich bin Soweit das ich die Pulse von der Frequenz anzeigen lassen kann.
Das Programm sieht jetzt soweit aus:



'Frequenzmessen

'---------------------------------
'Deklaration
'---------------------------------

$regfile = "m16DEF.DAT"
$crystal = 16000000


'---------------------------------
'Initialiserung:
'---------------------------------

Config Porta = Input
Config Portb = Input
Config Portc = Output
Config Portd = Output


Dim Pulse As Byte

Enable Timer0 'Timer0 An
Config Timer0 = Counter , Edge = Rising 'Config von Timer0

On Ovf0 Tim0_isr 'Interrupt bei Overflow AN

Enable Interrupts 'Interrupts An

Tcnt0 = 0 'TCNT0 auf Null gesetzt

'---------------------------------
'Hauptprogramm:
'---------------------------------
Do
Portd = Tcnt0 'Ausgabe von TCNT0 an PortD

Loop

'---------------------------------
'Interrupt
'---------------------------------
Tim0_isr:
Pulse = Tcnt0
Timer0 = 0 'Timer0 wird zurückgesetzt
Return
End


Das Problem an dem ich jetzt hänge ist das ich die Pulse nur ein Zeit X messen darf und dann müsste eigentlich ich mit einer kleinen Rechnung wissen was für eine Frequenz anliegt.

Habt ihr ein Tipp für mich?

lg TRyan

Sauerbruch
19.09.2008, 13:07
Also - Du möchtest als niedrigste Frequenz 1Hz messen. Die "Zählzeit" (oder auch Torzeit genannt) sollte deshalb nicht kleiner sein als 1 Sekunde. Längere Torzeiten liefern zwar eine höhere Auflösung, machen die Messung aber auch träger.

1 Sekunde hätte den Charme, dass Du den Wert nicht mehr umrechnen musst: Die Zahl der Impulse ist gleich der Frequenz. Wenn Du bis 1kHz messen möchtest müsstest Du allerdings berücksichtigen, dass Timer0 nur bis 256 zählen kann. Also müsstest Du eine Überlauf-ISR einrichten, in der zu einer (Word!)-Variablen bei jedem Überlauf 256 addiert wird - oder aber auf den 16bit-Timer1 wechseln.

Die Sekunde müsstest Du mit einem anderen Timer realisieren: Bei 16MHz und einem Prescaler von 1024 hättest Du eine Taktfrequenz von 15625Hz, d.h. der Timer zählt in 1 Sek um 15625 weiter. Wenn Du Timer1 mit (65535 - 15625), d.h. 49910 vorlädst, läuft er immer nach genau 1 Sekunde über. In der ISR müsstest Du dann den aktuellen Wert vom Frequenz-Zähl-Timer0 zu Deinem Word addieren, Timer0 auf 0 setzen, Timer1 auf 49910, den Wert von Word an eine andere Variable übergeben und Word für die nächste Zählung auf 0 setzen.

Klar? 8-[ (Bestimmt führen noch ein paar andere Wege nach Rom - aber diesen würde ich benutzen...)

P.S.: Mit "Enable TimerX" aktiviertst Du übrigens nicht den Timer (das geschieht mit Start TimerX), sondern seinen Überlauf-Interrupt!

Tryan
19.09.2008, 20:27
Hi, ich möchte gerne eine kleine Erfolgsmeldung verkünden :D
Der Frequenzmesser funktioniert!
Danke Sauerbruch du hast mir den entscheidenden Tipp gegeben.
So sieht mein Programm aus:



'Frequenzmessen

'---------------------------------
'Deklaration
'---------------------------------

$regfile = "m16DEF.DAT"
$crystal = 16000000


'---------------------------------
'Initialiserung:
'---------------------------------

Config Porta = Input
Config Portb = Input
Config Portc = Output
Config Portd = Output


Dim Pulselo As Word
Dim Freq As Integer
Dim Freq10 As Integer

Enable Timer0 'Timer0 An
Config Timer0 = Counter , Edge = Rising 'Config von Timer0
Enable Timer1
Config Timer1 = Timer , Prescale = 256


On Ovf1 Tim1_isr 'Interrupt bei Timer0 Overflow AN
On Ovf0 Tim0_isr 'Interrupt bei Timer0 Overflow AN

Enable Interrupts 'Interrupts An

Tcnt0 = 0 'TCNT0 auf Null gesetzt
Timer1 = 59284 'Timer1 auf 59284 setzten

Config Portd = Output
Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 20 * 4

Initlcd
Cls
Cursor Off
'---------------------------------
'Hauptprogramm:
'---------------------------------
Do
Portc = Freq
Locate 1 , 1
Lcd "Freq:"
Locate 1 , 7
Lcd Freq10
Locate 1 , 11
Lcd "Hz"
Waitms 500
Cls
Loop

'---------------------------------
'InterruptTimer01
'---------------------------------
Tim0_isr:
Pulselo = 255
Return

'---------------------------------
'Interrupt Timer1
'---------------------------------
Tim1_isr:
Stop Timer0
Freq = Tcnt0 + Pulselo
Freq10 = Freq * 10
Timer1 = 59284
Tcnt0 = 0
Pulselo = 0
Start Timer0
Return




End


Leider kann ich die Frequenz nur in 10-Schritten darstellen.
Als Auflösung haben ich 100 ms gefällt. Sonst kann ich nicht bis zu 1KHz messen.
Vielen Danke nochmal an alle die mir geholfen haben

Lg Tryan

Sauerbruch
19.09.2008, 22:08
Leider kann ich die Frequenz nur in 10-Schritten darstellen.
Als Auflösung haben ich 100 ms gefällt. Sonst kann ich nicht bis zu 1KHz messen.

Na - das sollte doch aber auch noch zu lösen sein - wo Du schon so weit bist...

Eine Frage erstmal: Möchtest Du den Frequenzwert tatsächlich über den PortC ausgeben? Das könnte nämlich schwierig werden, weil Du Freq als Integer dimensioniert hast (=16 bit), während der Port ja nur 8 bit breit ist.

Falls nicht - dann lass Freq ruhig als Integer, damit kannst Du nämlich auch bis 32768 zählen lassen (und somit auch 1000Hz auf 1Hz genau auflösen). Oder als Word: Das sind 16 Bit ohne Vorzeichen, d.h. es kann Werte von 0 bis 65535 annehmen.

Dazu müsstest Du den Timer1-Interrupt auf 1 Sekunde verlängern, also z.B. mit einem Prescaler von 256 und einem Preset von 3025.

Im Moment kann Timer0 ja nicht überlaufen, weil er in 1/10 Sek selbst bei 1KHz nur bis 100 zählt. Bei 1 Sekunde wäre das schon anders - dann gäbe es ab 256Hz regelmäßige Überläufe - und in der Tim0_isr muss dann stehen

Pulselo = Pulselo + 256

damit bei jedem erneuten Überlauf 256 dazugezählt werden. Sonst wäre Pulselo auch nach dem 2. und 3. Überlauf immer 256 - und die Frequenz würde ziemlich falsch angezeigt werden.

Die Zeile mit Freq10 = Freq * 10 bräuchte es dann gar nicht mehr - außerdem kann man Variablen übrigens auch ganz bequem mit sich selbst verwurschteln, also z.B. Freq = Freq*10. Auch wenn man von jedem Mathe-Lehrer für diese Gleichung ´ne glatte Sechs bekommen würde :-& - in Bascom ist das durchaus richtig.

Viel Spaß beim weiterentwickeln!!

Tryan
21.09.2008, 11:49
Mensch danke Sauerbruch,
jetzt funktioniert alles wunderbar.
Leider fällt mir jetzt auf, dass die Entscheidung eine Messung eine Sekunde laufen zu lassen nicht so optimal war.
D.h. Die Angezeigten Werte verhalten sich sehr träge...

Wenn ich aber soweit alles richtig verstanden habe. Könnte ich die Mess dauer auf 100ms verkleinern, aber dadurch würde sich die Auflösung der Messung um den Faktor 1/100 "verschlechtern"

Also hab ich die Wahl zwischen einer exakten aber langsamen Messung oder einer groben und schnellen Messung.

Stimmt das soweit?

Lg TRyan

Sauerbruch
21.09.2008, 12:19
Also hab ich die Wahl zwischen einer exakten aber langsamen Messung oder einer groben und schnellen Messung.

Stimmt das soweit?

Das stimmt ganz genau! Nur, dass sich die Auflösung nur auf 1/10 reduziert, wenn Du die Torzeit auf 1/10 reduzierst (und nicht auf 1/100).

Man könnte natürlich auch 10 mal die Impulse von 0,1 Sekunden messen und zusammenaddieren. Der erste Wert wäre zwar erstmal der selbe als hättest Du gleich 1 Sekunde gemessen. Man könnte dann aber alle 0,1 Sekunden den "ältsten" Wet aus dieser Summe rausschmeißen und durch den aktuellsten ersetzen. Damit hättest Du die Vorteile einer hohen Auflösung und einer gleichzeitig schnellen Messung - aber sicherlich auch eine Menge Programmcode :-k
Das Stichwort hierfür wäre "Arrays"...

Gruß,

Daniel

Besserwessi
21.09.2008, 13:22
Die andere Anlternative um schnell eine niedrige Frequenz genau zu messen, ist es nicht die Flanken zu zählen, sondern den Abstand zwischen 2 Flanken zu stoppen und dann die Freuqenz als f = 1 / T zu berechnen. Das ist dann der Weg mit dem ICP Interrupt. Bei 1-2 Hz hilft das allerdings auch nichts, denn man braucht wenigstens 2 Flanken.

raggy
21.09.2008, 17:17
Hallo Besserwessi
Ich bin seit 14 Tagen mit dem Problem die Zeit zwischen zwei flanken zu
messen,am suchen(google) aber was richtiges finde ich nicht.
Da ich kein Englisch kann faellt mir das mit den Datenblatt schwer.
Waere sehr nett wenn Du mir mit einem stueck code mal den weg zeigst.
betrachte mich auch immernoch als anfaenger,mit 62 nicht ganz so einfach.
gruß raggy

Besserwessi
21.09.2008, 18:31
Einen Beispielcode habe ich in den Wiki-bereich gestellt. Allerdings ist der Code in C und nicht Basic. Sieht etwas lang aus, aber die eigentliche Zeitmessung ist nur etwa Hälfte. Mit BASCOm habe ich nicht viel gemacht, aber das Übertragen sollte relativ einfach gehen. Die sache mit der Union geht auch irgendwie, das ist dann aber was für BASCOM spezialisten. Sonst dort den lang samen Weg mit multiplikation gehen.

der link ist oben auch schon mal:
https://www.roboternetz.de/wissen/index.php/Timer/Counter_(Avr)#Input_Capture

raggy
21.09.2008, 18:44
Hallo Besserwessi
Danke Dir,werd ich mich mal da durch lesen!

Ps ich hab es jetzt raus,wie es funktioniert
Nochmals ein DANKE,Gruß raggy