The BCD Format

<< 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;