TC3 Bitnummer als Dezimalzahl aus DB-Bitfeld in ST

Erikli

Level-1
Beiträge
14
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
TC3, Codesys

Hi,


ich habe eine Schrittkette mit 32 Schritten (0-31). Der Status des aktiven Schrittes steht in einem DB als Doppelwort. Ich benötige die aktive Schrittnummer als Dezimalzahl für eine Textliste in der Visu. Jetzt muss ich das in ST programmieren. Da ich wenig Erfahrung in ST habe, erhoffe ich mir ein Beispiel in ST. Vlt. gibts da ja auch einen Baustein in einer Lib? Wer hat sowas schon mal programmiert?

Danke und Grüße an Alle
 
Woran erkennst Du welcher Schritt der aktive ist? Ist das nicht schon eine Dezimalzahl? Oder verwendest Du Bool- oder Bit-Variablen als Schrittmerker? Was meinst Du mit "Status des aktiven Schrittes"?
Code:
Schrittnummer_Visu := interne_Schrittnummer;

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
ich benötige die Schrittnummer aus der Adresse

SchrittnummerDBX.Adresse
00.0
10.1
20.2
30.3
40.4
50.5
60.6
70.7
81.0
91.1
101.2
111.3
121.4
131.5
141.6
151.7
162.0
172.1
182.2
192.3
202.4
212.5
222.6
232.7
243.0
253.1
263.2
273.3
283.4
293.5
303.6
313.7
 
D.h., es ist immer nur 1 Bit von den 32 auf "1" und Du willst daraus eine Zahl 1..32 (oder 0..31) machen?
Die 32 Bit um 1 Position nach rechts schieben, bis das DoppelWort 0 wird und dabei die SchiebeVorgänge mitzählen.
Statt nach rechts zu schieben, kannst Du auch durch 2 dividieren, bis das Ergebnis 0 wird.
 
Zuletzt bearbeitet:
Du willst also die Nummer (Index) des niedrigsten 1-Bits ermitteln? Das kann man naiv abzählen beim bitweisen Rechts-Schieben oder diverse Algorithmen im Internet finden (z.B. hier)
Ergebnis: 1 .. 32 und 0 falls kein Bit gesetzt ist?

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich hatte an was mit Loop gedacht und dann n+1 bis true kommt. Bekomme dies mit meinen ST Kenntnissen nicht so ohne weiteres hin. Habe insgeheim gehofft, es gibt einen Baustein in einer lib. Ich programmier es über die Bitwertigkeit. Bsp.: IF VarDWord= 2097152 then Schrittnummer=13 End If;
obwohl mir die Lösung nicht gefällt. Trotzdem vielen Dank an Euch :)
 
Ich habe zwar noch nicht kapiert warum Doppelwort durch 2 aber ich werde dieser Theorie mal nachgehen.
1000 0000 0000 0000 / 2 = 0100 0000 0000 0000
0100 0000 0000 0000 / 2 = 0010 0000 0000 0000
0010 0000 0000 0000 / 2 = 0001 0000 0000 0000
u.s.w. . . .
0000 0000 0000 0100 / 2 = 0000 0000 0000 0010
0000 0000 0000 0010 / 2 = 0000 0000 0000 0001

0000 0000 0000 0001 / 2 = 0000 0000 0000 0000
Dein DoppelWort erst in DINT (besser UDINT bzw. LINT) konvertieren und dieses zum Dividieren benutzen.
Bei DINT dürfte es Probleme geben, wenn Bit 31 = 1 (negative Zahl!!)
Schieben dürfte also einfacher sein, dann aber nicht DINT, sondern DWORD.




 
Zuletzt bearbeitet:
Code:
SchleifenZähler := -1
Do While DoppelWort <> 0
    DoppelWort := DoppelWort / 2
    SchleifenZähler := SchleifenZähler + 1
    Loop
