ch2003_23 发表于 2009-4-16 19:33:12

Delphi通过串口接收下位机的数据总是乱套,内有详细描述,请指教,谢谢

自己在写一个PC机串口接收下位机ARM发送的AD转换的数据,并显示实时波形的东东,使用Delphi7+ComPort控件。

使用ComPort的OnRxFlag事件,事件字符设置为#10,当接收到数据帧头0x0a 时,触发这个事件,然后在事件句柄中调用ReadStr(Str,26)读取26个字节到Str中,这26个字节有一个字节的帧头0x0a,一个字节的帧尾0x0c,中间是AD采集到的数据24个字节(8通道*3字节)。

我用ARM发送26字节的固定数据到PC:
0x0a,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFE,0x0b,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFa,0xFb,0xFc,0xFD,0x0c。
用串口调试助手接收数据没有问题,可是用我自己编写的Delphi程序接收时经常会接收出错,具体是周期性发生读取到的Str不是要求的26字节,一会对,一会不对,大部分情况下是26字节,但偶尔会8个字节或18个字节,而且第一帧必是错误的。

后来我加上了判断帧头和帧尾,这样能够接收到正确数据,但是中间有大量数据被丢弃,按道理讲,每次接收到0x0a,则应该将其后的25个字节一起读取到Str中,为什么会少读呢,而且第一组数据必少?


源代码:(ComPort的RxFlag事件句柄)

procedure TForm1.ComPortRx(Sender: TObject);
var
Str:String;//每次接受的字符串
Ch1,Ch2,Ch3,Ch4,Ch5,Ch6,Ch7,Ch8: Integer;//三个字符连接得到整形(24位AD)
Cch1,Cch2,Cch3,Cch4,Cch5,Cch6,Cch7,Cch8: Single;//与参考电压计算后得到的电压值

begin
k :=ComPort1.ReadStr(Str, 26);//读取26个字节到Str

(*判断帧头和帧尾是否是0x0a和0x0c*)
if ((Str =Chr($0a)) and (Str =Chr($0c)))then
begin

(*接受到正确的数据次数加一,并用Label2显示*)
j :=j+1;
Label2.Caption :=IntToStr(j);

(*连接三个字符,并转化为整形*)
Ch1 :=StrByteToInt(Str+Str+Str);
Ch2 :=StrByteToInt(Str+Str+Str);
Ch3 :=StrByteToInt(Str+Str+Str);
Ch4 :=StrByteToInt(Str+Str+Str);
Ch5 :=StrByteToInt(Str+Str+Str);
Ch6 :=StrByteToInt(Str+Str+Str);
Ch7 :=StrByteToInt(Str+Str+Str);
Ch8 :=StrByteToInt(Str+Str+Str);

(*与参考电压计算,得到实际电压值*)
Cch1 :=3*Ch1/10000000;
Cch2 :=3*Ch2/10000000;
Cch3 :=3*Ch3/10000000;
Cch4 :=3*Ch4/10000000;
Cch5 :=3*Ch5/10000000;
Cch6 :=3*Ch6/10000000;
Cch7 :=3*Ch7/10000000;
Cch8 :=3*Ch8/10000000;

(*超过参考电压(2.5V)的转化为负数*)
if Cch1>2.5 then
begin
    Cch1 :=Cch1-5
end;
if Cch2>2.5 then
begin
    Cch2 :=Cch2-5
end;
if Cch3>2.5 then
begin
    Cch3 :=Cch3-5
end;
if Cch4>2.5 then
begin
    Cch4 :=Cch4-5
end;
if Cch5>2.5 then
begin
    Cch5 :=Cch5-5
end;
if Cch6>2.5 then
begin
    Cch6 :=Cch6-5
end;
if Cch7>2.5 then
begin
    Cch7 :=Cch7-5
end;
if Cch8>2.5 then
begin
    Cch8 :=Cch8-5
end;

(*实时波形显示,使用Iocomp控件*)
iPlot1.Channel.AddXY(px,Cch1);
iPlot1.Channel.AddXY(px,Cch2);
iPlot1.Channel.AddXY(px,Cch3);
iPlot1.Channel.AddXY(px,Cch4);
iPlot1.Channel.AddXY(px,Cch5);
iPlot1.Channel.AddXY(px,Cch6);
iPlot1.Channel.AddXY(px,Cch7);
iPlot1.Channel.AddXY(px,Cch8);
px :=px+1;

end

(*不是合法的数据帧*)
else

begin

(*接收到错误数据次数加一,并送Label显示*)
i :=i+1;
Label1.Caption :=IntToStr(i);
//k :=Length(Str);
//Memo1.Lines.Add (Str);
Memo1.Lines.Add (IntToStr(k));//显示不合法数据帧包含字节的个数

end;

end;

http://cache.amobbs.com/bbs_upload782111/files_14/ourdev_437157.JPG
1 (原文件名:1.JPG)

http://cache.amobbs.com/bbs_upload782111/files_14/ourdev_437158.JPG
2 (原文件名:2.JPG)

knight_avr 发表于 2009-4-17 09:31:56

逻辑错误:
if ((Str =Chr($0a)) and (Str =Chr($0c)))then

应该采用 循环查找

startIndex:integer;
endIndex:integer;
startIndex:=0;
endIndex:=0;
for i:=0 to bufferlength-1 do
begin
    if Str =Chr($0a)then
    begin
      startIndex:=i;
      break;
    end;
