Step 7 Bit in Word/ Double identifizieren und als Wert/ INT ausgeben

Pittie

Level-1
Beiträge
32
Reaktionspunkte
6
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo SPS- Experten,

ich möchte in einem Double herausfinden, welches Bit gesetzt ist und dies dann in einer INT- Zahl ausgeben.


Beispiel:

Ich betrachte den DB2.DBD300


DB2.DBX301.4 ist gesetzt, alle anderen 0. Jetzt möchte ich als INT- Ergebnis die 13 haben, da das 13. Bit des Double gesetzt ist.


Gibt es hier Funktion oder eine Lösung in AWL/ KOP, die leicht verständlich ist?

Konkrete Anwendung: Bei Sinumerik möchte ein Kunde die Alarmnummern (500xxx, 70xxxx usw.), als Ganzzahl übermittelt bekommen.

Vielen Dank!
 
In AWL würde ich auf die Schnelle sagen das DWord ins AR1 laden, eine Schleife mit LOOP programmieren, das aktuell angesprochene Bit auswerten, wenn das Bit gesetzt ist den Schleifenzähler in die Alarmnummer Variable schreiben und gegebenenfalls die Schleife abbrechen.

Wenn nicht gesetzt den Schleifenzähler erhöhen das AR1 um 0.1 erhöhen bis das komplette DWord durchlaufen ist.

Was soll passieren wenn mehrere Bits gesetzt sind ?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Eine S7-300/400 kennt keinen Datentyp "Double" - meinst Du ein DWORD?
In DBD300 ist DBX301.4 das Bit 20 - soll bei Dir wirklich 13 angezeigt werden?
Wie sollen die Bits nummeriert sein: 0..31 oder 1..32?
Was soll passieren, wenn mehrere Bits gesetzt sind?

Hier eine Funktion, die die Nummer des niederwertigsten 1-Bits ausgibt: 1..32 oder 0 wenn kein Bit gesetzt ist.
(wenn in dem (D)Word nur ein einziges Bit gesetzt ist, dann ist es gleichzeitig das niederwertigste und das höchstwertige gesetzte Bit)
Code:
FUNCTION LeastBitPos32 : INT
TITLE =Bitnummer des niederwertigsten 1-Bits
//Bitnummer des niederwertigsten 1-Bits: 1..32 / 0 wenn kein 1-Bit (IN = 0)
AUTHOR : PN_DP
VERSION : 1.0

VAR_INPUT
  IN : DWORD;
END_VAR
BEGIN
NETWORK
TITLE =
      L     0
      L     #IN
      ==D                  //#IN = 0 --> Result := 0
      SPB   MRET           //mit AKKU1 = 0
      L     DW#16#80000000 //weil S7-AWL kein DTR für UDINT hat
      ==D
      SPBN  MLBO
      L     32             //#IN = 16#80000000 --> Result := 32
      SPA   MRET
MLBO: POP                  //L #IN //aus AKKU2
      PUSH                 //für CPU mit 4 AKKUs und PLCSIM
      NEGD
      UD                   //LeastBitOnly := (IN & -IN)
      DTR
      SRD   23             //REAL-Exponent holen
      +     -126           //Bias 127 abziehen, + 1 für Result 1..32
MRET: T     #RET_VAL
END_FUNCTION

Harald
 
PS: Wie genau soll die Zählweise der Bits sein? Welches DBX ist Bit 1, welches DBX ist Bit 31? Wenn Du keine Standard-Zählweise verwenden kannst, dann geht vermutlich eine Lösung mit Schieben und Abzählen der Bitposition.

Harald
 
Wenn Du keine Standard-Zählweise verwenden kannst, dann geht vermutlich eine Lösung mit Schieben und Abzählen der Bitposition.
Aber nach "Deiner" Methode sollte es doch auch gehen, Harald.
Je nach dem, inwiefern vom "Standard" abgewichen wird, ggfs vorher die Reihenfolge der Bytes im Akku tauschen und/oder ggfs die ermittelte BitPosition von 33 bzw. von 32 subtrahieren.
An welche gemeinen Abweichungen vom "Standard" denkst Du denn so? Und warum sollte das Schieben und Abzählen dann eine einfachere oder "richtigere" Methode sein?

Gruss, Heinileini
 
Hab's getestet und läuft. Allerdings muss man zwei Dinge beachten. Erstens ersetzt Step 7 den Pointer P#DB2.dbx300.0 byte 4 in DB2.dbd300 was intern im Zähler zur Länge 1 führt. Habe deshalb einfach die Länge 5 angegeben (dahinter müssen also noch Daten sein) und zweitens einfach die Ausgabe um eins erhöhen, da bei gegebenem Beispiel 12 anstelle von 13 herauskommt (also 0 beim ersten Bit und -1 bei keinem Bit).
 
So, hab's nun an die Anforderungen angepasst. Word und DWord werden erkannt und kein Bit ergibt 0 anstelle von -1

Code:
FUNCTION FC 522 : VOID
TITLE =Convert the first found flag to decimal number
//Copyright Roth automation GmbH
//Free to use with this copyright notice
AUTHOR : 'E.Roth'
FAMILY : TopWeb
NAME : FlagToNo
VERSION : 1.0

