Delphi TCOM控件串口通信调试寻找文件传输速度慢的原因

PC工具: IC util

手持设备:Dynasty ARM嵌入式设备

通信设备: TTL-USB串口传输器

 

问题:IC_Util向dynasty传输所需的配置文件,传输速度慢。

传输10k大小的文件

顺利时耗时大约10s,文件分块传输,1k/block or 2k/block。

波特率115200

传输时对于接受和发送的函数需要做适当的延时处理,同时需要对传输时的block大小做一定规定,BLOCK为2k时传输正常,BLOCK为1k时IC_Util将会不时的出现接收超时的问题。

Delphi串口模块使用的是TCOM模块

以下为TCOM模块的配置:

object DtmdComm: TDtmdComm
  OldCreateOrder = False
  OnCreate = DataModuleCreate
  OnDestroy = DataModuleDestroy
  Left = 415
  Top = 275
  Height = 225
  Width = 314
  object cmmMain: TComm
    CommName = 'COM1'
    BaudRate = 9600
    ParityCheck = False
    Outx_CtsFlow = False
    Outx_DsrFlow = False
    DtrControl = DtrDisable
    DsrSensitivity = False
    TxContinueOnXoff = True
    Outx_XonXoffFlow = False
    Inx_XonXoffFlow = False
    ReplaceWhenParityError = False
    IgnoreNullChar = False
    RtsControl = RtsDisable
    XonLimit = 500
    XoffLimit = 500
    ByteSize = _8
    Parity = None
    StopBits = _1
    XonChar = #17
    XoffChar = #19
    ReplacedChar = #0
    ReadIntervalTimeout = 500
    ReadTotalTimeoutMultiplier = 0
    ReadTotalTimeoutConstant = 0
    WriteTotalTimeoutMultiplier = 0
    WriteTotalTimeoutConstant = 0
    OnReceiveData = cmmMainReceiveData
    Left = 48
    Top = 16
  end
  object tmrPack: TTimer
    Tag = 20
    Enabled = False
    OnTimer = tmrPackTimer
    Left = 136
    Top = 56
  end
end

接收函数的对应实现接口为cmmMainReceiveData()

以下为cmmMainReceiveData()的实现:

procedure TDtmdComm.cmmMainReceiveData(Sender: TObject; Buffer: Pointer;
  BufferLength: Word);
var
  tmpArray:array[1..8096] of Byte;
 i,DataCount: Integer;
 tmpRecvStr:string;
 OneCommand:string;
begin
  //Delay(100);
  tmpRecvStr := '';
  Move(Buffer^,PChar(@tmpArray)^,BufferLength);
  for   i:=1   to   BufferLength   do
    tmpRecvStr:=tmpRecvStr+inttohex(tmpArray[i],2);
  RecvPOSStr := RecvPOSStr +tmpRecvStr;
  OutputMemo.Lines.Add(FormatDateTime('hh:nn:ss-zzz',now)+'---->[Receive]:');
  OutputMemo.Lines.Add(GetBinStrWithSpace(tmpRecvStr));
  if Copy(RecvPOSStr,1,2) = '02' then
  begin
    CommandType := Copy(RecvPOSStr,3,2);
    try
      DataCount := StrToInt('$'+Copy(RecvPOSStr,5,2))*256;
      DataCount := DataCount +StrToInt('$'+Copy(RecvPOSStr,7,2));
      DataCount := DataCount * 2;
      if Copy(RecvPOSStr,11+DataCount,2) <> '03' then //03开头的数据
      begin
        MoniterMemoAppend(FormatDateTime('yy-mm-dd hh:nn:ss',now)+'---->Data format error,(02,03)。'+Copy(RecvPOSStr,11+DataCount,2),0,0,0);
        RecvPOSStr := '';
        POSCommand := '';
        Exit;
      end;

      if ((CommandType <> '40') and (CommandType <> '41') and
          (CommandType <> '42') and (CommandType <> '43') and
          (CommandType <> '44') and (CommandType <> '45') and
          (CommandType <> '46') and (CommandType <> '47') and
          (CommandType <> '48') and (CommandType <> '49') and
          (CommandType <> '50') and (CommandType <> '51') and
          (CommandType <> '52') and (CommandType <> '53') and
          (CommandType <> '54') and (CommandType <> '55') and 
          (CommandType <> '60') and (CommandType <> '61') and
          (CommandType <> '70') and (CommandType <> '71')) then
      begin
        RecvPOSStr := Copy(RecvPOSStr,9,DataCount);
        if CommandType <> '03' then
        begin
          LogOutputMemoAppend(FormatDateTime('yy-mm-dd hh:nn:ss',now)+'-------------------------------->[Transaction Log]:');
          OperPackageMessage(RecvPOSStr,tmpRecvStr);
          LogOutputMemoAppend(FormatDateTime('yy-mm-dd hh:nn:ss',now)+'-------------------------------->[End Log]');

          CommandType := IntToHex(StrToInt('$'+Copy(CommandType,1,2)) + 1 ,2);
          SendHostScript(CommandType);
        end;
      end
      else
      begin
        OneCommand := Copy(RecvPOSStr,9,DataCount);
        POSCommand := OneCommand;
        RecvPOSStr := '';
        if (CommandType = '46') then    //请求输入密码
        begin
          SendStringByCom(CommandType,StrToHex('ACK'));//响应ACK
          ExecInputPin(OneCommand);
          POSCommand := '';
        end
        else if (CommandType = '47') or (CommandType = '48')
             or (CommandType = '49') or (CommandType = '50')   then
        begin
          SendStringByCom(CommandType,StrToHex('ACK')); //响应ACK
          tmpRecvStr := '';
          OperSimulatorMessage(CommandType ,OneCommand, tmpRecvStr );
          POSCommand := '';
        end
        else if (CommandType = '51') or (CommandType = '52')
             or (CommandType = '60') or (CommandType = '61')  then
        begin
          SendStringByCom(CommandType,StrToHex('ACK')); //响应ACK
          tmpRecvStr := '';
          OperSimulatorMessage(CommandType ,OneCommand, tmpRecvStr );
          SendStringByCom(CommandType,tmpRecvStr);
          POSCommand := '';
        end
        else if(CommandType = '70') then
        begin
          //START TEST
          frmMain.ChangeLedColor(PosCommand);
          //EMD TEST
        end
        else if(CommandType = '71') then
        begin
          //START TEST
          frmMain.BeepSound(PosCommand);
          //EMD TEST
        end
      end;
      RecvPOSStr := '';
    except
      RecvPOSStr := '';
      POSCommand := '';
      MoniterMemoAppend(FormatDateTime('yy-mm-dd hh:nn:ss',now)+'---->Data length is not hex',0,0,0);
      Exit;
    end;
  end
  else  if (Copy(RecvPOSStr,1,2) = '0D') or (Copy(RecvPOSStr,1,2) = '0A') then
  begin
    MoniterMemoAppend(FormatDateTime('yy-mm-dd hh:nn:ss',now)+'---->Log:'+HexToStr(RecvPOSStr),0,0,0);
    RecvPOSStr := '';
    POSCommand := '';
  end
  else
  begin
    MoniterMemoAppend(FormatDateTime('yy-mm-dd hh:nn:ss',now)+'---->Log:'+HexToStr(RecvPOSStr),0,0,0);
    RecvPOSStr := '';
    POSCommand := '';
  end;
