Forum: Mikrocontroller und Digitale Elektronik Durchflusssensor auslesen mit µcontroller - Impulse/Frequenz/Durchfluss


von Max M. (Firma: conglomat gmbh) (dasjhdsajkhda)


Lesenswert?

Hi Leute,

ich habe einen Durchflusssensor 
(https://at.rs-online.com/web/p/durchflusssensoren/4468309/) den ich mit 
einem STM32F411RE Nucleo-64 Board auslesen möchte.

Der Sensor hat folgende Eigenschaften:
Flow Range = 1 - 15 L/min
Pulses per Liter = 2200
Frequency Output = 37 - 550 Hz

Nun frage ich mich, mit welcher Umrechnung, ich auf den Durchflusswert 
komme und wie ich das mit der Software umsetze auf dem µController?

Meine Überlegung momentan:
Die Ausgangsfrequenz umgerechnet von Hz zu 1/min ist 2200 1/min bis 
33.000 1/min. Geteilt durch die Anzahl der Impulse pro Liter würde mir 
L/min geben.

Durchflussrate [L/min] = Ausgangsfrequenz [Hz] * 60 / (Impulse pro 
Liter) [1/L]

Für das Lesen der Eingangssignals brauche ich ein Timer bzw. ein Timer 
Interrupt. Wie die Konfiguration und die Umsetzung gemacht wird verwirrt 
mich. Anhand des Reference Guides für STM32F411CC würde ich die general 
purpose timer benutzen - TIM2, TIM5, TIM3, TIM4.

Den Frequenzausgang würde ich mit GPIO_PIN_ReadPin durchgängig lesen.
Währenddessen zählt ein Timer hoch und vergleicht den aktuellen Wert mit 
dem alten + 1s (möchte in 1s abständen den Durchflusswert wissen). Wenn 
die 1s rum sind (untersuchen mit IF Kondition) konvertiere ich den 
zugehörigen Frequenzausgang zu diesem Zeitpunkt in eine Durchflussrate 
mit der o.g. Umformung. Diesen Wert lasse ich mir dann über 
USART_transmit in PUTTY anzeigen lassen.

Frage nun: wie setze ich diesen Timer auf? Brauche ich den Timer 
Interrupt überhaupt wenn ich sowieso durchgängig GPIO_PIN_ReadPin 
durchführe?

von Kevin M. (arduinolover)


Lesenswert?

Max M. schrieb:
> Den Frequenzausgang würde ich mit GPIO_PIN_ReadPin durchgängig lesen.

Für sowas benutzt man den Input Capture des Timers. Google STM32 PWM 
Input.

von Georg (Gast)


Lesenswert?

Max M. schrieb:
> Brauche ich den Timer
> Interrupt überhaupt wenn ich sowieso durchgängig GPIO_PIN_ReadPin
> durchführe?

Heisst das dein STM läuft in dieser Endlosschleife und tut nichts 
anderes?

Georg

von Max M. (Firma: conglomat gmbh) (dasjhdsajkhda)


Lesenswert?

Georg schrieb:
> Heisst das dein STM läuft in dieser Endlosschleife und tut nichts
> anderes?

Endziel nein. Da soll jede Sekunde der aktuelle Durchflusswert im PUTTY 
serial monitor angezeigt werden.
Meine erste Überlegung war mit HAL_Delay(1000) in der while(1) Schleife 
jede Sekunde GPIO_ReadPin auszuführen jedoch ist das Einsetzen eines 
Timers sinnvoller.
Da wäre meine Überlegung gewesen konstant ReadPin durchzuführen neben 
des Timers weil ich Input Capture vom Timer nicht verstanden habe.

von Kevin M. (arduinolover)


Lesenswert?

Max M. schrieb:
> Meine erste Überlegung war mit HAL_Delay(1000) in der while(1) Schleife
> jede Sekunde GPIO_ReadPin auszuführen

Wie soll das denn bitte funktionieren?


Max M. schrieb:
> Da wäre meine Überlegung gewesen konstant ReadPin durchzuführen neben
> des Timers weil ich Input Capture vom Timer nicht verstanden hadamit,

Dann befasst dich richtig damit, das tut am Ende nichts anderes als das 
was du mit deinem readPin vor hast nur eben in Hardware und ohne einen 
haufen Jitter den du jenachdem was du da so nebenher tust haben wirst.

Natürlich kann man sich darüber streiten wie problematisch das bei 
diesen Frequenzen und bei der Anwendung ist, man könnte die Zeit aber 
auch einfach darin investieren es anständig zu machen und ggf., Gott 
bewahre, sein Wissen zu erweitern.

PS: zu Input Capture gibt es online haufenweise Beispiele, gerade im 
Zusammenhang mit der HAL.

: Bearbeitet durch User
von Lutz (Gast)


Lesenswert?

Bei schnarchlangsamen 550 Hz und nur dieser Anwendung kannst du auch 
eine Variable mit jedem Impuls hochzählen. Alle 1000 ms kommt ein 
Timerinterrupt, in welchem die Differenz alter Wert / neuer Wert 
gebildet wird.

von H. (Gast)


Lesenswert?

Brauchst Du nur den aktuellen Durchfluss oder auch die Gesamtmenge?

von Wolfgang (Gast)


Lesenswert?

Max M. schrieb:
> Nun frage ich mich, mit welcher Umrechnung, ich auf den Durchflusswert
> komme und wie ich das mit der Software umsetze auf dem µController?

Ein Impuls entspricht 0,45 ml

von Max M. (Firma: conglomat gmbh) (dasjhdsajkhda)


Lesenswert?

Wolfgang schrieb:
> Ein Impuls entspricht 0,45 ml

wie kommst du auf 0,45 ml?

von Max M. (Firma: conglomat gmbh) (dasjhdsajkhda)


Lesenswert?

H. schrieb:
> Brauchst Du nur den aktuellen Durchfluss oder auch die Gesamtmenge?

An sich nur Durchfluss. Ah du meinst für Gesamtmenge wäre ein konstantes 
lesen des Pins sinnvoll --> Summe bilden?

von Max M. (Firma: conglomat gmbh) (dasjhdsajkhda)


Lesenswert?

Lutz schrieb:
> Bei schnarchlangsamen 550 Hz und nur dieser Anwendung kannst du auch
> eine Variable mit jedem Impuls hochzählen. Alle 1000 ms kommt ein
> Timerinterrupt, in welchem die Differenz alter Wert / neuer Wert
> gebildet wird.

Diese Differenz ist eine Zeitdifferenz oder meinst du Differenz der 
Durchflussrate?

von H. (Gast)


Lesenswert?

Es gibt zwei Ansätze, um auf den aktuellen Durchfluss zu kommen. 
Entweder bestimmst Du die Frequenz durch eine Zeitmessung von Puls zu 
Puls und/oder Du zählst die Impulse pro Zeiteinheit.

Die Hinweise auf die Harware-Capture-Einheit ja schon ganz gut. Du 
solltest auf 32bit capturen, sollte bei diesem uC kein Problem sein, 
habe ich aber nicht geprüft. Du misst damit die Zeit von Flanke zu 
Flanke und kannst damit präzise auf den aktuellen Durchflusss schließen. 
Allerdings wird die Zeitmessung höchstwahrscheinlich bei der Mechanik 
von Puls zu Puls stark schwanken. D.h. Du müsstest mehrere Zeitmessung 
nacheinander machen und mitteln. Gleitender Mittelwert oder 
arithmetischer Mittelwert. Bei Capture braucht man parallel dazu noch 
einen Timeout, der den aktuellen Durchflusswert auf 0 setzt, wenn für 
Zeit x kein neuer Puls kommt. Bei sehr niedrigen Frequenzen muss man das 
machen, da sonst der letzte Capturewert „eingefroren“ wird.

Zusätzlich könntest Du noch bei jedem Capture-Ereignis einen Interrupt 
auslösen lassen, der einfach nur einen 32bit Zähler hochzählt. Jedes 
Zählereignis summiert die Menge, 455 Microliter pro Puls. Diesen Zählee 
kannst Du im Hauptprogramm auslesen und damit z.B. den Gesamtdurchfluss 
bestimmen. Lösche diesen Zähler nie sondern rechne immer „Diff= 
ZaehlerAlt - ZaehlerJetzt“.

Das alles ist ein schönes Lehrstück, wenn Du es noch nie gemacht hast. 
Einige Fallstricke stehen bevor, man kann das alles im Detail hier 
schlecht aufschreiben. Fange einfach mal an und berichte.

von H. (Gast)


Lesenswert?

Wenn Du es gaaaanz einfach machen willst könntest Du einen 
Pinchange-Interrupt aufsetzen und in diesem Interrupt auch einfach eine 
globale uint32 Variable hochzählen, also einfach

void Pinchange_Int (void)
{
  counter++;
}

Diese Variable jede Sekunde auslesen, mit 0,000455*60 multiplizieren und 
diesen Wert als Liter/min ausgeben. Danach counter = 0;

Jaja, nicht ganz sauber aber geht erstmal grob.

von Wolfgang (Gast)


Lesenswert?

Max M. schrieb:
> wie kommst du auf 0,45 ml?

Durch Lesen deiner Angaben ;-)

