300分,请教串口通讯问题!(300分)

  • 主题发起人 主题发起人 chenshu_sc
  • 开始时间 开始时间
TO:hnxiong

没有其它的变通办法吗?因为我试了一下用同步,感觉实在是有些慢的(没用线程的情况)

若用线程对多串口设备进行操作的话,有没有例子呢? 谢谢
 
用线程的话可以提高收发速度,你可以根据你的硬件特点来选择,我的做法是:1.创建一个线程,将他挂起来。2.发送数据后,激活线程;3.收到数据或者超时后,挂起线程。
这中间还有很多小问题,可以根据不同的需要进行改进。我说几点我遇到过的问题:1.延时的问题,也就是超时的问题,我用的是一个整数的累加循环,我试过了GetTickCount延时,sleep延时,time延时,最后根据硬件特性还是选择了累加循环。2.如果收到的数据,需要即时处理的话,就要考虑了,因为线程是不会对外输出参数的(我也不知道说法准不准确),这就要求必须在线程中处理接收到的数据信息。这是我的一点小体会,希望对你有帮助。例子的话,也有,不过自己写的,不好意思拿出来,太差劲了呢。
 
TO:hnxiong
 别不好意思啊,把你写的给我看一下嘛,让我多学习一下啊^_^
 
我做过用SPCOMM连接Modem之后用AT指令来发送短信什么的,可能对你有点帮助。
联系:chenfreax@hotmail.com
 
明天我整理一下,贴出来给大家参考参考。
 
先谢谢楼上的两位大大了!
 
TO:hnxiong
 整理的事情进行得如何了啊?
 
1.协议已定好,上位机发送一个8位的数组给下位机,当下位机收到信号,并判断正确后,返回一个长度为2位数组。上位机进行判断,并显示下位机状态。
2.我的测试程序的目的是选出更适合下位机硬件(因为硬件已做好了)的方法,也就是更快、更稳定的通讯方式。

这个程序为同步通讯,time延时的通讯过程。
var
Form1: TForm1;
piCOM:integer;
hComm:thandle;
chrin:array [0..7] of byte;
Connected : Boolean;
inbuff: array[0..1] of byte;
implementation
type
timers=class
private
starttime:ttimestamp;
public
constructor create;
procedure start;
function get:longint;
end;
{$R *.dfm}
//*****************************初始化端口************************
function OpenComm(com:integer):Boolean;
var
CommTimeOut : TCOMMTIMEOUTS;

cc:TCOMMCONFIG;
Temp:string;
begin
case com of
1:Temp:='COM1';
2:Temp:='COM2';
3:Temp:='COM3';
end;
hComm:=CreateFile('COM1', GENERIC_READ or GENERIC_WRITE,
0, nil, OPEN_EXISTING, 0, 0); // 打开COM1
if (hComm = INVALID_HANDLE_VALUE) then begin // 如果COM 未打开
MessageBox (0, '打开通信端口错误!!','',MB_OK);
result:=False;
end
else
begin
CommTimeOut.ReadIntervalTimeout := MAXDWORD;
CommTimeOut.ReadTotalTimeoutMultiplier := 0;
CommTimeOut.ReadTotalTimeoutConstant := 0;
SetCommTimeouts(hComm, CommTimeOut);
GetCommState(hComm,cc.dcb); // 得知目前COM 的状态
cc.dcb.BaudRate:=9600; // 设置波特率为9600
cc.dcb.ByteSize:=8; // 字节为 8 bit
cc.dcb.Parity:=evenPARITY; // Parity 为 None
cc.dcb.StopBits:=TWOSTOPBITS; // 1 个Stop bit
if not SetCommState(hComm, cc.dcb) then
begin // 设置COM 的状态
MessageBox (0, '通信端口设置错误!!!','',MB_OK);
CloseHandle(hComm);
result:=False;
end
else
result:=True;
end;
end;

//***************************************************************

//***************************************************************
procedure TForm1.FormCreate(Sender: TObject);
begin
if opencomm(1)=true then
showmessage('端口创建成功。');
form1.Timer1.Enabled:=false;

