# Programmieraufgaben: Bitnummern aus Word als Zahlen und Blinkcode ausgeben



## PN/DP (20 Mai 2019)

Hallo Leute,

der letzte Programmierwettbwerb ist schon wieder lange her... Heute habe ich mal zwei Aufgaben.

Der Preis einer kleinen Maschine ist schon sehr knapp kalkuliert und nun soll noch eine detaillierte Störungsausgabe erfolgen (nicht nur einfaches Blinken einer Störungsleuchte). An der Anzeigestelle herrschen sehr nasse Umgebungsbedingungen, daher soll da kein HMI-Panel verbaut werden. Da kommt die Idee, eine einfache Digital/Zahlenanzeige einzusetzen. Es gibt 16 verschiedene Störungs-Alarme (Bitmeldungen), die als Bits in einem Word liegen.

*Aufgabe (1)*
Von einem 16-Bit-Word sollen im Abstand von 1 Sekunde zyklisch nacheinander die Nummern der gesetzten Bits (1-Bits) als Zahlen 1..16 ausgegeben werden, oder 0 wenn kein Bit gesetzt ist. Die Bit-Nummern sollen 1 (LSB) bis 16 (MSB) sein.

Beispiel:
16#6182 = 2#01100001_10000010 --> 2 8 9 14 15 2 8 9 ... soll ausgegeben werden

Während der Ausgabe kann sich der Inhalt/Wert des Word ändern, 1-Bits kommen dazu oder gehen.

Erstelle einen FB oder FC mit dem Störungsword als Eingang und dem Anzeigewert (INT oder BYTE) als Ausgang. Der Ausgabetakt (alle 1 Sekunde 1 Zyklus lang TRUE) liegt schon vor und kann dem Baustein als Eingang mitgegeben werden oder der Baustein wird nur im Takt aufgerufen.
Wie schon erwähnt: an der Anzeigestelle ist es sehr nass - das überlebt auch die einfache Digitalanzeige leider nicht lange.

*Aufgabe (2)*
Nun sollen die Bitnummern zyklisch als Blinkcode mit einer Störungsleuchte (LED) ausgegeben werden.

```
Nr  Code  Pulsfolge | Nr  Code  Pulsfolge      | Nr   Code  Pulsfolge
--------------------+--------------------------+--------------------------
 1 =  [COLOR="#0000FF"]1[/COLOR] = 1 kurz    |  6 = [COLOR="#0000FF"]L1[/COLOR] = 1 lang 1 kurz  | 11 = [COLOR="#0000FF"]LL1[/COLOR] = 2 lang 1 kurz
 2 =  [COLOR="#0000FF"]2[/COLOR] = 2 kurz    |  7 = [COLOR="#0000FF"]L2[/COLOR] = 1 lang 2 kurz  | 12 = [COLOR="#0000FF"]LL2[/COLOR] = 2 lang 2 kurz
 3 =  [COLOR="#0000FF"]3[/COLOR] = 3 kurz    |  8 = [COLOR="#0000FF"]L3[/COLOR] = 1 lang 3 kurz  | 13 = [COLOR="#0000FF"]LL3[/COLOR] = 2 lang 3 kurz
 4 =  [COLOR="#0000FF"]4[/COLOR] = 4 kurz    |  9 = [COLOR="#0000FF"]L4[/COLOR] = 1 lang 4 kurz  | 14 = [COLOR="#0000FF"]LL4[/COLOR] = 2 lang 4 kurz
 5 =  [COLOR="#0000FF"]5[/COLOR] = 5 kurz    | 10 = [COLOR="#0000FF"]L5[/COLOR] = 1 lang 5 kurz  | 15 = [COLOR="#0000FF"]LL5[/COLOR] = 2 lang 5 kurz

16 = Dauerblinken 1 Hz, die anderen Codes nicht ausgeben
```
Die Störung 16 (MSB des Word) ist speziell, die soll ein Dauerblinken mit 1 Hz bewirken solange das Bit gesetzt ist.
Zur Erkennung, wann ein neuer Blinkcode anfängt, soll eine Pause von ca. 2 Sekunden sein.

Basistakt: 2 Hz (250 ms Ein + 250 ms Aus)
lang: 750 ms Ein + 250 ms Aus
kurz: 250 ms Ein + 250 ms Aus
Zwischenpause: >= 2 s Aus

Beispiel für Ausgabe der Blinkcodes L2 + LL3:

```
2Hz  |*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|_|*|..

OUT  |*****|_|*|_|*|_________|*****|_|*****|_|*|_|*|_|*|_________|*...
```
Die Takte 1 Hz und 2 Hz liegen aus dem Taktmerkerbyte der CPU global vor und dürfen bei Bedarf direkt angesprochen werden: 
M3.5 = 1 Hz, M3.3 = 2 Hz (Puls-Pausen-Verhältnis: 1:1) Achtung: die Bits im Taktmerkerbyte ändern sich asynchron zum OB1.


Viel Spaß bei der Lösungsfindung!

Harald


----------



## vollmi (21 Mai 2019)

interessante Aufgabe ^^ 
Wie stellst du dir das vor. Soll man lösungsansätze gleich posten mit warnung?

Wie willst du den ersten Baustein. Soll der nach einem kompletten Zyklus nochmal 0 anzeigen um den start eines neuen Zyklus anzuzeigen, oder soll einfach wieder beim ersten Fehlercode wieder angefangen werden?


----------



## PN/DP (21 Mai 2019)

vollmi schrieb:


> interessante Aufgabe ^^
> Wie stellst du dir das vor. Soll man lösungsansätze gleich posten mit warnung?


Hallo vollmi

vielleicht erstmal Lösungs-Ideen/Algorithmen andeuten? Falls schon fertig: Die verwendete Programmiersprache der Lösung nennen, wie groß ist der Arbeitsspeicherbedarf des Codebausteins...

Ich würde mal sagen, ab heute abend 20:00 CEST können lesbare Komplettlösungen gepostet werden. Oder hat da jemand was dagegen/einen besseren Vorschlag? (Eigentlich wollte ich die Aufgaben schon vor dem Wochenende stellen, hatte da aber leider nur wenig Zeit..)
Wer unbedingt als Erster eine Komplettlösung posten will, könnte schon vorher eine Zip-Datei mit Passwort posten...

Bitte keine kompletten Megabyte-großen TIA-Projekte hochladen  sondern möglichst nur exportierte Quellen.

