用MScomm控件读写16台仪表,不要用sleep()低效延时,应该用什么策略???今天都在线等(100分)

  • 主题发起人 yuzhi2005
  • 开始时间
其实你说的ONCOMM.说白了,主动权都是撑握在你手里.也就是说你要什么时侯ONCOMM就会ONCOMM,除非设备坏了或者你发的命令不对.

道理还是一样,你不发符合协议的命令到仪表,仪表是不是响应的,也就不会ONCOMM.比如说发一个"FF"+仪表地址后,你与单片机的约定是300MS里接收完单片机所有的上传数据,那么一般的做法是发完这个命令后,等待300MS后,再去检测公共变量,是否收到上传的数据,如果没有收到,是再次请求数据,还是跳过处理(超时判断),这就要看你自已的定义了.
这个300MS怎么进行呢,当然不能用SLEEP.自已写一个延时调用程序,也不要用定时器,这样不好.
另一种情况,不知你是不是这样想的,我猜的,你不想等待这300MS延时.......
如果你想不等待这300MS,那就不好办了.你发完一个表的请求命令,再发第二个,第三个....这样,两个命令中间的延时很少,而对于这么短的时间来说,两个仪表可能在同一时间内响应命令,然后同是发数据上来,这样处理就不好办了.一般这种情况很少.因为串口只能在同一时刻内响应一个电平信号.485总线没有这样的竞争仲裁处理.
 
说了这么多,还是不行,我为你的应用做了个Demo。实际上,这是一个简单的串行口通信应用。你先试一下吧。
 
把你的Email地址给我。我的:ccwwdd@21cn.com
 
以前我做过类似的系统。大概是这样的。没有代码,同给出当时的想法,你参考。
var receive_str:string;//用于接收仪器返回的数据。
receive_flag:boolean;//接收正常标志
Err_flag:boolean;//超时标志。
i,count:integer;
//......循环发送命令到下位机
for i:=1 to count do
begin
receive_str:='';//先清空
Err_flag:=false;
Send_cmd(...);//发送命令。
delay(delay_time);//延时程序。delay_time是延时时间。不能使用sleep
if err_flag then
begin
showmessage('超时!');continue;//继续下一个表
end
else
begin
//处理接收回来的数据,
end;
end;
//。。。。。。。。。。。。。。。。
procedure oncomm;//你的接收事件
begin
//把下位机返回的信息放receive_str处理。
end;
//延时
procedure delpay(delay_time);
begin
....延时处理,但是不能使用sleep
如果超时,设置err_flag:=true;
end;
 
http://www.delphibbs.com/keylife/iblog_show.asp?xid=25233
不知为何我的笔记不能上传附件。文件大小只有26KB啊!
 
ccwwdd,我的邮箱是ycfwq@163.com
 
sxwy,你已经很了解我的想法了,我觉得可以等300M的延时,只要软件运行不卡就行了,很多朋友在sleep()之前加上交出程序控制权的语句,我试了,但是不好使,只要程序不是死循环,多等一点时间对我的程序来说也没有问题的,你再想想看,非常的感谢啊
 
sxwy,我的想法以及实现都和你写的差不多,关键问题就是delay我原先用的是sleep,你写的这个延时函数这种用法,我就是不会怎么不用sleep还能让时间等上几百毫秒再去设置err_flag:=true;
//延时
procedure delpay(delay_time);
begin
延时处理但是不能使用sleep
如果超时,设置err_flag:=true;
end;
 
我的QQ是191916049,大家加我吧
 
procedure delay(delay_time:integer);
var b:integer;
begin
b:=GetTickCount;
repeat
application.ProcessMessages ;
if receive_flag then //数据接受到,
begin
err_flag:=falsse;
exit;
end;
until (GetTickCount-b) >delay_time;
err_flag:=true;//超时
end;

procedure oncomm;//你的接收事件
begin
//把下位机返回的信息放receive_str处理。
数据返回时设置receive_flag:=true
end;
 
不要在延时内部判断是否超时,延时过后再判断,虽有一点误差,但可以忽略.
延时函数:
procedure tform1.timedelay(t:integer); //timedelay procedure
var i:integer;
begin
i:=gettickcount();
repeat
application.ProcessMessages ;
until gettickcount-i>t;
end;
procedure mscomm1.oncomm()
begin
Revhex := 接收十六进制据
end;

过程: PUBSENDDATA('ff000b001');//呼叫仪表地址
timedelay(500);等待500MS
if Revhex = '正确标识' then
处理
else
超时处理;
 
sxwy,我用过这个延时函数,软件运行时依然有明显卡的现象,好像死机了一样
procedure Delay(MSecs: Longint);
//延时函数,MSecs单位为毫秒(千分之1秒)
var
FirstTickCount, Now: Longint;
begin
FirstTickCount := GetTickCount();
repeat
Application.HandleMessage;
Now := GetTickCount();
until (Now - FirstTickCount >= MSecs) or (Now < FirstTickCount);
end;

你看看是怎么回事???
 
ccwwdd,我仔细的看了你的程序,写的很不错,我写的读命令部分不是太好,你给看看吧
我的协议是这样订的:
说明: [X] 内为字符的ASCII 码;$xx 表示要发送的十六进制数;( )内为解
说明: 微机下传命令均为6个字节。
1、取当前温度值及控制量参数
微机下传:[#]+[XX](地址)+[RE]+$0D
仪表上传:52个字节(占用:280—2B3H)
[*]+[XX](地址)+[XX](段号)+[XXXX](时间)+
[XXXX](总时间)+[XXXX](环温)+[XXXX](设定温度)+[XXXXX](开关状态)
[XXXXXX](温度)+[XX](输出)+(1号点)
[XXXXXX](温度)+[XX](输出)+(2号点)
[XXXXXX](温度)+[XX](输出)+(3号点)+[X](状态标志)+$0D
说明:
1 开关状态的ASCII码表示五个开关的状态,30表示关;31表示开。
2 状态标志: 30:检测状态+无参数修改31:检测状态+有参数修改
32:控制状态+无参数修改33:控制状态+有参数修改
我发读命令的子程序是这样写的:
procedure ReadTempt(adress1,adress2:byte);
var
send_1wendu:variant;//读参数的命令
InputLen,string_dingwei,swatch_i:integer;
wendu_inputstring,string_1wendu1,string_1wendu2,string_2wendu1,string_2wendu2,string_3wendu1,string_3wendu2:string;
string_huanjing1,string_huan:string;
begin
send_1wendu:= VarArrayCreate([0,5], varByte);
send_1wendu[0]:=$23;
send_1wendu[1]:=adress1;
send_1wendu[2]:=adress2;//通过这种方式传输变量我觉得很笨,
send_1wendu[3]:=$52;
send_1wendu[4]:=$45;
send_1wendu[5]:=$0D;
//如果串口已经打开,则发送数据
if MainForm.MSComm.PortOpen then
begin
// 首先清空缓冲区
MainForm.mscomm.InBufferCount:=0;
MainForm.mscomm.OutBufferCount:=0;
MainForm.MSComm.Output:=send_1wendu; //发送读命令
sleep(500);//这是造成程序卡的原因了!!!!
InputLen:=MainForm.MSComm.InBufferCount;
wendu_inputstring:=MainForm.MSComm.Input;//直接以ASCII码形式接收数据
string_dingwei:=pos('*',wendu_inputstring);//定位读入的字符串
end;
怎么样改成你那种要读下一台仪表直接+1就行了,怎么写个读下一台仪表的子程序啊???
 
不好意思,我对MSComm的发送命令不熟悉,所有笔记中的不对,我已经修改了。
发送函数改为:
procedure TForm1.Send_Device_ReadCommand(cmdNO: Integer);
var
SendBuf: Variant;
begin
if not MSComm1.PortOpen then Exit;

// 修改为你自己的命令字符串
// PC下传:[#]+[XX](地址)+[RE]+$0D
SendBuf := VarArrayCreate([0, 5], varByte);
SendBuf[0] := Ord('#'); //$23;
SendBuf[1] := Ord(cmdNO div 10);
SendBuf[2] := Ord(cmdNO mod 10);
SendBuf[3] := Ord('R'); //$52;
SendBuf[4] := Ord('E'); //$45;
SendBuf[5] := $0D;

MSComm1.InBufferCount := 0;
MSComm1.OutBufferCount := 0;
MSComm1.Output := SendBuf;

strInput_ := ''; // 清除接收数据为空
end;
上面的发送代码已经经过实际串行口验证,发送【23 00 01 52 45 0D】成功。
其中的:00 01对于不同的设备会变为:00 02,00 03,......01 04,01 05,01 06。
 
我解决此类问题的方法是:16个设备,利用16个mscommand,然后利用time来循环处理,就ok
 
改为
SendBuf[1] := Ord(cmdNO div 10) + Ord('0');
SendBuf[2] := Ord(cmdNO mod 10) + Ord('0');
更好一些,这样就符合你的协议了。
 
ccwwdd,你的办法应该可以,我试试看,谢谢啊,huxufeng,说的办法我也想过,但是没有实验,你觉得怎么样??这样就可以采用Oncomm触发方式了吧
 
huxufeng,你实验过没有,能操作吗??
 
一个小小的问题讨论了半天,太弱了吧.
给你个解决方案吧

1、

用下面的代码代理SLEEP(500)就不会卡壳了

WaitTime1:=Date+Time;
while ((Date+Time) < (WaitTime1+EncodeTime(0,0,0,500))) do
begin
Application.ProcessMessages;
if MainForm.MSComm.InBufferCount=52 then
begin
InputLen:=MainForm.MSComm.InBufferCount;
wendu_inputstring:=MainForm.MSComm.Input;//直接以ASCII码形式接收数据
string_dingwei:=pos('*',wendu_inputstring);//定位读入的字符串
break;
end;
end;

.....................


2、最好编写一个线程类,类的主要功能是采集仪表数据(完成协议编解码、协议通讯、数据存库功能),且在采集数据时创建互斥对象,记得要在完成采集仪表数据后释放互斥对象


3、用该线程类同时创建16个采集仪表数据,接着你就安心的等着这16个对象自己去完成采集仪表数据吧,不用担心这些对象因为使用串口打架,因为互斥对象可以使这些对象挨个来完成数据采集。
 
hn_chx,这种用法我没有用过,看的不是太明白,你能不能把这两句说的明白点:
WaitTime1:=Date+Time;
while ((Date+Time) < (WaitTime1+EncodeTime(0,0,0,500))) do
begin
包括变量定义
 

Similar threads

S
回复
0
查看
746
SUNSTONE的Delphi笔记
S
S
回复
0
查看
696
SUNSTONE的Delphi笔记
S
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
2K
DelphiTeacher的专栏
D
顶部