PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Durchflusssensor auswerten



Felix H.
19.10.2012, 16:55
Hi,

folgendes gegeben: Durchflusssensor an Heizungsanlage, Open Collektor Ausgang, 96 Impule pro Liter, Pullup 4,7K. Signal an pind.0 eines Atmega8 angeschlossen. Interner RC Oszillator. Ansonsten orginal Fusebits.

Gemessen wird mit Timer1 von Fallender zu Fallender Flanke. Allerdings bekomme ich keine richtigen Werte. Zeit von Fallender bis Fallender Flanke mit Oszi gemessen. 60ms. Ergibt laut Adam Riese 625 Liter pro Stunde. Der AVR zeigt mir aber irgendwas um die 910 Liter an. Habe auchmal einen neuen genommen. Gleiches Ergebnis. Hab ich irgend einen Denkfehler?




$regfile "m8def.dat"
$crystal = 2000000
$hwstack = 30
$swstack = 30
$framesize = 30

Config Lcd = 16 * 2
Config Lcdpin = Pin , Db4 = Portd.6 , Db5 = Portd.7 , Db6 = Portb.0 , Db7 = Portb.1 , E = Portd.5 , Rs = Portd.4


Config Timer1 = Timer , Prescale = 64


Enable Interrupts

Config Portd.3 = Output 'R/W vom Display

Config Pind.0 = Input

Portd.3 = 0

Dim State As Bit
Dim Durchfluss As Word
Dim Durchflusstemp As Word
Dim Tempword As Word
Dim Tempword1 As Word
Dim Templong As Long
Dim Tempsingle As Single
Dim Displaywarten As Byte

State = 0

Wait 1

Cls
Cursor Off

'Hauptprogramm

Do

If Pind.0 = 0 And State = 0 Then
Tccr1b = &B00000011
State = 1
Waitms 1
Gosub Wait_one
Gosub Wait_zero
End If


If State = 1 Then
State = 0
Gosub Auswerten
Gosub Wait_one
End If

Loop



Auswerten:

Tempword1 = Tcnt1l
Tempword = Tcnt1h * 256
Tempword = Tempword + Tempword1 'ca 2031.3
Templong = Tempword * 95 'Zeit für 1 Liter
Tempsingle = 31250 / Templong 'Liter pro Sekunde
Durchflusstemp = Tempsingle * 3600 'Liter pro Stunde
If Durchflusstemp > 200 And Durchflusstemp < 1000 Then
Durchfluss = Durchflusstemp
End If
Timer1 = 0
Incr Displaywarten

If Displaywarten = 50 Then
Locate 1 , 1 : Lcd "Durchfluss:"
Locate 2 , 1 : Lcd Durchfluss ; " L/h " ; Tempword ; " "
Displaywarten = 0
End If

Return



Wait_one:

Do

If Pind.0 = 0 Then
Return
End If

Loop


Return


Wait_zero:

Do

If Pind.0 = 0 Then
Tccr1b = &B00000000
Return
End If

Loop


Return

Sauerbruch
19.10.2012, 17:14
Hallo Felix H.,

bevor ich verstehen konnte welche Aufgabe die Unterprogramme Wait_one und Wait_zero haben, habe ich mich gefragt, weshalb Du in den Unterprogrammen eine Do-Loop-Schleife eingebaut hast - und noch dazu eine ohne Bedingung (im Sinne von "Loop until X = ...").
Ich könnte mir vorstellen, dass der Controller dann beim ersten Ausführen des Unterprogramms genau dort hängenbleibt, weil er nie den Return-Befehl erreicht.

Ob das eine Erklärung für den beobachteten Fehler ist, kann ich auf die Schnelle nicht sagen, weil sich mir das ganze Programm noch nicht vollständig erschließt, aber probier´s doch einfach mal ohne Do-Loop in den Subroutinen.

Searcher
19.10.2012, 19:40
Hallo,
einiges was mir bis jetzt so auffällt:



