PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Verständnisfrage zu SUB deklarieren



mat-sche
05.01.2009, 20:34
Hallo und einen schönen Abend!

Auch nach durchlesen einiger Artikel hier habe ich noch immer nicht den Sinn gefunden eine Sub zu declarieren.
Ich habe ein Prog geschrieben:



$regfile = "m8def.dat" ' specify the used micro
$crystal = 12000000 ' used crystal frequency ' use baud rate
$hwstack = 50 ' default use 32 for the hardware stack
$swstack = 50 ' default use 10 for the SW stack
$framesize = 40 ' default use 40 for the frame space
'$sim
'Declare Sub Fensterhoch_kue 'PinB0 =>SV2/8 Fernbedienungsempfänger
'Declare Sub Fensterrunter_kue 'PinB2 =>SV2/6
'Declare Sub Fensterhoch_bad 'PinC3 =>SV2/4
'Declare Sub Fensterrunter_bad
'Declare Sub Temp_holen
'Declare Sub Rel_wasser_an
'------------------------------------------------------------------------------- Twi_lcd
$lib "Lcd_i2c.lib" 'My i2c driver for the LCD

Config Lcd = 16 * 2
Config Lcdbus = 4
Initlcd
Cursor Off
Cls

Const Pcf8574_lcd = &H40 'Defines the address of the I/O expander for LCD

Config Scl = Portc.5 'Configure i2c SCL
Config Sda = Portc.4 'Configure i2c SDA
Dim _lcd_e As Byte 'Needed to control 4 line LCD
_lcd_e = 128

'------------------------------------------------------------------------------- Variablen
Const Rolltime = 32


Ddrd = &B11111111 'Config Portd = Output
Portb = &B00000101
Portc = &B00001011 'Pull up Portb.2 ein
Rel_bad_auf Alias Portd.0 'Alias für Relaise-Fensterheber Bad auf
Rel_bad_zu Alias Portd.1
Rel_kue_auf Alias Portd.2
Rel_kue_zu Alias Portd.3
Rel_wasser Alias Portd.6 'Eingangssignal von der Brennerzeitsteuerung
'Port_brenner Alias Portc.0 '{Portb.2}Eingang vom der Zeitsteuerung vom Ofen => Tag/Nachtprogramm: 0=Nachtabsenkung, 1=Tagprogramm

Dim Flag_bad_auf_1 As Word , Flag_bad_auf_2 As Word , Fenster_bad_offen As Bit
Dim Flag_bad_zu_1 As Word , Flag_bad_zu_2 As Word , Fenster_bad_zu As Bit
Dim Flag_kue_auf_1 As Word , Flag_kue_auf_2 As Word , Fenster_kue_auf As Bit
Dim Flag_kue_zu_1 As Word , Flag_kue_zu_2 As Word , Fenster_kue_zu As Bit
'Dim Flag_wo_auf_1 As Word , Flag_wo_auf_2 As Word
'Dim Flag_wo_zu_1 As Word , Flag_wo_zu_2 As Word

Dim Twi_daten As Byte
Dim Pump_st As Byte , Pump_st_z As Bit 'Zähler für die Pumpenstarts


'------------------------------------------------------------------------------- Timer1 konfigurieren
Config Timer1 = Timer , Prescale = 256
On Timer1 Counter_irq
Const Timer1vorgabe = 18661
Enable Timer1
Enable Interrupts
Dim Timecount As Word


'------------------------------------------------------------------------------- Configuration DS1820
Config 1wire = Portb.1
Dim Id1(8) As Byte
Dim Id2(8) As Byte
Dim Ar1(2) As Byte
Dim Ar2(2) As Byte
Dim T_vorl As Byte
'Dim Tempraturbyte(6) As Byte At T_vorl Overlay
Dim T_ruekl As Byte , T_ruekl_1 As Byte


Id1(1) = 1wsearchfirst()
Id2(1) = 1wsearchnext()

'************************************************* ****************************** Hauptprogramm


Do

Debounce Pinb.0 , 0 , Fensterhoch_kue , Sub 'PinB0 =>SV2/8 Fernbedienungsempfänger
Debounce Pinb.2 , 0 , Fensterrunter_kue , Sub 'PinB2 =>SV2/6
Debounce Pinc.3 , 0 , Fensterhoch_bad , Sub 'PinC3 =>SV2/4
Debounce Pinc.1 , 0 , Fensterrunter_bad , Sub 'PinC1 =>SV2/2

If Timecount = 0 Then
Lowerline 'select the lower line
Waitms 1
Locate 2 , 1
Lcd "t= "
End If
'
Lowerline 'select the lower line
Waitms 1
Locate 2 , 1
Lcd "t=" ; Timecount
Waitms 1
Locate 2 , 8
Lcd "pu=" ; Rel_wasser
Waitms 1
Locate 2 , 13
Lcd "br=" ; Pinc.0
Waitms 1
Upperline
Locate 1 , 11
Lcd "bs" ; Pump_st
' 'Port_brenner
Upperline
Locate 1 , 1
Lcd "Tr" ; T_ruekl
Waitms 1
Upperline
Locate 1 , 6
Lcd "Tv" ; T_vorl
Waitms 1
'
'************************************************* ****************************** Steuerung Umlaufpumpe Warmwasser
1wreset '1wirebus resetten
1wwrite &HCC : 1wwrite &H44
'Messung der temp beider Sensoren starten
If Pinc.0 = 0 Then 'Zeitprogramm vom Brenner
If Timecount = 0 Or Timecount = 800 Or Timecount = 1600 Or Timecount = 2400 Or Timecount = 3200 Or Rel_wasser = 1 Then 'aller 20min {0,1200,2400} Rücklauftemperatur überprüfen dann Sprung in Unterprogramm
Gosub Temp_holen
Gosub Rel_wasser_an
End If
End If 'beide DS1820 Messung anstoßen
'Gosub Temp_holen



'*************************************************
Select Case Twi_daten
'************************************************* ****************************** Rolladen Bad rauf
Case 1 : If Rel_bad_zu = 0 And Fenster_bad_offen = 0 Then 'Tastenverrieglung
Toggle Rel_bad_auf 'umschalten von Port zwischen 0&1
Fenster_bad_zu = 0 ' nach fenster vollständig runter, erst wieder möglich runter zu fahren nach dem es wieder rauffuhr
Else
Twi_daten = 0
Goto Ende
End If
'Locate 2 , 3
'Lcd Rel_bad_auf

If Rel_bad_auf = 1 And Flag_bad_auf_2 = 0 And Flag_bad_zu_2 = 0 Then 'if Abfrage jedesmal wenn case1 ist und somit der Port getoggle wird
Flag_bad_auf_1 = Rolltime + Timecount ' flag mit der Laufzeit für Fenster kompl. auf plus Sekundenzähler
Elseif Rel_bad_auf = 1 And Flag_bad_zu_2 > 0 Then
Flag_bad_auf_2 = Rolltime - Flag_bad_zu_2 'nach dem das Fenster runtergefahren wurde, wurde der Laufzeitwert im Flag_bad_zu_2 gespeichert, jetzt wird bei
Flag_bad_auf_1 = Timecount + Flag_bad_auf_2 ' erneutem auffahren die restliche Zeit, für komplett auf, neu in Flag_bad_auf_1 geschrieben
Flag_bad_zu_2 = 0 ' löschen der der Zeit die schon zugefahren ist
End If

If Rel_bad_auf = 0 And Flag_bad_auf_2 > 0 Then 'if Abfrage jedesmal wenn case1 ist und somit der Port getoggle wird
Elseif Rel_bad_auf = 1 And Flag_bad_auf_2 > 0 Then '
Flag_bad_auf_1 = Timecount + Flag_bad_auf_2
Flag_bad_auf_2 = 0
End If

If Rel_bad_auf = 0 And Flag_bad_auf_1 > 0 Then
Flag_bad_auf_2 = Timecount
Flag_bad_auf_2 = Flag_bad_auf_1 - Flag_bad_auf_2
End If

Twi_daten = 0

