3542 views|0 replies

57

Posts

0

Resources
The OP

Analysis of DELPHI Serial Port Programming [Copy link]

1. The basic principles of serial communication:

Generally, there are two ways for computers to communicate with external devices:

Parallel transmission: The transmission amount is 8 bits (1 byte) at a time, through a parallel port, such as a printer

Serial transmission: only one bit is transmitted at a time, through a serial port, such as RS-232

The concept of bits and bytes:

Each bit of 0 and 1 in binary is called a bit, and every 8 bits constitute a byte.

The rightmost bit in a byte is called bit 0 and the leftmost bit is called bit 7.

Types of bytes during transmission: There are generally two types.

1. When text (characters, letters, punctuation marks, etc.) is stored in a computer, each different character is represented by a different numerical value. These numerical values usually range from 0-127 or 0-255.

7 bits: ASCII code, one spare bit for each byte

8 bits: The first 128 bits follow the ASCII code rules, and the remaining 128 bits are used to encode extended characters, numeric symbols, graphic characters, etc.

2. Binary data:

Some executable instruction files and graphic image files are stored in binary form rather than ASCII code.

A data can be stored in binary form and can occupy multiple bytes. In the field of communications, this type of data is often called binary data.

What I want to talk about today is how to process binary data.

The text processing method is relatively simple. I have written a test software before and published it on the box. You can download it from this address: http://www.delphibox.com/article.asp?articleid=2877

Several concepts:

Baud rate: The maximum voltage state change rate that can be generated per second (the number of oscillations in one second) bps

The two communicating parties must achieve the same communication speed. After the original signal is sampled at different baud rates, the results obtained are completely different. For example, when the sampling speed is only half of the original, the signal is sampled in a skipped manner, resulting in data errors.

Data bit: 5, 6, 7, 8

Stop bit: The stop bit sent or received after the parity bit (select with parity) or data bit (select without parity). The length of the stop bit can be selected from 1, 1.5 or 2 bits).

Parity Bit: Data transmission is followed by the optional parity bit to be sent and received. The status of the parity bit depends on the selected parity type. If odd parity is selected, the number of bits that are 1 in the character data is added to the parity bit and the result should be an odd number. The options are odd, even or none.

To ensure smooth communication, the above 4 settings of both parties must be consistent.

A byte is 8 bits, the data bits can be 7 bits, and then a check bit makes 8 bits.

These parameters can be set by yourself. However, if you want to ensure smooth communication, the above 4 settings of both parties must be consistent.

2. Common controls commonly used for serial communication in Delphi

For serial communication, you can use Windows API functions:

The Win32 API has been declared in Delphi's Windows.pas unit file, so when using the API in Delphi, just add Windows to the uses section to make it reference the unit file.

Serial communication related functions:

CreateFile: Create a file, here use to open the communication port

CloseHandle: Closes the file created by CreateFile, and is used here to close the communication port

GetCommState: Get the setting parameters of the computer serial port

SetCommState: Set the parameters of the computer serial port

WriteFile: Write data to a file, here used to send data out of the serial port

ReadFile: Read data from a file, here used to get the data sent to the serial port

ClearCommError: Clear serial port errors and get information

PurgeComm: Clear the buffer on the serial port

EscapeCommFunction: Control the hardware status of the serial port

SetCommMask: Set the event mask to trigger the event

WaitCommEvent: Wait for the occurrence of the setting event

GetCommModemStatus: Get the hardware line status on the serial port

The use of Windows API functions is not recommended here.

Although API functions can achieve very powerful and flexible functions, it is bound to spend more time and energy on communication details. Delphi is a classic representative of RAD, and of course there is a simpler way, which is to use packaged controls.

The commonly used controls are spcomm, mscomm, comport, apro, etc. Among them, mscomm is an ActiveX control, and the other three controls are Delphi controls with their own source code, which can be downloaded from websites such as Delphi Box, Delphi Garden, and SourceForge. The specific usage is not introduced in detail here.