Interner RC Oszillator. Ansonsten orginal Fusebits.

Der Mega8 läuft dann mit 1MHz und $crystal muß auf 1000000 berichtigt werden
Das hat Auswirkungen auf die wait Anweisungen - die werden mit $crystal = 2Mhz doppelt so lang wie angegeben.

Und die Grundlage zur Berechnung der Liter pro Sekunde stimmt nicht.


Tempsingle = 31250 / Templong 'Liter pro Sekunde

Die Frequenz des Timers mit Prescale 64 ist nicht 31250Hz sondern 15625Hz




Auswerten:

Tempword1 = Tcnt1l
Tempword = Tcnt1h * 256
Tempword = Tempword + Tempword1

Geht auch mit Tempword = Tcnt1, dann macht Bascom den 16 Bit Registerzugriff aber kein Problem


Insgesamt finde ich die Methode zum Messen der Impulse nicht optimal.

Gruß
Searcher

Felix H.
19.10.2012, 21:27
hi,

habe den Oszillator schon auf 2 Mhz stehen ;) habe testweise auch 4 oder 8 versucht (mit abgeändertem Programm). Ohne Erfolg.
Wieso stimmt die grundlage zur berechnung nicht? Templong hat den Wert, den 1 Liter an Zeit braucht. Wenn ich 1 Sek. durch diesen Wert teile bekomme ich das richtige Ergebnis.

Ich habe eher das Gefühl, dass der Timer nicht richtig läuft.

Nein schön ist es nicht. Aber ich hab mehrere Versionen durchgetestet und nichts hat funktioniert.

MagicWSmoke
20.10.2012, 08:43
Das Ganze ist nur "nicht schön", sondern ein wildes Durcheinander. Wenn der Code Eingangs auf Low des Pins prüft und dann in beiden Subs nochmal, warum das, wegen Sensorprellen ? Wenn der Prellvorgang bei fallender Flanke geschieht, ist's sowieso egal, weil die LCD-Ausgabe bereits entprellt, so wie der Code angelegt ist. Sollte hingegen das Prellen bei steigender Flanke entstehen, so triggern dieses Prellen den Messvorgang, daraufhin wird in der ersten Sub auf fallende Flanke gewartet. Sobald die eintritt, wird die erste Sub verlassen und in der zweiten auf genau das Gleiche gewartet. Je nachdem durch welches Prellen die Messung usgelöst wird, gibt's 'ne längere und kürzere Periode. Außerdem muss bei abweichenden Clocks <> 1 MHz das entsprechende Calibration Byte in OSCCAL geschrieben werden, siehe Datenblatt.

Searcher
20.10.2012, 08:52
habe den Oszillator schon auf 2 Mhz stehen :wink: habe testweise auch 4 oder 8 versucht
$crystal ist NICHT der Oszillator!

Der $crystal Wert bestimmt nicht die µC Frequenz sondern umgekehrt. Der Wert muß auf den Wert eingestellt werden, mit dem der µC läuft. BASCOM-Help: http://avrhelp.mcselec.com/crystal_1.htm

Zum Berechnen der Durchflußmenge in Deinem Programm:

Die Timerfrequenz ist mit "Config Timer1 = Timer, Prescale = 64" auf 15625 Hz eingestellt
Ich gehe immer noch davon aus, daß der µC wegen der unveränderten Fuses mit 1Mhz läuft (1Mhz / 64 = 15625Hz)

Mit dem Oszilloskop hast Du eine Zeit von Flanke zur Flanke des Sensorsignals von 0,060 Sekunden gemessen.
In dieser Zeit sollte also der Timer vom Start bis zum Stopp 938 (937,5) Schritte gemacht haben. Es sollte also in der Auswerteroutine

in "Tempword" ein Wert von 938 stehen.



Templong = Tempword * 95 '(Templong = 89110)
Tempsingle = 31250 / Templong '(Tempsingle = 0,35...)
Durchflusstemp = Tempsingle * 3600 '(Durchflusstemp = 1262)
If Durchflusstemp > 200 And Durchflusstemp < 1000 Then
Durchfluss = Durchflusstemp
end if