'************************************************* ****************************** Rolladen Bad runter
Case 11 : If Rel_bad_auf = 0 And Fenster_bad_zu = 0 Then
Toggle Rel_bad_zu
Fenster_bad_offen = 0
Else
Twi_daten = 0
Goto Ende
End If

If Rel_bad_zu = 1 And Flag_bad_zu_2 = 0 And Flag_bad_auf_2 = 0 Then
Flag_bad_zu_1 = Rolltime + Timecount
Elseif Rel_bad_zu = 1 And Flag_bad_auf_2 > 0 Then
Flag_bad_zu_2 = Rolltime - Flag_bad_auf_2
Flag_bad_zu_1 = Timecount + Flag_bad_zu_2
Flag_bad_auf_2 = 0
End If

If Rel_bad_zu = 0 And Flag_bad_zu_2 > 0 Then
Elseif Rel_bad_zu = 1 And Flag_bad_zu_2 > 0 Then
Flag_bad_zu_1 = Timecount + Flag_bad_zu_2
Flag_bad_zu_2 = 0
End If
If Rel_bad_zu = 0 And Flag_bad_zu_1 > 0 Then
Flag_bad_zu_2 = Timecount
Flag_bad_zu_2 = Flag_bad_zu_1 - Flag_bad_zu_2
End If
Twi_daten = 0

'************************************************* ****************************** Rolladen Küche rauf
Case 2 : If Rel_kue_zu = 0 And Fenster_kue_auf = 0 Then
Toggle Rel_kue_auf
Fenster_kue_zu = 0
Else
Twi_daten = 0
Goto Ende
End If
Locate 2 , 5
Lcd Rel_kue_auf ' If Flag_bad_auf_2 = 0 And
If Rel_kue_auf = 1 And Flag_kue_auf_2 = 0 And Flag_kue_zu_2 = 0 Then
Flag_kue_auf_1 = Rolltime + Timecount
Elseif Rel_kue_auf = 1 And Flag_kue_zu_2 > 0 Then
Flag_kue_auf_2 = Rolltime - Flag_kue_zu_2
Flag_kue_auf_1 = Timecount + Flag_kue_auf_2
Flag_kue_zu_2 = 0
End If

If Rel_kue_auf = 0 And Flag_kue_auf_2 > 0 Then
Elseif Rel_kue_auf = 1 And Flag_kue_auf_2 > 0 Then
Flag_kue_auf_1 = Timecount + Flag_kue_auf_2
Flag_kue_auf_2 = 0
End If

If Rel_kue_auf = 0 And Flag_kue_auf_1 > 0 Then
Flag_kue_auf_2 = Timecount
Flag_kue_auf_2 = Flag_kue_auf_1 - Flag_kue_auf_2
End If
Twi_daten = 0

'************************************************* ****************************** Rolladen Küche runter
Case 22 : If Rel_kue_auf = 0 And Fenster_kue_zu = 0 Then
Toggle Rel_kue_zu
Fenster_kue_auf = 0
Else
Twi_daten = 0
Goto Ende
End If

If Rel_kue_zu = 1 And Flag_kue_zu_2 = 0 And Flag_kue_auf_2 = 0 Then
Flag_kue_zu_1 = Rolltime + Timecount
Elseif Rel_kue_zu = 1 And Flag_kue_auf_2 > 0 Then
Flag_kue_zu_2 = Rolltime - Flag_kue_auf_2
Flag_kue_zu_1 = Timecount + Flag_kue_zu_2
Flag_kue_auf_2 = 0
End If

If Rel_kue_zu = 0 And Flag_kue_zu_2 > 0 Then
Elseif Rel_kue_zu = 1 And Flag_kue_zu_2 > 0 Then
Flag_kue_zu_1 = Timecount + Flag_kue_zu_2
Flag_kue_zu_2 = 0
End If
If Rel_kue_zu = 0 And Flag_kue_zu_1 > 0 Then
Flag_kue_zu_2 = Timecount
Flag_kue_zu_2 = Flag_kue_zu_1 - Flag_kue_zu_2
End If
Twi_daten = 0
Ende:


End Select

