Frequenz messen

Hallo zusammen,

ich bin gerade an einem Projekt beschäftigt, das mir auf intelligente Weise meine Pflanzen bewässert. Dazu habe ich kapazitäre Feuchtesensoren gewählt (wegen Elektrolyse,...), die mir eine Frequenz als Anhaltspunkt für die Feuchte der Erde ausgeben.

Jetzt gelingt es mir aber nicht, die Frequenz mithilfe meines Arduino Mega 2560 auszulesen.
Kann mir jemand helfen?

Laut Entwickler der Feuchtesensoren geben diese je nach Fläche des Kondensators eine Frequenz von einigen 100kHz bis zu mehreren MHz aus. Im Anhang ist noch der Aufbau der Sensoren zu sehen...

Eine einfache Option ist die Frequenz auf einen externen Interrupt zu legen und dann die Impulse zu zählen.

Mit millis() kannst du da z.B. 250ms lang zählen. Du aktivierst den Interrupt (attachInterrupt), setzt die Zählvariable auf 0 und nach einer bestimmten Zeit schaltest du ihn wieder aus (mit detachInterrupt) und liest den Wert des Zählers aus. Bei 250ms multipliziert man das mit 4 und hat die Frequenz in Hz. Bei langsamen Frequenzen muss man da länger zählen, aber so hohen gehen auch kürzere Torzeiten.

Das sollte man dann mehrmals hintereinander machen und den Wert mitteln, da er vielleicht etwas schwankt.

Gibt aber noch andere Möglichkeiten.

von einigen 100kHz bis zu mehreren MHz aus

Bei solchen Ferquenzen ist Arduino überfordert.
Damit Du den Interrupt benutzen kannst mußt Du die Frequenz zuerst mittles eines Teilers um den Faktor 10 bis 100 herunterteilen.
Grüße Uwe

Die bessere Option ist statt dessen einen Timer/Counter zählen zu lassen. Damit belastet man nicht das Interrupt-System und das Programm wird nicht ständig unterbrochen.

Das Prinzip ist hier erklärt:
http://www.atmel.com/Images/doc8365.pdf

Da steht "Frequency measurement from 10Hz to Timer_Clock_frequency/2.5"

Wenn das stimmt gehen bei 16 MHz bis zu 6,4 MHz. Habe ich aber mit so hohen Frequenzen noch nicht gemacht (nur unter 100kHz).

Jetzt hat man da auf dem Mega das Problem, dass idiotischerweise die meisten Takt-Pins der Timer nicht auf das Arduino-Board herausgeführt wurden:
http://arduino.cc/en/Hacking/PinMapping2560
Mit T0 zerschießt man sich millis() und delay() auf Timer0, also nimmt man am besten Timer5 mit T5 auf Pin 47.

Timer5 funktioniert sehr ähnlich wie der Timer1 der hier erklärt ist:
http://maxembedded.com/2011/06/28/avr-timers-timer1/

Hier gibt es eine Übersicht auf deutsch:
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Timer

Die Register und Flags sind auch im Datenblatt erklärt:

Einfach z.B. nach TCNT5, TIMSK5 und TCCR5B suchen

  • Man verwendet hier auch den millis() Zähler von Timer0 um die Zeit zu zählen.
  • Pin 47 ist T5 und damit der externe Takt von Timer5. Die Flanke wird über die Bits CS52, CS51 und CS50 (Clock Select) im TCCR5B Register ausgewählt. Hier wird man wohl 111 für die steigende Flanke nehmen.
  • Wenn alle drei CS5n Bits 0 sind ist der Timer gestoppt. Das ist der Ausgangszustand
  • im TIMSK5 Register den Overflow Interrupt mit TOIE5 aktivieren (anders als im Tutorial oben hat hier jeder Timer ein eigenes Masken und Flag Register, aber das merkt man nur an den Namen der Register). Dann kann man in ISR(TIMER5_OVF_vect) mitzählen ob der Timer überläuft, wenn mehr als 65535 Impulse eintreffen.

Dann macht man das:
1.) Timer/Counter Register TCNT5 und Overflow Variable auf 0 setzten
2.) Timer5 über das Timer/Counter Control Register B freigeben mit TCCR5B |= 0x07; //untere drei Bits auf 1
3.) mit millis() entsprechend warten
4.) Nach einer bestimmten Zeit Timer5 wieder über die CS Bits stoppen mit TCCR5B &= ~0x07;
5.) TCNT5 enthält die Anzahl der eingegangen Impulse. Diese muss man mit den Überlaufen verrechnen. Über die Torzeit kommt man dann an die Frequenz

Hört sich jetzt kompliziert an, aber ist auf der MaxEmbedded Seite oben alles erklärt, was die Register und deren Bits betrifft. Beispiel Code für die Initialisierung (da aber CS nicht setzen, da du extern taktest) und den Overflow-Interrupt (da braucht man nur das Inkrementieren der Variable) ist auch dabei. Ist zwar für den AVR allgemein (deshalb die ganze direkte Port-Adressierung) aber läuft so auch auf dem Arduino (aber statt main() und while(1) einfach loop() nehmen).

Der Unterschied ist, dass der Timer da ständig läuft und nach x Überläufen was gemacht wird. Bei dir willst du den Timer starten und anhalten und danach die Anzahl der Überlaufe und den Zählerstand auslesen. Der Unterschied im Code ist aber minimal.

Du musst dann nur noch über millis() die Torzeit hinzufügen.Dazu steht was in den App Notes:

If a frequency of 500kHz is to be measured, a gate time of 1ms would result in good use of the 16-bit timer/counter’s range.

Die Methode mit der externen Digitalen Auswerteelektronik zum Frequenzteilen ist sicherlich die genaueste, aber auch die aufwändigste.

Für deine Zwecke reicht es vielleicht schon, wenn du das Signal auf einen Tiefpass gibst, dann gleichrichtest und den Spannungspegel mißt. Mit zunehmender Frequenz sinkt die Spannung. Um das Ergebnis zu verbessern könntest du das gleiche parallel mit einem Hochpaß machen und die beiden Größen dann vergleichen.

Letztlich mußt du ja deine Messmethde manuell abgleichen. Du wartest also, bis dein Blumentopf die minimale Trockenheit errreicht hat, und bei dem Wert schaltest du die Bewässerung ein.

guntherb:
Die Methode mit der externen Digitalen Auswerteelektronik zum Frequenzteilen ist sicherlich die genaueste, aber auch die aufwändigste.

Meint du den Kommentar von uwefed? Dabei ging es nur darum erst mal einen Frequenzteiler (z.B. 74HC590 8-Bit Binärzähler) vorzuschalten, damit der Interrupt nicht so oft ausgelöst wird.

Das Problem hat man nicht wenn man dazu einen Timer zählen lässt. Da muss man sich nur kurz um den Überlauf kümmern, aber das manuelle Zählen entfällt. Hier ist keine externe Beschaltung mehr nötig.

Für Frequenzen für ein paar Dutzend kHz habe ich sowas wie gesagt schon gemacht. Funktioniert wunderbar. Man muss wahrscheinlich nur etwas mit der Torzeit spielen, damit es über alle Frequenzen gleich gut geht.

Bei solchen Experimenten tut man sich ohne Oszilloskop meist schwer. Wenn man so gar nicht weiß ob die Schaltung überhaupt schwingt und wenn ja mit welcher Frequenz.
Ich habe mir in solchen Fällen immer ein CD4060, das ist 14-Stufen Binär Counter an den den Messpunkt gelegt. Die Frequenz wird stufenweise soweit runtergeteilt, dass man die Ursprungsfrequenz mit blinkenden LEDs messen kann. Evtl kann man den Counter auch direkt als Oszillator benutzen.
Die runtergeteilte Frequenz kann man dann mit dem Arduino auch viel leichter messen.
Gruß
Reinhard

Man bräuchte auch einen Frequenzgenerator oder einen kleinen Oszillator um das Programm zu testen.

Supergenau muss es für diese Anwendung glaube ich nicht sein. Da würde dann ein 74HC14 + RC-Glied reichen den man auf einen bestimmten Wert dimensioniert und dann diese Frequenz misst:
http://talkingelectronics.com/pay/BEC-2/Page49.html