In Durchfluß müßte also 0 oder ein ungültiger Wert stehen, da 1262 nicht kleiner als 1000 ist.

Ohne Bedingung würde in Durchfluss also ungefähr das Doppelte von dem stehen, daß Du haben müsstest (625 Liter)
Wenn Du statt mit 31250 mit 15625 rechnest (der "echten" Timerfrequenz), kommst Du auf 631 Liter, also fast den richtigen Wert. Man merkt schon, daß das durch Meßungenauigkeit und Rundungsfehler schon nicht besonders gut ist.

Falls trozdem große Abweichungen entstehen: Du läßt ja Tempword am Display ausgeben. Was steht den da drin? Falls da zu große Differenzen auftauchen müßte man die eigentliche Messung mal genauer unter die Lupe nehmen.



PS

Der AVR (http://www.rn-wissen.de/index.php/AVR-Einstieg_leicht_gemacht) zeigt mir aber irgendwas um die 910 Liter an
Ist das am Ende die Displayausgabe von Tempword und Durchfluß wird aus o.g. Gründen irgendwie nicht angezeigt :confused:

Noch ein PS:

96 Impule pro Liter,
Warum rechnest Du mit 95 ("Templong = Tempword * 95")? Mit 96 wird es genauer;)


Gruß
Searcher

Felix H.
20.10.2012, 11:37
$crystal ist NICHT der Oszillator!

hat auch niemand behauptet. Der Oszillator im AVR (Fusebits) steht auf 2Mhz wie im Programm selbst auch. Die Formel ist richtig, wie du selbst rausgefunden hast. Ok das mit den 95 stimmt natürlich. Mein Fehler.

In der Displayroutine:

If Displaywarten = 50 Then
Locate 1 , 1 : Lcd "Durchfluss:"
Locate 2 , 1 : Lcd Durchfluss ; " L/h " ; Tempword ; " "
Displaywarten = 0
End If

Lass ich mir nach L/h den Wert "Tempword" anzeigen. Der wird in

Tempword = Tempword + Tempword1

als letztes Berechnet und sollte Theoretisch den Timerwert darstellen. Auf dem Display steht aber immer nur 31 bzw. 32 egal bei welchem Durchfluss. Ich bin Ratlos...

Zudem liegt der Messfehler ja im Bereich von über 30%. So viel kann ein Oszillator doch nicht falsch gehen oder?

Felix H.
20.10.2012, 11:46
Wenn der Code Eingangs auf Low des Pins prüft und dann in beiden Subs nochmal, warum das, wegen Sensorprellen ? Wenn der Prellvorgang bei fallender Flanke geschieht, ist's sowieso egal, weil die LCD-Ausgabe bereits entprellt, so wie der Code angelegt ist.

Kapier ich nicht. Anfangs wird auf Low gewartet und der Timer gestartet. Danach wird auf High gewartet und nicht auf low?!? Zudem wird die LCD routine nur alle 50 Messungen aufgerufen. Weiss nicht was da dann Entprellen soll.


Sollte hingegen das Prellen bei steigender Flanke entstehen, so triggern dieses Prellen den Messvorgang, daraufhin wird in der ersten Sub auf fallende Flanke gewartet. Sobald die eintritt, wird die erste Sub verlassen und in der zweiten auf genau das Gleiche gewartet.

Ebenfalls Quark.


Je nachdem durch welches Prellen die Messung usgelöst wird, gibt's 'ne längere und kürzere Periode. Außerdem muss bei abweichenden Clocks <> 1 MHz das entsprechende Calibration Byte in OSCCAL geschrieben werden, siehe Datenblatt.

Stimmt. Aber das Signal müsste schon heftig Prellen um 30 oder 40% Fehler zu verursachen

Sauerbruch
20.10.2012, 12:09
Es ist aber auch ein wenig schwierig, das Konzept in dem Code zu erkennen.