If Rel_bad_auf = 1 And Timecount => Flag_bad_auf_1 Then 'rücksetzen des Ansteuerrelaise für Fensterheber nach abgelaufener Zeit
Reset Rel_bad_auf
Flag_bad_auf_1 = 0 'rücksetzen vom Zeitzähler
Fenster_bad_offen = 1 'wenn Fensterladen kompl. offen dann verhindern das nochmalig angesteuert werden kann
Fenster_bad_zu = 0 'nach dem Fensterladen kompl. runtergefahren war wird beim auffahren die nochmalige "Runterfahrsperre" zurück gesetzt
End If
If Rel_bad_zu = 1 And Timecount => Flag_bad_zu_1 Then
Reset Rel_bad_zu
Flag_bad_zu_1 = 0
Fenster_bad_offen = 0
Fenster_bad_zu = 1
End If
If Rel_kue_auf = 1 And Timecount => Flag_kue_auf_1 Then
Reset Rel_kue_auf
Flag_kue_auf_1 = 0
Fenster_kue_auf = 1
Fenster_kue_zu = 0
End If
If Rel_kue_zu = 1 And Timecount => Flag_kue_zu_1 Then
Reset Rel_kue_zu
Flag_kue_zu_1 = 0
Fenster_kue_zu = 1
Fenster_kue_auf = 0
End If


Loop
End

'+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++ Subroutinen
Counter_irq:

Timer1 = Timer1vorgabe
Incr Timecount ' increment every sec the counter with 1
If Timecount => 3600 Then ' 5400 here is the 1,50 std
Timecount = 0

' wenn nicht dann lade StartWert in Timer1
End If
Return

'+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++ Sensor 1 auslesen
Temp_holen:
1wreset 'reset
1wwrite &H55 'match rom command
1wwrite Id1(1) , 8 'id von sensor 1
1wwrite &HBE

Ar1(1) = 1wread(2)
T_ruekl = Makeint(ar1(1) , Ar1(2))
T_ruekl = T_ruekl / 2 'Ar(1)

'+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++ Sensor2 auslesen

1wreset 'reset
1wwrite &H55 'match rom command
1wwrite Id2(1) , 8 'id von sensor 2
1wwrite &HBE 'scratchpad lesen

Ar2(1) = 1wread(2)
T_vorl = Makeint(ar2(1) , Ar2(2))
T_vorl = T_vorl / 2

Return

'+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++ Warmwasserpumpe ein/ausschalten und senden der Warmwassertemperatur
Rel_wasser_an:
If T_vorl < T_ruekl Then
Goto Rel_wasser_1
End If
T_ruekl_1 = T_vorl - T_ruekl 'Abweichung zwischen Warmwassertemperatur und zurück geführte Umwälzwasser
If T_ruekl < 30 And T_vorl >= T_ruekl And T_vorl < 45 Then

Rel_wasser_1:
Rel_wasser = 1
If Pump_st_z = 0 Then
Pump_st_z = 1
Incr Pump_st
End If 'wenn Umwälzwasser kleiner als 26°C und die Warmwassertemperatur größer ist dann Umwälzpumpe an
' (1.Byte = Temperatur;2.-4. = 0Bytes; 5.Senderadresse; 6.Byte Komando)
Elseif T_ruekl_1 =< 15 And T_ruekl_1 => 0 And Rel_wasser = 1 Then
Rel_wasser = 0
Pump_st_z = 0
End If

Return
'+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++ Fernbedienungsempfänger

Fensterhoch_kue:
Twi_daten = 2
Return
'PinB0 =>SV2/8
Fensterrunter_kue:
Twi_daten = 22
Return
'PinB2 =>SV2/6
Fensterhoch_bad:
Twi_daten = 1
Return
'PinC3 =>SV2/4
Fensterrunter_bad: 'PinC1 =>SV2/2
Twi_daten = 11
Return

'################################################# ##############################


bei den ich einige Subroutinen mit Gosub anspringe. Ich nutze in dem Programm ein I2C LCD. Nun ohne die Deklaration läuft das Prog über eine gewisse Zeit hin stabil und macht was es soll. Aber nach einigen Stunden (5....6...?) fällt das Display aus und zeigt nur wirres Zeug an, auch geht dann ein bestimmter Programmteil nicht mehr.
Jetzt habe ich alle Unterroutinen mal deklariert und jetzt läuft's.