Ich habe z.B. für Aufgabe 1 eine Lösung mit 16 (oder 18 ) S7-AWL-Anweisungen, Arbeitsspeicherbedarf als FC oder FB: 84 bis 90 Bytes.
Für Aufgabe 2 habe ich eine (mehrfach bewährte, aber nicht optimierte) Lösung mit S7-Zählern mit 8 Netzwerken in FUP + AWL gemischt, mal sehen ob ich die noch verbessert bekomme.




vollmi schrieb:


> Wie willst du den ersten Baustein. Soll der nach einem kompletten Zyklus nochmal 0 anzeigen um den start eines neuen Zyklus anzuzeigen, oder soll einfach wieder beim ersten Fehlercode wieder angefangen werden?


Solange Bits gesetzt sind, alle Fehlercodes (Nummern der Bits) immer wieder ausgeben: 2 - 8 - 9 - 14 - 15 - 2 - 8 - 9 - 14 - 15 - 2 - 8 - 9 - ... 
0 nur anzeigen, wenn keine Bits gesetzt sind (wenn keine Störung anliegt).

PS: falls ein Bit nur kürzer als der komplette Ausgabezyklus gesetzt ist, dann wird es nicht ausgegeben. (Das kommt in der Praxis nicht vor. Fehlerbits bleiben gesetzt bis der Bediener die Fehler quittiert. Quittiert er schon vor der Ausgabe, dann wollte er die Fehlernummer nicht wissen.  )
Wer will, kann ja die Aufgabe erweitern auf Ausgabe auch sehr kurz anliegender Fehlernummern (allerdings würden die dann nur ein einziges Mal ausgegeben, und ob da gerade jemand hinschaut?)

Harald


----------



## vollmi (21 Mai 2019)

PN/DP schrieb:


> Ich habe z.B. für Aufgabe 1 eine Lösung mit 16 (oder 18 ) S7-AWL-Anweisungen, Arbeitsspeicherbedarf als FC oder FB: 84 bis 90 Bytes.



Okay das hört sich superoptimiert an.

Ich habe mich für SCL entschieden und bin schon bei 217Byte Arbeitsspeicher auf TIA15.1
Zum adressieren nutze ich scatter und lege das Wort auf ein Array of bool um direkt durch das wort zu loopen. Außerdem ein GOTO. Um das GOTO zu umgehen müsste ich IMHO den Code wesentlich grösser machen. Das würde mich interessieren wie die Profis das lösen.

Aufgabe zwei bereitet mir noch kopfzerbrechen. dafür hat die Mittagspause jetzt nicht gereicht. Aber ich brauch da glaub ich mindestens 3 indexzähler.

mfg René


----------



## PN/DP (21 Mai 2019)

vollmi schrieb:


> Okay das hört sich superoptimiert an.


Es gibt da so uralte Basic Algorithmen zur Ermittlung des niedrigsten 1-Bits und dessen Nummer (war auch schon öfters Thema hier im Forum) ... 

Aufgabe 2 habe ich lieber übersichtlich/verständlich mit 2 S7-Zählern gelöst.

Harald


----------



## vollmi (22 Mai 2019)

Da sich noch keiner gemeldet hat. Möchte ich mal für die erste Aufgabe was zur diskussion stellen.
Beide Funktionen müssen im Takt aufgerufen werden, in dem die Fehleranzeige wechseln soll. Kann z.B. auch in nem Weckalarm passieren.

Die erste Funktion wie gesagt mit Gatter.
Vorteil: gut lesbar
Nachteil: muss entsprechend umgebaut werden, sollte man das auf anderen Maschinen nutzen wollen.

```
FUNCTION "Fehlercodes_1" : Byte
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      wState : Word;   // Störungswort
   END_VAR


   VAR_IN_OUT 
      Zykl : SInt;
   END_VAR


   VAR_TEMP 
      stState : Array[0..15] of Bool;
      status : Int;
      i : Int;
   END_VAR




BEGIN
	SCATTER(IN:=#wState,
	        OUT=>#stState);
	#Fehlercodes_1 := 0; // Rückgabewert muss unbedingt belegt werden
	Mid:
	(*überprüfen welches Bit im array ansteht*)    
	
	WHILE #Zykl < 16 DO
	    IF #stState[#Zykl] = TRUE THEN
	        #Fehlercodes_1 := SINT_TO_BYTE(#Zykl + 1);
	        #Zykl := #Zykl + 1;
	        EXIT;
	    ELSE
	        #Zykl := #Zykl + 1;
	    END_IF;
	END_WHILE;
	
	(* // überprüfen ob ein zyklus abgeschlossen wurde aber ein alarm ansteht
	wenn ja Zyklus neu starten ohne fehlercode 0 auszugeben*)
	IF #Zykl >= 16 THEN
	    #Zykl := 0;
	    IF #wState <> 0 THEN
	        GOTO Mid;
	    END_IF;
	END_IF;
END_FUNCTION
```

Die andere Version wäre diese hier.

Ist vermutlich etwas schwieriger zu verstehen, aber dafür müsste es mit wirklich kleinen Anpassungen auf auf Codesys und anderen Systemen laufen.

```
FUNCTION "Fehlercodes" : Byte
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      wState : Word;   // Störungswort
   END_VAR


   VAR_IN_OUT 
      Zykl : SInt;
   END_VAR


   VAR_TEMP 
      status : Int;
      i : Int;
   END_VAR




BEGIN
	#Fehlercodes := 0; // Rückgabewert muss unbedingt belegt werden
	Mid:
	(*überprüfen welches Bit im array ansteht*)    
	
	WHILE #Zykl < 16 DO
	    IF (SHR_WORD(IN:=#wState, N:=SINT_TO_ULINT(#Zykl)) AND 1) = 1 THEN
	        #Fehlercodes := SINT_TO_BYTE(#Zykl + 1);
	        #Zykl := #Zykl + 1;
	        EXIT;
	    ELSE
	        #Zykl := #Zykl + 1;
	    END_IF;
	END_WHILE;
	
	(* // überprüfen ob ein zyklus abgeschlossen wurde aber ein alarm ansteht
	wenn ja Zyklus neu starten ohne fehlercode 0 auszugeben*)
	IF #Zykl >= 16 THEN
	    #Zykl := 0;
	    IF #wState <> 0 THEN
	        GOTO Mid;
	    END_IF;
	END_IF;
END_FUNCTION
```


----------



## PN/DP (22 Mai 2019)

Ich habe Deine beiden Programme nicht getestet, ich würde aber sagen, sie erfüllen die Aufgabenstellung der Aufgabe 1 

Harald


----------



## Heinileini (22 Mai 2019)