3. The concept of data frame

Today we are mainly talking about binary data processing, so let's first introduce the concept of data frame.

If we want to communicate data, then both parties must follow a certain protocol so that they can understand each other's data.

Frame is the basic unit for transmitting information. Each frame consists of six fields: frame start mark field, control field, data length field, data field, frame information longitudinal check field and frame end field. Each field consists of several bytes.

For example, there is such a frame format:

Code Bytes Description

68H 1 Frame start character

RTUA 4 Terminal Logical Address

MSTA 2 master station address and command number

68H 1 Frame start character

C 1 Control code

L 2 Data length

DATA variable length data field

CS 1 Check code

16H 1 End code

From this data frame format, we can see that a data frame has at least 13 bytes. And there are rules for the front, back, and middle. In this way, we can judge the meaning of this data frame by processing and analyzing some of the bytes, and then carry out other related work.

Of course, different systems have different data frame formats. We will use this format as an example today.

4. Receiving and sending data

Today I will only introduce how to receive and send data using the Comport control.

Receive data: in the OnRxChar event.

Prototype of onRxChar:procedur TForm1.ComPortRxChar(Sender: TObject; Count: Integer)

This event is triggered when there is data in the receive buffer, and count is the number of bytes in the buffer.

Send data: This control provides 6 functions for sending data:

WaitForAsync

WaitForEvent

Write

WriteAsync

WriteStr

WriteStrAsync

The more commonly used one is WriteStr.

function WriteStr(const Str: String): Integer;

The parameter is a string type and returns the number of bytes actually sent.

Apro is very powerful, and the company that developed this control group has gone bankrupt and contributed all the source code.

Except for mscomm, the other three have source code.

Why use WriteStr? Here we need to explain the string type.

Delphi's String type is very powerful and is compatible with PChar, Array of Char, WideString and other string or character array types. Another key function is that it can be used as a dynamic Byte array.

Although this usage is not introduced in many reference books, after many tests and practical experience, I found that there were no adverse reactions.

For example, if you want to send the characters $68 80 50 60 20 30 10 00 00 20, you can define a string A: string; and then use the following code:

setlength(A,10);

A[1]:=Chr(68);

A[2]:=Chr(80);

. . . .

A[9]:=Chr(00)

A[10]:=Chr(20)

writestr(A);

Of course it looks cumbersome, but this is not the advantage of String. Its real advantage is

1. No need to manage memory, leave it to Delphi.

2. It can be easily processed as a parameter or variable. More on this later.

5. Data processing

The previous 4 sections are all nonsense. The focus is on this section.

If data is continuously sent from the terminal to the server, for example, a frame of data is sent every 10 milliseconds, how can we distinguish this data?

You should know that serial communication is transmitted bit by bit, and received byte by byte, not frame by frame. In order to determine whether a byte is the data of the previous frame or the next frame, we can only determine it byte by byte.

Let's look at a piece of code first, and then I'll explain this code. In this way, we can clearly get the data of each frame. After that, I'll explain how to use Delphi's object-oriented features to process the received frame data.

The frame format uses the one just introduced.

First define a few global variables:

FDataStatus:Word; //0 Ready 1. Frame header 1 2. Frame header 2

FNextLength:Word; //The length to be read next

FCurrentLength:Word; //Current length

FtmpStr: string; // one frame of data

The code is as follows:

procedure ComPortRxChar(Sender: TObject; Count: Integer);

var

S1: string;

J:Integer;

begin

case FDataStatus of

0:begin

J:=0;

FtmpStr:='';

repeat

ComPort.ReadStr(S1,1);

J:=J+1;

until (Ord(S1[1])=$68) or (J=Count);

if Ord(S1[1])=$68 then

begin

FtmpStr:=S1;

FDataStatus:=1;

FNextLength:=10;

FCurrentLength:=0;

end;

end;

1: begin

J:=FCurrentLength;

repeat

ComPort.ReadStr(S1,1);

