Dimmsequenzer Funktionsbaustein

pauly2072

Level-1
Beiträge
31
Reaktionspunkte
2
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,

über eine Schnittstelle KNX <-> WAGO 750-849 mit KNX-TP-Klemmen und zwei DMX-Klemmen (RS485) steuere ich 4-kanalige CV und CC DMX-Decoder für 24V-LED-Stripes, bzw. 350MA-LED-Downlights an.

4 LED-Stripes an einem 4-kanaligen CV DMX-Decoder sollen in einer Schleife auf- und abgedimmt werden. Dafür habe ich für den Gira Homeserver einen Logikbaustein geschrieben, der soweit auch funktioniert. Damit aber ein einigermaßen stufenloses Dimmen erfolgt, sendet der Baustein natürlich mehrere Dimmwerte pro Sekunde auf den KNX-Bus (von dort über die Wago KNX-Klemme auf die Wago DMX-Klemme). Das hat zur Folge dass der Gira Homeserver so ausgebremst wird, dass die anderen Ein- und Ausgänge des Homeservers nur mit sehr erheblicher Zeitverzögerung gelesen, bzw. ausgegeben werden. Also unbrauchbar.

Homeserver Logikbaustein:
16-11-_2017_12-45-00.jpg

Da ich bei der Planung mit sowas schon gerechnet hatte, habe ich wohlweislich eine Wago mit in die KNX-Installation genommen und möchte die Dimmerfunktion nun vom Gira Homeserver auf die Wago "auslagern". Da meine Programmierkenntnisse mit Codesys ausbaufähig sind, bitte ich Euch um Unterstützung bei der Erstellung eines entsprechenden FBs in Codesys (v2.3).

