[AVR] Mit Arduino Impulse zählen.

OP #2874101
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
OP #2875233
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
Gast #2875247
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.
Gast #2875349
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.
Gast #2875406
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?
Gast #2875411
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).
Gast #2875498
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.
#2875693
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.
#2876066
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
Gast #2876153
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 ...
#2876331
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.
#2876360
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.
Gast #2876468
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.
Gast #2876588
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.
#2876617
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.
Gast #2876660
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!
#2880228
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.
(Firma: Stratum0 e.V.) #2952545
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
OP #2965165
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?
#2965331
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!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren