串口通讯控件开发的一点问题(20分)

  • 主题发起人 主题发起人 QSmile
  • 开始时间 开始时间
Q

QSmile

Unregistered / Unconfirmed
GUEST, unregistred user!
由于工作需要我时常要写串口通讯的东东。
近来我写了一个串口通讯的类发现了一个问题;
就是发送端一次发一个很长的数据时。
接收时分成了几段。
用 SPComm就没有这个问题。
我分析了一下 SPComm
它是应该是通过设定一个 TCommTimeouts 的
ReadIntervalTimeout =100,
再用 SetCommTimeouts() 来实现的。
我在我的程序里也加入了这样的语句
但还是不行。
谁有这方面的经验,指点一下。
 
他的接收缓冲区是有限制的
 
接收区都是 4096
 
你把两个缓冲区都设大一点,
还有注意把组织好的数据一次发出去
 
延时不够
 
数据是一次性发出的。
延时:如果延时?
 
缓冲区的问题
 
SPComm 的缓冲区是 4096
我定的也是 4096
 
你是用什么方法等待数据的呢?
如果是分成几段的话,你用循环等数据的话,应先用一个临时变量接收前一段,然后再循环等待,或者是用sleep(100)什么的等一下再接收一下看,我就是这样做的,其实spcomm和mscomm控件的使用方法大都差不多的
 
to :app2001
SPComm 与 MSComm 使用的方法绝对不是 Sleep 之后再接收。 SPComm 有源码你深入源码就可以看到它是我什么方法接收数据的了。用 WaitCommEvent .MSComm 没有源码,但我也一样可以肯定它也是用这种方法的。因为我在学 Delphi 之间是用VB写通讯。

不过你们的方法我都会试试。
 
我知道这些控件用的是什么方法,mscomm用的不过是oncomm事件,当有数据来是触发这个事件,但就连它的帮助上面也认为这个事件不一定准确,因为存在了一个影响传输二进制数据据的bug,还有一种方法就是直接用一个死循环来不停的检测它的接收缓冲,等若干时间直到有数据为止,否则就退出,在等的过程中我就用到了这个函数延时一会再来检测它的接收缓冲,这是我采用的方法。
至于spcomm我也用过一些,实际使用过程中我发现它的onreceivedata事件比mscomm能更为准确的捕捉到数据,但也不是百分之一百的的,所以最终我还是采用了死循环方式来接收数据。
做数据采集一直是我经常碰到的工作,虽然我用的方法不一定是最好的,但至少在我的工作中能满足到我的需要,我的本意是希望能帮到你,但并不是希望你提醒我看代码,谢谢
 
sleep(500);//TiamDelay 500MS
 
to app2001:
I am Sorry!

我看明白了,我研究了一下 SPComm 的源码。原来是这样的。
在 SPComm 的 ReadThread 里,是这样的。
....
SetCommMask(hCommFile, EV_ERR or EV_RLSD or EV_RING )
设置消息掩码时没有 EV_RXCHAR ,也就是说在接收到数据时没有消息返回给
WaitCommEvent( hCommFile, lpfdwEvtMask, lpOverlappedCommEvent )
它如何接收数据呢?
在这里它建立了一个 Overlapped 结构为 overlappedRead
再直接用异步方式读取串口。
ReadFile( hCommFile,
lpszInputBuffer^, dwSizeofBuffer, // <<-- 这里是 2048
lpnNumberOfBytesRead, lpOverlappedRead )
最后马上返回。
在 WaitForMultipleObjects 处等待所有的事件,可线程消息的传来。
接收的数据就用
ReadFile( hCommFile,
lpszInputBuffer^, dwSizeofBuffer, // <<-- 这里是 2048
lpnNumberOfBytesRead, lpOverlappedRead )
读取当 数据小于 2048 那就超时返回。如数据大于 2048 它就下次读取。
也就是说它每次返回一长串的数据是一个假象,如果一次发送的数据大于2048
它也是分两次接收。

to all:
对于你们的帮助我表示感谢,对于我语气的问题我表示抱歉。
Thank u very much and I am Sorry!

 
你的研究是比我深入多了,也让我受益非浅,谢谢
 
我自己开发过一个 串口通讯控件如下:
基本解决了延时的问题;
我是这样处理的,通过 ClearCommError(hCommHandle,dwError,@CS); 取得状态
然后看看缓冲区数据长度 :cs.cbInQue
然后在用ClearCommError(hCommHandle,dwError,@CS); 取得状态
比较这次和上次缓冲取数据的长度,看看是否一样长。如果一样长,说明已经收完了。
如果不一样长,重复上边的操作。
-------------------------------------------------------
下面是控件代码:

unit CommDai;

interface

uses
Windows, Messages, SysUtils, Classes,Forms ;