VAR_INPUT
  StartAddress : ANY ; //Start address in Data base area with length
END_VAR
VAR_OUTPUT
  FoundNo : INT ; //Actual current transformer tap
END_VAR
VAR_TEMP
  TargetDB : WORD ; 
  TargetStart : DWORD ; 
  TargetLen : INT ; //Length
  TargetTyp : INT ; //Type
END_VAR
BEGIN
NETWORK
TITLE =
      L     P##StartAddress; 
      LAR1  ; 
      L     W [AR1,P#4.0]; //Load Data base
      T     #TargetDB; 
      L     W [AR1,P#8.0]; //Load starting adress
      T     #TargetStart; 
      L     W [AR1,P#2.0]; 
      T     #TargetLen; 
      L     B [AR1,P#1.0]; 
      T     #TargetTyp; 
      L     #TargetTyp; 
      L     4; //Word
      SPBN  m001; 
      L     2; 
      T     #TargetLen; 
m001: NOP   0; 
      L     #TargetTyp; 
      L     6; //Dword
      SPBN  m002; 
      L     4; 
      T     #TargetLen; 
m002: NOP   0; 

      AUF   DI [#TargetDB]; 
      L     #TargetLen; //maximal 17 taps
      L     8; 
      *I    ; 
      L     1; 
      -I    ; 
N:    LAR2  #TargetStart; //Start register
      +AR2  ; 
      U     DIX [AR2,P#0.0]; //Test each flag
      SPB   T; //RLO = TRUE = Found
      LOOP  N; 
      LAR2  #TargetStart; //Start register
      U     DIX [AR2,P#0.0]; 
      L     0; 
      SPB   T; 
      L     -1; 
T:    L     1; //add 1
      +I    ; 
      T     #FoundNo; 
END_FUNCTION
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin Funkey,
misstraust Du Haralds Vorschlag aus #3? Für BitMuster von bis zu 32 Bit funktioniert sie bestens und das ganz ohne explizite "Schleiferei" - die steckt nämlich unsichtbar und zeitsparend im Befehl DTR.
Du baust auf einem eher zeitraubenden Ansatz auf, der ausserdem sehr allgemein gehalten wurde, um auch (wesentlich) mehr als 32 Bit erschlagen zu können.
("Mit Spatzen auf Kanonen schiessen" sagte man früher dazu, bevor die Tierschützer so aktiv wurden ;o).

Eigentlich waren wir jetzt schon an einem Punkt angelangt, wo es nur noch darum ging, ob es irgendwelche gemeinen Varianten von BitNumerierungsVerfahren geben könnte, die mit ein Bisschen "drum herum" um Haralds Lösung nicht genauso gut funktionieren wie das Mitzählen des zeitaufwändigen, wiederholten Schiebens und Prüfens. Ich kann da keinen Unterschied erkennen, denn Haralds Lösung schiebt und prüft - versteckt im DTR-Befehl - genau so.
In AWL gibt's Befehle um Worte bzw. Bytes im Akku zu tauschen - als Mehraufwand kann man das kaum bezeichnen, um ggfs eine Um-Endianisierung zu realisieren.
Und sollte - warum auch immer - das höchstwertige Bit die kleinste Nr haben und das niederwertigste Bit die höchste Nr dann hilft eine Konstante, von der man das Ergebnis subtrahiert.

Gruss, Heinileini

PS:
Warum nutzt der FC einen InstanzDB?
 
Zuletzt bearbeitet:
Vielen Dank für eure Hilfe. Ich hab mir jetzt alles nochmal durch den Kopf gehen lassen- richtig einfach ist keine Lösung, dazu müsste ich, nachdem ich einen DINT- Wert der Alarmnummer erhalten habe, nochmal eine Tabelle / DB mit den "richtigen" Nummern (z.B. Alarm 10 = 700010 usw.) erstellen. Da ich nur ca. 100 Meldungen habe, habe ich jetzt diese direkt zugewiesen. Klar, der Experte rümpft die Nase, aber es funktioniert so und dauert auch nicht länger, als wenn ich es programmiere, wie hier gezeigt. Danke nochmal für die Hilfe! :s12:
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Vielen Dank für eure Hilfe. Ich hab mir jetzt alles nochmal durch den Kopf gehen lassen- richtig einfach ist keine Lösung, dazu müsste ich, nachdem ich einen DINT- Wert der Alarmnummer erhalten habe, nochmal eine Tabelle / DB mit den "richtigen" Nummern (z.B. Alarm 10 = 700010 usw.) erstellen. Da ich nur ca. 100 Meldungen habe, habe ich jetzt diese direkt zugewiesen. Klar, der Experte rümpft die Nase, aber es funktioniert so und dauert auch nicht länger, als wenn ich es programmiere, wie hier gezeigt. Danke nochmal für die Hilfe! :s12:


Da rümpft sicher keiner die Nase. Das ist die sauberste und wahrscheinlich performanteste Lösung. Ausserdem versteht sie jeder der nach dir arbeitet. 👍
 
So, jetzt verwende ich doch die Lösung von Funkey #8, da das gewünsche Ergebnis hier angezeigt wird. Die Lösung von Harald ergibt die Bits in falscher Reihenfolge, das kann man bestimmt verbessern, ich kann das leider nicht. Anbei nochmal ein Bild, was ich als Ergebnis brauche (und mit #8 + Addieren von 701731 erhalte).

Vielen Dank nochmal!
 

Anhänge

  • Alarm-no.png
    Alarm-no.png
    21,9 KB · Aufrufe: 15
Die Lösung von Harald ergibt die Bits in falscher Reihenfolge [...]
Anbei nochmal ein Bild, was ich als Ergebnis brauche
Da brauchst Du nur einmal die Byte-Reihenfolge im DWORD umkehren (Little Endian zu S7-üblichem Big Endian) - also einmal "TAD" zufügen:
Code:
FUNCTION LeastBitPos32LE : INT
TITLE =Bitnummer des niederwertigsten 1-Bits
//Bitnummer des niederwertigsten 1-Bits: 1..32 / 0 wenn kein 1-Bit (IN = 0)
//Zählweise: Bytes im Little Endian
AUTHOR : PN_DP
VERSION : 1.0

VAR_INPUT
  IN : DWORD;              //4 Bytes mit Byte-Reihenfolge "Little Endian"
END_VAR
BEGIN
NETWORK
TITLE =
      L     0
      L     #IN
      ==D                  //#IN = 0 --> Result := 0
      SPB   MRET           //mit AKKU1 = 0
      [COLOR="#0000FF"]TAD                  //BigEndian(#IN): Byte-Reihenfolge umkehren[/COLOR]
      L     DW#16#80000000 //weil S7-AWL kein DTR für UDINT hat
      ==D
      SPBN  MLBO
      L     32             //#IN = 16#80000000 --> Result := 32
      SPA   MRET
MLBO: POP                  //BigEndian(#IN) aus AKKU2
      PUSH                 //für CPU mit 4 AKKUs und PLCSIM
      NEGD
      UD                   //LeastBitOnly := (IN & -IN)
      DTR
      SRD   23             //REAL-Exponent holen
      +     -126           //Bias 127 abziehen, + 1 für Result 1..32
MRET: T     #RET_VAL
END_FUNCTION

(Wie bist Du eigentlich auf Deine Zählweise der Alarm-Nummern gekommen? S7-üblich wären die Alarme 1..8 in DBB321 und Alarme 9..16 in DBB320 und Alarme 17..24 in DBB323 und Alarme 25..32 in DBB322)

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Okay, prima, deine Funktion geht jetzt auch :s12:



(Wie bist Du eigentlich auf Deine Zählweise der Alarm-Nummern gekommen? S7-üblich wären die Alarme 1..8 in DBB321 und Alarme 9..16 in DBB320 und Alarme 17..24 in DBB323 und Alarme 25..32 in DBB322)

Ich bin unschuldig! Das ist der Meldungsaufruf bei Sinumerik seit gefühlten 30 Jahren.
DB2.DBB180 erzeugt Meldungen 700 000 bis 700 007
DB2.DBB181 erzeugt Meldungen 700 008 bis 700 015
DB2.DBB182 erzeugt Meldungen 700 016 bis 700 023
DB2.DBB183 erzeugt Meldungen 700 024 bis 700 031
usw.
 
Ahh, dann ist das die Bit-Nummerierung wie in einem Array of Bool.

Hier eine Funktion, die gleich noch die Alarm-Nummer des ersten Bits dazuaddiert:
Code:
FUNCTION BitField2AlarmNo : DINT
TITLE =Nummer des niederwertigsten 1-Bits des Bitfeldes
//Nummer des niederwertigsten 1-Bits: FirstNo..FirstNo+31
//oder 0 wenn kein 1-Bit (IN = 0)
//Zählweise: Bytes im Little Endian, Bits wie in Array [n..n+31] Of Bool
AUTHOR : PN_DP
VERSION : 1.0

VAR_INPUT
  IN : DWORD;              //4 Bytes eines Array [n..n+31] Of Bool
  FirstNo : DINT;          //Nummer des ersten Bits
END_VAR
BEGIN
NETWORK
TITLE =
      L     0
      L     #IN
      ==D                               //#IN = 0 --> Result := 0
      SPB   MRET                        //mit AKKU1 = 0
      TAD                               //BigEndian(#IN): Byte-Reihenfolge umkehren
      L     DW#16#80000000              //weil S7-AWL kein DTR für UDINT hat
      ==D
      SPBN  MLBO
      L     31                          //#IN = 16#80000000 --> Result := 31
      SPA   MOUT
MLBO: POP                               //BigEndian(#IN) aus AKKU2
      PUSH                              //für CPU mit 4 AKKUs und PLCSIM
      NEGD
      UD                                //LeastBitOnly := (IN & -IN)
      DTR
      SRD   23                          //REAL-Exponent holen
      +     -127                        //Bias 127 abziehen, für Result 0..31
MOUT: L     #FirstNo
      +D
MRET: T     #RET_VAL
END_FUNCTION

Harald
 
Zurück
Oben