end ;
endIndex:=startIndex + (26-1);
if str<>char($0c) then exit;

这样就会 只辨认以第一个出现的 帧头,和帧头后的第26个帧尾

而且 在帧头之前接收的 一些数据 会自动忽略掉的

ch2003_23 发表于 2009-4-17 09:58:25

谢谢knight_avr 大侠,之前在Email中也请教过你

你说的循环查找的方法,我大致能够看懂,可是for i:=0 to bufferlength-1 do 这句中的bufferlength不知道什么意思?
因为我使用的是ComPort的RxFlag事件,即接收到0x0a这个字符就触发这个事件。然后读取26个字节到Str中。

请多多指点,谢谢

knight_avr 发表于 2009-4-17 10:19:26

bufferlength 就是实际接收到 的字节长度 我使用的SPCOMM有一个这个参数 ,commport 没有用过 呵呵
该怎么样操作 我也不知道

ch2003_23 发表于 2009-4-17 10:23:29

哦 谢谢啊 我试试看
SPCOMM不知道好不好用,改天用下试试

rube 发表于 2009-4-17 11:42:30

我是这样用的:
通过起始字符和帧长度来判断一桢是否接收完毕
定义一个buf,函数ComPort.Read(buf^, Count);读到buf
buf就是起始字符,buf就是帧长度,如果buf等于你设定的帧长就认为接收完成,开始处理接收帧

我认为像你这种通过起始字符和结束字符来判断帧的完整性应该采用DataPack,很好用的

zhchyl 发表于 2009-4-17 11:58:02

用DataPack比较好

ch2003_23 发表于 2009-4-17 12:01:26

再谢谢5楼 6楼的朋友
DataPack还没听说过,我先Google下看看哈,谢谢了

rube 发表于 2009-4-17 12:14:17

不用搜了,就是comport的一部分,如图,第二个
http://cache.amobbs.com/bbs_upload782111/files_14/ourdev_437297.jpg
(原文件名:截图00.jpg)

rube 发表于 2009-4-17 12:17:09

可以方便的设置StartString和StopString

ch2003_23 发表于 2009-4-17 12:21:41

谢谢 rube 朋友
这个我还没用过呢,试试看

ch2003_23 发表于 2009-4-17 15:35:00

再请教 rube 永丰庵 网友这个ComDataPacket怎么不好用呢,还是我设置有问题?
下图是我的设置,结果无法OnPacket

http://cache.amobbs.com/bbs_upload782111/files_14/ourdev_437352.JPG
ComDataPacket (原文件名:com.JPG)

OnPacket事件程序:
procedure TForm1.OnPacket(Sender: TObject; const Str: String);
begin
j :=j+1;
Label2.Caption :=IntToStr(j);
Memo1.Lines.Add(Str);
end;
结果是一直没有数据

ch2003_23 发表于 2009-4-17 15:36:47

下位机固定发送26个字符,一个开始字符0x0b(#11),一个停止字符0x0c(#12)

rube 发表于 2009-4-17 18:27:07

size设成0,或$11/$12试试,我也是初手,delphi的进制表示方法我没记住

ch2003_23 发表于 2009-4-17 18:58:15

Size应该表示停止位的字节数
#表示ASCII $表示十六进制吧

rube 发表于 2009-4-18 11:09:55

size和stopstring设一个就行了

ch2003_23 发表于 2009-4-19 09:26:14

哦 我试试看哈,谢谢

rube 发表于 2009-4-19 10:08:23

楼主能否将你的iocomps v3.0.4 SP2 R1共享一下

ch2003_23 发表于 2009-4-19 10:10:51

iocomps v3.0.4 SP2 R1在本站 伍奇网友发的18B20的帖子上有下载,你可以搜一下,网速不太好,上传总失败,实在找不着的话,加我QQ:277565600,我传给你

zhchyl 发表于 2009-4-19 11:35:33

开始字符和结束字符最好在程序中设置

ch2003_23 发表于 2009-4-20 10:58:35

果然如 【19楼】 zhchyl 指挥 所说,要在程序中设置起始停止字符和帧长度才有效,谢谢
   ComDataPacket1.ComPort :=ComPort1;
   ComDataPacket1.CaseInsensitive :=True;
   ComDataPacket1.StartString :=Chr($55);
   ComDataPacket1.StopString :=Chr($0d);
   ComDataPacket1.Size :=28; //帧长度,不是停止位长度

谢谢 zhchyl 指挥

rkfch 发表于 2009-9-10 09:40:52

DataPacket设置接收固定长度的数据帧,如果帧长不固定,应该还是使用接收缓冲区循环检测好一些。Cport的OnRxChar事件函数中有一个参数Count就是接收区的长度,读数据时可以,Comport.Read/ReadStr(地址,Count).

dhyana 发表于 2009-9-10 10:57:55

最为可靠的办法还是自己建立缓冲区,检测到结束标志就回溯查找起始标记。(如果有校验字的话,则检测到结束标记的后一个)然后解析接收到的数据。一般不要用控件直接提供的此类方法,兼容性和依赖性都不理想。
另外,串口名称建议使用形如 "\\.\" + "COM1"类型的命名
页: [1]
查看完整版本: Delphi通过串口接收下位机的数据总是乱套,内有详细描述,请指教,谢谢