Ausgang 1
- Der Ausgang dimmt zwischen 0 und 255 auf und ab innerhalb der dem FB zu übergebenden Zykluszeit in Sekunden.
- Per FB-Eingang wird parametriert, wieviele Dimmwerte pro Zykluszeit am Ausgang ausgegeben werden (z.B. bei einer Zykluszeit von 10 s 40 Dimmwerte (= 4 Dimmwerte pro Sekunde)
- Durch optionale Vorgaben eines min-Wertes und/oder eines max-Wertes kann der Dimmbereich eingeschränkt werden.

Ausgang 2
Wie Ausgang 1 nur 180° phasenverschoben, bzw. invertiert

Ausgang 3
Wie Ausgang 1 nur phasenverschoben um 0-50% der Zyklusdauer.
- Per FB-Eingang wird diese Verschiebung übergeben (0-50)

Ausgang 4
Wie Ausgang 3 nur zusätzlich 180° phasenverschoben, bzw. invertiert

16-11-_2017_12-46-05.jpg

Der Baustein soll bei "1" an E1 gestartet und bei "0" gestoppt werden. Bei Stopp sollen alle Ausgänge auf 0 gesetzt werden.
Wenn der Baustein läuft, soll "1" an A5 ausgegeben werden und "0", wenn er nicht läuft.

Nice to have: Per FB-Eingang wird parametriert (0/1), ob die Dimmkurven linear oder logarithmisch ausgegeben werden.

Eingänge:

- E1 1 bit (Start/Stop FB)
- E2 1 Byte (min-Wert 0-255)
- E3 1 Byte (max-Wert 0-255)
- E4 1 Byte (Zykluszeit in Sekunden 0-255)
- E5 1 Byte (Dimmwerte pro Zykluszeit 0-255)
- E6 1 Byte (Phasenverschiebung 0-50% A3 und A4)
[- E7 1 bit ("0" = lineare Dimmkurve / "1" = logarithmische Dimmkurve]

Ausgänge:

- A1 1 Byte (0-255)
- A2 1 Byte (0-255)
- A3 1 Byte (0-255)
- A4 1 Byte (0-255)
- A5 1 bit (FB läuft/läuft nicht)

Wäre superklasse, wenn mir hier jemand aufs Pferd helfen könnte. ;)

Herzlichen Dank im Voraus

Gruß

Peter
 

Anhänge

  • 16-11-_2017_12-45-00.jpg
    16-11-_2017_12-45-00.jpg
    38,7 KB · Aufrufe: 8
Und du erwartest jetzt, daß wir dir den kompletten Baustein eben so mal implementieren?

Oder hast du schonmal angefangen und bist irgendwo stecken geblieben?
 
Also ich würde folgendermaßen anfangen, erstmal einen Timer (TON), der mir sagt, wann ein neuer Dimmwert fällig ist, etwa so:
Code:
DimmTick(IN := NOT DimmTick.Q, PT := TO_TIME(Zykluszeit*1000/AnzahlDimmWerte));
dann ein R_TRIG, der auf DimmTick.Q reagiert, und den eigentlichen Code ausführt
Code:
IF DimmTrigger(DimmTick.Q) THEN
  DimmWert := DimmWert + Schrittweite * SEL(Phase, +1,-1);
END_IF

Wie du die Schrittweite und Phase bestimmst, sei dem geneigten Leser als leichte Übung überlassen. Auch die MIN und MAX Funktionen solltest du dir anschauen.
 
Zuletzt bearbeitet:
Herzlichen Dank Eddi, das ist schonmal ein guter Start für mich!

Das schwierigste habe ich auch schon: :D

Code:
FUNCTION_BLOCK fbl4LED_SEQUENZER    (* 4 x 1 Byte Output Auf- und Abdimmen *)
VAR_INPUT
 inpStartStop      :BOOL;       (* Baustein Start/Stop *)
 inpRangeMin     :BYTE;       (* Dimmbereich unterer Grenzwert 0-255 *)
 inpRangeMax    :BYTE;       (* Dimmbereich oberer Grenzwert 0-255 *)
 inpValPerCycle  :BYTE;       (* Dimmwerte pro Zykluszeit 0-255 *)
 inpCycle           :TIME;       (* Zykluszeit Dimmwert hoch und runter *)
 inpPhaseDelay  :BYTE;       (* Phasenverschiebung 0-50% A3 und A4 *)
 inpLinLog         :BOOL;       (* "0" = lineare Dimmkurve / "1" = logarithmische Dimmkurve *)
END_VAR
VAR_OUTPUT
 outValueLED1   :BYTE;       (* Ausgang 1 *)
 outValueLED2   :BYTE;       (* Ausgang 2 - Wie Ausgang 1 nur 180° phasenverschoben, bzw. invertiert*)
 outValueLED3   :BYTE;       (* Ausgang 3 - Wie Ausgang 1 nur phasenverschoben um 0-50% der Zyklusdauer*)
 outValueLED4   :BYTE;       (* Ausgang 4 - Wie Ausgang 3 nur zusätzlich 180° phasenverschoben, bzw. invertiert*)
END_VAR
 
Zuviel Werbung?
-> Hier kostenlos registrieren
So, dann werde ich am WE mal testen...

Bei Deaktivierung des FBs (inpStartStop=FALSE) sollen die Ausgänge auf Null gesetzt werden. Wenn ich aber jetzt anderweitig aus KNX diese LEDs ansteuere, setzt mir der FB diese immer gleich wieder auf 0. Wie kann ich das verhindern?

Code:
FUNCTION_BLOCK fbl4LED_SEQUENZER    (* 4 x 1 Byte Output Auf- und Abdimmen *)

VAR_INPUT
 inpStartStop   :BOOL;       (* Baustein Start/Stop - 1 bit *)
 inpRangeMin   :BYTE;       (* Dimmbereich unterer Grenzwert - 1 Byte 0-255 *)
 inpRangeMax   :BYTE;       (* Dimmbereich oberer Grenzwert - 1 Byte 0-255 *)
 inpValPerCycle  :WORD;      (* Anzahl Dimmwerte pro Zykluszeit - 16 Bit 0-65.535 *)
 inpCycle    :WORD;      (* Zykluszeit Dimmwert hoch und runter in Sekunden - 16 Bit 0-65.535 *)
 inpPhaseDelay  :BYTE;       (* Phasenverschiebung 0-50% A3 und A4 - 8 Bit 0-255 *)
 inpLinLog    :BOOL;       (* "0" = lineare Dimmkurve / "1" = logarithmische Dimmkurve  - 1 bit *)
END_VAR

VAR_OUTPUT
 outpValueLED1  :BYTE;       (* Ausgang 1 - 1 Byte 0-255 *)
 outpValueLED2  :BYTE;       (* Ausgang 2 - Wie Ausgang 1 nur 180° phasenverschoben, bzw. invertiert - 1 Byte 0-255 *)
 outpValueLED3  :BYTE;       (* Ausgang 3 - Wie Ausgang 1 nur phasenverschoben um 0-50% der Zyklusdauer - 1 Byte 0-255 *)
 outpValueLED4  :BYTE;       (* Ausgang 4 - Wie Ausgang 3 nur zusätzlich 180° phasenverschoben, bzw. invertiert - 1 Byte 0-255 *)
 outpFBRun   :BOOL;       (* Ausgang 5 - Baustein läuft - 1 bit *)
END_VAR

VAR
 TimerDimmStep  :TON;       (* Timer Berechnung und Ausgabe Dimmwert (Zeitabstand zwischen den Dimmschritten) *)
 DimmStepTime  :TIME;       (* Zeitabstand zwischen den Dimmschritten *)
 DimmVal1    :UINT;       (* Dimmwert *)
 StepVal1    :UINT;       (* Dimm-Schrittweite *)
 StepCount1   :UINT;       (* Durchlaufmerker *)
END_VAR

Code:
StepVal1:=512/WORD_TO_UINT(inpValPerCycle);

DimmStepTime:=UDINT_TO_TIME(WORD_TO_UDINT(inpCycle)*1000/WORD_TO_UDINT(inpValPerCycle));  (* Zykluszeit * 1.000 /  Anzahl Dimmwerte pro Zykluszeit = Zeitabstand zwischen den Dimmschritten in Millisekunden *)

TimerDimmStep(                           (* Timer Berechnung und Ausgabe Dimmwert (Zeitabstand zwischen den Dimmschritten) *)
   IN:=NOT TimerDimmStep.Q,
   PT:=DimmStepTime);

IF TimerDimmStep.Q AND StepCount1<256 THEN
Stepcount1:=Stepcount1+StepVal1;
DimmVal1:=Stepcount1;

ELSIF TimerDimmStep.Q AND StepCount1>255 AND StepCount1<512 THEN
Stepcount1:=Stepcount1+StepVal1;
DimmVal1:=511-Stepcount1;

ELSIF TimerDimmStep.Q AND StepCount1>=512 THEN
Stepcount1:=0;

END_IF

IF inpStartStop THEN
outpValueLED1:=UINT_TO_BYTE(DimmVal1);
outpValueLED2:=UINT_TO_BYTE(255-DimmVal1);
outpFBRun:=TRUE;

ELSE
outpValueLED1:=0;
outpValueLED2:=0;
outpFBRun:=FALSE;

END_IF
 
Du könntest den ELSE-Teil noch durch einen F_TRIG absichern, so daß das :=0 nur in dem Moment ausgeführt wird, wenn der StartStop ausgeschaltet wird.
Damit hättest du danach Freiraum, um die Werte anderweitig zu setzen
 
Moin!
Ich sehe da eine Struktur
Code:
IF Bed1 AND Bed2 THEN
    ...
ELSEIF Bed1 AND Bed3 THEN
    ...
ELSEIF Bed1 AND Bed4 THEN
    ...
END_IF
In solchen Fällen, wenn ein und dieselbe Bedingung (hier Bed1) für alle "Zweige" gelten soll, finde ich folgende Alternative übersichtlicher:
Code:
IF NOT(Bed1) THEN
    (* hier wird nichts getan, ausser dafür zu sorgen, dass die folgenden Abfragen gar nicht erst durchlaufen werden *)
ELSEIF Bed2 THEN
    ...
ELSEIF Bed3 THEN
    ...
ELSEIF Bed4 THEN
    ...
END_IF
Zugegeben, ist gewöhnungsbedürftig ...
Gruss, Heinileini
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich finde ja, etwas intuitiver wäre diese Ausfaktorisierung:
Code:
IF Bed1 THEN
  IF Bed2 THEN
    ...
  ELSIF Bed3 THEN
    ...
  ...
  END_IF
END_IF
Auch wenn man dadurch eine zusätzliche Einrückung hat...

Im übrigen, ich würde dir dringend empfehlen, die Einrückungen auch tatsächlich zu benutzen. Sonst hat man nur noch Spaghetticode, und am ende versteht nichtmal mehr der Autor selber, warum etwas gerade nicht so funktioniert wie es sollte.
 
Der Baustein läuft mal soweit.

Bevor ich mich nun an min/max und Phasenverschiebung mache, noch eine Frage:

Ich übergebe die Eingangsvariablen aus KNX:

inpRangeMin :BYTE; (* Dimmbereich unterer Grenzwert - 1 Byte 0-255 *)
inpRangeMax :BYTE; (* Dimmbereich oberer Grenzwert - 1 Byte 0-255 *)
inpValPerCycle :WORD; (* Anzahl Dimmwerte pro Zykluszeit - 16 Bit 0-65.535 *)
inpCycle :WORD; (* Zykluszeit Dimmwert hoch und runter in Sekunden - 16 Bit 0-65.535 *)
inpPhaseDelay :BYTE; (* Phasenverschiebung 0-50% A3 und A4 - 8 Bit 0-255 *)
inpLinLog :BOOL; (* "0" = lineare Dimmkurve / "1" = logarithmische Dimmkurve - 1 bit *)

Klappt soweit prima, aber wenn ich das SPS-Programm neu in die Wago lade, sind die Werte natürlich weg.
Wie mache ich die "remanent" bis zum evtl. Empfang eines neuen Wertes?
 
dafür gibt es die Schlüsselwörter "RETAIN" und "PERSISTENT", die aber bei jeder SPS subtil anders funktionieren. Da mußt du in der Dokumentation nachschauen
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe jetzt mal die entsprechenden Variablen in der Deklaration des FBs als RETAIN PERISTENT deklariert.

Aber nach einem Programmdownload sind die Werte trotzdem weg...

Code:
FUNCTION_BLOCK fblLED_SEQUENZER    (* 4 x 1 Byte Output Auf- und Abdimmen *)
VAR_INPUT
 inpStartStop   :BOOL;       (* Baustein Start/Stop - 1 bit *)
END_VAR
VAR_INPUT RETAIN PERSISTENT
 inpRangeMin   :BYTE;       (* Dimmbereich unterer Grenzwert - 1 Byte 0-255 *)
 inpRangeMax   :BYTE;       (* Dimmbereich oberer Grenzwert - 1 Byte 0-255 *)
 inpValPerCycle  :WORD;      (* Anzahl Dimmwerte pro Zykluszeit - 16 Bit 0-65.535 *)
 inpCycle    :WORD;      (* Zykluszeit Dimmwert hoch und runter in Sekunden - 16 Bit 0-65.535 *)
 inpPhaseDelay  :BYTE;       (* Phasenverschiebung 0-50% A3 und A4 - 8 Bit 0-255 *)
 inpLinLog    :BOOL;       (* "0" = lineare Dimmkurve / "1" = logarithmische Dimmkurve  - 1 bit *)
END_VAR
VAR_OUTPUT
 outpValueLED1  :BYTE;       (* Ausgang 1 - 1 Byte 0-255 *)
 outpValueLED2  :BYTE;       (* Ausgang 2 - Wie Ausgang 1 nur 180° phasenverschoben, bzw. invertiert - 1 Byte 0-255 *)
 outpValueLED3  :BYTE;       (* Ausgang 3 - Wie Ausgang 1 nur phasenverschoben um 0-50% der Zyklusdauer - 1 Byte 0-255 *)
 outpValueLED4  :BYTE;       (* Ausgang 4 - Wie Ausgang 3 nur zusätzlich 180° phasenverschoben, bzw. invertiert - 1 Byte 0-255 *)
 outpFBRun   :BOOL;       (* Ausgang 5 - Baustein läuft - 1 bit *)
END_VAR
VAR
 FTrigStop    :F_TRIG;      (* Setzen der Ausgänge auf :=0 nur in dem Moment, wenn der StartStop ausgeschaltet wird *)
 TimerDimmStep  :TON;       (* Timer Berechnung und Ausgabe Dimmwert (Zeitabstand zwischen den Dimmschritten) *)
 DimmStepTime  :TIME;       (* Zeitabstand zwischen den Dimmschritten *)
 DimmVal1    :UINT;       (* Dimmwert *)
 StepVal1    :UINT;       (* Dimm-Schrittweite *)
 StepCount1   :UINT;       (* Durchlaufmerker *)
END_VAR

Code:
(* Code *)
StepVal1:=512/WORD_TO_UINT(inpValPerCycle);
DimmStepTime:=UDINT_TO_TIME(WORD_TO_UDINT(inpCycle)*1000/WORD_TO_UDINT(inpValPerCycle));  (* Zykluszeit * 1.000 /  Anzahl Dimmwerte pro Zykluszeit = Zeitabstand zwischen den Dimmschritten in Millisekunden *)
TimerDimmStep(                           (* Timer Berechnung und Ausgabe Dimmwert (Zeitabstand zwischen den Dimmschritten) *)
   IN:=NOT TimerDimmStep.Q,
   PT:=DimmStepTime);
IF TimerDimmStep.Q AND StepCount1<256 THEN
 DimmVal1:=StepCount1;
 StepCount1:=StepCount1+StepVal1;
ELSIF TimerDimmStep.Q AND StepCount1>255 AND StepCount1<512 THEN
 DimmVal1:=511-StepCount1;
 StepCount1:=StepCount1+StepVal1;
ELSIF TimerDimmStep.Q AND StepCount1>=512 THEN
 StepCount1:=0;
END_IF
IF inpStartStop THEN
 outpValueLED1:=UINT_TO_BYTE(DimmVal1);
 outpValueLED2:=UINT_TO_BYTE(255-DimmVal1);
 outpFBRun:=TRUE;
ELSE outpFBRun:=FALSE;
END_IF
FTrigStop(CLK:= inpStartStop);
IF FTrigStop.Q THEN
 outpValueLED1:=0;
 outpValueLED2:=0;
END_IF
 
Zurück
Oben