Zeitschaltuhr Pulse

emilio20

Well-known member
Beiträge
825
Punkte Reaktionen
20
Zuviel Werbung?
->Hier kostenlos registrieren
Hallo
habe ein kleines Problem. Ich wollte mir eine Zeitschaltuhr erstellen die ich für mehrers Funktionen einsetzen möchte.
Die Zeitschaltuhr solle für jeden Wochentag je nach verwendeter Funktion 1-10 Einschlatzeiten und Auschaltzeiten habe, die einen Puls ausgeben.

Mein Problem ist das ich die Größe des verwendete Arrays nicht Variabel machen kann.
Hier mein Programm
Code:
FUNCTION_BLOCK FB234

VAR_TEMP
    
    TEMP_INT : INT ;
    DATUM_ZEIT_DT  : DT;
    SPS_TIME : TOD; 
    Wochentag : INT;
    i:INT; 
    i2:INT;
    SPS_TIME_I:DINT;
    
END_VAR

VAR_OUTPUT
    
    PULS_EIN:BOOL;
    PULS_AUS:BOOL;
    
END_VAR

VAR
    //SCHALTUHR 1..7 = Wochentage von 1=SO, 2=MO usw , 1..10 SPEICHER PULS SCHALTUHR EIN
    SCHALTUHR_EIN : ARRAY[1..7,1..10] OF TOD; 
    //SCHALTUHR 1..7 = Wochentage von 1=SO, 2=MO usw , 1..10 SPEICHER PULS SCHALTUHR AUS
    SCHALTUHR_AUS : ARRAY[1..7,1..10] OF TOD; 
    
          
      
       
   
END_VAR


//______________________________________________________  
BEGIN

// Zeiten aus SPS Extrahieren  
  
 // Datum und Zeit aus SPS Lesen
      TEMP_INT:=READ_CLK(CDT :=DATUM_ZEIT_DT); 
  
 // Zeit aus Datum und Zeit Extrahieren  
      SPS_TIME:=DT_TOD(IN :=DATUM_ZEIT_DT);
 // ZEIT ind DINT umwandeln und durch 1000 Teile um sec zu erhalten     
      SPS_TIME_I:=TOD_TO_DINT (SPS_TIME)/1000 ;
 // Wochentag aus SPS Zeit Extrahieren
      Wochentag:=DT_DAY(IN :=DATUM_ZEIT_DT);  


// Vergleich von SPS_TIME in sec mit allen EINSCHALTZEITEN des jeweiligen Wochentages. Wenn EINSCHALTZEIT mit SPS_TIME
// übereinstimmt wird FOR Schleife beendet und Ausgang PULS_EIN IST TRUE     
    FOR i := 1 TO 10 BY 1 DO
        
      IF SPS_TIME_I = TOD_TO_DINT (SCHALTUHR_EIN[Wochentag,i])/1000 AND TOD_TO_DINT (SCHALTUHR_EIN[Wochentag,i])/1000>0  THEN
        PULS_EIN:=TRUE;
        EXIT;
        ELSE
        PULS_EIN:=FALSE;
      END_IF;

        
                ;
    END_FOR;

 // Vergleich von SPS_TIME in sec mit allen AUSCHALTZEITEN des jeweiligen Wochentages. Wenn AUSCHALTZEIT mit SPS_TIME
// übereinstimmt wird FOR Schleife beendet und Ausgang PULS_AUS IST TRUE     
    FOR i2 := 1 TO 10 BY 1 DO
        
      IF SPS_TIME_I = TOD_TO_DINT (SCHALTUHR_AUS[Wochentag,i2])/1000 AND TOD_TO_DINT (SCHALTUHR_AUS[Wochentag,i2])/1000>0 THEN
        PULS_AUS:=TRUE;
        EXIT;
        ELSE
        PULS_AUS:=FALSE;
      END_IF;

        
                ;
    END_FOR;
   
   
    
          
    
   
END_FUNCTION_BLOCK
  
//*********************************ENDE FB***************************************
 

SCM