Max M. schrieb:
> Pulses per Liter = 2200

1000 ml / 2200 Pulse = 0.4545 ml/Puls

von Max M. (Firma: conglomat gmbh) (dasjhdsajkhda)


Lesenswert?

H. schrieb:
> Du misst damit die Zeit von Flanke zu
> Flanke und kannst damit präzise auf den aktuellen Durchflusss schließen.
> Allerdings wird die Zeitmessung höchstwahrscheinlich bei der Mechanik
> von Puls zu Puls stark schwanken.

Folgendes habe ich nun gemacht:
TIM3 auf rising edge mit global interrupt enabled
Code:
1
 
2
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); 
3
4
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
5
{
6
in1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
7
...
8
in2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
9
dif = in2 - in1;
10
Frequency = HAL_RCC_GetPCLK1Freq() / dif;
11
}

--> was ist der Unterschied zwischen __HAL_TIM_GET_COUNTER() und 
HAL_TIM_ReadCapturedValue()? Die geben beide die Anzahl der clock cycles 
als output?

Ansatz mit GPIO external interrupt:
Pin PD_8 als GPIO_EXTI8 mit global interrupt enabled. Auch wieder rising 
edge. Dieses macht aber nach meinem Verständnis keinen Sinn da diese nur 
ein Interrupt detektiert und nicht die Frequenz des Signals auslesen 
wird.