Mit z.B. 47pF und 10k hat man 2,553 MHz. Wenn man den Widerstand auf 100k erhöht hat man 255 kHz. Bei 200k sind es 127,6 kHz. Bei 300k 85kHz

Wow, so viele antworten. ich werd mich gleich morgen mal ans ausprobieren machen.
hab ich das richtig rausgelesen, dass beim arduino mega dann nur der pin 47 fürs messer der frequenz in frage kommt?

Soweit ich sehen kann, ja. Oder du nimmst die Version mit vorgeschaltetem Frequenzteiler (z.B. CD4060 oder 74HC590) und Interrupt-Eingang. Dann kommt der Takt langsam genug, damit der Interrupt nicht ständig ausgelöst wird. Dafür wurden dann alle Hardware Interrupt Pins gehen, oder theoretisch sogar die Pin Change Interrupts.
Der CD4060 ist da besser, da der mit 14 Bit einen höheren Teiler erlaubt. Ganz vergessen, das Ding.

Wenn du nur eine Frequenz zählen willst, brauchst du das aber nicht unbedingt.

Auf dem Mega gibt es noch mehr so Murks mit den Sonderfunktionen der Pins. z.B. ist nur ein Eingang des Analog-Komperators zugänglich. Den anderen kann man nur auf die interne Referenz legen. Ich nehme mal an man hat gedacht, dass die meisten Leute diese Funktionen nicht nutzen (woran natürlich was wahres ist) und hat lieber andere Pins herausgeführt.
Der Prozessor hat Takt-Pins mit T0 (50), T1 (49), T3 (8 ), T4 (27) und T5 (37). Die Nummern da sind die Prozessor IC Pin Nummern, nicht die auf dem Arduino! Und man kommt nur an T0 und T5. Und auf Timer0 läuft wie gesagt millis(). T2 gibt es nicht, da Timer2 anders aufgebaut ist und darauf ausgelegt ist optional mit einem Uhrenquarz getaktet zu werden.

Und wie gesagt wäre ein kleiner Taktgenerator mit einem 74HC14 oder CD4093 nicht schlecht um das Programm zu testen. Damit hast du eine definierte Frequenz, was bei dem Sensor nicht der Fall ist (es sei den es gibt irgendeinen Testmodus oder ähnliches).

ok, danke. Das heißt dann aber auch, dass ich mir eine Art Verteiler /Adapter zusammenlöten müsste, wenn ich mehrere Feuchtesensoren nutzen möchte. Und die dann eben nacheinander auslesen lassen. Mein Plan war es eigentlich 10 solcher Sensoren zu nutzen.

Nacheinander auslesen ist kaum ein Problem. Dazu legst du dir einen 16-Bit Multiplexer zu (oder baust einen aus 2 x 8 Bit):
http://www.nxp.com/documents/data_sheet/74HC_HCT4067.pdf

Damit kannst du einen von 16 Eingängen auf deinen Takt-Eingang schalten

Muss eigentlich nicht analog sein, aber das Ding gibts halt :slight_smile:

EDIT:
74HC151 ist nichts, da der Ausgang kein Tristate hat. Kann man also nicht kaskadieren :s
Der 74HC251 (8 Bit) ist die Tristate Version. Wenn man jeweils Strobe/Output Enable auf einem anderen Pegel hält (z.B. einfach einen NPN als Inverter) kann man die Ausgänge zusammenschalten:
http://www.nxp.com/documents/data_sheet/74HC_HCT251.pdf

Der Digital-Multiplexer könnte da vielleicht eine bessere Performance haben.

ich hatte mir mal ein Messgerät geholt, dass Frequenz messen kann. Im unberührten Zustand zeigt es mir 50 Hz an und ändert die Frequenz wenn ich ihn beispielsweise in die Hand nehme. Bekomm dann so max. 40 kHz raus...

Bei dem Ansatz von guntherb könnte ich doch dann die analoge Pins belegen und dort den Spannungspegel mit analogRead() auslesen. oder stehen mir da auch nicht alle zur Verfügung?

Nein. Die hast du alle. Sind ja auf dem Arduino Board da. :slight_smile:

Letzlich willst du ja keine Frequenz messen.