type
TParity = ( None, Odd, Even );
TStopBits = ( _1, _1_5, _2 );
TDataBits = ( DB5, DB6, DB7, DB8 );
TCommDai = class(TComponent)
private
FBaudRate: DWORD;
FreadComCount:integer;
FCommName: String;
FParity: TParity;
FStopBits: TStopBits;
FRecieveDelay: Word;
hCommHandle:Thandle;
FFtuAddress:word;
FDataBits: TDataBits;
procedure SetBaudRate(const Value: DWORD);
procedure SetParity(const Value: TParity);
procedure SetStopBits(const Value: TStopBits);
procedure SetRecieveDelay(const Value: Word);
procedure SetDataBits(const Value: TDataBits);


{ Private declarations }
protected
{ Protected declarations }
public
property Handle: THandle read hCommHandle write hCommHandle;
property readComCount:integer read FreadComCount;
function SendString(str:string): string;
// constructor Create( AOwner: TComponent ); override;
// destructor Destroy; override;
function stringtohex(str:string): string;
Procedure TimeDelay(DT:DWORD);
function StartComm:boolean;
procedure StopComm;
procedure SendByteArray(SendStr:String) ; overload;
procedure SendByteArray(ByteArray:Array of Byte; SendCount:word);overload;
procedure RecieveByteArray(var ByteArray:Array of Byte;var RecieveCount:word);overload;
function BytesToString(Bytes:Array of Byte;Count:word):String;
function RecieveByteArray:string; overload;

{ Public declarations }
published
property CommName: string read FCommName write FCommName; // String read FCommName write
property BaudRate: DWORD read FBaudRate write SetBaudRate; //read FBaudRate write
property Parity: TParity read FParity write SetParity; //read FParity write FParity
property StopBits: TStopBits read FStopBits write SetStopBits; //read FStopBits write SetStopBits
property RecieveDelay :Word read FRecieveDelay write SetRecieveDelay;
property FtuAddress: word read FFtuAddress write FFtuAddress;
property DataBits: TDataBits read FDataBits write SetDataBits;
{ Published declarations }
end;

procedure Register;
{$R CommDai.dcr}
implementation

procedure Register;
begin
RegisterComponents('dai', [TCommDai]);
end;

{ TCommDai }

procedure TCommDai.SetBaudRate(const Value: DWORD);
begin
FBaudRate := Value;
end;


procedure TCommDai.SetParity(const Value: TParity);
begin
FParity := Value;
end;

procedure TCommDai.SetRecieveDelay(const Value: Word);
begin
FRecieveDelay := Value;
end;


procedure TCommDai.SetStopBits(const Value: TStopBits);
begin
FStopBits := Value;
end;
function TCommDai.stringtohex(str:string): string;
var temp:string;
begin
str:=trim(str);
while length(str)>1 do
begin
temp:=temp+'$'+copy(str,1,2);
str:=copy(str,3,length(str)-2);
str:=trim(str);
end;
result:=temp;
end;
function TCommDai.SendString(str:string): string;
var temp:string;
begin
str:=trim(str);
while length(str)>1 do
begin
temp:=temp+' '+copy(str,1,2);
str:=copy(str,3,length(str)-2);
str:=trim(str);
end;
result:=temp;
end;
{ public function or procedure}
// constructor Create( AOwner: TComponent ); override;
// destructor Destroy; override;
Procedure TCommDai.TimeDelay(DT:DWORD);
var
TT:DWORD;
begin
//取得现在的Tick值
//TT:=GetTickCount();
//计算Tick差值是否超过设置值

// while GetTickCount()-TT<DT do
// begin
sleep(DT);
// Application.ProcessMessages; //释放控制权
// end;
end;

//以下是打开通信端口的程序
function TCommDai.StartComm:boolean;
var
cc:TCOMMCONFIG;
Temp:string;
begin
hCommHandle:=CreateFile(PChar(FCommName),//文件名指针,如com1,com2
GENERIC_READ or GENERIC_WRITE, //存取属性

2, //共享模式 0-不共享, 1-共相书
nil, OPEN_EXISTING, 0, 0); // 打开COM
if (hCommHandle = INVALID_HANDLE_VALUE) then begin // 如果COM 未打开
result:=false;
exit;
end;

GetCommState(hCommHandle,cc.dcb); // 得知目前COM 的状态

cc.dcb.BaudRate:= BaudRate;//CBR_9600; // 设置波特率为9600
cc.dcb.ByteSize:=ord(FDataBits)+5; // 字节为 8 bit
cc.dcb.Parity:=ord(FParity); //NOPARITY; // Parity 为 None
cc.dcb.StopBits:=ord(StopBits); //ONESTOPBIT; // 1 个Stop bit
if not SetCommState(hCommHandle, cc.dcb) then
begin// 设置COM 的状态
MessageBox (0, '通信端口设置错误!!!','',MB_OK);
CloseHandle(hCommHandle);
result:=false;
exit;
end;
result:=true;
end;