von Max M. (Firma: conglomat gmbh) (dasjhdsajkhda)


Lesenswert?

H. schrieb:
> Diese Variable jede Sekunde auslesen, mit 0,000455*60 multiplizieren und
> diesen Wert als Liter/min ausgeben. Danach counter = 0;

0,000455 Liter/Pulse ist kein allgemeiner Wert sondern nur bei 1 L/min, 
daher kann ich doch nicht alle ausgelesene Werte mit 0,000455*60 
multiplizieren?
Ich muss die allgemeine Formel beibehalten mit Durchfluss = Frequenz * 
60 / 2000

Oder meintest du die 0,000455 nur für 1 L/min?

von Axel S. (a-za-z0-9)


Lesenswert?

Max M. schrieb:
> H. schrieb:
>> Diese Variable jede Sekunde auslesen, mit 0,000455*60 multiplizieren und
>> diesen Wert als Liter/min ausgeben. Danach counter = 0;
>
> 0,000455 Liter/Pulse ist kein allgemeiner Wert sondern nur bei 1 L/min,
> daher kann ich doch nicht alle ausgelesene Werte mit 0,000455*60
> multiplizieren?

Natürlich nicht. Der Sensor ist spezifiziert mit:

Max M. schrieb:
> Pulses per Liter = 2200

da steht nirgendwo etwas davon, daß dieser Wert abhängig von der 
Durchflußrate ist. Das wäre auch absolut ungewöhnlich, wenn man sich 
vergegenwärtigt, wie so ein Sensor funktioniert.

> Ich muss die allgemeine Formel beibehalten mit Durchfluss = Frequenz *
> 60 / 2000