FtmpStr:=FtmpStr+S1;

FCurrentLength:=FCurrentLength+1;

until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);

if FCurrentLength=FNextLength then

begin

FDataStatus:=2;

FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;

FCurrentLength:=0;

end;

end;

2: begin

J:=FCurrentLength;

repeat

ComPort.ReadStr(S1,1);

FtmpStr:=FtmpStr+S1;

FCurrentLength:=FCurrentLength+1;

until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);

if FCurrentLength=FNextLength then

begin

FDataStatus:=0;

FNextLength:=0;

FCurrentLength:=0;

FReceiveFrame.Str:=FtmpStr;

SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));

end;

end;

end;

end;

case FDataStatus of

Determine the data segment currently received.

0:begin

J:=0;

FtmpStr:='';

repeat

ComPort.ReadStr(S1,1);

J:=J+1;

until (Ord(S1[1])=$68) or (J=Count);

if Ord(S1[1])=$68 then

begin

FtmpStr:=S1;

FDataStatus:=1;

FNextLength:=10;

FCurrentLength:=0;

end;

end;

If it is the beginning of a frame, then the relevant parameters are initialized and the data frame is initialized to empty.

J:=0;

FtmpStr:='';

Then start reading until you see the frame start character $68 or finish reading. If not, discard the data you read (of course you can also write it down separately, but it doesn't make much sense).

repeat

ComPort.ReadStr(S1,1);

J:=J+1;

until (Ord(S1[1])=$68) or (J=Count);

J is the data read, and J=count means that the buffer has been read.

Ord(S1[1])=$68, S1 is a string, and ORD(S1[1]) represents the first byte of S1.

ComPort.ReadStr(S1,1); reads only one byte at a time, so there is only 1 byte in S1.

If $68 is in the second byte you read, then isn't that a sham?

If $68 is read, the read state is set to the second stage, the frame data is added with $68, the length of the next stage is 10, and the number of bytes read is 0. Then the function ends.

if Ord(S1[1])=$68 then

begin

FtmpStr:=S1;

FDataStatus:=1;

FNextLength:=10;

FCurrentLength:=0;

end;

If there is still data in the buffer, it will trigger the OnRxChar event again.

As long as there is data in the buffer and it has not been processed or is not being processed, the OnRxChar event will continue to be triggered.

The read state has just been set to the second stage, so the second stage code will now be executed:

Case statement to judge.

1: begin

J:=FCurrentLength;

repeat

ComPort.ReadStr(S1,1);

FtmpStr:=FtmpStr+S1;

FCurrentLength:=FCurrentLength+1;

until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);

if FCurrentLength=FNextLength then

begin

FDataStatus:=2;

FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;

FCurrentLength:=0;

end;

end;

J:=FCurrentLength; //Counter, record the total number of reads.

RTUA 4 Terminal Logical Address

MSTA 2 master station address and command number

68H 1 Frame start character

C 1 Control code

L 2 Data length

These are the 10 bytes we will read in the second stage.

The second stage of processing is to read 10 bytes, no matter how many bytes are in the buffer and no matter how many times they are read, until 10 bytes are read.

repeat

ComPort.ReadStr(S1,1);

FtmpStr:=FtmpStr+S1;

FCurrentLength:=FCurrentLength+1;

until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);

FNextLength=10 is set in the first stage.

After reading 10 bytes, the third stage will begin.

if FCurrentLength=FNextLength then

begin

FDataStatus:=2;

FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;

FCurrentLength:=0;

end;

How many books should I read in the next stage?

FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;

DATA variable length data field

CS 1 Check code

16H 1 End code

This is the number of bytes to be read in the final stage.

But DATA is variable length, so we divide it into one more stage. If it is fixed length, we only need to determine the frame start character and then read the required number of bytes.

Fortunately, there is

L 2 Data length

Indicates the length of DATA.

So there is this sentence.

FNextLength:=(Ord(FtmpStr[11]) shl 8) + Ord(FTmpStr[10])+2;