=> weshalb dies? Alle Variablen waren dimensioniert.
Wer hat für mich eine Erklärung für mich und die Zukunft?

Danke und Gruß MAT

TomEdl
05.01.2009, 20:48
Hallo!

Dieselbe Frage stelle ich mir auch. Was ist der Vorteil einer SUB gegenüber eines mit GOSUB angesprungen Programmteils.

Eine weitere Frage fällt mir gerade ein, wo wir schon dabei sind:
Wenn ich in einer GOSUB oder SUB bin, wird dann bei einem Interrupt dieser ausgeführt oder die Unterroutine erst fertig ausgeführt?

Danek für die Hilfe
Thomas

Gento
05.01.2009, 22:41
Sub haben auch den Sinn das die immer besser lesbar sind.
Mit Copy & Paste lassen sich so ganze Abschnitte leichter in andere Projekte einbinden.
Der größte Vorteil sind Locale Variablen.
Weiterer Vorteil sind Parameterübergaben.

Nachteile sind.
Die Programme laufen einen Hauch langsamer.
Es wird mehr Stack benötigt.

lg
Gento

bytecook
12.01.2009, 22:29
also ich schließe mich Gento an ...

eine SUB ist sowas wie eine Funktion ohne Rückgabewert,
in Delphi oder Pascal nennt man das Ganze Procedure,
in C Void() ...

Einen Sinn gibt es klarerweise dann, falls Du

- Strukturierten Code einem Spaghetticode vorziehst...
Ist auch leichter zu debuggen dann :)

- Subs mit lokalen Variablen und Übergabeparameter in anderen
Programmen wiederverwenden willst, ohne die Routiine gleich neu
schreiben willst... dann bestens gleich schöne Bezeichnervariable
wählen ...

- größere Projekte angehen möchtest, denn damit isset das Ganze
übersichtlicher.. [Meine Sources im Real Life bewegen sich so um die
160 MB in einem aktuellen Langzeitprojekt ... ]

Eine Sub spart in diesem Falle natürlich auch den reservierten Platz des
Ergebniswertes einer Funktion...

Je nach Intelligenz des Compilers könnte man dann externe Toolsamlungen/Units einbinden und diese dann im Hauptteil einbinden...
Der Compiler sollte dann natürlich nur die verwendeten Routinen compilieren, mal sehen, bin erst seit 2 Tagen damit unterwegs...

Spart eine Menge Zeit....

@ TomEdl


Zum Interrupt-Timer:

dieser führt beim nächsten Rechenschritt des Prozessors seinen eigenen Code aus, unabhängig von der momentan ausgeführten Routine.. nach der Ausführung des Timercodes setzt er die eigentliche Routine fort...

Soll der Timer das gerade nicht machen, dann würde ich einen einfachen Lockingmechanismus verwenden, etwa mit einer globalen Variable, um das OnTimerEvent wieder zu verlassen...

Das Sub / Die Funktion würde dann eine globale Variable, beispielsweise ein Byte, um eins erhöhen und vor dem Verlassen der Routine dieses wieder um eins erniedrigen. (Die Erhöhung kann man auch durch andere Routinen schleifen und erhöhen bzw erniedrigen, dann könnte man den Timer anweisen, mittels Case Abfrage nicht nur auf 0 zu reagieren...)

Im Timer würde ich dann diese Variable beispielsweise auf den Wert 0 überprüfen.
Falls dieser 0 ist, dann wird der Timerteil ausgeführt, andernfalls die
Routine verlassen...


um

lg

Peter

TomEdl
13.01.2009, 17:19
Zum Interrupt-Timer:
Soll der Timer das gerade nicht machen, dann würde ich einen einfachen Lockingmechanismus verwenden, etwa mit einer globalen Variable, um das OnTimerEvent wieder zu verlassen...

Doch, dass soll er unbedingt machen. Danke für deine ausführliche Antwort, du hast mir sehr weitergeholfen.

Gruß
Thomas