| |
Computing Starter Pack : Temperaturregelung
Aufgabe
"Die
Heizung wird simuliert durch die Linsenlampe M2, Als Kühlaggregat dient das Gebläse am
Ausgang M1. Zur Temperaturmessung verwenden wir den NTC-Widerstand am Eingang EX.
Programmiere das Modell so , daß oberhalb einer bestimmten Temperatur die Heizung aus-
und das Gebläse einschaltet. Dieses soll so lange kühlen, bis ein unterer Grenzwert
erreicht ist. Dann soll das Gebläse aus- und die Heizung eingeschaltet werden."
Soweit das Zitat aus Computing Starter Begleitheft. Wenn man den Kasten nicht besitzt,
dann kann man auch bei www.knobloch-gmbh.de
die Bauanleitung 30434 und das Begleitheft 30435 erwerben (zusammen ca. 30 DM).
Lösung mit LLWin 3.0
von der LLWin 3.0 CD-ROM
Visual Basic 6 & FishFa50.OCX
Zu Delphi gehts hier lang
Die beiden Grenzwerte werden in den Textboxen txtHeiß und txtKalt angezeigt
über die sie auch modifiziert werden können. Der aktuelle Temperaturwert wird in
lblTemperatur angezeigt. Die Namen mHeizung, mKühlung, aFühler ... sind Konstanten denen
die entsprechenden Ein- und Ausgänge des Interfaces zugeordnet sind.
Es werden drei Lösungen vorgestellt :
1. Eine Lösung, die der LLWin-Lösung möglichst nahe ist : "Goto
Label"-Lösung.
2. Eine Lösung, die die modernen sprachlichen Möglichkeiten von Visual Basic nutzt :
"Select Case"-Lösung
3. Eine Lösung, die das Programm bedienungssicher macht : "Enabled"-Lösung.
Der eigentliche Programmablauf ist bei allen Programmversionen gleich.
Die vollständigen Sources zu den Programmen sind in Starter.ZIP
zu finden. Zusätzlich ist das Päckchen FishFa50.ZIP
erforderlich.
Tempar1 : "Goto Label"-Lösung
Private Sub cmdAction_Click()
ft.AnalogScan = True
ft.OpenInterface "COM1"
Start:
If ft.Finish(0) Then GoTo Ende
Kühlen:
If Temperatur >= Val(txtHeiß) Then GoTo Heizen
ft.SetMotor mHeizung, ftiAus
ft.SetMotor mKühlung, ftiEin
GoTo Start
Heizen:
If Temperatur <= Val(txtKalt) Then GoTo Start
ft.SetMotor mHeizung, ftiEin
ft.SetMotor mKühlung, ftiAus
GoTo Start
Ende:
ft.CloseInterface
End Sub
Private Sub tmrTemperatur_Timer()
Temperatur = ft.GetAnalog(aFühler)
lblTemperatur = Temperatur
End Sub
cmdAction_Click ist die Prozedur, die dem Klick-Ereignis des START-Buttons zugeordnet
ist. Hier wird der Ablauf der Reglung gesteuert :
- ft.AnalogScan = True
Die Analog-Eingänge EX und EY sollen regelmäßig abgefragt werden
ft.OpenInterface "COM1"
Herstellen der Verbindung zum Intelligent Interface an COM1 (ggf. ändern). Bei LLWin
werden diese Punkte innerhalb der Entwicklungs Umgebung erledigt.
- Start:
If ft.Finish(0) then GoTo Ende
Start markiert den Beginn der Regelungsschleife (das grüne Männchen)
Finish erlaubt ein Beenden der Regelungsschleife durch die ESC-Taste, wenn dem Modelle ein
zusätzlicher Ende-Taster spendiert wird kann hier anstelle des Parameters 0 die Nummer
seines E-Einganges angegeben werden.
- Kühlen:
If Temperatur >= Val(txtHeiß) Then GoTo Heizen
ft.SetMotor mHeizung, ftiAus
ft.SetMotor mKühlung, ftiEin
GoTo Start
Hier wird abgefragt, ob die aktuelle Temperatur noch nicht zu hoch ist (LLWin fragt
ab, ob die akt. Temp. zu hoch ist, das wurde hier geändert um die If-Ausgänge zu
vertauschen). Mit SetMotor werden die M-Ausgänge für die Linsenbirne aus- und für den
Kühlermotor eingeschaltet. Mit Start gehts wieder in die Regelungsschleife.
- Heizen:
Analog Kühlen. Abgefragt wird, ob die Temperatur noch ausreichend hoch ist.
- Ende:
ft.CloseInterface
Sprungziel für das Beenden der Regelungsschleife.
CloseInterface schließt die Verbindung zum Interface.
- tmrTemperatur_Timer
Hier wird unabhängig von der Regelungsschleife der aktuelle Temperaturwert abgefragt und
angezeigt (zweites grünes Männchen).
Tempar2 : "Select Case"-Lösung
Private Sub cmdAction_Click()
ft.AnalogScan = True
ft.OpenInterface "COM1"
Do
Temperatur = ft.GetAnalog(aFühler)
lblTemperatur = Temperatur
Select Case Temperatur
Case Is < Val(txtHeiß)
ft.SetMotor mHeizung, ftiAus
ft.SetMotor mKühlung, ftiEin
Case Is > Val(txtKalt)
ft.SetMotor mHeizung, ftiEin
ft.SetMotor mKühlung, ftiAus
End Select
Loop Until ft.Finish(0)
ft.CloseInterface
End Sub
Hier die gleiche Funktionalität nochmal. Anstelle GoTo und Label wurden hier aber das
Select Case Konstrukt verwendet außerdem wurde einfachheitshalber die Abfrage des
Temperaturwertes in die Regelungsschleife aufgenommen.
- Select Case Temperatur
Case Is < Val(txtHeiß)
Case Is > Val(txtKalt)
End Select
Kann man als ein kompaktes Mehrfach-If verstehen bei dem der erste zutreffende Fall (case)
aus geführt wird, der Rest des Select Case wird dann übersprungen.
Is < Val(txtHeiß) fragt, ob die Temperatur kleiner als der Heiß-Grenzwert ist.
- Do
Loop Until ft.Finish(0)
markiert die Regelungsschleife, sie wird solange (Until) durchlaufen bis die ESC-Taste
erkannt wurde.
- Val(txtHeiß)
Der Grenzwert für Heiß wird in der Form als Text eingeben. Hier wird er in eine Zahl
umgewandelt. Bei unzulässigen Zeichen in der Zahl wird nur bis zu dem unzul. Zeichen
umgewandelt, ist schon das erste Zeichen unzul. gibt das dann eine Null.
Tempar3 : "Enabled"-Lösung
Private Sub cmdAction_Click()
Dim Temperatur&
ft.AnalogScan = True
If ft.OpenInterface("COM1") = ftifehler Then
MsgBox "Interfaceproblem", vbOKOnly
Exit Sub
End If
cmdAction.Enabled = False
cmdEnde.Caption = "HALT"
Do
Temperatur = ft.GetAnalog(aFühler)
lblTemperatur = Temperatur
Select Case Temperatur
Case Is < Val(txtHeiß)
ft.SetMotor mHeizung, ftiAus
ft.SetMotor mKühlung, ftiEin
Case Is > Val(txtKalt)
ft.SetMotor mHeizung, ftiEin
ft.SetMotor mKühlung, ftiAus
End Select
ft.Pause 500
Loop Until ft.Finish(0)
ft.ClearMotors
ft.CloseInterface
cmdEnde.Caption = "ENDE"
cmdAction.Enabled = True
cmdEnde.SetFocus
End Sub
Private Sub cmdEnde_Click()
If cmdEnde.Caption = "HALT" Then
ft.NotHalt = True
Else
Unload Me
End If
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
If cmdEnde.Caption = "HALT" Then Cancel = 1
End Sub
Die Regelungsschleife entspricht der von Tempar2. Hier wurde zusätzlicher Code
eingefügt um das Programm bedienungssicherer (robuster) zu machen. In den bisherigen
Lösungen kann man z.B. den START-Button (und auch den ENDE-Button) drücken während die
Regelungsschleife läuft, das stiftet dann Verwirrung. Ebenso kann man vergessen vor dem
START das Interface mit der Stromquelle zu verbinden, das produziert dann Ratlosigkeit.
Das Beenden eines Programmes ist gar nicht so einfach, wenn eine Endlosschleife (die
Regelschleife) läuft. LLWin kümmert sich da diskret selber drum, wenn man auf die Ampel
klickt :
- If ft.OpenInterface("COM1") = ftiFehler Then
Msgbox "Interfaceproblem", vbOKOnly
Exit Sub
End If
OpenInterface liefert einen Rückgabewert (Returncode), der anzeigt, ob das Open
erfolgreich war. Hier wird abgefragt, ob der Returncode = ftiFehler war, das wird dann
gemeldet und die Routine schlagartig verlassen.
- cmdAction.Enabled = False
cmdEnde.Caption = "HALT"
Wenn OpenInterface erfolgreich war, soll die Regelungsschleife anlaufen, dazu wird
der START-Button verriegelt um einen erneuten START zu verhindern, die Beschriftung des
ENDE-Buttons wird in HALT geändert, er soll jetzt nicht mehr das Programm beenden,
sondern nur die Regelungsschleife (wie das bisher nur durch die ESC-Taste geschah).
- If cmdEnde.Caption = "HALT" Then
ft.NotHalt = True
Else
Unload Me
End If
Der ENDE-Button hat jezt eine zweite Aufgabe bekommen : das Beenden der
Regelungsschleife. Das geschieht, wenn seine Beschriftung (caption) HALT lautet, dann wird
ft.NotHalt = True gesetzt. Mehr geschieht hier nicht. ft.Finish(0) am Ende der
Regelungsschleife wertet das aus (neben der ESC-Tast und ggf. einem Ende-Taster).
- ft.ClearMotors
ft.CloseInterface
cmdEnde.Caption = "ENDE"
cmdAction.Enabled = True
cmdEnde.SetFocus
Am Ende der Regelungsschleife wird aufgeräumt. ClearMotors löscht alle
M-Ausgänge. Motor und Lampe werden hier also gezielt abgeschaltet und nicht erst
irgendwann beim Beenden der Verbindung durch CloseInterface. Außerdem bekommt der
ENDE-Button seine angestammte Beschriftung wieder und der START-Button wird reaktiviert.
- Private Sub Form_QueryUnload(Cancel As
Integer, UnloadMode As Integer)
If cmdEnde.Caption = "HALT" Then Cancel = 1
End Sub
Und dann ist da noch das Kreuz mit dem Kreuz rechts oben am Fensterrahmen mit dem
man so schön ein Programm beenden kann, hier nicht : Bei dem Versuch die Form zu entladen
(QueryUnload) wird geblockt und der Rückgabeparameter auf 1 gesetzt (Form entladen
abgelehnt).
Wenn man schon bei Form-Ereignissen ist und es dumm findet, daß bei jedem START eine
Verbindung zum Interface hergestellt und wieder abgebrochen wird, kann man das natürlich
auch in die Form_Load und Form_Unload Ereignisprozeduren legen ......
Und wenn man mit den "umgekehrten" Temperaturen , die der NTC liefert, auf
Kriegsfuß steht kann man sie natürlich auch umrechnen : Temperatur
= 600 - ft.GetAnalog(aFühler) wäre da schon mal ein Anfang und dann die
Vergleiche umdrehen .......
Wie kommt es eigentlich, daß sich manchmal gar nichts rührt, wenn man das Programm
startet. Es heizt nicht, es kühlt nicht, es tut einfach nichts und wird erst aktiv, wenn
der Temperaturfühler (NTC) kalt genug ist. Wobei es sonst doch so unermüdlich kühlt und
heizt. Wie wäre es mit einem zusätzlichen Case Val(txtHeiß) To Val(txtKalt) ? ....
Und dann gibt es da noch das Projekt Tempar4, das ist aber reine Spielerei, man
sollte es sich deswegen sofort ansehen.
Delphi 4 und umFishEx / umFish20Ex
Damit die Delphi-Anhänger nicht zu kurz
kommen, hier - als Kontrast - eine Delphi-Lösung auf Basis von umFish.DLL und
umFishEx.PAS. Die Sources sind ebenfalls in Starter.ZIP
enthalten, Zusätzlich ist umFishU.ZIP erforderlich. Es ist
eine Lösung, die schon etwas Komfort bietet, dies mal aber nur mit einem Button, Ende
über das Kreuz (x) rechts oben :
procedure TForm1.cmdActionClick(Sender:
TObject);
const
mKuehlung = 1; mHeizung = 2; aFuehler = 0;
var
Temperatur, Heiss, Kalt: LongInt;
begin
if cmdAction.Caption = 'HALT' then begin ftiNotHalt :=
True; exit end;
cmdAction.Caption := 'HALT';
ftiOpenInterface('COM1');
Heiss := StrToInt(txtHeiss.Text);
Kalt := StrToInt(txtKalt.Text);
repeat
Temperatur := ftiGetAnalog(aFuehler);
lblTemperatur.Caption := IntToStr(Temperatur);
if Temperatur < Heiss then
begin
ftiSetMotor(mHeizung, ftiAus);
ftiSetMotor(mKuehlung, ftiEin);
end
else if Temperatur > Kalt then
begin
ftiSetMotor(mHeizung, ftiEin);
ftiSetMotor(mKuehlung, ftiAus);
end;
until ftiFinish(0);
ftiClearMotors();
ftiCloseInterface;
cmdAction.Caption := 'START';
end;
procedure TForm1.FormCloseQuery(Sender: TObject; var
CanClose: Boolean);
begin
if cmdAction.Caption = 'HALT' then CanClose := False;
end;
Zusätzlich noch bei den uses am Anfang ein umFishEx
/umFish20Ex einfügen.
- ftiOpenInterface('COM1') stellt die Verbindung zum Interface her (ggf. anderen
Parameterwert angeben. Ein if ftiOpenInterface('COM1') = ftiFehler then ... würde
zusätzlich noch Nachricht geben, wenn die Verbindungsbemühungen erfolglos waren.
ftiClearMotors schaltet die M-Ausgänge aus und ftiCloseInterface trennt dann die
Verbindung zum Interface wieder. Hier werden auch die aktuellen Grenzwerte für Heiß und
Kalt von der Form übernommen.
- Die Regelungsschleife selber ist in einer repeat ... until Konstruktion
geklammert. Beendet wird sie durch die ESC-Taste oder durch ftiNotHalt = True.
Beides wird in ftiFinish(0) abgefragt, bei einem Parameterwert > 0 würde zusätzlich
noch der entsprechende E-Eingang abgefragt.
- Gleich zu Beginn der repeat-Schleife wird über ftiGetAnalog der
aktuelle Temperaturwert abgefragt und in lblTemperatur auf der Form angezeigt.
- Bei if Temperatur < Heiss ist die erlaubte Temperatur überschritten
(NTC : negative temparature coefficient, je heißer - je kleiner) und mit ftiSetMotor
mHeizung die Lampe ausgeschaltet und mit ftiSetMotor mKühlung der Ventilator
eingeschaltet.
- Bei if Temperatur > Kalt ist zu kalt, da gehts umgekehrt.
- Zu Beginn der procedure wird abgefragt, ob die Regelungsschleife schon
läuft (if cmdAction.Caption = 'HALT') dann wird über NotHalt := True
ein Ende-Wunsch angemeldet (der Button hat jetzt ja die HALT-Beschriftung) und und die
procedure gleich wieder verlassen. Der Ende-Wunsch wird durch ftiFinish(0) ausgewertet.
Man sieht : die gleiche procedure kann auch aufgerufen werden wenn sie noch läuft
(ftiGetAnalog ... schalten intern diese Unterbrechungsmöglichkeit
(Application.ProcessMessages um auch andere dran zu lassen z.B. den HALT-Button).
- Deswegen wird mit cmdAction.Caption := 'HALT' am Beginn und := 'START' am Ende der
Procedure für einen zweiten Aufruf gesperrt und gleichzeitig für das Ende beschriftet.
- Und dann gibt es da noch die procedure TForm1.FormCloseQuery, die -
nach einem Blick auf die Button-Beschriftung - auf die Frage : Dürfen wir schließen?
schlicht Nein sagt, wenn sie HALT lautet.
|