Ich möchte euch die Weiterentwicklung des Uhrenbausteins vorstellen, mit dem ich diverse Zeitsignale erzeuge.
Mittlerweile ist er etwas umfangreicher, weil verschiedene Sachen mit der Visu gemacht werden können und auch der Versorgung
der Schaltuhren mit diversen Daten dient.
Zu der besonderen Steuerung des Anlaufmerkers mittels Event, werde ich noch einen Beitrag schreiben.
FUNCTION_BLOCK Uhr2
(*
##################################################################################
Programmbeschreibung:
Baustein zum lesen und stellen der System RTC
Für diverse Verwendungen wird die aktuelle Zeit als String und aufgesplittet
ausgegeben. Systemzeitsignale erzeugen (Blinker und Flanken)
Der Startmerker aus EventTask Start(FUN)ermöglicht den Anlaufmerker mit jedem
Starten aus beliebigen Zuständen zu setzen.
Der Zeitoffset ist auf -2 - +3h beschränkt, (sollte für Mitteleuropa ausreichen)
benötigt: strZeit, SysRtc23,
Test OK auf XV100 v3.5.12
##################################################################################
Änderungsindex
Name: TL Datum: 30.12.13 Version: 1.0 Grund: neu
Name: TL Datum: 20.06.15 Version: 1.1 Grund: Anlaufmerker eingefügt
Name: TL Datum: 09.03.19 Version: 1.2 Grund: Änderung Anlaufmerker durch Event,
RTC lesen und schreiben, Erweiterung mit Zeitdaten DB, 5Hz-Takt zu 0,5Hz geändert
Name: TL Datum: 15.02.21 Version: 1.3 Grund: Ausgang Zeitstring
Name: TL Datum: 07.09.23 Version: 2.0 Grund: FB's der EA23_SysLibRtc_Add entfernt und neu geschrieben
direkte Ausgänge der geplitteten Zeitdaten eingefügt, Offset für Zeitverschiebung
##################################################################################
*)
VAR_INPUT
lesen: BOOL; //Lesebefehl Uhrzeit
schreiben: BOOL; //Schreibbefehl Uhrzeit
OffsetUTC: SINT:=1; //Zeitverschiebung in h
END_VAR
VAR_IN_OUT
Zeitdaten:strZeit; //InstanzDB mit Zeitwerten
END_VAR
VAR_OUTPUT
DatZeit: DATE_AND_TIME;// aktuelles Datum+Zeit
strDatZeit: STRING(22);// aktuelles Datum+Zeit als String
Uhrzeit: TOD;// aktuelle Tageszeit von RTC
Jahr: WORD;
Monat: BYTE;
Tag: BYTE;
Stunde: BYTE;
Minute: BYTE;
Sekunde: BYTE;
Wochentag: BYTE; //Mo=1 ...So=7
BL_05: BOOL; (*Systemblinkausgang 0,5Hz*)
BL_1: BOOL; (*Systemblinkausgang 1Hz*)
TR_05: BOOL; (*Systemtriggerausgang 0,5Hz*)
TR_1: BOOL; (*Systemtriggerausgang 1Hz*)
TR_Tag: BOOL; (*Systemtrigger Tagesbeginn 0:00Uhr zB für tägliche Speicheraugaben*)
Anlauf: BOOL; (*Anlaufmerker, nur im 1.Zykl =1 *)
Meldesystem: BOOL; (* Meldesystem nach Verzögerung aktiv schalten*)
END_VAR
VAR
SollDatZeit: DATE_AND_TIME;// SollZeit zur RTC
DatZeitUTC: DATE_AND_TIME;// aktuelles Datum+Zeit von RTC
SetFlanke:R_TRIG; //Flanke für Uhr stellen
readFlanke:R_TRIG;//Flanke für Uhr lesen
T_sync: TON; (*Uhrzeitsyncronisationstimer*)
Takt_05: TON; (*Takt 0,5Hz*)
Trig05: R_TRIG; (*Flankentrigger 0,5Hz*)
Trig1: R_TRIG; (*Flankentrigger 1Hz*)
TR_Tag_beginnt: R_TRIG; (*Flankentrigger Tagesbeginn 0:00Uhr*)
HM1: BOOL := 0; (*Hilfsmerker für Anlauferkennung*)
VerzMeldungen: TON; //Timer für verzögertes einschalten des Alarmsystems
Zeitstring: STRING[22]; //Arbeitsvariable
Schaltjahr: BOOL;
//Hilfsvariablen zum Uhr stellen
setYear: WORD;
setMonth: BYTE;
setDay: BYTE;
setHour: BYTE;
setMinute: BYTE;
setSecond: BYTE;
count: INT;
SET_DT: DATE_AND_TIME;
END_VAR
VAR CONSTANT
Synctime: TIME := t#24h; (*Syncronisationszeit mit RTC*)
END_VAR
#####################################################################
(*Anlaufmerker nur im 1. Zykl true steuern*)
//der Startmerker wird beim "prepare to run-Event" durch Start(FUN) auf TRUE gesetzt
Anlauf:= ZeitDB.Startmerker; //Startmerker aus EventTask Start
ZeitDB.Startmerker:= FALSE;
(*Zeitsyncronisation alle 24h*)
T_sync(IN:= NOT T_sync.Q, PT:= Synctime, Q=> , ET=> );
(*Zeit aus der RTC holen*)
DatZeitUTC:= SysRtcGetTime(T_sync.Q OR SetFlanke.Q); //akt.UTC ausgeben
//Korrektur Zeitverschiebung
CASE OffsetUTC OF
-2: DatZeit:= DWORD_TO_DT(DT_TO_DWORD(DatZeitUTC) - 7200);
-1: DatZeit:= DWORD_TO_DT(DT_TO_DWORD(DatZeitUTC) - 3600);
0: DatZeit:= DatZeitUTC;
1: DatZeit:= DWORD_TO_DT(DT_TO_DWORD(DatZeitUTC) + 3600);
2: DatZeit:= DWORD_TO_DT(DT_TO_DWORD(DatZeitUTC) + 7200);
3: DatZeit:= DWORD_TO_DT(DT_TO_DWORD(DatZeitUTC) + 10800);
ELSE DatZeit:= DatZeitUTC;
END_CASE
Zeitdaten.DatZeit:= DatZeit ; //akt Zeit in DB schreiben
Uhrzeit:=DT_TO_TOD(DatZeit); //akt Uhrzeit als TIME OF DAY ausgeben
Zeitdaten.Uhrzeit:=Uhrzeit; //akt Uhrzeit in DB schreiben
(*Zeit in einen String schreiben und DT# entfernen für Visu*)
strDatZeit:= DELETE(DT_TO_STRING(DatZeit),3,1);
Zeitdaten.strDatZeit:=strDatZeit;
(*Zeit aufsplitten und in DB schreiben*)
Zeitstring:= strDatZeit;
//Jahr, Monat, Tag extrahieren
Jahr:= STRING_TO_WORD(LEFT(STR:= Zeitstring, SIZE:= 4));
Zeitstring:= (DELETE(STR:= Zeitstring, LEN:= 5, POS:= 1));
Monat:= STRING_TO_BYTE(LEFT(STR:= Zeitstring, SIZE:= 2));
Zeitstring:= (DELETE(STR:= Zeitstring, LEN:= 3, POS:= 1));
Tag:= STRING_TO_BYTE(LEFT(STR:= Zeitstring, SIZE:= 2));
//Zeit extrahieren
Stunde:= DWORD_TO_BYTE(TOD_TO_DWORD(Uhrzeit) / 3600000);
Minute:= DWORD_TO_BYTE(TOD_TO_DWORD(Uhrzeit) / 60000 - TOD_TO_DWORD(Uhrzeit) / 3600000 * 60);
Sekunde:= REAL_TO_BYTE(DWORD_TO_REAL(TOD_TO_DWORD(Uhrzeit) - TOD_TO_DWORD(Uhrzeit)/60000 * 60000) / 1000.0);
//Wochentag ermitteln
Wochentag := DWORD_TO_BYTE((DATE_TO_DWORD(DT_TO_DATE(DatZeit)) / 86400 + 3) MOD 7) + 1;
//Daten in DB schreiben
Zeitdaten.Jahr:= Jahr;
Zeitdaten.Monat:= Monat;
Zeitdaten.Tag:= Tag;
Zeitdaten.Stunde:= Stunde;
Zeitdaten.Minute:= Minute;
Zeitdaten.Sekunde:= Sekunde;
Zeitdaten.Wochentag:= Wochentag;
(*Trigger Tagesbeginn *)
TR_Tag_beginnt(CLK:= (Uhrzeit < TOD#00:00:01 ), Q=> TR_Tag);
(*Systemzeitsignale erzeugen (Blinker und Flanken)*)
Takt_05(IN:= NOT Takt_05.Q, PT:= T#1S, Q=> , ET=> );
IF NOT BL_05 AND Takt_05.Q THEN
BL_05:=TRUE;
ELSIF (BL_05 AND Takt_05.Q) THEN
BL_05:=FALSE;
END_IF
IF Takt_05.ET > T#500MS THEN
BL_1:=TRUE;
ELSE
BL_1:=FALSE;
END_IF
Trig05(CLK:= BL_05, Q=> TR_05);
Trig1(CLK:= BL_1, Q=> TR_1);
//Freigabe Alarmmeldungen 30s nach Anlauf
VerzMeldungen(IN:= NOT Anlauf, PT:= T#30S, Q=> Meldesystem, ET=> );
(*akt Zeit per Befehl auf die Sollzeit kopieren*)
readFlanke(CLK:= lesen, Q=> );
IF readFlanke.Q THEN
Zeitdaten.setJahr:= Zeitdaten.Jahr;
Zeitdaten.setMonat:= Zeitdaten.Monat;
Zeitdaten.setTag:= Zeitdaten.Tag;
Zeitdaten.setStunde:= Zeitdaten.Stunde;
Zeitdaten.setMinute:= Zeitdaten.Minute;
Zeitdaten.setSekunde:= Zeitdaten.Sekunde;
END_IF
(*SollZeit per Befehl in die RTC schreiben(Uhr stellen)*)
SetFlanke(CLK:= schreiben, Q=> );
IF SetFlanke.Q THEN
(*Sollzeit zu DT zusammenstellen und in die RTC schreiben*)
ACT_setDT();
END_IF
Dazu die Action ACT_setDT
(hier musste ich mir ein paar Zeilen aus der OSCAT leihen um den Monat in die DT zu bekommen)
(*Sollzeit zu DT zusammenstellen und in die RTC schreiben*)
//Daten aus DB holen
setYear:= Zeitdaten.setJahr;
setMonth:= Zeitdaten.setMonat;
setDay:= Zeitdaten.setTag;
setHour:= Zeitdaten.setStunde;
setMinute:= Zeitdaten.setMinute;
setSecond:= Zeitdaten.setSekunde;
// DT erstellen
IF setMonth > 2 THEN
count := (setMonth - 1) * 30;
IF setMonth > 7 THEN count := count + SHR(setMonth - 3,1); ELSE count := count + SHR(setMonth - 4,1); END_IF;
(* chech for leap year and add one day if true *)
IF SHL(setYear,14) = 0 THEN count := count + 1; END_IF;
ELSE
count := (setMonth - 1) * 31;
END_IF;
SET_DT := DWORD_TO_DT(((INT_TO_DWORD(count + setDay - 1) + SHR(WORD_TO_DWORD(setYear) * 1461 - 2878169, 2)) * 86400) + INT_TO_DWORD(setSecond) + INT_TO_DWORD(setMinute) * 60 + INT_TO_DWORD(setHour) * 3600);
//DT in RTC schreiben
SysRtcSetTime(ActDateAndTime:= SET_DT);
Und der zugehörige Zeit-DB, deklariert in der GVL
VAR_GLOBAL
ZeitDB: strZeit; //ZeitDatenbaustein für FB Uhr
END_VAR
TYPE strZeit :
(* Struktur des Zeit Datenbaustein
Achtung, Änderung nur mit Abgleich IN_OUT (FB)Uhr*)
STRUCT
Startmerker:BOOL;//Systemmerker zur Erkennung Anlauf (Nicht beschreiben!!!)
DatZeit: DATE_AND_TIME;// aktuelles Datum+Zeit von RTC
strDatZeit: STRING(22);// aktuelles Datum+Zeit als String
Uhrzeit: TOD;// aktuelle Tageszeit von RTC
Jahr: WORD; //Istwert Jahr
Monat: BYTE; //Istwert Monat
Tag: BYTE; //Istwert Tag
Stunde: BYTE; //Istwert Stunde
Minute: BYTE; //Istwert Minute
Sekunde: BYTE; //Istwert Sekunde
Wochentag: BYTE; //Istwert Wochentag
setJahr: WORD; //Sollwert Jahr
setMonat: BYTE; //Sollwert Monat
setTag: BYTE; //Sollwert Tag
setStunde: BYTE; //Sollwert Stunde
setMinute: BYTE; //Sollwert Minute
setSekunde: BYTE; //Sollwert Sekunde
END_STRUCT
END_TYPE