+2 is the last two bytes.

Next comes the third stage:

2: begin

J:=FCurrentLength;

repeat

ComPort.ReadStr(S1,1);

FtmpStr:=FtmpStr+S1;

FCurrentLength:=FCurrentLength+1;

until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);

if FCurrentLength=FNextLength then

begin

FDataStatus:=0;

FNextLength:=0;

FCurrentLength:=0;

FReceiveFrame.Str:=FtmpStr;

SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));

end;

end;

end;

Foshan-sky A (11116580) 20:55:21

The reading method in the third stage is the same as the previous one, which is to read one and count one until the required bytes are read.

J:=FCurrentLength;

repeat

ComPort.ReadStr(S1,1);

FtmpStr:=FtmpStr+S1;

FCurrentLength:=FCurrentLength+1;

until (FCurrentLength=FNextLength) or (FCurrentLength-J=Count);

Initialize after reading:

if FCurrentLength=FNextLength then

begin

FDataStatus:=0;

FNextLength:=0;

FCurrentLength:=0;

FReceiveFrame.Str:=FtmpStr;

SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));

Finally, we get a complete frame of data FtmpStr, and then send this data to the function that processes the data.

SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));

The relevant function will handle it.

In this way, no matter what comes or when, a complete frame of data is in the string FtmpStr.

Of course, the specific reading method depends on the format of the data frame, but the essence remains the same: comparing the read bytes with the given format.

With this knowledge, you can basically cope with writing serial communication programs.

Considering the object-oriented nature of Delphi, it would be a pity not to use classes to standardize the format of the data frame.

Let's take the frame data format as an example:

We can define the base class like this.

TCustomGY = class(TObject)

//0 Base class of specification

private

FFrameBegin:Byte; //Frame start character

FTerminalLogicAddr:T4Byte; //Terminal logical address

FMasterStation:T2Byte; //Master station address and command number

FFrameBegin2:Byte; //Frame start character

FFrameControl:Byte; //Control code

FDataLength:Word; //Data length

FFrameVerify:Byte; //Verification code

FFrameEnd:Byte; //End code

FPosSend:TFramePosTerminal; //Frame initiator

FPosReceive:TFramePosTerminal; //Frame receiving end

procedure SetDataLength(const Value: Word);

procedure SetFrameBegin(const Value: Byte);

procedure SetFrameBegin2(const Value: Byte);

procedure SetFrameControl(const Value: Byte);

procedure SetFrameEnd(const Value: Byte);

procedure SetFrameVerify(const Value: Byte);

function ReadFrameDataLengthHi: Byte;

function ReadFrameDataLengthLo: Byte;

function ReadFrameFSEQ: Byte;

function ReadFrameISEQ: Byte;

function ReadFrameMSTA: Byte;

function ReadFrameMSTA2: Byte;

procedure SetPosReceive(const Value: TFramePosTerminal);

procedure SetPosSend(const Value: TFramePosTerminal);

procedure SetMS1(const Value: Byte);

procedure SetMS2(const Value: Byte);

procedure SetFrameFSEQ(const Value: Byte);

procedure SetFrameISEQ(const Value: Byte);

procedure SetFrameMSTA(const Value: Byte);

procedure SetFrameMSTA2(const Value: Byte);

function GetTerminalAddr: Word;

function GetTerminalLogicAddr(const AIndex: T4Integer): Byte;

procedure SetTerminalAddr(const Value: Word);

procedure SetTerminalLogicAddr(const AIndex: T4Integer;

const Value: Byte);

function GetMasterStation(const AIndex: T2Integer): Byte;

procedure SetMasterStation(const AIndex: T2Integer; const Value: Byte);

function GetTerminalLogicAddrStr: string;

procedure SetTerminalLogicAddrStr(const Value: string);

function GetMasterstationStr: string;

procedure SetMasterstationStr(const Value: string);

protected

procedure SetData(const Value: string); virtual; abstract; //Set the data area virtual function, the implementation method is in the subclass