Sondern einen Schaltpunkt in Abhänging von der Bodenfeuchte haben.
Es macht also meiner Meinung nach keinen Sinn, ein Gerät zu bauen, dass dann Frequenzen im Bereich von 20Hz bis 2MHz hochgenau messen kann, um daraus später einen Gießzeitpunkt abzuleiten.

Wenn du gerade schon ein Frequenzmessgerät hast, dann messe doch erst mal, welche Frequnzen dein Sensor im interessanten Feuchtebereich abgibt. Dann kannst du deine Bemühungen daraufhin optimieren.

Ich denke, dass die Genauigkeit dieser Sensoren sowieso nicht besonders gut ist. Meiner Meinung nach reicht hier eine analoge Auswertung völlig aus. (und ich denke, der Zeitpunkt wann gegossen wird, muß auch nicht aufs letzte µg H2O/cm³ Erde genau sein müssen)

Was nicht heißen will, das der Weg des Frequenzzähler keinen Spaß macht und nicht zum Erfolg führt. Aber etwas overengineered ist das schon.

Das stimmt. Wie wäre es mit einem Frequenzfilter höherer Ordnung (mit OpAmps), der eine relativ steile Flanke in einem Frequenz/Spannungs-Diagram erzeugt (ein einfaches RC-Glied taugt da nicht viel für diese Version). Diese kann man dann mit einem Komparator mit einer Schaltschwelle vergleichen. Durch dessen Hysterese spielen dann geringfügige Frequenzschwankungen keine Rolle.

Da sind da aber etwas detailliertere Elektronik-Kenntnisse gefragt. Frequenzen kann man auch noch als nicht-Elektroniker messen :slight_smile:

EDIT: Vor allem braucht man bei sowas wieder ein Oszilloskop. Ohne ist man da aufgeschmissen.

Ich glaube nicht, dass man hier grossen Aufwand treiben muß. Letztlich ist es eine Frage der gewünschten Genauigkeit, die erzielt werden soll.
Aber ich gebe dir recht, Digitaltechnik fällt den meisten leichter als Analoge.

Man kann ja mal mit einem einfachen RC-Glied als Filter rumspielen und sehen was da rauskommt. Das kann man einfach mit analogRead() messen. Da wie gesagt neben dem Sensor vielleicht noch einen einstellbaren RC-Oszillator auf einem Breadboard als Referenz.

Mit Potis kann man dann die Frequenz des Oszillators und die Grenzfrequenz des Filters in gewissen Grenzen ändern.

emjay:
hey.
ich denke auch, dass es nicht auf jedes Hz ankommt. ich bin leider noch relativ unerfahren, was das programmieren angeht. die bewässerungsanlage soll ein werkstück innerhalb meines studiums werden.
ich verstehe zwar, wie du vorgehen würdest, weiß aber nicht, wie ich es umsetzen könnte. wie kann ich die tief- bzw hochpässe erhalten? was brauch ich dazu?

liebe grüße
marijan

Ein Tief- oder Hochpass besteht im einfachsten Fall jeweils aus einem Widerstand und einem Kondensator.
Was die Sache hier noch etwas komplizierter macht, dass der Arduino ja nur Gleichspannung messen kann, wir also eine Gleichrichtung brauchen, zusätzlich aber die Spannung recht klein ist.

Mein Vorschlag für einen Tiefpass wäre der hier:

Damit solltest du im Frequenzbereich von 100kHz bis ca. 10Mhz was brauchbaren einstellen können.
Was studierst du denn?

Vielen dank für den Aufbau.werde ihn morgen gleich mal nachbauen und testen. Und was wäre wenn ich unter die 100kHz rutschen würde? Kann man da noch mit den werten der Bauelementen spielen oder bekommt man dann keine brauchbaren werte mehr?

Ich studiere Lehramt und mitunter auch das fach Technik.da muss ich eben auch mehrere Werkstücke anfertigen, denen am besten eine Problemstellung zugrunde liegt. Eben pflanzen anhängige Bewässerung...
wir haben zwar Elektronik angeschnitten, aber eben nicht so tief gehend, dass ich damit alleine zurecht kommen würde.