Well-known member
Beiträge
335
Punkte Reaktionen
33
Meinst du die Verwendeten EIN/Ausschaltzeiten bzw. überhaupt die "Größe" einstellbar machen?
Dann könntest dir ja eine IN Variable anlegen die die aktiven Tage/Zeiten angibt!
Dann kann zum einen die Bearbeitung übersprungen werden wenn der Tag überhaupt nicht aktiviert ist. Des weiteren werden dann nur die aktiven Zeiten von den 10 abgefragt!
Wird dann aber nur bei den For schleifen etwas bringen!Den die Zeiten usw. liegen ja im Statischen bereich!Dieser wird aber nicht zur Laufzeit in Größe odgl. verändert!
Was noch gehen würde ist die Parameter für die Schaltuhr auch als IN bzw. IN_OUT angeben!
dann kannst ja nur den Bereich einlesen der auch aktiv ist!

Mfg
 

DerMatze

Well-known member
Beiträge
517
Punkte Reaktionen
19
Zuviel Werbung?
->Hier kostenlos registrieren
Hallo,

meine Schaltuhr habe ich in AWL gemacht, 4 Schaltzeiten (erweiterbar) und Wochentage werden auch berücksichtigt.
Somit habe ich kein Problem mit einem Array und wie SCM schon schreibt, es wird nur der "aktive Tag" bearbeitet.

Gruß
DerMatze
 
OP
emilio20

emilio20

Well-known member
Beiträge
825
Punkte Reaktionen
20
Meinst du die Verwendeten EIN/Ausschaltzeiten bzw. überhaupt die "Größe" einstellbar machen?
Dann könntest dir ja eine IN Variable anlegen die die aktiven Tage/Zeiten angibt!
Dann kann zum einen die Bearbeitung übersprungen werden wenn der Tag überhaupt nicht aktiviert ist. Des weiteren werden dann nur die aktiven Zeiten von den 10 abgefragt!
Wird dann aber nur bei den For schleifen etwas bringen!Den die Zeiten usw. liegen ja im Statischen bereich!Dieser wird aber nicht zur Laufzeit in Größe odgl. verändert!
Was noch gehen würde ist die Parameter für die Schaltuhr auch als IN bzw. IN_OUT angeben!
dann kannst ja nur den Bereich einlesen der auch aktiv ist!

Mfg

Ich müsste also als IN Out den Array vom jeweiligen Tag einlen ?
Kanst du mir ein Beispiel nennen?
 

SCM

Well-known member
Beiträge
335
Punkte Reaktionen
33
Also du machst als in Type zum Beispiel Any!Und holst dir dann vom Parameter DB genau nur soviele Daten die auch aktiviert worden sind!
Die Frage stellt sich was du genau möchtest!Am einfachsten ist du machst eine Abfrage ob der Tag überhaupt aktiv ist ansonsten Baustein beenden!
Dann änderst die For schleifen so das nur mehr die aktiven Zeiten des jeweiligen Tages durchlaufen werden!
So wird der Baustein schon etwas feiner.
Mfg
 
OP
emilio20

emilio20

Well-known member
Beiträge
825
Punkte Reaktionen
20
Zuviel Werbung?
->Hier kostenlos registrieren
Verstehe das nicht so ganz was du meinst.

Der Baustein soll folgendes können
Es soll Zeiten vergleichen und bei übereinstimmung einen Puls setzen.

Ich möchte am Baustein einen DB mit Zeiten ansetzen der von 1 Zeit je Tag bis 10 oder mehr Zeiten je Tag eingestellt werden kann, Somit könnte ich die Zeitschaltuhr für eine einfache Rollosteuerung, 1 Zeit je Tag 7:00 Uhr Rollo Aus
bishin zu einer Heizungststeuerng mehrere Zeiten je Tag z.b 8:00 Heizung ein 10:00 Uhr Heizung aus 12:00 Heizung ein 22:00 uhr Heizung aus verwenden.
 

SCM

Well-known member
Beiträge
335
Punkte Reaktionen
33
Also folgendes du hast also Tag 0-7 und Zeiten für Tag 0-7 .
Die Zeiten Struktur sieht immer so aus Zeit ein, Zeit aus oder?
Genau diese Ein/Aus Struktur soll variabel in den Baustein gelesen werden.
Also Brauchst du mal eine Variable die definiert ob der Tag aktiv ist.
Dann eine Variable die definiert wieviel Zeiten an dem aktuellen Tag aktiv sind!
Genau mit diesen Informationen kannst dir die Daten indirekt aus dem Datenbaustein holen!