procedure TcommDai.StopComm;
begin
SetCommMask( hCommHandle,$0);
CloseHandle(hCommHandle);
end;
procedure TcommDai.SendByteArray(SendStr:string) ;
var StrTemp :string;
SendByteCount :integer;
SendBuff :Array[0..2047] of Byte;
nBytesRead, dwEvent, dwError:LongWORD ;
cs:TCOMSTAT;
pt:pbyte;
lrc:dword;
i:integer;
begin

StrTemp:=self.stringtohex(SendStr);
SendByteCount:=Length(StrTemp) div 3;
//SetLength(SendBuff,SendByteCount);
for i:=0 to SendByteCount-1 do
SendBuff:=StrToInt(copy(StrTemp,i*3+1,3));
//实际的传送动作
try
WriteFile(hCommHandle,SendBuff, SendByteCount ,lrc, nil); // 送出
except
application.MessageBox('发送失败!','系统提示!',mb_ok+mb_iconinformation);
end;
end;

PROCEDURE TcommDai.SendByteArray(ByteArray:Array of Byte; SendCount:word);
var
lrc:dword;
begin
try
WriteFile(hCommHandle,ByteArray, SendCount ,lrc, nil); // 送出
except
application.MessageBox('发送失败!','系统提示!',mb_ok+mb_iconinformation);
end;
end;

procedure TcommDai.RecieveByteArray(var ByteArray:Array of Byte;var RecieveCount:word);

var
StrTemp :String;
RecieveBuff :Array[0..2047] of Byte;
cs:TCOMSTAT;
pt:pbyte;
lrc:dword;
nBytesRead, dwEvent, dwError:LongWORD ;
i:integer;
comp:integer;
begin
timedelay(20);
ClearCommError(hCommHandle,dwError,@CS); //取得状态
comp:=cs.cbInQue;
// timedelay(1);
for i:=1 to 50 do
begin
timedelay(20);
ClearCommError(hCommHandle,dwError,@CS); //取得状态
if (comp=cs.cbInQue) and (cs.cbInQue>1) then break;
if (i>15) and (cs.cbInQue=0) then break;
comp:=cs.cbInQue;
end;
FreadComCount:=i;
// application.MessageBox(pchar(inttostr(i)),'系统提示!',mb_ok+mb_iconinformation);
RecieveCount:=cs.cbInQue;
if cs.cbInQue >2048 then
begin
PurgeComm(hCommHandle, PURGE_RXCLEAR);
RecieveCount:=0;
// 清除COM 数据
exit;
end;
try
ReadFile(hCommHandle, ByteArray,cs.cbInQue,nBytesRead,nil); // 接收COM 的数据
except

end;

end;




function TcommDai.RecieveByteArray:string; //用不到了
var
StrTemp :String;
RecieveBuff :Array[0..2047] of Byte;
cs:TCOMSTAT;
pt:pbyte;
lrc:dword;
nBytesRead, dwEvent, dwError:LongWORD ;
i:integer;
comp:integer;
begin

timedelay(10 );
ClearCommError(hCommHandle,dwError,@CS); //取得状态
comp:=cs.cbInQue;
timedelay(0);
i:=1;
while i<100 do
begin
timedelay(10 );
ClearCommError(hCommHandle,dwError,@CS); //取得状态
if (comp=cs.cbInQue) and (cs.cbInQue>1) then break;
comp:=cs.cbInQue;
inc(i);
end;


// 数据是否大于我们所准备的Buffer
if cs.cbInQue=0 then
begin
result:='';

exit;
end;
if cs.cbInQue >2048 then
begin
PurgeComm(hCommHandle, PURGE_RXCLEAR);
result:='';
application.MessageBox('接受溢出,可能是未打开端口!','系统提示!',mb_ok+mb_iconinformation);
// 清除COM 数据
exit;
end;
try
ReadFile(hCommHandle, RecieveBuff,cs.cbInQue,nBytesRead,nil); // 接收COM 的数据
except
application.MessageBox('接受失败!','系统提示!',mb_ok+mb_iconinformation);
end;
StrTemp:='';
for i:=0 to cs.cbInQue -1 do
StrTemp:=StrTemp+inttohex(RecieveBuff,2)+' ';
Result:=StrTemp;
end;

function TcommDai.BytesToString(Bytes:Array of Byte;Count:word):String;
var i :integer;
StrTemp :String;
begin
strTemp:='';
for i:=0 to Count-1 do
strTemp:=strTemp+ inttohex(Bytes,2)+' ';
Result:=strTemp;
end;


procedure TCommDai.SetDataBits(const Value: TDataBits);
begin
FDataBits := Value;
end;

end.
 
to : 青云
我是 RedFox,想不到在这里遇到你!近来干什么呀?
你的方法不失为一个好办法。
 
接受答案了.
 
后退
顶部