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?
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.
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
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.
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
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.
Brauchst Du nur den aktuellen Durchfluss oder auch die Gesamtmenge?
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
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?
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?
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.
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.
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
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.
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?
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.
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.
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?
>--> 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
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?
Jens M. schrieb: > oder etwa nicht? Arduino? Programme zu schreiben die eine Sekunde nichts als Zählen machen ist sehr sinnvoll.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.