Also sagen wir mal Tag Freitag hat 3 aktive Zeiten.Dann steht in deiner Tagesvariable z.b.: 6
In der Variable Zeiten am Tag steht dann 3.
Damit du jetzt den Pointer für die Erste Zeit des Tages Freitag ermitteln kannst musst du die Anzahl der aktiven Zeiten der anderen Tage addieren!
Dadurch erhälst du dann die Startadresse für die erste Zeit vom Freitag!

Hoffe es ist halbwegs verständlich!Ist ja schon eine etwas gröbere Sache wenn es völlig variabel sein soll!

Gruß
 

SCM

Well-known member
Beiträge
335
Punkte Reaktionen
33
Im Endeffekt willst du eine Parametrierbare Wochenschaltuhr machen wenn ich dich richtig verstehe!
Wieso willst du es variabel haben?!Zykluszeit schonen?
Ansonsten kannst ja e für jeden Tag ein Array von 10 Zeiten odgl. anlegen!Damit wirds sicher reichen!
So wie du es ja jetzt hast!

Gruß
 
OP
emilio20

emilio20

Well-known member
Beiträge
825
Punkte Reaktionen
20
Zuviel Werbung?
->Hier kostenlos registrieren
Ich wollte halt für eine Rollosteueurng keinen DB mit 10 Zeiten verwenden da ich diesen für 12 Rollos brauche. Das ist ja jedes mal ein risen DB. weiterhin wollte ich so Flexibel sein da ich auch mal eine Koplexe Heizungssteuerung mit vielen Zeiten abbilden kann.

Bei einer Buderus Heizungssteueeurng mit Zeitschaltuhr kann ich x belibig viele Ein und Ausschaltzeiten einfügen. Sowas wollte ich nachbilden.

Versuche mal mein Glück.
 

SCM

Well-known member
Beiträge
335
Punkte Reaktionen
33
Achso okay!
Ja es ist sicher machbar!Nur halt etwas aufwendiger wenn man es wirklich ganz Flexibel haben will!
Aber mach mal und stell wieder ein!Dann kann man ja wieder weiterschauen wenns hackt!
Gruß
 

Thomas_v2.1

Well-known member
Beiträge
8.139
Punkte Reaktionen
2.461
Bei einer Buderus Heizungssteueeurng mit Zeitschaltuhr kann ich x belibig viele Ein und Ausschaltzeiten einfügen. Sowas wollte ich nachbilden.

Die Buderus-Steuerung holt sich den Speicher aber auch nicht aus der Luft. Da wird genauso eine maximale Zahl hinterlegt sein, nur bist du da wahrscheinlich noch nicht drauf gestoßen.

Mach dir doch einfach zwei FBs, einen für kleine Wochenprogramme und einen für große.
In SCL kannst du die Anzahl auch durch eine Konstante ersetzen
CONST
ANZAHL_INTERVALLE := 10;
END_CONST

Überall wo du jetzt 10 stehen hast setzt du ANZAHL_INTERVALLE ein. Dann brauchst du den Wert nur an einer Stelle zu ändern.
 

Onkel Dagobert

Well-known member
Beiträge
5.159
Punkte Reaktionen
1.048
Zuviel Werbung?
->Hier kostenlos registrieren
Hallo Emilio,

wenn du flexibel sein willst, dann solltest du m.E. die Schaltzeiten extern in einem DB ablegen und als ANY an den Baustein übergeben. Über die Längenangabe des ANY ermittelst du die Anzahl der Schaltzeitpaare. Der Wochentag bzw. der Wochentagbereich müßte variabel mit den Schaltzeiten abgelegt werden. Falls dieser Weg für dich in Frage kommt, könnte ich dir noch etwas Starthilfe geben, habe ja mal Zeit zum Basteln :ROFLMAO: .


Gruß, Onkel
 
OP
emilio20

emilio20

Well-known member
Beiträge
825
Punkte Reaktionen
20
Hallo Emilio,

wenn du flexibel sein willst, dann solltest du m.E. die Schaltzeiten extern in einem DB ablegen und als ANY an den Baustein übergeben. Über die Längenangabe des ANY ermittelst du die Anzahl der Schaltzeitpaare. Der Wochentag bzw. der Wochentagbereich müßte variabel mit den Schaltzeiten abgelegt werden. Falls dieser Weg für dich in Frage kommt, könnte ich dir noch etwas Starthilfe geben, habe ja mal Zeit zum Basteln :ROFLMAO: .