. . . und ich möchte mal ein "Embedding" der ersten Aufgabe in die zweite Aufgabe zur Diskussion stellen.
Aufgabe 1 soll in einem festen Takt nacheinander die Nummern der anstehenden Meldungen ausgeben.
Aufgabe 2 soll in einem variablen Takt (die Ausgabe von Meldung 1 dauert 1,5 s . . . Meldung LL5 dauert 5,5 s) diese Nummern codiert anzeigen.

Ich neige dazu, die Aufrufe von Aufgabenlöser 1 an den sich "zufällig" ergebenden Takt des Aufgabenlösers 2 zu koppeln, sprich das Vorlegen der nächsten MeldungsNr immer nur dann zu durchlaufen, 
wenn der CodierBlinker einen kompletten Code samt Pausenzeichen ausgegeben hat.
Sonst muss es doch automatisch zu einem StroboskopEffekt kommen, der dafür sorgt, dass bestimmte Meldungen zu häufig und andere zu selten ausgegeben werden!?

KopfGrimmen bereitet mir auch der 1Hz-Takt für Meldung 16 bzw. die damit verbundenen Pausen. Habe deshalb für Meldung 16 zwei von der Aufgabenstellung abweichende Varianten ausgeknobelt:
1. die UnMorse-Variante, die - noch ziemlich nah an der Vorgabe - "Pakete" von 4 Impulsen im 1Hz-Takt und dann die "Zwischen-2-Codes-Pause" sendet und
2. die Gag-Variante, die "SOS" (3 kurz, MiniPause, 3 lang, MiniPause, 3 kurz, Pause) sendet. 

Mein all-in-one SCL-Entwurf kennt nur einen Zähler, und zwar bei der Ermittlung der MeldungsNr, da diese z.Z. (noch?) auf "UrAltBasicTricks" verzichtet.
Über die Länge des Programms in Anzahl Byte wage ich nicht zu spekulieren. Vielleicht habe ich wider Erwarten doch noch den Nerv und die Zeit, eine AWL-Variante davon zu produzieren . . .

Gruss, Heinileini

Wie immer - garantiert ungetestet (Edit: jetzt nicht mehr - Harald war so lieb! Danke!):

```
// Aufruf alle 250 ms          
FUNCTION_BLOCK SOS          
VAR_INPUT          
    InWort : WORD ;    
END_VAR          
VAR_TEMP          
    Zahl :   INT ;    
    Z :      INT ;    
    T :      WORD ;    
END_VAR          
VAR               
    F :      WORD   := 0 ;
    B :      DWORD  := 0 ;
END_VAR          
VAR_OUTPUT      
    Out :    BOOL ;    
END_VAR          
BEGIN          
IF [COLOR=#0000cd]DWORD_TO_DINT(B)[/COLOR] < 8 THEN // wenn Ausgabe fertig          
    // - - - - < BitMuster-Meldungen --> Zahl > - - - -         
    IF [COLOR=#0000cd](InWort AND 16#8000) <> 0[/COLOR] THEN // wenn Fehler 16         
        Zahl := 16 ;        
        F := 0 ; // Puffer leeren        
    ELSE // wenn Fehler 1..15 oder kein Fehler         
        IF F = 0 THEN F := InWort ; [COLOR=#0000cd]END_IF ;[/COLOR] // wenn Puffer leer, dann aktualisieren        
        Zahl := 0 ;        
        T := F AND INT_TO_WORD(-WORD_TO_INT(F)) ; // LS1B isolieren        
        F := F XOR T ; // und im Puffer löschen        
        WHILE T<>0 DO // Bit-Position (1..16) ermitteln         
            T := SHR(IN:=T, N:=1) ;       
            Zahl := Zahl +1 ;       
        END_WHILE ;        
    END_IF ;         
    // - - - < Ende BitMuster-Meldungen --> Zahl > - - -         
    IF Zahl < 1 THEN // wenn kein Fehler         
        B := 16#E ;        
    ELSIF Zahl > 15 THEN // wenn FehlerNr = 16, dann SonderBlinken         
        B := 16#E153BB95; // Ausgabe "SOS"        
        // B := 16#383333; // alternativ: Ausgabe 4er Paket 1 Hz        
    ELSE // sonst "NormalCode"         
        Z := Zahl - 1 ;        
        B := 16#1C15500 ; // BitMuster STOP + 5 * "kurz"        
        B := SHR(IN:=B, N:=(4 - Z MOD 5) * 2) ; // Anz. "kurz" ggfs reduz.       
        B := B OR 16#77 ; // BitMuster 2 * "lang" hinzu        
        B := SHR(IN:=B, N:=(2 - Z / 5) * 4) ; // Anz. "lang" ggfs reduz.
    END_IF ;         
END_IF ;          
Out := (B AND 1) = 1 ; // ungerade --> Lampe AN          
B := SHR(IN:=B, N:=1) ;           
END_FUNCTION_BLOCK
```


----------



## vollmi (22 Mai 2019)

Heinileini schrieb:


> Ich neige dazu, die Aufrufe von Aufgabenlöser 1 an den sich "zufällig" ergebenden Takt des Aufgabenlösers 2 zu koppeln, sprich das Vorlegen der nächsten MeldungsNr immer nur dann zu durchlaufen,
> wenn der CodierBlinker einen kompletten Code samt Pausenzeichen ausgegeben hat.
> Sonst muss es doch automatisch zu einem StroboskopEffekt kommen, der dafür sorgt, dass bestimmte Meldungen zu häufig und andere zu selten ausgegeben werden!?



Das sehe ich auch so. Ich denke man müsste mit dem start jedes neuen Blinkcodes einmal den codgenerator laufen lassen. Und ansonsten nicht.
Sonst müsste der Codegenerator ja im Takt weiterlaufen. dann kann man aber grad sogut gleich die Bits des statusworts mit dem Codierblinker auswerten und ihn dann die Blinkcodes von da abgeleitet machen.
Der Codegenerator für so für den Blinkgenerator gar keinen Sinn machen.


----------



## PN/DP (22 Mai 2019)

Heinileini schrieb:


> Ich neige dazu, die Aufrufe von Aufgabenlöser 1 an den sich "zufällig" ergebenden Takt des Aufgabenlösers 2 zu koppeln, sprich das Vorlegen der nächsten MeldungsNr immer nur dann zu durchlaufen,
> wenn der CodierBlinker einen kompletten Code samt Pausenzeichen ausgegeben hat.