Zum einen habe ich es im Simulator gerade mal getestet: Mit der Do-Loop-Schleife im Unterprogramm wird der Return-Befehl niemals erreicht. Wozu soll das denn gut sein???

Und der Sinn des Unterprogramms Wait_one erschließt sich auch nicht ohne weiteres (selbst wenn man sich die Do-Loop-Schleife wegdenkt):




Wait_one:

Do

If Pind.0 = 0 Then
Return
End If

Loop

Return




Das heißt doch im Klartext: Wenn PIND.0 = 0, dann folgt return - und wenn nicht, dann auch :-)
Vielleicht stehe ich ja nur auf dem Schlauch (das ist um diese Uhrzeit manchmal der Fall) - aber ich kann beim besten Willen nicht erkennen, wozu diese Sub gut sei soll.

Du willst doch nur den Zeitabstand zwischen zwei fallenden Flanken messen. Warum legst Du das Signal nicht einfach auf einen der Interrupt-Eingänge, konfigurierst ihn auf falling, und liest in der ISR den Timerinhalt aus?

Searcher
20.10.2012, 12:38
Der Oszillator im AVR (Fusebits) steht auf 2Mhz wie im Programm selbst auch
Na gut.


Auf dem Display steht aber immer nur 31 bzw. 32 egal bei welchem Durchfluss
Wenn das der echte Tempword Inhalt ist, kann bei der Durchflußberechnung nichts sinnvolles herauskommen/angezeigt werden.

Um das mal zu überprüfen, könnte man nach der Zeile: "Tempword = Tempword + Tempword1"
Tempword mit dem erwarteten Wert überschreiben, also "Tempword = 1875" (bei dem 2MHz Systemtakt) und schauen, was das Display für Tempword und Durchfluss anzeigt.

Gruß
Searcher

Felix H.
20.10.2012, 13:17
Das heißt doch im Klartext: Wenn PIND.0 = 0, dann folgt return - und wenn nicht, dann auch :-)

Jetzt beginne ich langsam an der Qualität dieses Forums zu zweifeln :D Sag mir doch bitte mal wie deiner Meinung nach ein Return erfolgen soll wenn pind.0 = 1 ist.
Statt dem Return in der If pind.0 = 0-Bedingung könnte man natürlich auch ein Exit Do einfügen. Kommt aufs gleiche raus. Es wird nur ein Return ausgeführt wenn pind.0 = 0 ist

Felix H.
20.10.2012, 13:23
So, hab jetz die 1875 gesetzt. Resultierender Durchfluss = 631L/h. Auf dem Display erscheinen nun auch die 1875 für Tempword.

Searcher
20.10.2012, 14:01
So, hab jetz die 1875 gesetzt. Resultierender Durchfluss = 631L/h. Auf dem Display erscheinen nun auch die 1875 für Tempword.

Die 95 noch nach 96 tauschen :wink:

Boah, jetzt muß man wohl daran, wo Sauerbruch und MagicWSmoke schon rummäkeln. Ehrlich gesagt gefällt mir das auch nicht aber ich versuch mal genauer im einzelnen da durchzusteigen. Auf den ersten Blick könnte es gehen, erzeugt aber doch falsche Werte in Tempword.

Möchte aber noch wissen, was für ein Sensor das ist?
Die Pulse am Oszi waren auch schön ausgeprägt zwische 0 und 5V ?
Sensor Stromversorgung ist die Gleiche wie vom µC?

Gruß
Searcher

Sauerbruch
20.10.2012, 15:28
Auf dem Display steht aber immer nur 31 bzw. 32 egal bei welchem Durchfluss.

Wie soll´s denn auch anders sein?

Dass etwas angezeigt wird bedeutet doch, dass das Programm bei "Gosub Auswerten" angekommen ist.
Und danach kommt "Gosub Wait_one".
Und in dieser Subroutine bleibt der Controller dank "Do-Loop" hängen - bis zum jüngsten Tag. Die erste Anzeige ist also auch die letzte.