Gruß, Onkel

Das mir dem Any hört sich gut an.
Habe alerdings mit Any noch nichts gemacht da ich meistens mit For schleifen Arbeite.

Wie würde wo was in SCL aussehen ?
 

nutellahase

Well-known member
Beiträge
180
Punkte Reaktionen
28
Gibst du die Zeitnocken via Panel vor, oder direkt im Programm (also ohne veränderbaren Zugriff??). Wenn per Panel könntest du ja eine Textliste erstellen wo du die Wochentage (1-7) einträgst. Ich habe bei meiner Schaltuhr noch die Option "täglich" (Wert 0) hinzugefügt. Wenn du die Rollo z.B. jeden Tag um die gleiche Uhrzeit steuern willst sparst du dir auch wieder Zeitnocken!

Ansonsten habe ich zu meinen Eingegebenen Schaltzeitpunkt jeweils eine Sekunde dazuaddiert. Dann ist meine Zeitnocke genau 1 Sekunde lang aktiv und ich komme ohne Schleifen aus. In deinem Fall würde ich dann für jeden Wochentag einen FB erstellen und dort die 10 Zeitnocken runterklopfen (hält sich ja noch in Grenzen).
 
OP
emilio20

emilio20

Well-known member
Beiträge
825
Punkte Reaktionen
20
Zuviel Werbung?
->Hier kostenlos registrieren
Hallo
Ja mache es über's Panel. Das mit dem Wochentag ist nicht das Problemm.

Habe das Problem das ich kein TOD über Any Struct einlesen kann
Code:
VAR_INPUT
    
    SCHALTZEITEN: ANY ;
    aSCHALTZEITEN AT SCHALTZEITEN :STRUCT
        EINSCHALTZEIT : TOD;
        AUSSCHALTZEIT : TOD;
        WOCHENTAG: INT;
        AKTIV: BOOL;
    END_STRUCT;
    
    ANZAHL_SCHALTZEITEN:INT;
    
END_VAR
 

Onkel Dagobert

Well-known member
Beiträge
5.159
Punkte Reaktionen
1.048
Wir basteln eine Schaltzeituhr ;-)

Folge 1 - Die Datenablage

Für die Ablage der Schaltzeitpaare legen wir uns zunächst einen Datentyp an.
Die Länge des Datentyps beträgt in diesem Fall 10 Byte.

Code:
TYPE UDT 1
VERSION : 0.1


  STRUCT     
   WOCHENTAG_START : BYTE ;    // 1-So 2-Mo ..
   WOCHENTAG_END : BYTE ;    // 1-So 2-Mo ..
   UHRZEIT_EIN : TIME_OF_DAY ;    // Einschaltzeit
   UHRZEIT_AUS : TIME_OF_DAY ;    // Ausschaltzeit
  END_STRUCT ;    
END_TYPE

Je nach Anwendung verwenden wir diesen Datentyp in einem DB.
Das könnte z.Bsp. so aussehen:

Code:
DATA_BLOCK DB 1
TITLE =
VERSION : 0.1


  STRUCT     
   JALOUSIE_1 : UDT 1;    // EINE Schaltzeit
   JALOUSIE_2 : ARRAY  [1 .. 7 ] OF UDT 1;  // SIEBEN Schaltzeiten
   HEIZUNG : ARRAY  [1 .. 100 ] OF UDT 1;   // EINHUNDERT Schaltzeiten
   BIER_HOLEN : ARRAY  [1 .. 1000 ] OF UDT 1;   // EINTAUSEND Schaltzeiten
  END_STRUCT ;    
BEGIN
END_DATA_BLOCK


Die Übergabe der Schaltzeiten an den Programmbaustein erfolgt dann als ANY-Pointer:
Code:
      CALL FC     1 (SCHALTZEITEN := DB1.JALOUSIE_1);// Zugriffsbreite: 10 Byte

      CALL FC     1 (SCHALTZEITEN := DB1.JALOUSIE_2);// Zugriffsbreite: 70 Byte

      CALL FC     1 (SCHALTZEITEN := DB1.HEIZUNG);// Zugriffsbreite: 1.000 Byte
 
      CALL FC     1 (SCHALTZEITEN := DB1.BIER_HOLEN);// Zugriffsbreite: 10.000 Byte

