Forum: Mikrocontroller und Digitale Elektronik [AVR] Mit Arduino Impulse zählen.


von Johannes S. (firefield)


Lesenswert?

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

von avr (Gast)


Lesenswert?

Der Timer bietet diese Funktionalität an. Genaueres steht im Datenblatt.

von Programmierer (Gast)


Lesenswert?

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.

von avr (Gast)


Lesenswert?

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.

von Johannes S. (firefield)


Lesenswert?

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

von Stefan K. (Gast)


Lesenswert?

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.

von Stefan K. (Gast)


Lesenswert?

Achja, Schau Dir mal diesen Befehl an... der hilft Dir bestimmt:

http://arduino.cc/en/Reference/AttachInterrupt

von avr (Gast)


Lesenswert?

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.

von CAN (Gast)


Lesenswert?

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?

von avr (Gast)


Lesenswert?

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

von Michael H. (michael_h45)


Lesenswert?

Und die rechnerische Beziehung zwischen Impulsdauer und Impulsrate is 
dir zu kompliziert? Hallo cyblord...

ICP ist schon genau richtig.

von Ernst L. (extraterrestrial)


Lesenswert?


von avr (Gast)


Lesenswert?

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.

von Michael H. (michael_h45)


Lesenswert?

gute güte, hast du ein empfindliches ego...
falsch liegst du trotzdem ^^

von Cyblord -. (cyblord)


Lesenswert?

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

von avr (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Michael H. (michael_h45)


Lesenswert?

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? ^^

von avr (Gast)


Lesenswert?

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.

von Michael H. (michael_h45)


Lesenswert?

alles klar ^^

von µC-Bastler (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Frank (Gast)


Lesenswert?

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

von Frank (Gast)


Lesenswert?


von Michael H. (michael_h45)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von avr (Gast)


Lesenswert?

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.

von temp (Gast)


Lesenswert?

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.

von Michael H. (michael_h45)


Lesenswert?

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.

von temp (Gast)


Lesenswert?

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!

von Jürgen S. (jurs)


Lesenswert?

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.

von DooMMasteR (Firma: Stratum0 e.V.) (doommaster)


Lesenswert?

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

von Michael H. (michael_h45)


Lesenswert?

hier ist dein thread: Beitrag "Drehzahlmesser" ^^

von Johannes S. (firefield)


Lesenswert?

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?

von Jürgen S. (jurs)


Lesenswert?

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
Noch kein Account? Hier anmelden.