Guten Abend, Ich möchte mit meinem Arduino Uno (Atmega328P) und einem Durchflusssensor auf Hall Basis den Durchfluss einer Kühlung bestimmen. Das signal ist als 5V Impuls bereitsaufbereitet. Die zu erwartende Impulsmenge beträgt zwischen 0-160 pro Sekunde (8 Imp jeh Sec / Liter) Welchen Eingang benutze ich dafür? Die Nutzung des LCDKeypadShields zur Visualisierung ist auch geplant. Wo kann ich evtl beim Programmieren ein Beispiel finden?. Gibt es vlt schon vorgefertigte Bausteine? vielen Dank, Johannes
Der Timer bietet diese Funktionalität an. Genaueres steht im Datenblatt.
avr schrieb: > Der Timer bietet diese Funktionalität an. ... und wenn du dazu den/einen ICP (input capture pin) nutzt, wird das ganze recht genau und komfortabel.
Programmierer schrieb: > ... und wenn du dazu den/einen ICP (input capture pin) nutzt, wird das > ganze recht genau und komfortabel. Nein, ICP ist bei 160 Impulse pro Sekunde das falsche Stichwort. Außerdem darf er gerne selbst suchen. Durch das Stichwort Timer weiß er ja schon das Kapitel.
Erstmal zur Logik der Sache: Die Impulse kommt über einen Eingang auf einen Zähler, der nach 1 Sekunde zurück gesetzt wird, aber vorher seinen Wert in ein Register schreibt. Den wert x 60 und / 480 und schon haben wir die Liter/min. (1Liter in 1 Minute gleich 480Impjeh Minute)so müsste die Logik stimmen? Ich habe bisher leider nur Erfahrung mit Industrie SPS, da würde das so hin hauen denke ich. Im 555-Seitigen Datenblatt des 328P sind zwar verschiedenste Eingänge gelistet doch kann ich keine Werte bezüglich der Schaltfolgen/sekunde sehen? vielen Dank, Johannes
Johannes S. schrieb: > Die zu erwartende Impulsmenge beträgt zwischen 0-160 pro Sekunde (8 Imp > jeh Sec / Liter) Nur auch für Dich zum "Verständnis"... Wie sehen denn die Impulse genau aus? Ist davon auszugehen, dass im allgemeinen die Impulsleitung "LOW" (0 Volt) liefert und dann aller paar Millisekunden mal kurz "HIGH" (5 Volt) für 1-2 Millisekunden? Oder zählt als Impuls jeweils der Wechsel, d.h. wenn es HIGH auf LOW wechselt zählt es als ein Impuls und wenn es von LOW auf HIGH wechselt ebenso? Du solltest dafür dann einen entsprechenden "Trigger" nutzen, der dir entsprechend bei "High-Low" (fallende Flanke) oder "Low-High" (steigende Flanke) oder "Change" (fallende -UND- steigende Flanke) ein Signal gibt. Viel Erfolg.
Achja, Schau Dir mal diesen Befehl an... der hilft Dir bestimmt: http://arduino.cc/en/Reference/AttachInterrupt
Johannes S. schrieb: > Im 555-Seitigen Datenblatt des 328P sind zwar verschiedenste Eingänge > gelistet doch kann ich keine Werte bezüglich der Schaltfolgen/sekunde > sehen? Meinentwegen ein weiterer Tipp: Timerkapitel-Registerdescription-TCCRnB. Mit diesem Register kannst du den Timer für dein Vorhaben konfigurieren.
avr schrieb: > Nein, ICP ist bei 160 Impulse pro Sekunde das falsche Stichwort. > Außerdem darf er gerne selbst suchen. Durch das Stichwort Timer weiß er > ja schon das Kapitel. Darf man denn aus Interesse heraus fragen warum? 160 Hz sollten doch für einen Arduino, der mit (soweit ich weiß) 16 MHz getaktet ist kein Problem sein?
Weil man durch ICP die Länge zwischen den Impulsen messen kann. Da man aber wissen möchte wieviele Impulse pro Sekunde erfasst werden, gibt es eine einfachere Methode mit dem Timer (Ich glaube allerdings nicht dass es dafür eine fertige Arduinolib gibt).
Und die rechnerische Beziehung zwischen Impulsdauer und Impulsrate is dir zu kompliziert? Hallo cyblord... ICP ist schon genau richtig.
Michael H. schrieb: > Und die rechnerische Beziehung zwischen Impulsdauer und Impulsrate is > dir zu kompliziert? Nein ist sie nicht. Aber man muss es sich nicht komplizierter machen als es ist. Mit dem richtigen Modus geht das ganze ohne irgendeine Rechnung. Und stell dir vor: Den Modus, den ich meine ist genau dafür gemacht. Manchen Leuten genügt auch das Wissen, dass man den Timer nachladen kann. Von CTC haben die noch nie was gehört (dazu scheinst du zu gehören). Genauso unsinnvoll ist es mit PulseIn das Programm zu blockieren.
gute güte, hast du ein empfindliches ego... falsch liegst du trotzdem ^^
Michael H. schrieb: > Und die rechnerische Beziehung zwischen Impulsdauer und Impulsrate is > dir zu kompliziert? Hallo cyblord... Was hab ich damit zu tun? Lass mich da raus. Wenn ich mich bei jedem solchen Arduino Thread aufrege dann brauch ich ne Herzklinik in der Nähe. Zum Glück ruhe ich in mir, wie ein buddhistischer Bär im Winterschlaf. gruß cyblord
Michael H. schrieb: > falsch liegst du trotzdem ^^ Lies auch du dir die betreffende Stelle im Datenblatt durch, dann wirst du bemerken dass es deutlich einfacher ist.
Jau. Es ist deutlich einfacher, aus einem halben Puls pro Sekunde was sinnvolles auszurechnen :-) Aber auch die Umkehrung hat so ihre Probleme. Nämlich dann, wenn der zeitliche Abstand zwischen den Pulsen so groß ist, dass der Timer (mehrfach) überläuft. So eine 'gemähte Wiese' ist das dann auch wieder nicht.
avr schrieb: > Lies auch du dir die betreffende Stelle im Datenblatt durch, dann wirst > du bemerken dass es deutlich einfacher ist. hab ich vorher schon. aber sag doch mal... wie willst ctc denn auslösen? ^^
Michael H. schrieb: > hab ich vorher schon. > aber sag doch mal... wie willst ctc denn auslösen? ^^ ... Das war ein anderes Beispiel am Rande und hat nichts mit dem Thema zu tun.
Karl Heinz Buchegger schrieb: > So eine 'gemähte Wiese' ist das dann auch wieder nicht. Einiges gibt es da trotzdem schon: http://www.mikrocontroller.net/articles/Frequenzz%C3%A4hlermodul
avr schrieb: > Aber man muss es sich nicht komplizierter machen als > es ist. Mit dem richtigen Modus geht das ganze ohne irgendeine Rechnung. Ooch nö, was ist denn daran so kompliziert
1 | a = b / c; |
hinzuschreiben? Eine Sekunde zu warten, ist doch sehr lange. Ergonomisch ist eine Darstellungsrate von 3..5 Messungen/s. Mit Input-Capture kein Problem. Peter
Hä? Warum ist das so "kompliziert"? - in einem Pin-Change-Interrupt erhöht man eine Variable um Eins - in einem Timer-Interrupt liest man die Variable aus und setzt sie auf Null Wegen der (sehr kurzen) Interrupt-Routinen bleibt der eigentliche Programmloop quasi transparent und man kann da drinnen mit dem Messwert in aller Ruhe tun, was immer man will ...
Frank schrieb: > - in einem Pin-Change-Interrupt erhöht man eine Variable um Eins > - in einem Timer-Interrupt liest man die Variable aus und setzt sie auf > Null "Hä? Warum ist das so "kompliziert"?" =) Denn das geht auch komplett Hardware-gesteuert mit ICP. Klar geht deine Methode. Die Methode PulseIn geht auch. Viele Wege führen nach Rom - und ICP ist der eleganteste.
Was ich in dieser 'welche Methode' Diskussion komplett vermisse, ist das Überdenken, welche Methode für das Vorhaben eigentlich das geeignetere ist. Das ganze ist eine Frequenzmessung. Es gibt 2 mögliche Ansätze: * man misst die Anzahl der SChwingungen in einem definiertem Zeitintervall * man misst die Periodendauer Jede der beiden Methoden hat ihre Vor und Nachteile. Unter anderem den, dass sie sich in der Messgenauigkeit unterscheiden. Und zwar je nach der zu messenden Frequenz. Bei kleinen Freuqenzen ist die Messung der Periodendauer besser Bei großen Frequenzen ist die Messung der Schwingungen pro Zeiteinheit besser.
Peter Dannegger schrieb: > Ooch nö, was ist denn daran so komplizierta = b / c; > hinzuschreiben? Nichts. Dennoch ist es komplizierter. Wenn wir kompliziert mit erzeugten Code gleichsetzen braucht meine Vorschlag sogar deutlich weniger. Frank schrieb: > - in einem Pin-Change-Interrupt erhöht man eine Variable um Eins > - in einem Timer-Interrupt liest man die Variable aus und setzt sie auf > Null Genau das war mein Vorschlag, außer dass man den Timer zählen lässt. Schließlich hat man in der Regel mehr als genug.
Mag sein, dass ich mir hier keine Freunde mache aber alle Ansätze die hier einen Interrupt oder direkten Timereingang verwenden sind anfällig gegenüber Störungen auf den Leitungen. Ich würde hier besser Pollen. d.h. Einen Timer der Zyklisch einen Interrupt auslöst mit z.B. 100 oder 200us für die genannte min. Pulslänge von 1ms. Darin kann man sich einmal einen Sekundenzähler beliebiger Länge basteln und zum anderen einen wahlfreien Pin Abfragen. Den kann man dann prima Entprellen in dem nur 2 oder 3 gleiche Zustände hintereinander als gültig akzeptiert werden. Das ganze sollte mit einem stelligen Prozentbereich der CPU auskommen. Insbesondere wenn hier Reed-Kontakte als Geber im Spiel sind wie z.B. bei Gaszählern. Indem Fall ist der kürzeste Impuls aber ehr in Richtung 100ms so dass der Timerinterrupt nicht so häufig kommen muss. Beliebtes Spiel wie man es falsch macht ist der diverse Code der zur Auswertung von 868MHz Empfängern im Umlauf ist. Das sind Störspikes ohne Ende auf dem Ausgang der Empfänger, aber die Entwickler denken mit einem Flankeninterrupt sowas auswerten zu können. Und dann wird sich gewundert warum durch eine dünne Wand schon kein Signal mehr richtig ankommt.
temp schrieb: > hier einen Interrupt oder direkten Timereingang verwenden sind anfällig > gegenüber Störungen auf den Leitungen sigh... auch du darfst das datenblatt lesen und den "noise canceler" finden. und einen interrupt abzulehnen, weil man dumme hardware baut, ist genau das: dumm. also leute, mir reichts in dem thread. hier sagen mehrere kompetente leute ICP - und ohne sich das stichwort und den hintergrund dazu überhaupt zu gemüte zu führen, plappert man lieber den letzten rotz daher. soll man euch hier ICP und die einstellung des timers vorkauen? sind wir in der vorschule? macht euren mist alleine, ich bin raus.
Michael H. schrieb: >> hier einen Interrupt oder direkten Timereingang verwenden sind anfällig >> gegenüber Störungen auf den Leitungen > sigh... > auch du darfst das datenblatt lesen und den "noise canceler" finden. An den die Schlauheit mit Löffeln gefressen habenden Michael H. Der noise canceler verzögert das ganze um genau 4 Takte. d.H. bei 8MHz 500ns. Wenn das bei dir reicht um lange Leitungen oder Reedkontakte zu entprellen ist dir nicht zu helfen. > macht euren mist alleine, ich bin raus. Nur gut dass ich solche wie dich nicht brauche um meinen Mist zu machen. Geh raus und bleib es. Bitte!
ICP Interrupt oder nicht - ich mußte erstmal nachlesen, was das überhaupt sein soll. ICP-Interrupts werden von der Arduino-Plattform nicht direkt unterstützt, und ich würde auch sagen, dass man nicht wirklich die Zeit zwischen den Impulsen messen möchte, sondern vielmehr die Anzahl der Impulse in einer bestimmten Zeiteinheit: Pro Sekunde, pro Minute, pro Stunde. Also bevor ich als Arduino-Programmierer da in die Niederungen von Ports und Registern und Assembler einsteigen würde, würde ich die Ereignisse ganz einfach mit normalen Hardwareinterrupts zählen. Diese werden von der Plattform bestens unterstützt, einfach mit attachInterrupt(interrupt, function, mode) eine Serviceroutine zuweisen und gut. Softwaremäßiges Entprellen funktioniert auch in einer Interrupt-Behandlungsroutine, wenn man es braucht. Johannes S. schrieb: > Die zu erwartende Impulsmenge beträgt zwischen 0-160 pro Sekunde (8 Imp > jeh Sec / Liter) > > Welchen Eingang benutze ich dafür? Die Nutzung des LCDKeypadShields zur > Visualisierung ist auch geplant. Wo kann ich evtl beim Programmieren ein > Beispiel finden?. Gibt es vlt schon vorgefertigte Bausteine? Bei dieser Impulszahl pro Sekunde würde ich auf jeden Fall die Impulse per Interrupt zählen. Also Anschluß an einem der beiden interruptfähigen Pins des Atmega328: - Digital 2 (Interrupt 0) oder - Digital 3 (Interrupt 1) Dazu eine Counter-Variable als "volatile" deklarieren, den Interrupt auf "fallende Flanke" aktivieren und den Counter jedesmal innerhalb der ISR um eins hochzählen. Falls es unsicher ist, ob der Schaltimpuls "prellen" kann, dann innerhalb der ISR auch den aktuellen Wert von "millis()" prüfen, ob der zeitliche Abstand zwischen zwei Impulsen mindestens so groß ist, wie er minimal zu erwarten ist. Wenn man davon ausgeht, dass höchstens 250 Impulse pro Sekunde kommen können also beispielsweise alle Zählimpulse beim Zählen verwerfen, wenn sie mit einem zeitlichen Abstand von weniger als 4 Millisekunden eintreffen. Im Hauptprogramm würde ich die "Time" library einbinden, damit Du sowas wie einen Sekundentakt im Programm generieren kannst und quasi eine ab Reset gestartete Uhrzeit zur Verfügung hast. In der Hauptschleife würde ich dann eine Logik nach diesem Schema laufen lassen: #include <Time.h> void setup() { // Hier Setup-Routinen, aktivieren der Interrupt-Serviceroutine } int volatile counter=0; // Hier fehlt die Interrupt-Serviceroutine zum Hochzählen void loop() { long lastsecond=0, thissecond=0; // Sekundenzähler int anzeigecounter; // der anzuzeigende Wert thissecond=now(); // Anzahl der Sekunden seit letztem Reset if (thissecond!=lastsecond) // Sekundenwechsel! { anzeigecounter=counter; // counter sofort in andere Variable sichern counter=0; // und den counter auf 0 zurücksetzen lastsecond=thissecond; // die aktuelle Sekunde wird zur alten // Hier sollte die Ausgabe des Anzeigewerts stehen } } Falls Du nicht nur den Durchfluss der letzten Sekunde anzeigen möchtest (der dürfte immer stark schwanken), sondern auch den Durchfluss der letzten Minute oder letzten Stunde, könntest Du die Logik natürlich erweitern, mit einem Ringpufferspeicher für 60 Sekundenwerte bzw. 60 Minutenwerte, die vor der Anzeige jeweils zusammengezählt und dann als Durchfluss pro Minute oder Durchfluss pro Stunde angezeigt werden. Damit hättest Du dann immer auch gleitende Mittelwerte für "Durchfluss letzte Minute" und "Durchfluss letzte Stunde". Gerätehersteller von Meßgeräten, die aktuelle Werte, Minutenmittelwerte und Stundenmittelwerte anzeigen, werden wohl auch eine Library für sowas haben, aber so viel ist da auch nicht dran zu coden. Die komplexeste Aufgabe wäre noch, Stundenwerte "hochzurechnen", wenn der Ringpuffer noch nicht mit 60 einzelnen Minutenwerten gefüllt ist. Aber im Endeffekt ist das auch nur eine Fleißaufgabe, die Anzahl der Minutenwerte im Ringpuffer mitzuzählen und solange der Ringpuffer noch nicht voll ist, die angezeigte Duchflussmenge nicht als Summe von 60 Minutenwerten im Puffer anzuzeigen, sondern von weniger Minutenwerten und dann auf eine Stunde hochzurechnen. Ist ja aber auch die Frage, ob Du Stundenwerte überhaupt brauchst. Schöne Programmieraufgabe für einen Arduino mit LCD-Shield. Die ist bestimmt schneller erledigt als das Gerät dann hinterher schön in ein Gehäuse mit LCD-Frontblende einzubauen.
tjaha… nun bin ich wieder bei dem ursprünglichen Problem eine problemfrage habe ich noch, bleibt ICP aktiv auch wenn ICF1 gesetzt ist, oder habe ich dann ewig zeit die register zu lesen? denn ich habe noch andere Interrupblockierende dinge (z.B. OneWire usw.) Dann wäre es recht einfach init: Prescaler auf 64 setzen (dann bekomme ich Zeiten <500ms ohne OVF mit) loop{ wenn ICP aus ist PIN(int0) rise interrupt aktivieren } pin_rise_isr{ PIN(int0) rise interrupt deaktivieren ICP interrupt aktivieren timer1 auf 0 setzen } icp_isr{ zeit auslesen ICP wieder ausschalten } dann bekomme ich pro loop maximal eine neue zeit und kann brav jeweils die drehzahl berechnen
Hallo, Ich hab mir jetzt mal aus einem KW-Leistungsmesser was zusammen gebaut, aber das Programm läuft nicht ab. Beim "WTS-Tech im Bildschirm tut sich nichts mehr
1 | #include <LiquidCrystal.h> |
2 | #include <DFR_Key.h> |
3 | #include <Time.h> |
4 | LiquidCrystal lcd(8, 9, 4, 5, 6, 7); |
5 | DFR_Key keypad; |
6 | //Number of pulses, used to measure energy.
|
7 | long pulseCount = 0; |
8 | //Used to measure power.
|
9 | unsigned long pulseTime,lastTime; |
10 | //power and energy
|
11 | double pulse, waterperhour; |
12 | //Number of pulses per wh - found or set on the meter.
|
13 | int pph = 1; |
14 | //1000 pulses/kwh = 1 pulse per wh
|
15 | void setup() |
16 | {
|
17 | Serial.begin(115200); |
18 | lcd.begin(16, 2); |
19 | lcd.clear(); |
20 | lcd.setCursor(0, 0); |
21 | lcd.print("WTS-Tech"); |
22 | delay(2500); |
23 | keypad.setRate(10); |
24 | // KWH interrupt attached to IRQ 1 = pin3
|
25 | attachInterrupt(1, onPulse, FALLING); |
26 | }
|
27 | void loop() |
28 | {
|
29 | }
|
30 | // The interrupt routine
|
31 | void onPulse() |
32 | {
|
33 | //used to measure time between pulses.
|
34 | lastTime = pulseTime; |
35 | pulseTime = micros(); |
36 | //pulseCounter
|
37 | pulseCount++; |
38 | //Calculate power
|
39 | pulse = (3600000000.0 / (pulseTime - lastTime))/pph; |
40 | //Find kwh elapsed
|
41 | waterperhour = (1.0*pulseCount/(pph*480)); |
42 | //multiply by 1000 to pulses per wh to kwh convert wh to kwh
|
43 | //Print the values.
|
44 | lcd.clear(); |
45 | lcd.setCursor(0, 0); |
46 | lcd.print("WPH:"); |
47 | lcd.setCursor(12, 0); |
48 | lcd.print(waterperhour); |
49 | Serial.print(pulse,2); |
50 | Serial.print(" "); |
51 | Serial.println(waterperhour,1); |
52 | }
|
Wo liegt der Fehler?
Michael H. schrieb: > Wo liegt der Fehler? Serial.print innerhalb einer Interrupt-Behandlungsroutine ist ein absolutes No-Go! Das mag bei sehr langsamer Interruptrate eine Weile gutgehen, aber bei höherer Interruptrate crasht das immer mit bösen Folgen für das Programm. Wenn Du was ausgeben möchtest: Schreibe die auszugebenden Werte innerhalb der Interrupt-Routine in char-Arrays oder Variablen und setze ein Flag, dass etwas ausgegeben werden soll. Dann gib die Variablen innerhalb der Loop über Serial.print aus! Und: Alle Variablen, auf die sowohl von der Loop als auch von der Interruptroutine zugegriffen werden sollen, immer als "volatile" deklarieren!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.