如何使用spcomm控件读写串口数据 ( 积分: 100 )

  • 主题发起人 主题发起人 zjxno1
  • 开始时间 开始时间
Z

zjxno1

Unregistered / Unconfirmed
GUEST, unregistred user!
需要实现的功能:
需要根据下位机的状态值写下位机的数据.如:要对下位机的地址A赋值,需根据下位机的B地址的值来定,若A值为0,则在B地址写入0;若A值为1,则在B地址写入1.

因为需要对下位机先进行读操作,再进行写操作,但是要获取读来的数据,必须等待串口接受数据的事件触发,从缓冲区解析获取的数据,然后才能再对下位机进行写操作,这个过程如何实现. 是用延时等待还是用Timer控制?
如果是用延时,我这样处理好象不行:
....
SendReadCommand(data); //向串口发读取数据的命令
sleep(10); //等待串口接受收据
if (ReadFromBuffer<>'') then //如果接收到数据
begin
SendWriteCommand(sendData); //发送数据
end;
在调用sleep的过程中,好象spcomm的recieveData事件总是不执行,也就是说ReadFromBuffer一定是空的, 就不会执行发送数据的命令了.
 
需要实现的功能:
需要根据下位机的状态值写下位机的数据.如:要对下位机的地址A赋值,需根据下位机的B地址的值来定,若A值为0,则在B地址写入0;若A值为1,则在B地址写入1.

因为需要对下位机先进行读操作,再进行写操作,但是要获取读来的数据,必须等待串口接受数据的事件触发,从缓冲区解析获取的数据,然后才能再对下位机进行写操作,这个过程如何实现. 是用延时等待还是用Timer控制?
如果是用延时,我这样处理好象不行:
....
SendReadCommand(data); //向串口发读取数据的命令
sleep(10); //等待串口接受收据
if (ReadFromBuffer<>'') then //如果接收到数据
begin
SendWriteCommand(sendData); //发送数据
end;
在调用sleep的过程中,好象spcomm的recieveData事件总是不执行,也就是说ReadFromBuffer一定是空的, 就不会执行发送数据的命令了.
 
你这么写是不行的
看一下这个帖子,你在接收事件里设置一个定时器,然后在定时器的触发事件里写发送代码
http://www.delphibbs.com/delphibbs/dispq.asp?lid=3122872
 
var i:integer;

SendReadCommand(data); //向串口发读取数据的命令
i:=0;
while i< 100 do
begin //等待串口接受收据
if (ReadFromBuffer<>'') then //如果接收到数据
begin
SendWriteCommand(sendData); //发送数据
break;
end;
sleep(10);
i:=i+10;
end else
MessageBox(Handle, '无返回数据!', '提示', MB_ICONASTERISK);
 
用SLEEP会搞得像死机一样,什么事也做不了
 
to maxim88:我已经这样做了6年了,软件也卖出不知多少套了,难道你有更好的等待通讯返回的方法吗?请赐教!
 
to maxim88:
在TCom232.TimerSendTimer事件中,作者这样写的
if not HaveData(SendDataQueue) then exit;
//现在如何确信com依然打开呢?否则readfrombuf 读了就被删除了
try
wmsg := nil;
if not ReadFromBuf(wmsg, SendDataQueue) then exit;
if wmsg = nil then exit; //如果成功会自动从队列删除
setlength(buf, wmsg.Len);
copymemory(buf, wmsg.Msg, wmsg.Len);

BytesToHexStr(TmpStr,wmsg.Msg, wmsg.Len);
//也可以BytesToHexStr(TmpStr, @buf[0], wmsg.Len);完全正确
SendLog(TmpStr);
他是在TCom232中使用TTimer来实现,我开始也是想这样实现的,但问题是他只是实现将接受到的数据再发往串口,而我想实现的规则可能是变化的,比如在 AComm.WriteCommData(@buf[0], wmsg.Len);之前可能要对buf的内容先进行处理,处理的方法要根据具体情况定,这样TimerSendTimer至少要增加一个处理过程的参数来决定该如何处理接受到的数据.这样在TCom232的create中就不能直接用TimerSend.OnTimer := TimerSendtimer;了,有什么好的解决办法没?
 
To daniel77:
在你的while循环执行的过程中,spcomm的onrevieveData中的代码会执行吗?
 