end;
//***********************发送接收数据*******************************
procedure TForm1.Button1Click(Sender: TObject);
var
returncord:integer;
begin
chrin[0]:=1;
chrin[1]:=$15; //34
chrin[2]:=$43;
chrin[3]:=10;
chrin[4]:=1;
chrin[5]:=$15;
chrin[6]:=$43;
chrin[7]:=10;// 51 204
returncord:=send_msg(chrin,200); //发送接收数据
form1.Memo1.Lines.Add(inttostr(returncord)); //显示接收数据

end;


//*************************发送接收模块******************************
//采用的是TIMER延时
//sndata为发送数组,tmout为延时时间
function TForm1.Send_msg(sndata:array of byte;tmout: integer):integer;
var
timer:timers;
lrc:longword;
okstring:string;
comd:string;
waitS,comdLen:Integer;
outbyte :array of char;
i,j:integer;
inbuff: array[0..2] of byte;
nBytesRead, dwEvent, dwError:LongWORD ;
cs:TCOMSTAT;
ok:boolean;
begin
timer:=timers.create;
timer.start;
PurgeComm(hComm, PURGE_RXCLEAR and PURGE_TXCLEAR); // 清除COM 数据
for i:=0 to 7 do
WriteFile(hcomm,snddstr,Length(snddstr),lrc,nil); // 送出数据
repeat until (cs.cbInQue>0) or (timer.get>tmout);
ClearCommError(hComm,dwError,@CS); //取得状态
ReadFile(hComm,inbuff,cs.cbInQue,nBytesRead,nil); // 接收COM 的数据
memo1.Lines.Add(inttostr(inbuff[0]));
memo1.Lines.Add(inttostr(inbuff[1]));
result:=inbuff[0];
timer.Free;
end;
//*****************************************************************



procedure TForm1.Button2Click(Sender: TObject);
begin
form1.Timer1.Enabled := FALSE;

form1.Caption:='端口关闭!';

end;


{ timers }
//****************创建timers********************
constructor timers.create;
begin
inherited create;
start;
end;
//**********************************************

function timers.get: longint;
var
curtime:ttimestamp;
begin
curtime:=datetimetotimestamp(now);
result:=(curtime.Date-starttime.Date)*24*3600000+(curtime.Time-starttime.Time);
end;

procedure timers.start;
begin
starttime:=datetimetotimestamp(now);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
chrin:array [0..7]of byte;
returncord:integer;
begin
chrin[0]:=1;
chrin[1]:=$91; //34
chrin[2]:=$84;
chrin[3]:=85;
chrin[4]:=1;
chrin[5]:=$91;
chrin[6]:=$94;
chrin[7]:=85;// 51 204
returncord:=send_msg(chrin,300);
form1.Memo1.Lines.Add('');
form1.Memo1.Lines.Add(inttostr(returncord));
form1.Memo1.Lines.Add('');
end;

end.
 
第2个程序为同步通讯,线程接收,累加循环延时:
var
Form1: TForm1;
piCOM:integer;
hComm:thandle;
chrin:array [0..7] of byte;
CommThread : TCommThread;
Connected : Boolean;
implementation
{$R *.dfm}
function OpenComm(com:integer):Boolean;
var
CommTimeOut : TCOMMTIMEOUTS;

cc:TCOMMCONFIG;
Temp:string;
begin
case com of
1:Temp:='COM1';
2:Temp:='COM2';
3:Temp:='COM3';
end;
hComm:=CreateFile('COM1', GENERIC_READ or GENERIC_WRITE,
0, nil, OPEN_EXISTING, 0, 0); // 打开COM1
if (hComm = INVALID_HANDLE_VALUE) then begin // 如果COM 未打开
MessageBox (0, '打开通信端口错误!!','',MB_OK);
result:=False;
end
else
begin
CommTimeOut.ReadIntervalTimeout := MAXDWORD;
CommTimeOut.ReadTotalTimeoutMultiplier := 0;
CommTimeOut.ReadTotalTimeoutConstant := 0;
SetCommTimeouts(hComm, CommTimeOut);
GetCommState(hComm,cc.dcb); // 得知目前COM 的状态
cc.dcb.BaudRate:=9600; // 设置波特率为9600
cc.dcb.ByteSize:=8; // 字节为 8 bit
cc.dcb.Parity:=evenPARITY; // Parity 为 None
cc.dcb.StopBits:=TWOSTOPBITS; // 1 个Stop bit
if not SetCommState(hComm, cc.dcb) then
begin // 设置COM 的状态
MessageBox (0, '通信端口设置错误!!!','',MB_OK);
CloseHandle(hComm);
result:=False;
end
else
result:=True;
end;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
if opencomm(1)=true then
showmessage('端口创建成功。');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin

chrin[0]:=1;
chrin[1]:=$91; //34
chrin[2]:=$92;
chrin[3]:=238;
chrin[4]:=1;
chrin[5]:=$91;
chrin[6]:=$92;
chrin[7]:=238;// 51 204
if send(chrin)=true then
begin
Connected:= TRUE;
if CommThread.Suspended then CommThread.Resume ;
end
else
begin
Connected:= false;
if CommThread.Suspended then CommThread.Resume ;
end;

end;



procedure TForm1.timeDelay(MSecs: Integer);
var
FirstTickCount:longint;
begin
FirstTickCount:=GetTickCount;
repeat
Application.ProcessMessages;
until ((GetTickCount-FirstTickCount) >= Longint(msecs));
end;

{ TCommThread }

constructor TCommThread.Create;
begin
// inherited;
FreeOnTerminate := false;
inherited Create(true);

end;



procedure TCommThread.Execute;
var
nBytesRead,dwErrorFlags,dwLength : DWORD;
ComStat : PComStat;
fReadStat : Boolean;
iwait:integer;
inbuff:array [0..1] of byte;
begin
iWait := 0;
while (Connected=true) do
begin
GetMem(ComStat,SizeOf(TComStat));
ClearCommError(hcomm, dwErrorFlags, ComStat);
PurgeComm(hcomm,(PURGE_RXABORT and PURGE_RXCLEAR));
dwLength := ComStat.cbInQue;
if (dwLength>0) then
begin
fReadStat := ReadFile(hcomm, inbuff, dwLength,nBytesRead, nil);
if (fReadStat) then
begin
form1.Memo1.Lines.Add(strtoint(inbuff[0]));
connected:=false;
CommThread.Suspend;
end
else
connected:=true;
end;

iWait := iWait+1;
if iwait>10 then
CommThread.Suspend;
end;{while}
FreeMem(ComStat);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Connected := FALSE;
CloseHandle(hcomm);
{终止线程}
CommThread.Terminate;
form1.Caption:='端口关闭!';
end;


function TForm1.send(snddstr: array of byte): boolean;
var
i:integer;
ok:boolean;
lrc:dword;
begin
PurgeComm(hcomm, PURGE_RXCLEAR); // 清除COM 数据
for i:=0 to 7 do
begin
ok:=WriteFile(hcomm,snddstr,Length(snddstr),lrc,nil); // 送出数据
form1.Memo1.Lines.Add(inttostr(snddstr));
end;
if ok=true then
begin
result:=true;
connected:=true;
end
else
begin
result:=false;
connected:=false;
end;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
try
CommThread := TCommThread.Create;
except
Connected := FALSE;
form1.Memo1.Lines.Add('线程建立失败');
Exit;
end;
end;

end.
 
3。异步通讯,sleep延时

var
Form1: TForm1;
piCOM:integer;
hComm:thandle;
chrin:array [0..7] of byte;
Connected : Boolean;
inbuff: array[0..1] of byte;
osread,oswrite:overlapped;
implementation