function ReadData:string; virtual; abstract; //ReadData virtual function, overridden by subclasses.

function ToVerifyFrame: Boolean; virtual; //Is the frame correct?

function ReadWholeFrame: string; virtual;

procedure setWholeFrame(const Value: string); virtual;

public

property FrameBegin:Byte read FFrameBegin write SetFrameBegin; //frame start character

property RTUA[const AIndex:T4Integer]:Byte

read GetTerminalLogicAddr write SetTerminalLogicAddr; //Terminal logical address

property RTUAStr:string read GetTerminalLogicAddrStr write SetTerminalLogicAddrStr;

property TerminalAddr:Word read GetTerminalAddr write SetTerminalAddr; //Terminal address

property MSTAs[const AIndex:T2Integer]:Byte read GetMasterStation write SetMasterStation; //Master station address and command number

property MSTAStr:string read GetMasterstationStr write SetMasterstationStr;

property FrameBegin2:Byte read FFrameBegin2 write SetFrameBegin2; //The second frame start character

property FrameControl:Byte read FFrameControl write SetFrameControl; //Control code

property DataLength:Word read FDataLength write SetDataLength; //Data length, 2 bits, low bit first, high bit last

property Data: string read ReadData write SetData; //data, virtual property, implemented in subclass

property CS:Byte read FFrameVerify write SetFrameVerify; //Frame check bit

property FrameEnd:Byte read FFrameEnd write SetFrameEnd; //frame end code

property FrameIsRight:Boolean read ToVerifyFrame; //Check if the frame is correct

property WholeFrame:string read ReadWholeFrame write setWholeFrame; //whole frame information, virtual function, subclass implementation

property MS1:Byte read FMasterStation[0] write SetMS1; //MS1

property MS2:Byte read FMasterStation[1] write SetMS2; //MS2

property MSTA:Byte read ReadFrameMSTA write SetFrameMSTA; //MSTA

property MSTA2:Byte read ReadFrameMSTA2 write SetFrameMSTA2; //MSTA2, valid when both the transmitter and receiver are master stations

property ISEQ:Byte read ReadFrameISEQ write SetFrameISEQ; //ISEQ

property FSEQ:Byte read ReadFrameFSEQ write SetFrameFSEQ; //FSEQ

property DataLengthLo:Byte read ReadFrameDataLengthLo; //data length low bit

property DataLengthHi:Byte read ReadFrameDataLengthHi; //data length high bit

property PosSend:TFramePosTerminal read FPosSend write SetPosSend; //frame sender

property PosReceive:TFramePosTerminal read FPosReceive write SetPosReceive; //Frame receiving end

class function VerifyFrame(S:string): Boolean; //Is the frame correct?

class function GetVerifyByte(S: string):Byte; //Get the verification code

constructor Create; virtual;

destructor Destory; virtual;

function DecodeFrame(const S:string;NoError:Boolean=True):Boolean; //dynamic; //Return True after decomposition operation

function SumVerify:Byte; dynamic; //Calculate the data frame CS code

function SumDataLength:Word; dynamic; //Calculate data length

end;

All judgment and processing are handed over to this class.

Then different commands derive different subclasses.

Leave two interfaces, one for input and one for output.

The other intermediate data can be handled at will.

The implementation code will not be posted.

Here is another simplest application derived from the base class:

TSimplyGY = class(TCustomGY)

private

FData: TarrayByte; //Processed Data, the data that needs to be reversed has been reversed

protected

procedure SetData(const Value: string); override; //Set data area

function ReadData:string; override; //ReadData

public

end;

The subclass implements the virtual functions specified by the base class.

With such a class, you can derive different subclasses for the Data area. All you need to do is process the Data. The format of the Data field is different for different commands.

The processing principle of the Data field is similar to that mentioned above, which is nothing more than byte comparison and judgment.

Let me share a few more functions that I often use in serial communication. Those who are interested can take a look. Here are some for beginners to refer to:

function StrToHexStr(const S:string):string;

//Convert the string into hexadecimal string

var

I:Integer;

begin

for I:=1 to Length(S) do

begin

if I=1 then

Result:=IntToHex(Ord(S[1]),2)

else Result:=Result+' '+IntToHex(Ord(S[I]),2);

end;

end;


function HexStrToStr(const S:string):string;

//Convert hexadecimal string into string

var

t:Integer;

ts:string;

M,Code:Integer;

begin

t:=1;

Result:='';

while t<=Length(S) do

begin

while not (S[t] in ['0'..'9','A'..'F','a'..'f']) do

inc(t);

if (t+1>Length(S))or(not (S[t+1] in ['0'..'9','A'..'F','a'..'f'] )) then

ts:='$'+S[t]

else

ts:='$'+S[t]+S[t+1];

Val(ts,M,Code);

if Code=0 then

Result:=Result+Chr(M);

inc(t,2);

end;

end;


function StrToTenStr(const S:string):string;

//Convert the string into a decimal string

var

I:Integer;

begin

for I:=1 to Length(S) do

begin

if I=1 then

Result:=IntTostr(Ord(S[1]))

else Result:=Result+' '+IntToStr(Ord(S[I]));

end;

end;


function StrToByteArray(const S:string):TarrayByte;

//Convert the string into the corresponding Byte

var

I:Integer;

begin

SetLength(Result,Length(S));

for I:=1 to Length(S) do

Result[I-1]:=Ord(S[I]);

end;


function ByteArrayToStr(const aB:TarrayByte):string; //Byte is converted into the corresponding string

var

I:Integer;

begin

SetLength(Result,High(aB)+1);

for I:=0 to High(aB) do

Result[I+1]:=Chr(aB[I]);

end;


function StrToBcdByteArray(const S:string; const Rotate:Boolean=False):TarrayByte; //Convert string to corresponding BCDByte

var

ts:string;

I:Integer;

begin

if Odd(Length(S)) then

ts:='0'+S

else ts:=S;

SetLength(Result,Length(ts) div 2);

for I:=0 to High(Result) do

begin

if Rotate then

Result[High(Result)-I]:=StrToInt('$'+ts[2*I+1]+ts[2*I+2])

else

Result[I]:=StrToInt('$'+ts[2*I+1]+ts[2*I+2]);

end;

end;


function BcdByteArrayToStr(const aNum:TarrayByte; const Rotate:Boolean=False):string; //Convert BCDByte to corresponding string

var

I:Integer;

t:string;

begin

SetLength(Result,2*(High(aNum)+1));

for I:=0 to High(aNum) do

begin

t:=IntToHex(aNum[I],2);

if Rotate then

begin

Result[2*(High(aNum)-I)+1]:=t[1];

Result[2*(High(aNum)-I)+2]:=t[2];

end

else begin

Result[2*I+1]:=t[1];

Result[2*I+2]:=t[2];

end;

end;

end;


//Convert the collection into an array

function SetToByteArray(s:array of const):TarrayByte;

var

I:Byte;

begin

SetLength(Result,High(s)+1);

for I:=0 to High(s) do

Result[I]:=S[I].VInteger;

end;


function SetToWordArray(s:array of const):TarrayWord;

var

I:Byte;

begin

SetLength(Result,High(s)+1);

for I:=0 to High(s) do

Result[I]:=S[I].VInteger;

end;


function StrToBinStr(const S:string):string;

//Convert the string to a binary string

var

I:Integer;

begin

Result:='';

for I:=1 to Length(S) do

begin

if I=1 then

Result:=BinStrArray[Ord(S[1])]

else Result:=Result+' '+BinStrArray[Ord(S[I])];

end;

end;


function ByteToBinStr(const B:Byte):string;

// Convert Byte type to binary string

var

I:Integer;

begin

Result:='';

for I:=7 downto 0 do

Result:=Result+Chr(((B shr I) and 1)+48);

end;