//串口接收,只有串口接收到数据帧才会触发此事件,可对数据帧进行协议判断和相应的处理
procedure TForm1.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;
BufferLength: Word);
var i:integer;
rxbuf:array of byte;
s:string;
begin
setlength(rxbuf,bufferlength);
move(buffer^,rxbuf[0],bufferlength);
//ASC显示
s:='';
for i:=0 to bufferlength-1 do s:=s+Char(rxbuf);
Memo2.Lines.Add(s);
//按你的要求,此处可加入协议判断,发送相应数据
 SLEEP(100);//如果需要延时
Comm1.WriteCommData(PAnsiChar('TEST'),4);
END;
按你“接收”再“发送”的流程,所有的事情在接收事件里就全部完成了,你也不需要轮询接收缓冲区
 
to maxim88:
问题是不是所有的时候都需要读完就写的呀,多数时候只要读取串口值就行了,不需要再写了。
 
To daniel77:
为什么我用你的方法不行啊?
总是执行会MessageBox(Handle, '无返回数据!', '提示', MB_ICONASTERISK);
 
各位不要用SLEEP,这样操作权无法转移到串口的触发事件,用APPLICATIN.PROCESSMESSAGES,
自已定义一个延时函数就可以了.我也做了好多串口通信软件,延时都是这样做的.
procedure tform1.timedelay(t:integer); //timedelay procedure
var i:integer;
begin
i:=gettickcount();
repeat
application.ProcessMessages ;
until gettickcount-i>t;
// application.ProcessMessages ;
end;
 
加个对读出数据的判断就可以了,需要写就写,不需要写就直接退出处理过程了
procedure TForm1.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;
BufferLength: Word);
var i:integer;
rxbuf:array of byte;
s:string;
begin
setlength(rxbuf,bufferlength);
move(buffer^,rxbuf[0],bufferlength);
//ASC显示
s:='';
for i:=0 to bufferlength-1 do s:=s+Char(rxbuf);
Memo2.Lines.Add(s);
//按你的要求,此处可加入协议判断,发送相应数据
 if s='需要写操作' then Comm1.WriteCommData(PAnsiChar('TEST'),4);
END;
 
maxim88大侠,你上次http://www.delphibbs.com/delphibbs/dispq.asp?lid=3122872给我的代码我看了,为什么我的Comm1ReceiveData事件总是不触发呢
 
to: loverofangel1
大侠不敢当,我的水平很有限
你先查查SPCOMM的属性设置有无问题,若还不行我可以给你发一个完整例子
 
to maxim88
还是您给我发个完整的例子吧,谢谢先!
 
to sxwy:
我用过你的方法,只是没写成函数,我是这么做的:
readData;
while i<100 do
begin
application.processMessages;
Inc(i, 5);
end;
if readBuff then
begin
dealData; //对要发送的数据进行处理
writeData;
end;
结果还是不行;

to maxim88:
这样的话S应该是个全局变量,在发命令的时候要对S先赋值.但如果是发了两个命令,在第一个命令发出后还未得到反馈,则第二个命令可能就覆盖第一个命令的S值,这样是不是会引起混乱?
 
肯定不会混乱。
如果当前的接收事件处理未完成,那么新接收的数据只会放在接收缓冲区,不会引发新的接收事件,S的值始终是第一个命令。
 
我还是说的详细点吧:
目前我用的通讯协议是MODBUS协议,下位机是西门子的S7-200 PLC, 而MODBUS协议中的地址和下位机的地址是这样的:
40001 --- Vb2 ;
40002 --- Vb4 ;
对一个MODBUS地址写入信息,其实是对应S7-200的两个地址,如写0x01到40001,其实是将0x0001写入vb2和vb3,即vb2=0x00; vb3=0x01;但是这样的话就会把vb2原有的值给覆盖了,而我的原意只想写vb3这个字节,我现在想这样实现: 先读出40001的值,假设读出的值为0x0100,然后将该值与0xFF00进行与操作,以保留高位字节,再将结果和0x01(待写入的值),最后将得到的结果写入40001这个地址.这样结果就是vb2=0x01, vb3=0x01.
所以就会有前面提到的读和写的说法.请问这该如何用你们的方法实现?
 
后退
顶部