{$R *.dfm}
function OpenComm(com:integer):Boolean;
var
CommTimeOut : TCOMMTIMEOUTS;
cc:TCOMMCONFIG;
Temp:string;
begin
case com of
1:Temp:='COM1';
2:Temp:='COM2';
3:Temp:='COM3';
end;
hComm:=CreateFile(PChar(Temp), GENERIC_READ or GENERIC_WRITE,
0, nil, OPEN_EXISTING, file_flag_overlapped, 0); // 打开COM1
if (hComm = INVALID_HANDLE_VALUE) then begin // 如果COM 未打开
MessageBox (0, '打开通信端口错误!!','',MB_OK);
result:=False;
end
else
begin
CommTimeOut.ReadIntervalTimeout := MAXDWORD;
CommTimeOut.writeTotalTimeoutConstant := 5000;
setupcomm(hcomm,1024,1024);
SetCommTimeouts(hComm, CommTimeOut);
GetCommState(hComm,cc.dcb); // 得知目前COM 的状态
cc.dcb.BaudRate:=9600; // 设置波特率为9600
cc.dcb.ByteSize:=8; // 字节为 8 bit
cc.dcb.Parity:=evenPARITY; // Parity 为 None
cc.dcb.StopBits:=twoSTOPBITs; // 1 个Stop bit
fillchar(osread,sizeof(osread),0);
fillchar(oswrite,sizeof(oswrite),0);
osread.hEvent:=createevent(nil,true,false,nil);
oswrite.hEvent:=createevent(nil,true,false,nil);
if not SetCommState(hComm, cc.dcb) then
begin // 设置COM 的状态
MessageBox (0, '通信端口设置错误!!!','',MB_OK);
CloseHandle(hComm);
result:=False;
end
else
result:=True;
end;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
if opencomm(1)=true then
showmessage('端口创建成功。');
form1.Timer1.Enabled:=false;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
re:integer;
begin
chrin[0]:=1;
chrin[1]:=255; //34
chrin[2]:=255;
chrin[3]:=0;
chrin[4]:=1;
chrin[5]:=255;
chrin[6]:=255;
chrin[7]:=0;// 51 204
send(chrin);
sleep(150);
readbuff;
end;



function TForm1.Send_msg(Sndstr: array of byte):integer;
var
lrc:dword;
cs:TCOMSTAT;
i,j:integer;
Connected:boolean;
chout:array[0..1] of byte;
nBytesRead, dwEvent, dwError:LongWORD ;
FirstTickCount:longint;
begin
PurgeComm(hComm, PURGE_RXCLEAR and PURGE_TXCLEAR); // 清除COM 数据
//mainform.Delay(80) ;
for i:=0 to 7 do
begin
WriteFile(hComm,sndstr,Length(sndstr),lrc,nil); // 送出数据
end;
PurgeComm(hComm, PURGE_RXCLEAR and PURGE_TXCLEAR);
sleep(200);
ClearCommError(hComm,dwError,@CS); //取得状态
if(cs.cbInQue>0) then
begin
ReadFile(hComm,chout,cs.cbInQue,nBytesRead,nil); // 接收COM 的数据
if chout[0]=chout[1] then
begin
result:=chout[1];
end;
form1.Memo1.Lines.Add(inttostr(chout[1]));
end
else
begin
result:=100;
end;
end;

procedure TForm1.timeDelay(MSecs: Integer);
var
FirstTickCount:longint;
begin
FirstTickCount:=GetTickCount;
repeat
Application.ProcessMessages;
until ((GetTickCount-FirstTickCount) >= Longint(msecs));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
form1.Timer1.Enabled:=false;
Connected := FALSE;
CommThread.Terminate;
form1.Caption:='端口关闭!';
end;


procedure TForm1.Timer1Timer(Sender: TObject);
var
re:integer;
doorchrout:array[0..1]of byte;
begin
chrin[0]:=0;
chrin[1]:=255;
chrin[2]:=255;
chrin[3]:=85;
chrin[4]:=0;
chrin[5]:=255;
chrin[6]:=255;
chrin[7]:=85;//
send(chrin);
sleep(350);
readbuff;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
form1.Timer1.Enabled:=false;
end;


//**********************读取模块***************************************
function TForm1.readbuff: integer;
var
i:integer;
cs:comstat;
readstat:boolean;
dwerror,byteread,dwerrorflages,dwlength:dword;
chout:array[0..1] of byte;
begin
clearcommerror(hcomm,dwerrorflages,@cs);
dwlength:=cs.cbInQue;
if dwlength>0 then
begin
readstat:=readfile(hcomm,chout,dwlength,byteread,@osread);
if (not readstat) and (getlasterror=error_io_pending) then
begin
while(not getoverlappedresult(hcomm,osread,byteread,true)) do
begin
dwerror:=getlasterror();
if dwerror=error_io_incomplete then
continue
else
break;
end;
end;
for i:=0 to 1 do
begin
form1.Memo1.Lines.Add(inttostr(chout));
end;
end
end;
//****************************************************************