Richtig! 
Bei der Aufgabe 2 steht auch nicht, daß die Codes im 1 Hz Rhythmus ausgegeben werden sollen. Der nächste Code soll ausgegeben werden, wenn die Ausgabe eines Codes (Blinkmusters) samt Nach-Pause beendet ist. Dann soll wieder (einmal) der Baustein (bzw. Code) von Aufgabe 1 aufgerufen werden um die nächste Bitnummer zu ermitteln.

Haralds


----------



## PN/DP (22 Mai 2019)

Heinileini schrieb:


> KopfGrimmen bereitet mir auch der 1Hz-Takt für Meldung 16 bzw. die damit verbundenen Pausen. Habe deshalb für Meldung 16 zwei von der Aufgabenstellung abweichende Varianten ausgeknobelt:
> 1. die UnMorse-Variante, die - noch ziemlich nah an der Vorgabe - "Pakete" von 4 Impulsen im 1Hz-Takt und dann die "Zwischen-2-Codes-Pause" sendet und
> 2. die Gag-Variante, die "SOS" (3 kurz, MiniPause, 3 lang, MiniPause, 3 kurz, Pause) sendet.


Man könnte testen ob das höchste Bit im Word gesetzt ist und dann die ganze Codeermittlung "überspringen". Und erst am Lampenausgang einen Oder-Zweig zufügen:
Q_Lampe := ( Pulsausgabe_läuft AND Bit_aus_Pulsausgabe ) OR ( NOT(Pulsausgabe_läuft) AND Fehler_16_aktiv AND Takt_1Hz ) OR Lampentest;

Je nachdem, wie die Pulsausgabe programmiert ist, würde sich ohne Sonderbehandlung des Fehler 16 möglicherweise die Ausgabe "3x lang 1x kurz" (LLL1) ergeben - das wollte ich aber nicht  Ein Fehlerbit ungenutzt lassen wollte ich allerdings auch nicht. In einer früheren Variante meiner Pulsausgabe hatte ich das Dauerblinken für einen eventuellen Programmierfehler genutzt, wenn zwar eine "Sammelstörung" aktiv ist, aber kein spezifisches Meldebit 1..15 gesetzt ist. Früher hatte ich auch noch eine Priorisierung in der Blinkausgabe, daß immer nur der neueste gekommene Fehlercode ausgegeben wurde und erst nach Quittierung alle noch anstehenden Fehlercodes reihum ausgegeben wurden, doch das würde hier den Umfang der Aufgabe sprengen. Sowas kann sich ja bei Bedarf jeder selber in das Grundgerüst einbauen 

Die "SOS" Variante ist gar keine schlechte Idee - Personen-Notruf integriert 

Harald


----------



## Heinileini (22 Mai 2019)

PN/DP schrieb:


> Je nachdem, wie die Pulsausgabe programmiert ist, würde sich ohne Sonderbehandlung des Fehler 16 möglicherweise die Ausgabe "3x lang 1x kurz" (LLL1) ergeben - das wollte ich aber nicht


Dachte ich mir schon, Harald.
Ich wollte mir eine Sonderbehandlung ersparen und habe deshalb das Schema Code + Pause beibehalten.
Als Code kam mir 1 * 1Hz-Impuls + Pause zu sparsam vor und mehr als 4 * 1Hz-Impuls + Pause zu langatmig.
Kann aber durch Anpassen einer Konstante leicht auf 2 * 1Hz-Impuls + Pause oder 3 * 1Hz-Impuls + Pause umstellen (oder auch auf "SOS").
SOS finde ich besser, da es wohl eher die Aufmerksamkeit auf sich zieht. Zu 4 * 1Hz-Impuls + Pause habe ich mich durchgerungen, weil ich damit die Aufgabenstellung nicht ganz so sehr auf den Kopf stelle.

Gruss, Heinileini

PS:
Habe vorhin in #8 den Code hinzugefügt. Obwohl ich noch ein klein wenig SparPotenzial sehe, möchte ich eigentlich nicht weiter komprimieren - die Lesbarkeit leidet auch so schon an Schwindsucht.


----------



## PN/DP (22 Mai 2019)

Heinileini schrieb:


> Habe vorhin in #8 den Code hinzugefügt. Obwohl ich noch ein klein wenig SparPotenzial sehe, möchte ich eigentlich nicht weiter komprimieren - die Lesbarkeit leidet auch so schon an Schwindsucht.