Den Baustein FC1 erstellen wir in der nächsten Folge in SCL.


Euer Onkel Dagobert :p
 
Zuletzt bearbeitet:
OP
emilio20

emilio20

Well-known member
Beiträge
825
Punkte Reaktionen
20
OK aber wie sied die Übergabe aus?

Was muss im den Baustein stehe wo ich es übergebe ?

SCHALTZEITEN : ANY; ?

Wie kann ich diese dan weiterverarbeiten ?

Wie ist die jetzt die Adresse von Einschaltzeit Rollo ?
 

nutellahase

Well-known member
Beiträge
180
Punkte Reaktionen
28
Zuviel Werbung?
->Hier kostenlos registrieren
Hallo
Ja mache es über's Panel. Das mit dem Wochentag ist nicht das Problemm.

Habe das Problem das ich kein TOD über Any Struct einlesen kann
Code:
VAR_INPUT
    
    SCHALTZEITEN: ANY ;
    aSCHALTZEITEN AT SCHALTZEITEN :STRUCT
        EINSCHALTZEIT : TOD;
        AUSSCHALTZEIT : TOD;
        WOCHENTAG: INT;
        AKTIV: BOOL;
    END_STRUCT;
    
    ANZAHL_SCHALTZEITEN:INT;
    
END_VAR

So wird das nichts... der Datentyp ANY besteht aus 10 Byte. klick
 

nutellahase

Well-known member
Beiträge
180
Punkte Reaktionen
28
OK aber wie sied die Übergabe aus?

Was muss im den Baustein stehe wo ich es übergebe ?

SCHALTZEITEN : ANY; ?

Wie kann ich diese dan weiterverarbeiten ?

Wie ist die jetzt die Adresse von Einschaltzeit Rollo ?

Also zuerst würde ich mal Onkel Dagoberts Vorschlag mit den UDT übernehmen! Das ist wie eine Schablone, die du überall in jeden Baustein nutzen kannst und falls du mal eine Änderung vornehmen musst brauchst du sie nur im UDT vornehmen und kannst danach eine Bausteinkonsistenzprüfung drüber laufen lassen (somit ist gewährleistet dass in allen Bausteinen dein UDT wieder aktuell ist im Gegensatz zu einer reinen Struct wo du alles händisch nachbessern müsstest!).
Der ANY-Pointer zeigt, einfach ausgedrückt, auf einen Datenbereich. Dabei enthält er noch eine ID (deren Bedeutung ich bis heute nicht verstanden habe), den Datentyp und die Länge. Ein POINTER kennt nur den Datenbereich (auch DB-Pointer bzw. 6 Byte Pointer genannt)! Der Datenbereich gliedert sich in DB-Nummer und einen Pointer auf den Adressbereich (bereichsübergreifender bzw. bereichsinterner Pointer).


Wenn du die Daten per ANY-Pointer übergibst, gibst du auch jede Art von Typprüfung auf! Also würde ich zur Sicherheit den ANY-Pointer prüfen (falls es reiner privater Zweck ist kann es auch entfallen, aber sicher ist sicher). Prüfen kannst du z.B. ob der ANY-Pointer auf einen Bereich in einem DB zeigt, ob er vom Typ Byte ist (bei Arrays und Struct ist glaub ich immer der Typ "Byte" angegeben) und bei Verwendung von UDTs kannst du auch noch prüfen ob du tatsächlich den "richtigen" UDT übergeben hast! Dazu kannst du die UDT Größe als Const angeben (in Onkel Dagoberts Bsp. ist die UDT 10 Byte groß).

Ist die per ANY-Pointer übergebene Länge durch die Länge der UDT teilbar kannst du dein Programm weiter ausführen. (MOD Befehl ... das Ergebnis muss ungleich 0 sein!). In weiterer Folge kannst du dir dann auch die Array-Elemente so berrechnen (z.B. übergebene Daten = 70 Byte dividiert durch UDT Länge = 10 Byte => Array mit 7 Elementen).
 
Zuletzt bearbeitet:

