<< Click to Display Table of Contents >> Navigation: Multi-Protocol MasterOPC Server > Lua 5.1 Reference Manual > Examples and Other Useful Things > Data Conversion > The BCD Format |
In the BCD format, each decimal digit of a number is written as its 4-bit binary code (Wikipedia:Binary-coded decimal). That is, each half-byte contains one number. The format has a number of disadvantages (larger memory usage, complicated mathematical operations, etc.) and is obsolete. However, it still continues to apply in many controllers. To convert a number from BCD to integer, you can use a number of simple functions in the script.
The conversion "BCD to INT"
For example, we have read out a MODBUS register from a device (2 bytes); let's convert the received value to a 4-bit BCD number.
Function example
function FromBCD(register)
local shift=register;
local bytes={};
local exp=1;
local number=0;
for i=1,4,1 do
local byte=bit.BitAnd(shift,0xF);--get a right half-byte by masking
number=number+byte*exp; --multiply the number and add to the number saved
exp=exp*10; --increase a multiplication degree
shift=bit.BitRshift(shift,4); --shift to the right
end
return number; --return the number
end
If a number dimension is greater (for example, 4 bytes), it is sufficient to replace 4 by 8 in the for cycle (according to a number of BCD numbers).
Example of function invocation:
val=4660; --1234 in BCD
BCD=FromBCD(val);
The conversion "INT to BCD"
Now let's consider the reverse function, and convert a number from the int format to the BCD format (for example, for sending to a device). It should be noted that BCD has a lower "capacity" with respect to a usual value of the same dimension. That is, a number of the uint16 format can be from 0 to 65535, and only to 9999 in BCD.
Function example
function ToBCD(number)
local val=number;
if val>9999 or val<0 then return nil; end; --check if a number belongs to the admissible range
local shift=0;
for i=1,4,1 do
shift=bit.BitRshift(shift,4); --shift our register to the right
local ost;
val,ost=math.modf(val*0.1); --multiply the number by 0.1 and get integer and fractional parts
ost=bit.BitAnd(ost*10,0xF); --mask the fractional part
ost=bit.BitLshift(ost,12); --shift to the left by 12 bits (to the upper byte)
ost=bit.BitAnd(ost,0xF000); --mask (remove unnecessary bits)
shift=bit.BitOr(ost,shift); --apply to the current register
end;
return shift;
end;