Delphi中ComPort通信中的数据处理范文.docx
- 文档编号:24885831
- 上传时间:2023-06-02
- 格式:DOCX
- 页数:21
- 大小:23.65KB
Delphi中ComPort通信中的数据处理范文.docx
《Delphi中ComPort通信中的数据处理范文.docx》由会员分享,可在线阅读,更多相关《Delphi中ComPort通信中的数据处理范文.docx(21页珍藏版)》请在冰豆网上搜索。
Delphi中ComPort通信中的数据处理范文
Delphi中ComPort通信中的数据处理
1.串口通信的基本原理:
一般计算机与外部设备通讯有两种方式:
并行传送(Parallel):
一次的传输量为8个位(1字节),通过并行端口,如打印机
串行传送(Serial):
一次只传输1个位,通过串行端口,如RS-232
位与字节的概念:
二进制中的每一位0和1,被叫做一个位,每8个位构成一个字节
一个字节中最右面的位被称为第0位,最左面的位被称为第7位。
传输过程中的字节类型:
一般有两种。
1.文本(字符字母、标点符号等)在计算机中存储时,每个不同的字符都用不同的数值来表示。
这些数值的范围通常在0-127或0-255范围。
7位:
ASCII码,每个字节留一个备用位
8位:
前128个遵循ASCII码规则,其余的128个用来做扩展字符、数字符号、图形字符等编码。
2.二进制数据:
某些可执行指令文件和图形图像文件就是以二进制形式而不是ASCII码形式存储的。
一个数据可用二进制形式存储,可以占多个字节。
在通信领域,常常把这种类型的资料叫做二进制数据。
今天要讲的就是有关二进制数据的处理方法。
几个概念:
波特率:
每秒所能产生的最大电压状态改变率(一秒钟可以振荡的次数)bps
通信双方必须要取得一样的通信速度。
原始信号经过不一样的波特率取样后,所得的结果完全不一样,如取样速度只有原来一半时,信号被跳着取样,数据因此错误。
数据位:
有5,6,7,8四种
停止位:
在奇偶位(选择有奇偶校验)或数据位(选择无奇偶校验)之后发送或接收的停止位。
停止位的长度可在1、1.5或2位三者中选择)。
奇偶校验位:
数据传输之后是可供选择的奇偶校验位发送和接收。
奇偶位的状态取决于选择的奇偶校验类型。
如果选择奇校验,则该字符数据中为1的位数与校验位相加,结果应为奇数。
可选奇,偶或无。
如果要保证通讯畅通。
通讯双方以上4项设置必须一致。
一个字节是8位,数据位可以7位,然后一位校验位就8位了。
这些参数可以自己设置。
但是如果要保证通讯畅通。
通讯双方以上4项设置必须一致。
2.Delphi中串口通信常用的常用控件
进行串口通讯可以用Windows的Api函数:
Delphi的Windows.pas单元文件中已经将Win32API均声明进去,因此在Delphi里面使用API时只要在uses区段中加入Windows,使其引用该单元文件即可。
串行通信相关函数:
CreateFile:
建立文件,在此用打开通信端口
CloseHandle:
关闭由CreateFile建立的文件,在此用于关闭通信端口
GetCommState:
取得计算机串口的设置参数
SetCommState:
设置计算机串口的参数
WriteFile:
将数据写入文件,在此用来将数据由串口送出
ReadFile:
由文件中读取数据,在此用来取得送到串口的数据
ClearCommError:
清除串行端口的错误,并取得信息
PurgeComm:
清除串口上的缓冲区
EscapeCommFunction:
控制串口的硬件状态
SetCommMask:
设置事件的掩码,用以触发事件
WaitCommEvent:
等待设置事件的发生
GetCommModemStatus:
取得串口上的硬件线路状态
这里不推荐使用WindowsAPI函数。
虽然用API函数可以实现很强大很灵活的功能,但是势必要花更多的时间和精力在通讯细节上。
而Delphi的是RAD的经典代表,当然会有更简单的方法,那就是使用封装好的控件。
较常用的控件有spcomm,mscomm,comport,apro等。
其中mscomm是ActiveX控件,另外3个控件都是Delphi控件,自带源码,可以到delphi盒子,Delphi园地,sourceforge等网站下载。
具体使用方法这里不详细介绍。
3.数据帧的概念
今天我们主要讲的是二进制数据的处理,所以先介绍下数据帧的概念。
我们要进行数据通讯,那么通讯双方必须遵循一定的协议,这样,通讯双方才能够相互理解从对方所接收过来的数据。
帧是传送信息的基本单元,每帧由帧起始符标志域,控制域,数据长度域,数据域,帧信息纵向校验域及帧结束域等6个域组成。
每个域由若干字节组成。
比如有这样一个帧格式:
代码 字节数 说明
68H1 帧起始符
RTUA 4 终端逻辑地址
MSTA 2 主站地址与命令序号
68H 1 帧起始符
C 1 控制码
L 2 数据长度
DATA 变长 数据域
CS 1 校验码
16H 1 结束码
从这个数据帧格式可以看出,一个数据帧至少有13个字节。
而且前后中间都有规定。
这样我们就可以通过处理分析其中的某些字节来判断这一个数据帧的意义,来进行其他相关的工作。
当然不同的系统数据帧格式会有不同。
我们今天就用这个格式作例子。
4.数据的接收与发送
今天我只介绍下使用Comport控件接收和发送数据。
接收数据:
在OnRxChar事件中。
onRxChar的原型:
procedurTForm1.ComPortRxChar(Sender:
TObject;Count:
Integer)
这个事件当接收缓冲区中有数据时触发,count为缓冲区中的字节数。
发送数据:
发送数据本控件提供了6个函数:
WaitForAsync
WaitForEvent
Write
WriteAsync
WriteStr
WriteStrAsync
较常使用的是WriteStr。
functionWriteStr(constStr:
String):
Integer;
参数为字符串类型,返回实际发送的字节数。
apro功能很强大,而且开发这个控件组的公司已经倒闭,贡献了所有的源代码。
除了mscomm以外,另外3个都有源代码。
为什么用WriteStr,这里要解释下string类型。
Delphi的String类型非常强大,可以兼容PChar,ArrayofChar,WideString等字符串或字符数组类型。
还有一个很关键的作用是可以作为动态Byte数组来使用。
虽然很多参考书上没有介绍这种用法,但是经过我多次测试以及实践经验,发现没有出现任何不良反应。
比如你要发送$68805060203010000020这几个字符,可以定义一个字符串A:
string;然后用下面的代码:
setlength(A,10);
A[1]:
=Chr(68);
A[2]:
=Chr(80);
。
。
。
。
A[9]:
=Chr(00)
A[10]:
=Chr(20)
writestr(A);
当然看起来很烦琐,其实这不是String的优势所在。
它真正的优势在于
1.无须管理内存,交给Delphi来管理。
2.可以很方便的作为参数或者变量来进行处理。
后面讲。
5.数据的处理
前面讲了4节,都是废话。
重点在这一节。
如果从终端不断的发送数据到服务器上来,例如每隔10毫秒发送一帧数据,我们如何区分这些数据呢?
要知道,串口通讯是一位一位来传送的,接收则是一个字节一个字节来接收的,不是一帧一帧来接收的。
为了要判断一个字节是前一帧的数据还是后一帧的数据,我们也只能一个字节一个字节的判断。
先看一段代码,然后我再解释这段代码。
这样我们就可以明确地获取每一帧的数据了。
讲完这个之后,我再解释如何利用Delphi的面向对象特性来处理收到的这一帧数据。
帧格式就用刚才介绍的那个格式。
先定义几个全局变量:
FDataStatus:
Word; //0就绪 1.帧头1 2.帧头2
FNextLength:
Word; //接下来要读的长度
FCurrentLength:
Word; //当前的长度
FtmpStr:
string;//一帧数据
代码如下:
procedureComPortRxChar(Sender:
TObject;Count:
Integer);
var
S1:
string;
J:
Integer;
begin
caseFDataStatusof
0:
begin
J:
=0;
FtmpStr:
='';
repeat
ComPort.ReadStr(S1,1);
J:
=J+1;
until(Ord(S1[1])=$68)or(J=Count);
ifOrd(S1[1])=$68then
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);
ifFCurrentLength=FNextLengththen
begin
FDataStatus:
=2;
FNextLength:
=(Ord(FtmpStr[11])shl8)+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);
ifFCurrentLength=FNextLengththen
begin
FDataStatus:
=0;
FNextLength:
=0;
FCurrentLength:
=0;
FReceiveFrame.Str:
=FtmpStr;
SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));
end;
end;
end;
end;
caseFDataStatusof
判断目前接收的数据段。
0:
begin
J:
=0;
FtmpStr:
='';
repeat
ComPort.ReadStr(S1,1);
J:
=J+1;
until(Ord(S1[1])=$68)or(J=Count);
ifOrd(S1[1])=$68then
begin
FtmpStr:
=S1;
FDataStatus:
=1;
FNextLength:
=10;
FCurrentLength:
=0;
end;
end;
如果是一帧开始,那么就对相关参数进行初始化,数据帧初始化为空。
J:
=0;
FtmpStr:
='';
然后开始读,直到读到帧起始符$68或者读完为止,没有的话,就抛弃读到的数据(当然你也可以另外记下来,不过意义不大)。
repeat
ComPort.ReadStr(S1,1);
J:
=J+1;
until(Ord(S1[1])=$68)or(J=Count);
J就是读到的数据,J=count表示读完了缓冲区。
Ord(S1[1])=$68,S1是个字符串,ORD(S1[1])表示S1的第一个字节。
ComPort.ReadStr(S1,1);每次只读一个字节,所以S1里只有1个字节。
如果$68在你读的第二个字节里,那不是蒙混过关了?
如果读到了$68,就接下来设置读状态为第二阶段,帧数据加上$68,下阶段长度为10,读取的字节数为0.然后结束函数。
ifOrd(S1[1])=$68then
begin
FtmpStr:
=S1;
FDataStatus:
=1;
FNextLength:
=10;
FCurrentLength:
=0;
end;
如果缓冲区还有数据,它将再次触发OnRxChar事件。
只要缓冲区中有数据,没有处理或者没有正在处理,就会一直触发OnRxChar事件。
刚才将读状态设置为第二阶段了,那么现在就会执行第二阶段的代码:
Case语句来判断的。
1:
begin
J:
=FCurrentLength;
repeat
ComPort.ReadStr(S1,1);
FtmpStr:
=FtmpStr+S1;
FCurrentLength:
=FCurrentLength+1;
until(FCurrentLength=FNextLength)or(FCurrentLength-J=Count);
ifFCurrentLength=FNextLengththen
begin
FDataStatus:
=2;
FNextLength:
=(Ord(FtmpStr[11])shl8)+Ord(FTmpStr[10])+2;
FCurrentLength:
=0;
end;
end;
J:
=FCurrentLength;//计数器,记下共读了几个。
RTUA 4 终端逻辑地址
MSTA 2 主站地址与命令序号
68H1 帧起始符
C 1 控制码
L 2 数据长度
这就是我们第二阶段要读的10个字节。
第二阶段的处理是读10个字节,不管缓冲区里有几个字节,不管读几次,读满10个为止。
repeat
ComPort.ReadStr(S1,1);
FtmpStr:
=FtmpStr+S1;
FCurrentLength:
=FCurrentLength+1;
until(FCurrentLength=FNextLength)or(FCurrentLength-J=Count);
FNextLength=10是第一阶段设置的。
读到10个字节之后将进入第三阶段。
ifFCurrentLength=FNextLengththen
begin
FDataStatus:
=2;
FNextLength:
=(Ord(FtmpStr[11])shl8)+Ord(FTmpStr[10])+2;
FCurrentLength:
=0;
end;
下一阶段要读几个呢?
FNextLength:
=(Ord(FtmpStr[11])shl8)+Ord(FTmpStr[10])+2;
DATA 变长 数据域
CS 1 校验码
16H1 结束码
这就是最后一阶段要读的字节数。
但是DATA是变长,所以我们才会多分一阶段,如果都是定长的话,只用判断到帧起始符,然后再读需要的字节数就可以了。
好在有个
L 2 数据长度
表示DATA的长度。
所以就有了这一句。
FNextLength:
=(Ord(FtmpStr[11])shl8)+Ord(FTmpStr[10])+2;
+2是最后两个字节。
接下来就是第三个阶段了:
2:
begin
J:
=FCurrentLength;
repeat
ComPort.ReadStr(S1,1);
FtmpStr:
=FtmpStr+S1;
FCurrentLength:
=FCurrentLength+1;
until(FCurrentLength=FNextLength)or(FCurrentLength-J=Count);
ifFCurrentLength=FNextLengththen
begin
FDataStatus:
=0;
FNextLength:
=0;
FCurrentLength:
=0;
FReceiveFrame.Str:
=FtmpStr;
SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));
end;
end;
end;
第三个阶段的读法跟前面一样,都是读一个计一个,直到读完需要的字节为止。
J:
=FCurrentLength;
repeat
ComPort.ReadStr(S1,1);
FtmpStr:
=FtmpStr+S1;
FCurrentLength:
=FCurrentLength+1;
until(FCurrentLength=FNextLength)or(FCurrentLength-J=Count);
读好了就初始化:
ifFCurrentLength=FNextLengththen
begin
FDataStatus:
=0;
FNextLength:
=0;
FCurrentLength:
=0;
FReceiveFrame.Str:
=FtmpStr;
SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));
最后就得到一帧完整的数据FtmpStr,然后将这个数据发送给处理数据的函数。
SendMessage(CommServer.Handle,XM_OutData,0,LongWord(@FReceiveFrame));
相关的函数就会来处理它了。
这样,不关什么东西来,什么时候来,一帧完整的数据都在FtmpStr这个字符串里
当然具体的读法还要根据数据帧的格式来定,但是万变不离其宗:
就是将读到的字节跟给定的格式比较。
有了这些知识,就基本上可以应付串口通讯程序的编写了。
考虑到Delphi面向对象的特性,如果不用类来规范数据帧的格式似乎有些可惜了。
还是拿刚才那帧数据格式来举例:
我们可以这样定义基类。
TCustomGY=class(TObject)
//0规约基类
private
FFrameBegin:
Byte; //帧起始符
FTerminalLogicAddr:
T4Byte; //终端逻辑地址
FMasterStation:
T2Byte; //主站地址与命令序号
FFrameBegin2:
Byte; //帧起始符
FFrameControl:
Byte; //控制码
FDataLength:
Word; //数据长度
FFrameVerify:
Byte; //校验码
FFrameEnd:
Byte; //结束码
FPosSend:
TFramePosTerminal; //帧发起端
FPosReceive:
TFramePosTerminal; //帧接收端
procedureSetDataLength(constValue:
Word);
procedureSetFrameBegin(constValue:
Byte);
procedureSetFrameBegin2(constValue:
Byte);
procedureSetFrameControl(constValue:
Byte);
procedureSetFrameEnd(constValue:
Byte);
procedureSetFrameVerify(constValue:
Byte);
functionReadFrameDataLengthHi:
Byte;
functionReadFrameDataLengthLo:
Byte;
functionReadFrameFSEQ:
Byte;
functionReadFrameISEQ:
Byte;
functionReadFrameMSTA:
Byte;
functionReadFrameMSTA2:
Byte;
procedureSetPosReceive(constValue:
TFramePosTerminal);
procedureSetPosSend(constValue:
TFramePosTerminal);
procedureSetMS1(constValue:
Byte);
procedureSetMS2(constValue:
Byte);
procedureSetFrameFSEQ(constValue:
Byte);
procedureSetFrameISEQ(constValue:
Byte);
procedureSetFr
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Delphi ComPort 通信 中的 数据处理 范文