function BinStrToByte(const S:string):Byte;

//Convert binary string to number

var

S1: string[8];

L:Integer;

I:Integer;

begin

S1:='00000000';

L:=min(8,Length(S));

for I:=L downto 1 do

if S[I]='1' then

S1[8+IL]:=S[I]

else

S1[8+IL]:='0';

Result:=0;

for I:=0 to 7 do

Result:=Result+(((Ord(S1[I+1])-48) shl (7-I)));

end;


function T2ByteToWord(const T:T2Byte;const LowFront:Boolean=False):Word;

//Convert T2Byte type to Word type

var

a1,a2:Byte;

begin

if LowFront then

begin

a1:=T[1];

a2:=T[0];

end

else begin

a1:=T[0];

a2:=T[1];

end;

Result:=a1;

Result:=(Result shl 8) + a2;

end;


function WordToT2Byte(const aNum:Word;const LowFront:Boolean=False):T2Byte;//Convert Word type to T2Byte type

var

a1,a2:Byte;

begin

a1:=aNum;

a2:=aNum shr 8;

if LowFront then

begin

Result[0]:=a1;

Result[1]:=a2;

end

else begin

Result[0]:=a2;

Result[1]:=a1;

end;

end;


function ByteToBCDByte(const B:Byte;const aType:TBCDType=bt8421):Byte;

//Byte converted to BCD code

var

B1,B2:Byte;

function T2421(const P:Byte):Byte;

begin

if P<5 then

Result:=P

else Result:=P+6;

end;

begin

if B>99 then

Result:=0

else begin

B1:=B div 10;

B2:=B mod 10;

case aType of

bt8421:begin

Result:=(B1 shl 4)+B2;

end;

bt2421:begin

Result:=(T2421(B1) shl 4)+T2421(B2);

end;

btR3:begin

Result:=((B1+3) shl 4)+(B2+3)

end;

else Result:=(B1 shl 4)+B2;

end;

end;

end;


function BCDByteToByte(const B:Byte;const aType:TBCDType=bt8421):Byte;

//Convert BCD to Byte

var

B1,B2:Byte;

function Ts2421(const P:Byte):Byte;

begin

if P<5 then

Result:=P

else Result:=P-6;

end;

begin

B1:=(B and $F0) shr 4;

B2:=B and $0F;

case aType of

bt8421: begin

Result:=B1*10+B2;

end;

bt2421: begin

Result:=(Ts2421(B1)*10)+Ts2421(B2);

end;

btR3: begin

Result:=((B1-3)*10)+(B2-3)

end;

else Result:=B1*10+B2;

end;

end;


function BCDByteToByteFF(const B:Byte;Auto:Boolean=True):Byte;

//Convert BCD to Byte type FF-->FF 8421 code

var

B1,B2:Byte;

begin

if (B=$FF)or(Auto and (B>=$AA)) then

Result:=B

else begin

B1:=(B and $F0) shr 4;

B2:=B and $0F;

Result:=B1*10+B2;

end;

end;


function ByteToBCDByteFF(const B:Byte;Auto:Boolean=True):Byte;

//Byte converted to BCD code FF-->FF 8421 code

var

B1,B2:Byte;

begin

if (B=$FF)or(Auto and (B>=$AA)) then

Result:=B

else if B>99 then

Result:=0

else begin

B1:=B div 10;

B2:=B mod 10;

Result:=(B1 shl 4)+B2;

end;

end;

6. Conclusion

No matter hard disk space, memory space, CPU cache space, serial port cache space, etc., they are just a carrier. What is filled in them is completely meaningless if it is not interpreted according to certain rules. The communication program is just the application of some simpler rules.
This post is from MCU

Guess Your Favourite
Find a datasheet?

EEWorld Datasheet Technical Support

Related articles more>>

    EEWorld
    subscription
    account

    EEWorld
    service
    account

    Automotive
    development
    circle

    Robot
    development
    community

    Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
    快速回复 返回顶部 Return list