end;

procedure TDtmdComm.DataModuleCreate(Sender: TObject);
begin
  bOperPackBusy := False;
  PosMaxCommLen := 2000;
  RecvPOSStr := '';
end;

以下为文件下载中判断Poscommand是否有数据的函数

function TDtmdComm.ReceACKByComm(SendCommandType:string;TimeOut:Integer): Boolean;
var
  RecvStr:string;
begin
  Result := False;
  tmrPack.Tag := TimeOut;
  tmrPack.Enabled := True;
  while True do
  begin
    if POSCommand <> '' then
    begin
      RecvStr := POSCommand;
      tmrPack.Enabled := False;
      Break;
    end;
    if not tmrPack.Enabled then  //连接超时
    begin
      Exit;
    end;
    if not bComOpened then
    begin
      tmrPack.Enabled := False;
      Exit;
    end;
    Application.ProcessMessages;
  end;

  if SendCommandType <> CommandType then Exit;
  
  UnpackTLV(RecvStr);
  FindTLV($0,RecvStr);
  if RecvStr <> 'ACK' then
  begin
    Exit;
  end
  else
  begin
    Result := True;
  end;
end;

超时间为5s

文件传输函数将会对Poscommand的数据进行判断,传输过程中经常出现Poscommand数据为NULL,也就是没有成功获取从ARM设备端获取到数据从而超时的情况。

 

经过测试发现,在发送和接收之间增加适当的延时或者修改配置文件中ReadIntervalTimeout值对于传输超时现象有很大的改善。(最上面配置文件中红字部分)

1.当将ReadIntervalTimeout从原始的100ms增加为500ms时,无论对于Block为1k或Block为2k的传输单位都不会出现PC接收超时的情况。

2.在PC向ARM设备发送Block数据之间增加500ms的延时,也可以有效的改善PC接收超时的情况。

for j := 0 to 3 do
     begin
       Delay(500);//ms
       SendStringByCom('41',SendStr);//发送连接相应
       if not ReceACKByComm('41',5) then//5s超时等待接收数据
       begin
         MoniterMemoAppend(FormatDateTime('yy-mm-dd hh:nn:ss',now)+'------>Download file error['+IntToStr(j+1)+']',0,0,0);
         Continue;
       end;
       Break;
     end;

 但当Delay的时间少于500ms时,每次传输超时现象出现概率大大增加。

注:以上两种方法采用任意一种均可达到消除超时的效果。

 

================================  我是分割线 ======================================

分析为什么这两种操作可以改善超时?

尤其是为什么增加ReadIntervalTimeout参数可以改善超时现象?

因为根据ReadInervalTimeout的定义:

来确定所接收到的数据是否属子同一帧数据,其默认值是100ms,也就是说,只要任何两个字节到达的时间间隔小于1OOms,都被认为是属于同一帧数据。

想了半天脑袋都快炸了!!还是没有明白~哪天脑洞一开解决了这个问题再补上,先挖个坑...

 

就这样,祝您身体健康~

 

转载于:https://www.cnblogs.com/simon-code/p/5661102.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值