2200. Und selbstverständlich ist das die gleiche Formel.
Von Rundungsfehlern wegen 0.000455 ~= 1/2200 mal abgesehen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

H. schrieb:
> Danach counter = 0;
Das Rücksetzen von Zählern ist generell eine schlechte Idee. Meine 
Zähler (oder auch Zeiten) laufen immmer weiter. Ich merke mir den 
Zählerstand der letzten Auswertung und arbeite mit Differenzen.

In einen normalen ulong passen so trotz der hohen Auflösung von 454,5µl 
immerhin fast 20 tausend Hektoliter vor es zu einem Überlauf kommt. Das 
ist fast 3x mehr als beim Oktoberfest Bier ausgeschenkt wird:
https://de.statista.com/statistik/daten/studie/165503/umfrage/ausgeschenkte-menge-bier-auf-dem-oktoberfest-seit-1980/

Es ist z.B. auch eine ganz schlechte Idee, hier mit floats zu arbeiten 
und pro Zählerinterrupt einfach immer die 454,5 Mikroliter aufaddieren 
und z.B. 987,654 l + 0,0004545 l rechnen zu wollen. Ein float kann die 
dafür nötige Dynamik nicht abbilden und wird Rechenfehler produzieren.

von Martin (Gast)


Lesenswert?

Max M. schrieb:
> Oder meintest du die 0,000455 nur für 1 L/min?

Das ist höhere Mathematik, das kann man nur nach 3 Jahren auf einer 
Eliteuni oder war das doch der Dreisatz aus der Grundschule?

von J. V. (janvi)


Lesenswert?

>--> was ist der Unterschied zwischen __HAL_TIM_GET_COUNTER() und
>HAL_TIM_ReadCapturedValue()? Die geben beide die Anzahl der clock cycles
>als output?


GET_COUNTER liefert den aktuellen Wert des Timers wie er gerade steht.
Wenn der Timer läuft ist es mehr oder weniger einfach dort wo die 
Software gerade trifft. Aufruf eignet sich z. Bsp. um die Laufzeit einer 
Funktion anzuzeigen.

ReadCaptureValue liefert den Wert des Timers wie er beim Capture 
Ereignis gestanden hat. Das Capture Ereignis funktioniert per Hardware 
und hilft dabei, daß die Software nicht ständig abfragen muß um den 
korrekten oder zumindest nächstmöglichen Zeitpunkt einer Flanke der 
Signaländerung oder einen sonstigen Ereignis zu erkennen.

Aus diesem Grund empfehlen auch alle Anderen hier für diese 
Aufgabenstellung ein Capture Ereignis einzurichten. Damit könnte der uC 
auch noch was anderes sinnvolles nebenbei tun als Warteschleifen zählen 
oder einen Eingang abzufragen.

: Bearbeitet durch User
von Jens M. (schuchkleisser)


Lesenswert?

Versteh ich das Problem nicht, oder ist das so einfach das ihr das nicht 
seht?
Jede Sekunde soll in der Anzeige der aktuelle Durchflusswert stehen.
Also
1
while(1){
2
  countPulseForASecond();
3
  flow=pulses/2200;
4
  updatedisplay();
5
  }
oder etwa nicht?

von Kevin M. (arduinolover)


Lesenswert?

Jens M. schrieb:
> oder etwa nicht?

Arduino?

Programme zu schreiben die eine Sekunde nichts als Zählen machen ist 
sehr sinnvoll.

von Jens M. (schuchkleisser)


Lesenswert?

Wenn sie nichts anderes machen müssen ist es aber egal, ob sie eine 
Sekunde warten ob ein Zählerimpuls kommt, oder eine Sekunde warten ob 
der Timer abgelaufen ist.
In beiden Fällen wartet die Software auf ein Flag.

Kann man natürlich beliebig kompliziert machen, muss man aber nicht.
Nur weil man einen schnellen Prozessor hat, muss man das Programm ja 
nicht so komplex machen, das es den auch "nützlich" auslastet.
Man kann ja evtl. später auf was sparsameres umsteigen, wenn das 
ausreichend ist.

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.