//*************************发送模块*******************************
function TForm1.send(senddata: array of byte): boolean;
var
fwritestat:boolean;
dwerrorflages,dwlength,dwbyteswritten:dword;
cs:comstat;
freadstat:boolean;
i:integer;
begin
purgecomm(hcomm,purge_txclear and purge_rxclear);
for i:=0 to 7 do
begin
fwritestat:=writefile(hcomm,senddata,length(senddata),dwbyteswritten,@oswrite);
if (not fwritestat) and ( getlasterror()=error_io_pending) then
begin
while (not getoverlappedresult(hcomm,oswrite,dwbyteswritten,false)) do
begin
dwerrorflages:=getlasterror();
if dwerrorflages=error_io_incomplete then
continue
else
showmessage('can not continue!');
end;
end;
end;
end;
//***************************************************************************
end.
 
昨天有事去了,不好意思让chenshu_sc久等了。这几个程序各有各的优缺点,我的程序中最终采用的是同步+累加循环延时。这主要是我的硬件和程序的要求所决定的。你可以自己去看一下,最好是结合你的硬件去实验一下,这样才能找到最适合你程序的通讯方式。
我顺便告诉你一个函数,是DELPHI封装的为filewrite,fileread这两个函数为读写函数,在98下使用是createfile可以是异步或同步,到2000下使用时,只能为同步,异步初始化串口不能正常工作。希望我的程序对大家有帮助。
 
TO:hnxiong
 谢谢你的代码,我正在仔细研究.....
PS:
我在2000上用了异步写是正常的啊,而且SPCOMM在2000上也是正常的啊(SPCOMM虽然是同步异步都可以,但从运行状态来看,运行的时候应该是异步的),所以我想2000是应该可以用异步操作的吧?
 
还有账吗?才做过实例的,
 
学习中.................
 
TO:paulorwys

 什么叫“还有账吗?”....你刚做完类似的项目吗?
 
没人回答了吗?
 
不是大家都放假了吧?![V]
 
是的!直接用API会比较好,我觉得用控件在写数据时没怎么放便,用API的话呢,可以直接定义多种类型数组!
procedure Oil_OpenComm; //打开端口
var
cc:TCOMMCONFIG;
Temp:string;
begin
Temp:='COM1'; // 选择所要打开的COM
hComm:=CreateFile(PChar(Temp), GENERIC_READ or GENERIC_WRITE,0, nil, OPEN_EXISTING, 0,0); // 打开COM
if (hComm = INVALID_HANDLE_VALUE) then begin // 如果COM 未打开
MessageBox (0, '打开通信端口错误!','',MB_OK);
exit;
end;

GetCommState(hComm,cc.dcb); // 得知目前COM 的状态
cc.dcb.BaudRate:=CBR_4800; // 设置波特率为9600
cc.dcb.ByteSize:=8; // 字节为 8 bit
// cc.dcb.Parity:=NOPARITY; // Parity 为 None
cc.dcb.Parity:=EVENPARITY;
cc.dcb.StopBits:=ONESTOPBIT; // 1 个Stop bit

// cc.dcb.XonLim:=3;
// cc.dcb.XoffLim:=20;

if not SetCommState(hComm, cc.dcb) then begin// 设置COM 的状态
MessageBox (0, '通信端口设置错误!!!','',MB_OK);
CloseHandle(hComm);
exit;
end;
end;

WriteFile(hComm,P^,a,lrc,nil); //写端口
procedure Tfm_ICandOil.Get232Byte;
var
Temp : string;
nBytesRead, dwError:LongWORD ;
cs:TCOMSTAT; //记录端口状态与数据长度
i:integer;
begin
ClearCommError(hComm,dwError,@CS); //取得状态
if (cs.cbInQue = 0) or (hComm = 0) then exit;
if cs.cbInQue > sizeof(inbuff) then begin // 数据是否大于我们所准备的Buffer
PurgeComm(hComm, PURGE_RXCLEAR); // 清除COM 数据
exit;
end;
if cs.cbInQue <> 0 then
begin
for i:=Low(inBuff) to High(inBuff) do inbuff:=$00;
if ReadFile(hComm, inbuff,cs.cbInQue,nBytesRead,nil) then // 接收COM 的数据
PurgeComm(hComm,PURGE_TXCLEAR OR PURGE_RXCLEAR); //清空输入输出数据
CloseHandle(hComm);//关闭端口
 
多人接受答案了。
 
后退
顶部