Onkel Dagobert

Well-known member
Beiträge
5.159
Punkte Reaktionen
1.048
Wir basteln eine Schaltzeituhr ;-)

Folge 2 - Der Programmbaustein

Nachdem wir uns in Folge 1 um die Datenablage gekümmert haben, erfolgt nun die Programmierung des Bausteins in SCL.
Der Baustein ist nur kurz getestet und kann noch Unstimmigkeiten enthalten.

Code:
(************************************************************************************************************************)
FUNCTION "SCHALTZEITUHR": BOOL
(************************************************************************************************************************)
VAR_INPUT
  SCHALTZEITPAARE                   : ANY;                          // ANY-Pointer auf x Schaltzeitpaare
END_VAR

VAR_TEMP
  ANY_SZP                           : ANY;
  SZP AT ANY_SZP:
    STRUCT
      ID                            : BYTE;
      TYP                           : BYTE;
      ANZAHL_TYP                    : INT;
      DB_NR                         : WORD;
      ZEIGER                        : DWORD;
    END_STRUCT;
  WD                                : INT;                           // Wochentag
  ANZAHL_SZP                        : INT;
  BYTE_INDEX                        : INT;
  n                                 : INT;
  TEMP_INT                          : INT;
  AKT_T                             : TOD;
  AKT_DT                            : DT;
  ADT AT AKT_DT:
    STRUCT
      JAHR                          : BYTE;
      MONAT                         : BYTE;
      TAG                           : BYTE;
      STUNDE                        : BYTE;
      MINUTE                        : BYTE;
      SEKUNDE                       : BYTE;
      MS_MSD                        : BYTE;                          // MSD (most significant decade) von ms 
      MS_LSD                        : BYTE;                          // LSD (least significant decade) von ms & Wochentag
    END_STRUCT;
      
END_VAR


BEGIN

// Initialisierung
SCHALTZEITUHR := false;

// Überprüfung Datenbereich
ANY_SZP := SCHALTZEITPAARE;
IF SHR(IN:=SZP.ZEIGER, N:=24) <> 2#10000100 OR         // kein DB
   SZP.TYP <> 16#02 OR                                 // Typ nicht Byte
   SZP.ANZAHL_TYP MOD 10 <> 0                          // Länge Datenbereich nicht plausibel
   THEN RETURN;
END_IF;

// Ermittlung der Anzahl der Schaltzeitpaare und der Startadresse
ANZAHL_SZP := SZP.ANZAHL_TYP / 10;
BYTE_INDEX := WORD_TO_INT(DWORD_TO_WORD(SHR(IN:=SZP.ZEIGER, N:=3)) AND 16#FFFF);     // korrigiert, 29.12.2012
 
// Aktuelle Systemzeit
TEMP_INT := READ_CLK(CDT := AKT_DT);

// Aktuelle Tageszeit
AKT_T := DT_TOD(IN := AKT_DT); 

// aktueller Wochentag
WD := WORD_TO_INT(BYTE_TO_WORD(ADT.MS_LSD AND 16#0F));

// Schaltzeiten abfragen
FOR n:= 1 TO ANZAHL_SZP DO
  IF WD >= WORD_TO_INT(BYTE_TO_WORD(WORD_TO_BLOCK_DB(SZP.DB_NR).DB[byte_index])) AND
     WD <= WORD_TO_INT(BYTE_TO_WORD(WORD_TO_BLOCK_DB(SZP.DB_NR).DB[byte_index+1])) THEN
     IF AKT_T >= DINT_TO_TOD((DWORD_TO_DINT(WORD_TO_BLOCK_DB(SZP.DB_NR).DD[byte_index+2]))) AND
        AKT_T <= DINT_TO_TOD((DWORD_TO_DINT(WORD_TO_BLOCK_DB(SZP.DB_NR).DD[byte_index+6]))) THEN
        SCHALTZEITUHR := true;
        EXIT;
     END_IF;
  END_IF;
  byte_index:=byte_index+10;
END_FOR;

END_FUNCTION;

Wenn du Pulse brauchst, dann erzeugst du sie am besten extern mit dem Rückgabewert des Bausteins durch Flankenbildung (FP/FN).


Gruß, Onkel Dagobert
 
Zuletzt bearbeitet:
Oben