-> Hier kostenlos registrieren
Habs jetzt nochmal getestet und angepasst!
Ziffern 11/12, welche bei einem 10-Ziffern DINT eigentlich nur das '-' enthalten können dürfen
werden vorab geprüft und ein Fehler ausgelöst, wenn da was anders als ein 'F' drinsteht.
Das lässt sich dann ganz einfach durch auskommentieren der Schleife entfernen.
Hier geht es wirklich nur um die Fehleranzeige: Da ein 32-DINT nicht mehr als 10 Ziffern enhalten kann,
kann man getrost abbrechen. Die Frage ist nur will man einen Fehler haben, weil man das vorher nicht
geteset hat oder will man einfach die BCDs konvertiert haben!
Mittlerweile sehe ich die Stringversion tatsächlich als die einfachere Version an. Das macht
programmiertechnisch definitiv weniger Probleme.
Die Frage bei der Stringversion ist nur, was passiert, wenn man STRING_TO_DINT auf ungültige Werte
loslässt! Wie reagiert da Step7, TIA, CodeSys
// hier nochmal der überarbeitete Code
Ziffern 11/12, welche bei einem 10-Ziffern DINT eigentlich nur das '-' enthalten können dürfen
werden vorab geprüft und ein Fehler ausgelöst, wenn da was anders als ein 'F' drinsteht.
Das lässt sich dann ganz einfach durch auskommentieren der Schleife entfernen.
Hier geht es wirklich nur um die Fehleranzeige: Da ein 32-DINT nicht mehr als 10 Ziffern enhalten kann,
kann man getrost abbrechen. Die Frage ist nur will man einen Fehler haben, weil man das vorher nicht
geteset hat oder will man einfach die BCDs konvertiert haben!
Mittlerweile sehe ich die Stringversion tatsächlich als die einfachere Version an. Das macht
programmiertechnisch definitiv weniger Probleme.
Die Frage bei der Stringversion ist nur, was passiert, wenn man STRING_TO_DINT auf ungültige Werte
loslässt! Wie reagiert da Step7, TIA, CodeSys
// hier nochmal der überarbeitete Code
Code:
FUNCTION m7_BCDL_TO_DINT : DINT
TITLE ='Converts Long BCD (10 digits and sign) to a DINT'
// ==============================================================================
// Converts long BCD value (up to 10 digits and sign) to a DINT
// The BCD must be provided in an ARRAY[1..] OF BYTE in little endian Format
// BCD[1] contains the lowes digit
// each BYTE represents 2 digits of the BCD. 5 Bytes = 10 digits
// plus the possible minus '-' ('-' is B#16#F in BCD code)
// the '-' can be anywhere in the BCD. If a '-' is detected the
// conversion will stop at this position.
// For BCD{#0F, #F0) what both is '-0', the FUNCTION returns 0.
// IF an Error occures ,the Function returns -2147483648 / DW#16#80000000
// and FC-ENO := FALSE // the ok-Flag
// ELSE it returns the DINT value of the BCD {-2147483647 .. 2147483647}
// and FC-ENO := TRUE; // the ok-Flag
// ==============================================================================
//
// AUTHOR: S.Maag
// DATE: 4/2020
//
// CHANGELOG:
// ------------------------------------------------------------------------------
// DATE NAME DESCRIPTION
// ------------------------------------------------------------------------------
// 06.04.2020 S.Maag Added Error check for digit 11/12 which
// will exceed 32-Bit DINT range. So far digit 11/12
// hve been ignored because they exceed the DINT-range.
// If we want to ignore again, we have to remove
// Err:=TRUE; at Byte6 (digit 11/12) check.
// See comment in the code!
// ------------------------------------------------------------------------------
VERSION : '1.0'
AUTHOR : 'S.Maag'
FAMILY : 'Maagic7'
VAR_INPUT
inBCDs : ARRAY[1..6] OF BYTE; // 1:Lo..6:Hi; Array with the BCD digits (with sign max 12 digits)
END_VAR
VAR_OUTPUT
END_VAR
VAR_TEMP
I : DINT; // LOOP COUNTER
tmpDI : DINT; // Our DINT value
diBCD : DINT; // Value of a 2-digit BCD value
Err : BOOL; // Error (BCD-digit not valid; NOT{0..9})
negative: BOOL; // BCD is negative
dFact : DINT; // Factor for BCD_TO_INT (1_digit, 10_digit, 100_digit ...)
BCDhi : WORD; // Value of hi BCD digit of a BYTE
BCDlo : WORD; // Value of lo BCD digit of a BYTE
END_VAR
CONST
cstReturnValIfError := -2147483648; // -2147483648 / DW#16#80000000 we use the -2147483648 as Error indication
END_CONST
BEGIN
Err := FALSE;
negative := FALSE;
tmpDI := 0;
dFact := 1; // Faktor für (1er, 10er),(100er, 1000er) ...
// **********************************************************************
// *** A BCD can contain a max. of 10 digits and a '-' to fit into ***
// *** a DINT. (At a 10digit BCD the '-' is in digit 11 of 12) ***
// *** If you want to ignore numbers in digit 11/12, then remove ***
// *** this 1st check for valid digit 11/12. ***
// *** Removing this 1st check do not affect the processing of the ***
// *** the 'F', BCD '-' ***
// *** With this check an Error is fired when digit 11/12 contains ***
// *** digits different from 'F'/'-', without the check ***
// *** only 10 digits & '-' are processed ***
// **********************************************************************
// (* // remove this 'IF THEN', if you want to ignore numbers in digit 11/12
// check first digit 11/12: If it contains a number, we will exceed DINT range
BCDhi := BYTE_TO_WORD( SHR(IN:=inBCDs[6], N:=4) ); // Value of hi BCD digit
BCDlo := BYTE_TO_WORD( inBCDs[6] AND B#16#F); // Value of lo BCD digit
IF BCDlo <> W#16#0 AND BCDlo <> W#16#F THEN
Err := TRUE;
ELSIF BCDHi <> W#16#0 AND BCDHi <> W#16#F THEN
Err := TRUE;
END_IF;
// **********************************************************************
// *)
I := 0;
REPEAT
I := I + 1; // inBCDs[1..6]
BCDhi := BYTE_TO_WORD( SHR(IN:=inBCDs[I], N:=4) ); // Value of hi BCD digit
BCDlo := BYTE_TO_WORD( inBCDs[I] AND B#16#F); // Value of lo BCD digit
// Caution BCD digits must be checked for correct range {0..9}
// PLCs may stop if BCD contains invalid digits
IF WORD_TO_INT(BCDhi) > 9 THEN // IF BCD_DIGIT IS NOT VALID
IF BCDhi = B#16#F THEN
negative := TRUE;
ELSE
Err := TRUE; // invalid BCD digit
END_IF;
BCDhi := 0;
END_IF;
IF WORD_TO_INT(BCDlo) > 9 THEN // IF BCD_DIGIT IS NOT VALID
IF BCDlo = B#16#F THEN
negative := TRUE;
BCDHi := 0; // ignore BCDhi if BCDlo is the sign '-'
ELSE
Err := TRUE; // invalid BCD digit
END_IF;
BCDlo := 0;
END_IF;
// calculate the DINT value of the 2 BCD digits only up to 10 digits max
// digit 11..12 is the '-' at 10 digit long BCDs
IF I<= 5 THEN
diBCD := INT_TO_DINT(WORD_TO_INT(BCDhi)*10) + INT_TO_DINT(WORD_TO_INT(BCDlo));
IF I = 5 THEN // Check overflow if 10digit BCD
IF diBCD > 21 THEN // 21-47-48-36-47 = DW#16#7FFFFFFF; maxDINT
Err := TRUE;
ELSIF diBCD = 21 THEN
IF tmpDI > 47483647 THEN ERR := TRUE; END_IF;
END_IF;
END_IF;
tmpDi := tmpDi + diBCD * dFact; // add the value of the 2 digits in the right position of the DINT
dFact := dFact *100; // 100 it's ShiftLeftBCD(myBCD,2_digits) as DINT
// dFact = {1, 100, 10.000, 1.000.000, 100.000.000} fact = 10.000.000.000 will exceed the DINT range
END_IF;
UNTIL negative OR Err OR (I = 6)
END_REPEAT;
IF negative THEN // add the negative sign to the DINT
tmpDI := -tmpDI;
END_IF;
IF Err THEN
m7_BCDL_TO_DINT := cstReturnValIfError; // -2147483648; DW#16#80000000
ELSE
m7_BCDL_TO_DINT := tmpDI;
END_IF;
OK := NOT Err; // OK-Bit = TRUE if BCD is converted without an Error! If OK is FALSE, the returned DINT is not valid!
END_FUNCTION