Ja. Um Deine Variante der Bitmuster-Erzeugung/Zusammenstellung nachzuvollziehen, muß ich mein Gehirn auf Binär-Mode umstellen, um die Einsen und Nullen zu sehen 
(In meiner Lösung arbeite ich mit visuell verständlicheren Codierungen (z.B. 16#14 für "L4", 16#23 für "LL3") und mathematischen Umrechnungen anstatt dem Zusammenbasteln Bit für Bit.)

Deine Lösung funktioniert. Allerdings nicht so schön: Du machst nur einmal einen Schnappschuß vom InWort und gibst dann alle Bits aus die drin waren, auch wenn zwischendurch das InWort auf 0 geht.
In der Praxis: Es stehen mehrere Störungen an, die werden zyklisch ausgegeben - der Bediener quittiert - alle Störungen gehen - es werden aber schlimmstenfalls noch fast eine Minute lang Blinkcodes ausgegeben, obwohl gar keine Störung mehr anliegt. Das soll besser sofort nach Ende der Ausgabe des aktuellen Codes aufhören. Das Programm sollte bei jedem Ausgabestart bzw. -ende das InWort neu auswerten. Dafür müsste sich das Programm allerdings anders merken, welche Codes schon ausgegeben wurden... es könnte sich die Nummer oder einfacher die Bitmaske des zuletzt ausgegebenen Bits merken.

PS: Bei der Zeile "IF F = 0 THEN F := InWort ; // wenn Puffer leer, dann aktualisieren" fehlt ein "END_IF;"
Das kommt vermutlich daher, weil Du das Programm nur mit Zettel und Stift entwirfst (und dafür gar keinen Compiler brauchst). Hut ab! 
	

	
	
		
		

		
			





Harald


----------



## Heinileini (23 Mai 2019)

Danke Harald für's Testen, die Kritik und die Blumen!

Bitmuster contra "visuell" ist sicherlich GeschmacksSache - Bitmuster finde ich gar nicht sooo "unvisuell" und visuell habe ich es auch gerne!
Hatte mir in Excel ein Blättchen gemacht, um die WirkungsWeise zu veranschaulichen - habe sogar bei Deiner |*|_|*|__________|*. . .-Darstellung Anleihe genommen (allderdings "seitenverkehrt").
War so schön und dann habe ich auf Seitenansicht gewechselt und damit Excel in den Tiefschlaf versenkt. Nichts rührte sich mehr, mit TaskManager abgebrochen und alles war futsch, obwohl ich zwischendurch manuell hin und wieder abgespeichert hatte und Excel automatisch immer wieder mal - wie ich doch diese spontanen Aufhängereien bei Excel liebe!

Zum Thema "nicht schön": sehe ich genau so . . . und habe aus derselben Überlegung heraus auch das Neuaufsetzen auf den aktuellen Stand der aktiven Meldungen eingebaut, allerdings an einer anderen Stelle: wenn Meldung 16 aktiv ist, wird gelöscht und sobald Meldung 16 verschwindet, wird aktualisiert. Das Thema "aktualisieren nach jeder abgeschlossenen CodeBlinkerei" habe ich allerdings nach oberflächlicher Betrachtung gleich beiseite geschoben und nicht weiterverfolgt. Es erschien mir einfach zu aufwändig und sogar nutzlos, nach einer sinnvollen Strategie Ausschau zu halten, ohne konkreteres über die paar Meldungen und deren Bedeutung/Auftreten/Verschwinden zu wissen - buchen wir es unter "Bequemlichkeit" ab. 

Zettel und Stift können überhaupt nix für das Fehlen des END_IF;! Die Ursache ist, dass ich in SCL sehr ungeübt bin und noch zu sehr in VBA denke - da wäre es so richtig gewesen (warum auch immer!?). 

Gruss, Heinileini


----------



## PN/DP (23 Mai 2019)

Heinileini schrieb:


> Das Thema "aktualisieren nach jeder abgeschlossenen CodeBlinkerei" habe ich allerdings nach oberflächlicher Betrachtung gleich beiseite geschoben und nicht weiterverfolgt. Es erschien mir einfach zu aufwändig


Das ist eigentlich nicht aufwendig, wenn sich das Programm die zuletzt verwendete Bitmaske merkt (bei Dir: T), und beim Suchen des nächsten 1-Bits diese Bitmaske links-verschiebt und auf das neu aktualisierte InWort anwendet.

Harald


----------



## PN/DP (23 Mai 2019)

Optimier/Sparpotenzial: die Schleife zum Suchen des nächsten 1-Bit eliminieren (dann braucht man auch kein GOTO mehr)

Um die Nummer des auszugebenden Bits zu ermitteln, muß man nicht in einer Schleife abzählen wieviele Bits dahinter kommen.
Man kann sich auch das REAL/FLOAT-Format IEEE 754 zu Nutze machen, wo das Dezimalkomma bis zum höchstwertigen 1-Bit verschoben wird und der Exponent angibt, um wieviele Stellen das Komma verschoben ist. 

Dafür zuerst dafür sorgen daß das auszugebende Bit das höchstwertige Bit im WORD ist. Das geht am leichtesten, wenn man das Bit zum einzigen Bit im WORD macht - was dadurch gleichzeitg das höchstwertige Bit ist. Dann das WORD in REAL umwandeln und im Exponent nachschauen, um wieviele Stellen das Dezimalkomma verschoben ist. Dieser Trick basiert auf diesem "uralten" Algorithmus:

Bit Twiddling Hacks: Zählen der rechts aufeinanderfolgenden 0-Bits


> *Count the consecutive zero bits (trailing) on the right by casting to a float*
> 
> 
> ```
> ...


In S7-AWL für 16 Bit INT übersetzt:

```
L   #v     // (16 Bit INT)
PUSH
NEGI
UW         // (v & -v)
DTR        // in REAL wandeln
SRD 23     // Exponent holen
+   -127   // Bias abziehen
T   #r     // ergibt Bit-Position 0..15, oder -127 wenn v = 0
```
In S7-SCL für 16 Bit INT übersetzt:

```
f := DINT_TO_REAL(WORD_TO_DINT(v AND INT_TO_WORD( -WORD_TO_INT(v) )));
r := DWORD_TO_INT(SHR(IN:=REAL_TO_DWORD(f), N:=23)) - 127;
```
In den Codesys/ST-Dialekten geht das so nicht, da muß man das ungewollte Konvertieren zwischen (D)WORD und REAL mit Pointern (oder ab V3 geht auch UNION) umgehen (so wie im obigen C-Code).


Dieser Trick ist auch bei Siemens bekannt (*):
Maskieren vom niederwertigsten oder höchstwertigen gesetzten Bit in WORD und DWORD Variablen


> Die Bits 30 bis 23 enthalten den Exponent+127.
> Um die Position des ersten gesetzten Bits zu ermitteln, müssen Sie die Bits mit dem Exponenten um 23 nach rechts verschieben. Subtrahieren Sie davon 127, so erhalten Sie in AKKU1-LL die gesuchte Position.



(*) Der FC96 ENCO aus der Step7 Standard Bibliothek verwendet allerdings den nicht so effizienten trivialen Algorithmus mit dem Abzählen der 0-Bits in einer Schleife, und weist auch noch in jedem Schleifendurchlauf dem Rückgabewert Zwischenwerte zu!! :roll:


Weitere nicht allgemein geläufige, aber hier hilfreiche Tricks:

```
[B]Das niederwertigste 1-Bit isolieren[/B],
so daß es zum einzigen und gleichzeitig höchstwertigen Bit wird
z.B. 2#1001_0110_1000_0000 --> 2#0000_0000_1000_0000

Formel:  [COLOR="#0000FF"]wLeastBitOnly := wInWord AND -wInWord;[/COLOR]
S7-SCL:  [COLOR="#0000FF"]wLeastBitOnly := wInWord AND INT_TO_WORD(-WORD_TO_INT(wInWord));[/COLOR]
S7-AWL:  [COLOR="#0000FF"]PUSH + NEGI + UW[/COLOR]
```


```
[B]Alle höherwertigen Bits vor dem einzigen 1-Bit bis zum höchstwertigen Bit auf 1 setzen[/B]
(z.B. für Bitmaske für niedrigstes und alle höheren Bits)
z.B. 2#0000_0000_1000_0000 --> 2#1111_1111_1000_0000

Formel:  [COLOR="#0000FF"]wHighMask := -wLeastBitOnly;[/COLOR]
S7-SCL:  [COLOR="#0000FF"]wHighMask := INT_TO_WORD(-WORD_TO_INT(wLeastBitOnly));[/COLOR]
S7-AWL:  [COLOR="#0000FF"]NEGI[/COLOR]
```

Harald


----------



## PN/DP (23 Mai 2019)

Knobelt noch jemand an den Aufgaben?
Gibt es tatsächlich nicht mehr verschiedene Ansätze wie man die Aufgaben lösen könnte?

Harald


----------



## PN/DP (24 Mai 2019)

Meine Lösung der Aufgabe (1)
in AWL (Codegröße 84 Byte)

```
FUNCTION "FC_ENCO_NEXT" : INT
TITLE =Nummer des nächsten 1-Bits (1..16) oder 0 ausgeben
VAR_INPUT
  IN_Word : WORD ;
END_VAR
VAR_IN_OUT
  lastBit : WORD ;
END_VAR

[COLOR="#008000"]//nächstes 1-Bit ermitteln, dazu bereits ausgegebene niedrigere Bits ausblenden[/COLOR]
      L     #IN_Word
      L     #lastBit            [COLOR="#008000"]//zuletzt ausgegebenes Bit (Maske mit nur einem 1-Bit)[/COLOR]
      NEGI                      [COLOR="#008000"]//Bitmaske bis Bit 15 erweitern[/COLOR]
      SLW   1                   [COLOR="#008000"]//Maske höher schieben für zuletzt ausgegebenes Bit ausblenden[/COLOR]
      UW                        [COLOR="#008000"]//tiefere Bits ausmaskieren[/COLOR]
      SPN   MLBO                [COLOR="#008000"]//#IN_Word enthält höhere 1-Bits als #lastBit[/COLOR]
[COLOR="#008000"]//wenn da 0 rauskam, wieder alle Bits zulassen ("maskieren" volle Maske)[/COLOR]
      POP                       [COLOR="#008000"]//#IN_Word zurück in AKKU1[/COLOR]
[COLOR="#008000"]//niedrigstes 1-Bit isolieren (LeastBitOnly)[/COLOR]
MLBO: PUSH  
      NEGI  
      UW                        [COLOR="#008000"]//LeastBitOnly := (IN & -IN)[/COLOR]
      SPZ   MBNO                [COLOR="#008000"]//kein 1-Bit enthalten? --> 0 ausgeben[/COLOR]
      T     #lastBit            [COLOR="#008000"]//für nächste Ausgabe merken[/COLOR]
[COLOR="#008000"]//Nummer niedrigstes 1-Bit ausgeben[/COLOR]
      DTR   
      SRD   23                  [COLOR="#008000"]//REAL-Exponent holen[/COLOR]
      +     -126                [COLOR="#008000"]//Bias 127 abziehen, + 1 für Result 1..16[/COLOR]
MBNO: T     #RET_VAL            [COLOR="#008000"]//Ausgabe: 1..16, oder 0 bei Sprung[/COLOR]
```
und das gleiche in SCL (Codegröße 182 Byte)

```
FUNCTION "FC_ENCO_NEXT_SCL" : INT
TITLE ='Nummer des nächsten 1-Bits (1..16) oder 0 ausgeben'
VAR_INPUT
  IN_Word : WORD ;
END_VAR
VAR_IN_OUT
  lastBit : WORD ;
END_VAR
VAR_TEMP
  HBits : WORD ;
  LeastBitOnly : WORD;
  f : REAL;
END_VAR

  HBits := DWORD_TO_WORD(SHL(IN:=INT_TO_DWORD( -WORD_TO_INT(lastBit) ), N:=1)) AND IN_Word;
  IF HBits = 0 THEN HBits := IN_Word; END_IF; [COLOR="#008000"]//keine höheren 1-Bits? wieder alle Bits verwenden[/COLOR]

  LeastBitOnly := HBits AND INT_TO_WORD( -WORD_TO_INT(HBits) );
  IF LeastBitOnly = 0 THEN
    FC_ENCO_NEXT_SCL := 0; [COLOR="#008000"]//Result 0[/COLOR]
  ELSE
    lastBit := LeastBitOnly; [COLOR="#008000"]//für nächste Ausgabe merken[/COLOR]
    f := DINT_TO_REAL(WORD_TO_DINT(LeastBitOnly)); [COLOR="#008000"]//WORD in REAL konvertieren[/COLOR]
    [COLOR="#008000"]//REAL-Exponent holen und Bias 127 abziehen, + 1 für Result 1..16[/COLOR]
    FC_ENCO_NEXT_SCL := DWORD_TO_INT(SHR(IN:=REAL_TO_DWORD(f), N:=23)) - 127 + 1;
  END_IF;
END_FUNCTION
```
(Der S7-SCL-Compiler übersetzt SHL/SHR nur mit DWORD gut. SHL/SHR mit WORD oder BYTE führt zu sehr aufgeblähter Codegröße.)

Harald


----------



## Heinileini (24 Mai 2019)

PN/DP schrieb:


> 1. Knobelt noch jemand an den Aufgaben?
> 2. Gibt es tatsächlich nicht mehr verschiedene Ansätze wie man die Aufgaben lösen könnte?


Zu 1.: Habe die KnobelPhase jetzt beendet, nach dem Einbau der AltlastenAnzeigeUnterdrückung und der REALisierung der BitNrn-Ermittlung (letzteres ohne Rücksicht auf Lesbarkeit - bin gespannt, ob wenigstens der Compiler mich versteht ;o).
Zu 2.: Meinen Ansatz bezüglich 'all-in-one'-Lösung (Aufgabe 1 in Aufgabe 2 integriert) und BlinkGenerator habe ich jedoch beibehalten.

```
// Aufruf alle 250 ms          
FUNCTION_BLOCK SOS          
VAR_INPUT          
    InWort : WORD ;    
END_VAR          
VAR_TEMP          
    Z :      INT ;    
    T :      WORD ;    
    F :      WORD ;    
END_VAR          
VAR               
    M :      WORD   := 0 ;
    B :      DWORD  := 0 ;
END_VAR          
VAR_OUTPUT      
    Out :    BOOL ;    
END_VAR          
BEGIN          
IF DWORD_TO_DINT(B) < 8 THEN // wenn Ausgabe fertig          
    // - - - - < MeldungsBitMuster --> MeldungsNr > - - - -         
    F := InWort AND M ; IF F = 0 THEN F := InWort ; END_IF ; // maskiert bzw. komplett einlesen
    IF (F AND 16#8000) <> 0 THEN // wenn Fehler 16         
        Z := 16 ;        
        M := 0 ;        
    ELSE // wenn Fehler 1..15 oder kein Fehler         
        T := F AND INT_TO_WORD(-WORD_TO_INT(F)) ; // LS1B isolieren        
        M := T XOR INT_TO_WORD(-WORD_TO_INT(T)) ; // MaskenBildner
        IF T= 0 THEN Z := 0 ; ELSE Z := DWORD_TO_INT(SHR(IN:=REAL_TO_DWORD(DINT_TO_REAL(WORD_TO_DINT(T))), N:=23)) - 126 ; END_IF ; // BitPosition + 1
    END_IF ;         
    // - - - < Ende MeldungsBitMuster --> MeldungsNr > - - -         
    IF Z < 1 THEN // wenn kein Fehler         
        B := 16#E ;        
    ELSIF Z > 15 THEN // wenn FehlerNr = 16, dann SonderBlinken         
        B := 16#E153BB95 ; // Ausgabe "SOS"; alternativ: 'B := 16#383333 ;' für 4er Paket 1 Hz              
    ELSE // sonst "NormalCode" 1..5, L1..L5, LL1..LL5
        Z := Z - 1 ;        
        B := 16#1C15500 ; // BitMuster STOP + 5 * "kurz"        
        B := SHR(IN:=B, N:=(4 - Z MOD 5) * 2) ; // Anzahl "kurz" ggfs reduzieren       
        B := B OR 16#77 ; // BitMuster 2 * "lang" hinzu        
        B := SHR(IN:=B, N:=(2 - Z / 5) * 4) ; // Anzahl "lang" ggfs reduzieren
    END_IF ;         
END_IF ;          
Out := (B AND 1) = 1 ; // ungerade --> Lampe AN          
B := SHR(IN:=B, N:=1) ;           
END_FUNCTION_BLOCK
```
Vielsten Dank an Dich, Harald, für die Aufgaben, Anregungen und kritischen Anmerkungen! 

Gruss, Heinileini

Edit: Nachtrag, um die BlinkAusgabe per SchiebeRegister zu veranschaulichen:


----------



## Heinileini (27 Mai 2019)

Sooo, habe doch nochmal geknobelt - an der ZählerVariante von Aufgabe 2.
Dabei ist mir jetzt aufgefallen, dass ich in den vorausgegangenen Beiträgen Müll geschrieben habe, was die Längen der Pausen zwischen 2 Codes betrifft.
Ich habe von 2s- bzw. 2,5 s-Pausen geschrieben, aber es war jeweils halb so lange Pausen. 
Total unreflektiert bin ich auf die Unmassstäblichkeit in der Aufgabenstellung hereingefallen:


> Zwischenpause: >= 2 s Aus
> Beispiel für Ausgabe der Blinkcodes L2 + LL3:
> Code:
> 
> ...



Na ja, ich erspare mir, rückwirkend die Beiträge zu überarbeiten und bitte um Nachsicht.
Hier meine Variante, die versucht, sich eng an die Aufgabenstellung zu halten (Keine SOS-Ausgabe für Meldung 16. Aber für den Anteil von "Aufgabe 1" belasse ich es dabei, dass alle anderen Meldungen unterdrückt werden, solange Meldung 16 ansteht.):

```
FUNCTION "FC_AUFG1" : INT // Aufruf in FB_AUFG2; V3: BitNr mit REAL; ohne AltLasten; Mldg 16 hat Vorrang  
TITLE ='Nächste 1-Bit-Nr (1..16) oder 0 ausgeben'
VAR_INPUT
    WD_BIMU : WORD ; // Wort mit 16 BitMeldungen
END_VAR
VAR_IN_OUT
    WD_MASK : WORD ; // Maske für nächste Auswertung
END_VAR
VAR_TEMP
    Z       : INT  ; // RetVal
    T       : WORD ; // LS1B
    F       : WORD ; // eingelesenes (maskiertes) BitMuster
END_VAR
BEGIN
// - - - - < MeldungsBitMuster --> MeldungsNr > - - - -         
F := WD_BIMU AND WD_MASK; IF F = 0 THEN F := WD_BIMU ; END_IF ; // maskiert bzw. komplett einlesen
IF (F AND 16#8000) <> 0 THEN // wenn Fehler 16 (Vorrang)
    Z := 16 ;
    WD_MASK := 0 ;   // Maske löschen für Neubeginn, sobald Meldung 16 verschwindet
ELSE // wenn Fehler 1..15 oder kein Fehler
    T := F AND INT_TO_WORD(-WORD_TO_INT(F)) ;       // LS1B isolieren        
    WD_MASK := T XOR INT_TO_WORD(-WORD_TO_INT(T)) ; // MaskenBildner
    IF T = 0 THEN Z := 0 ; ELSE Z := DWORD_TO_INT(SHR(IN:=REAL_TO_DWORD(DINT_TO_REAL(WORD_TO_DINT(T))), N:=23)) - 126 ; END_IF ; // BitPositions# + 1
END_IF ;
FC_AUFG1 := Z ;
END_FUNCTION
```
Und mit "ausartend" vielen Zählern ("Stücker 4") - diesmal habe ich wenigstens meine VBA-Variante davon getestet:

```
FUNCTION_BLOCK FB_AUFG2 // Aufruf alle 250 ms; V1: 4 Zähler, Meldung 16: Dauer-1Hz
VAR_INPUT
    WD_ERRBITS : WORD          ; // zum Durchreichen an FC_AUFG1
END_VAR
VAR_OUTPUT
    BL_BLNK    : BOOL          ; // MeldeLeuchte
END_VAR
VAR
    WD_AFG1    : WORD := 0     ; // externes Gedächtnis für FC_AUFG1
    HZ         : INT  := 0     ; // Zähler 1Hz  (500 ms + 500 ms)
    LI         : INT  := 0     ; // Zähler Langer Impuls (750 ms + 250 ms)
    KI         : INT  := 0     ; // Zähler Kurzer Impuls (250 ms + 250 ms)
    PF         : INT  := 0     ; // Zähler Pausen Impuls (2000 ms)     
    AC         : BOOL := FALSE ; // Meldung 16 stand an
END_VAR
VAR_TEMP
    X          : WORD          ; // Maske
    Z          : INT           ; // FehlerNr 1..16 bzw. 0
END_VAR
BEGIN
IF HZ > 0 THEN
    HZ := HZ - 1 ;               // Zähler "1Hz-ImpulsBildung" dekrementieren
    X := INT_TO_WORD(HZ) AND 3 ; // Maske für "1Hz"
ELSIF LI > 0 THEN
    LI := LI - 1 ;               // Zähler "LangImpulsBildung" dekrementieren
    X := INT_TO_WORD(LI) AND 3 ; // Maske für "LangImpuls"
ELSIF KI > 0 THEN
    KI := KI - 1 ;               // Zähler "KurzImpulsBildung" dekrementieren
    X := INT_TO_WORD(KI) AND 1 ; // Maske für "KurzImpuls"
ELSIF PF > 0 THEN
    PF := PF - 1 ;               // Zähler "PausenFüller" dekrementieren
    X := 0 ;                     // Maske unwirksam für "Pause" 
ELSE
    X := 0 ;                     // Maske unwirksam für "Pause" 
    Z := FC_AUFG1(WD_BIMU := WD_ERRBITS, WD_MASK := WD_AFG1) ; // <===<<<  
    IF Z > 15 THEN               // Meldung 16 im "SonderFormat"
        HZ := 3 ;
        AC := TRUE ;
    ELSIF AC THEN                // Pause nach Meldung 16 starten
        PF := 5 ;
        AC := FALSE ;
    ELSE                         // Meldungen 1..5, L1..L5, LL1..LL5 im "StandardFormat" vorbereiten
        Z := Z - 1 ;
        LI := ( Z / 5 ) * 4 ;
        KI := ( Z MOD 5 ) * 2 + 2 ; 
        PF := 6 ;
    END_IF ;
END_IF ;
BL_BLNK := X <> 0 ; // ImpulsAusgabe
END_FUNCTION_BLOCK
```
Gruss, Heinileini


----------



## Heinileini (24 Juni 2019)

PN/DP schrieb:


> Knobelt noch jemand an den Aufgaben?
> Gibt es tatsächlich nicht mehr verschiedene Ansätze wie man die Aufgaben lösen könnte?
> 
> Harald


Es ist nur eine vage Vermutung, aber könnte es sein, Harald, dass ganz viele ihre Lösungen noch ein paar Tage zurückhalten, um Dich am 30. mit einer geballten Ladung von Lösungen zu überraschen? 

Gruss, Heinileini


----------



## Heinileini (8 Juli 2019)

Moin Harald,
meine "vage Vermutung" war doch wohl sehr vom Wunschdenken geprägt. 
Wie schaut's aus? Damit Deine Aufgabenstellung nicht so ganz klanglos im Sande verläuft, könntest Du noch Deine (bisher nur angedeutete) Lösung von Aufgabe 2 vorstellen . . .

Gruss, Heinileini

PS:
. . . oder wenigstens den EinsendeSchluss verkünden?


----------



## exclis066 (18 Oktober 2022)

PN/DP schrieb:


> Meine Lösung der Aufgabe (1)
> in AWL (Codegröße 84 Byte)
> 
> ```
> ...


Hi Harald,

ich habe  FC_ENCO_NEXT_SCL mit 1Hz Taktmerker  unter PLCSIM getestet, Baustein funktioniert aber  zB aktive Bits 1,5,9.
Ich kriege nicht  Abstand von 1 Sekunde zyklisch nacheinander sondern  1, 5, 9 in zufällige Zeiten.

Gruß
exclis 
​


----------



## PN/DP (18 Oktober 2022)

Dann rufst Du wohl den FC_ENCO_NEXT_SCL nicht im Abstand von genau 1s auf? Wie erzeugst Du den Aufruf-Takt oder wie sieht der Aufruf aus?

Harald


----------



## exclis066 (18 Oktober 2022)

ich nutze System Taktmerker Clock_1Hz und eine Änderung  #FC_ENCO_NEXT_SCL := #LeastBitOnly;



IF "Clock_1Hz"  THEN

        #tempWord := "FC_ENCO_NEXT_SCL"(IN_Word := #statWord, lastBit := #Word);

END_IF;


----------



## PN/DP (18 Oktober 2022)

Da fehlt noch eine Flankenerkennung, damit der Baustein alle 1s nur genau einmal aufgerufen wird. Der System Taktmerker Clock_1Hz ist 0.5s lang TRUE (und 0.5s lang FALSE).

Harald


----------



## exclis066 (18 Oktober 2022)

Danke Harald jetzt geht.


#FP_Start := "Clock_1Hz" AND NOT #Merker_Impulse;
#Merker_Impulse := "Clock_1Hz";

IF #FP_Start THEN

#tempWord := "FC_ENCO_NEXT_SCL"(IN_Word := #statWord, lastBit := #Word);

END_IF;


----------



## PN/DP (18 Oktober 2022)

exclis066 schrieb:


> #FP_Start := "Clock_1Hz" AND NOT #Merker_Impulse;
> #Merker_Impulse := "Clock_1Hz";


Normalerweise funktioniert dieser Code gut. Du musst hier aber eine Besonderheit der CPU Taktmerker und damit "Clock_1Hz" beachten: die Taktmerker ändern sich *asynchron *irgendwann im OB1 ! Deshalb dürfen sie nicht mehrmals abgefragt und verknüpft werden, weil sie sich zwischen den Abfragen ändern können. Das wird zwar hier in den 2 Zeilen selten vorkommen, aber wir SPS-Programmierer wollen doch Software schreiben, die 100% immer funktioniert und nicht nur meistens  
Will man sicherstellen daß sich die Takt-Bits im OB1 nicht ändern, dann muß man sich einmal im OB1 eine Kopie der Taktmerker machen (quasi ein Prozessabbild für den OB1) und dann nur noch diese Kopien verwenden. Hier in Deinem Fall reicht eine Kopie direkt vor der Verwendung:

```
#Kopie _Clock   := "Clock_1Hz";
#FP_Start       := #Kopie_Clock AND NOT #Merker_Impulse;
#Merker_Impulse := #Kopie_Clock;

IF #FP_Start THEN
  #tempWord := "FC_ENCO_NEXT_SCL"(IN_Word := #statWord, lastBit := #Word);
END_IF;
```

Harald


----------



## exclis066 (18 Oktober 2022)

wieder etwas gelernt danke


----------



## dekuika (19 Oktober 2022)

PN/DP schrieb:


> Will man sicherstellen daß sich die Takt-Bits im OB1 nicht ändern, dann muß man sich einmal im OB1 eine Kopie der Taktmerker machen (quasi ein Prozessabbild für den OB1) und dann nur noch diese Kopien verwenden.


Danke für den Hinweis. Wusste ich natürlich noch nicht. So geht es bei mir besser.


```
U     "Clock_1Hz"
              FP    "Flankenmerker_1s"
              =     "Takt_1s"
```

Edit: Ich hatte mich schon gewundert, dass die Taktmerker in meinen Bausteinen sichtbar unregelmäßig kamen. Aber über die Eigenschaften der Taktmerker findet man recht wenig. Jetzt sieht es jedenfalls besser aus. Danke noch mal.


----------