Wegen der Division funktioniert das nur richtig mit DoppelWort = UDINT oder wie in TC und Codesys vermutlich zulässig mit DWORD.
(außerdem ermittelt das das höchste 1-Bit (doch wenn garantiert nur 1 Bit gesetzt ist, dann ist das gleichzeitig das niedrigste 1-Bit))

In Step7 gibt es eine fertige Funktion (ENCO), ob es in Codesys oder TC oder vielleicht OSCAT was fertiges gibt, weiß ich nicht.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
LeastBitPos32

Effizient die Bitnummer des niederwertigsten gesetzten Bits eines DWORD berechnen durch Umwandeln in REAL und Auswerten des REAL-Exponenten.
Code:
FUNCTION LeastBitPos32 : INT
[COLOR="#008000"]//Bitnummer des niederwertigsten 1-Bits: [COLOR="#0000FF"]1..32 / 0[/COLOR] wenn kein 1-Bit (IN = 0)[/COLOR]
[COLOR="#008000"]//Version für Codesys, TwinCAT[/COLOR]
VAR_INPUT
  IN : DWORD;
END_VAR
VAR
  f : REAL;
  LBitOnly : DWORD;
  r : INT;
  pDW : POINTER TO DWORD;
END_VAR

  IF IN = 0 THEN
    r := [COLOR="#0000FF"]0[/COLOR];
  ELSIF IN = 16#8000_0000 THEN
    r := [COLOR="#0000FF"]31 + 1[/COLOR]; [COLOR="#008000"]//+ 1 für 1..32[/COLOR]
  ELSE
    LBitOnly := IN AND DINT_TO_DWORD(-DWORD_TO_DINT(IN)); [COLOR="#008000"]// (v & -v)[/COLOR]
    f := DINT_TO_REAL(DWORD_TO_DINT(LBitOnly)); [COLOR="#008000"]//f = (float)(v & -v)[/COLOR]

    [COLOR="#008000"]//r = (*(uint32_t *)&f >> 23) - 0x7f;[/COLOR]
    pDW := ADR(f);
    r := DWORD_TO_INT(SHR(IN:=pDW^, N:=23)) - 127 [COLOR="#0000FF"]+ 1[/COLOR]; [COLOR="#008000"]//REAL-Exponent holen, Bias 127 abziehen, + 1 für 1..32[/COLOR]
  END_IF;

  LeastBitPos32 := r;

END_FUNCTION

[COLOR="#008000"](*
Based on:
http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightFloatCast
Count the consecutive zero bits (trailing) on the right by casting to a float

unsigned int v;            // find the number of trailing zeros in v
int r;                     // the result goes here
float f = (float)(v & -v); // cast the least significant bit in v to a float
r = (*(uint32_t *)&f >> 23) - 0x7f;

Although this only takes about 6 operations, the time to convert an integer to a float 
can be high on some machines. 
The exponent of the 32-bit IEEE floating point representation is shifted down, and 
the bias is subtracted to give the position of the least significant 1 bit set in v. 
If v is zero, then the result is -127. 
*)[/COLOR]
TC3 kann vermutlich UNION, da könnte man die Übername des Bitmusters des REAL-Werts anstatt mit Pointer auch mit einer UNION machen.

Harald
 
Ich habe es so realisiert:

FUNCTION_BLOCK fbSchrittnummer
VAR_INPUT
bEN :BOOL; // Auswertung aktiver Schritt (Schritt ermitteln)
aSchrittkette :ARRAY [0..31] OF BOOL ; // Speicherbereich aktuelle Schritte // Anzahl Bits im Array
END_VAR
VAR_OUTPUT
uiSchrittnummer :UINT; // aktueller Schritt als Dezimalzahl
END_VAR
VAR
uin :UINT; //Schleifenzähler
END_VAR

uin := 0;
FOR uin:=0 TO 31 BY 1 DO
IF aSchrittkette[uin]=TRUE THEN
uiSchrittnummer:=uin;
EXIT;
END_IF;
END_FOR
 
Zurück
Oben