Solange das so ist, sind alle Überlegungen zu Taktfrequenzen, Rechenwegen und allem sonstigen drum & dran müßig :-)

Searcher
20.10.2012, 15:46
Und danach kommt "Gosub Wait_one".
Und in dieser Subroutine bleibt der Controller dank "Do-Loop" hängen - bis zum jüngsten Tag

Ich glaube schon, daß er, wenn Pind.0 auf 0 ist, da raus kommt? Im then-zweig steht ein Return.

Allerdings wird danach entweder wieder "Gosub Wait_one" oder "Gosub Wait_zero" aufgerufen. In beiden Subroutinen wird der Pind.0 auf 0 geprüft, bevor es weiter geht.

Möglicherweise wird hier dasselbe Low auf Pind.0 erkannt. Ich kann nicht erkennen, wo wirklich ein high abgewartet wird. Das könnte auch den viel zu kleinen Wert in Tempword erklären.

PS: Vielleicht geht mehr, wenn in der "Wait_one" Pind.0 statt auf 0 auf 1 geprüft wird.

Gruß
Searcher

Felix H.
20.10.2012, 16:31
Hab jetzt noch ein paar wartezeiten eingefügt zum entprellen eingefügt. Und die Bedingungen der Subroutinen geändert. Nach anpassung der 31250 auf die tatsächliche Taktfrequenz läuft es nun bestens.


$regfile "m8def.dat"
$crystal = 8000000
$hwstack = 30
$swstack = 30
$framesize = 30

Config Lcd = 16 * 2
Config Lcdpin = Pin , Db4 = Portd.6 , Db5 = Portd.7 , Db6 = Portb.0 , Db7 = Portb.1 , E = Portd.5 , Rs = Portd.4


Config Timer1 = Timer , Prescale = 256


Enable Interrupts

Config Portd.3 = Output 'R/W vom Display

Config Pind.0 = Input

Portd.3 = 0

Dim State As Bit
Dim Durchfluss As Word
Dim Durchflusstemp As Word
Dim Tempword As Word
Dim Tempword1 As Word
Dim Templong As Long
Dim Tempsingle As Single
Dim Displaywarten As Byte

State = 0

Wait 1

Cls
Cursor Off

'Hauptprogramm

Do

If Pind.0 = 0 And State = 0 Then
Start Timer1
State = 1 'Status 1
Waitms 1 'Entprellen
Gosub Wait_one
Waitms 1
Gosub Wait_one
Waitms 2
Gosub Wait_zero
End If

If State = 1 Then
State = 0 'Status 0
Gosub Auswerten
Waitms 1 'Evtl. Entprellen
Gosub Wait_one
Waitms 1
gosub wait_one
End If

Loop



Auswerten:

Tempword1 = Tcnt1l
Tempword = Tcnt1h * 256
Tempword = Tempword + Tempword1
Templong = Tempword * 96 'Zeit für 1 Liter
Tempsingle = 31600 / Templong 'Liter pro Sekunde
Durchflusstemp = Tempsingle * 3600 'Liter pro Stunde
Durchfluss = Durchflusstemp
Timer1 = 0
Incr Displaywarten

If Displaywarten = 30 Then
Locate 1 , 1 : Lcd "Durchfluss:"
Locate 2 , 1 : Lcd Durchfluss ; " L/h " ; Tempword ; " "
Displaywarten = 0
End If

Return


Wait_one:

Do

Loop Until Pind.0 = 1

Return


Wait_zero:

Do

Loop Until Pind.0 = 0

Stop Timer1

Return



Im Endstadium werde ich wie bei meiner anderen Anlage auch das ganze über den ICP Pin einlesen.

Searcher
20.10.2012, 16:39
... läuft es nun bestens.
*Aufatmen* :-)

Im Endstadium werde ich wie bei meiner anderen Anlage auch das ganze über den ICP Pin einlesen
*nochmal Aufatmen* :-) :-)

Gruß
Searcher