主页C++ Builder 资料C++ Builder 串口控件Victor 串口控件示例程序数据包收发演示程序
C++ Builder 串口控件
Victor 串口控件使用说明
Victor 串口控件示例程序
 • 文本收发演示程序
 • 二进制数据收发演示程序
 • 数据包收发演示程序
C++ Builder 编程技巧
C++ Builder 操作指南
C++ Builder 参考手册
网友留言/技术支持
数据包收发演示程序(首、尾、控制符格式) - Victor 串口控件示例程序

首、尾、控制符的格式的串口数据包

数据包协议是为了在连续发送多条命令时,接收方能够容易判断从哪里开始是命令的开始,到哪里结束。
当属性 PackageType = cptFrameHeadTail; 并且属性 UsePackage = true; 时,
会启动首、尾、控制符的格式的数据包,可以通过 UsePackage 属性随时启动和停止数据包支持。

数据包有几种,例如:
1. 延时,如果持续有多长时间没接收到数据,再接收到数据时认为是命令的开始,要想可靠,需要长时间的延时,所以速度很慢,
   因为实现简单,很多人都在用;当属性 PackageType = cptFrameTimeout; 时,会启用这个类型的数据包;
2. 标识 + 长度 + 数据,如果接收方收到误码,把长度收错,后面的数据就乱套了,如果由于误码长度变大,再接收到的标识还以为是数据呢;
3. 首、尾、控制符的格式的数据包:头标识 + 数据 + 尾标识,
   这种方法发送的数据中,如果有作为头尾标识的字节,要转换为:控制符 + 字节,
   当属性 PackageType = cptFrameHeadTail; 会启用这个类型的数据包。这种方法的优点:
   ① 数据可以连续发送,不用间断;
   ② 接收到误码不串位,遇到包尾和包首会很容易同步;
   ③ 如果数据中包含CRC(循环冗余校验码),很容易判断数据是否有误码。

首、尾、控制符的格式的数据包看起来有些复杂,在对数据可靠程度要求比较高的时候用的比较多。
实际上在单片机上实现这个算法也很简单,编码和解码程序都不超过30条汇编语言语句(8051汇编)。

发送自定义的数据包时,FrameSettings的FrameHead、FrameTail、FrameCtrl 分别表示数据包的开始、结束、控制符,这些数据也发送到对方。
数据包格式:
数据包头 + 数据 + 数据包尾
如果数据中含有表示数据包头和数据包尾的字节时,要发送控制符 + 发送的字节

例如:头 0xDB, 尾 0xDE, 控制 0xDC
要发送 0x02 0xDB 0xEC,从串口里发出的数据是:
0xDB 0x02 0xDC 0xDB 0xEC 0xDE
包头 数据 控制 数据 数据 包尾

╰─ 这个0xDB因为前面有个0xDC控制符,所以按照数据处理,而不是包头。
如果收到误码,前面的0xDC坏了,收到0xDE时就会结束这个命令,通过数据里面的CRC校验码可知此包是错误的,下个0xDB自动同步,而不是数据串位。

以上编码和解码由控件内部完成,只需要调用 ReadPackage 和 WritePackage 方法就可以了
调用 WritePackage 时只需要给出 0x02 0xDB 0xEC, 接收端调用 ReadPackage 读出的数据是已经解码的数据: 0x02 0xDB 0xEC

例 (此例子已包含在控件压缩包里面):

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  YbCommDevice1->PackageType = cptFrameHeadTail;
  YbCommDevice1->FrameSettings->FrameHead = 0xdb; //数据包头
  YbCommDevice1->FrameSettings->FrameTail = 0xde; //数据包尾
  YbCommDevice1->FrameSettings->FrameCtrl = 0xdc; //数据控制符

  try
   {
     YbCommDevice1->Active = true;
   }
  catch(Exception &e)
   {
     ShowMessage("YbCommDevice1: "+e.Message);
     if(!YbCommDevice1->SettingsDialog(this,true))
       Application->Terminate();
   }

  YbCommDevice1->PackageSize = 4096; //最大可发送 4096 个字节的数据包
  YbCommDevice1->UsePackage = true; //启动数据包 (可以随时启动和停止, 与 Active 属性无关)
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonSetClick(TObject *Sender)
{
  YbCommDevice1->SettingsDialog(this,true);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonSendClick(TObject *Sender)
{
  const int BufSize = 4096;
  char Buffer[BufSize];
  int nBytes = 0;
  wchar_t *EndPtr=0; //指示数据转换错误 if(EndPtr){if(*EndPtr){ 转换失败 }}

  UnicodeString t,s = Edit1->Text.Trim();
  while((s.Length()>0) && (nBytes<BufSize))
   {
     int p = s.Pos(' '); //空格
     if(p>0)
      {
        t = s.SubString(1,p-1);
        s = s.SubString(p+1,s.Length()).Trim();
        Buffer[nBytes++] = wcstol(t.w_str(), &EndPtr, 16); //十六进制字符串转成字节
      }
     else //还剩下最后一个字节
      {
        t = s;
        s = L"";
        Buffer[nBytes++] = wcstol(t.w_str(), &EndPtr, 16); //十六进制字符串转成字节
      }
   }

  YbCommDevice1->WritePackage(Buffer,nBytes); //发送数据包
}
//---------------------------------------------------------------------------
void __fastcall TForm1::YbCommDevice1Package(TObject *Sender, int NotifyType)
{
  const int BufSize = 4096;
  unsigned char Buffer[BufSize];
  int nBytes = 0;

  while((nBytes=YbCommDevice1->ReadPackage(Buffer,BufSize))>0)
   {
     UnicodeString s;
     for(int i=0; i<nBytes; i++)
       s += IntToHex(Buffer[i],2) + " ";
     s = s.Trim();

     if(!s.IsEmpty())
       Memo1->Lines->Add(s);
   }
}
//---------------------------------------------------------------------------
◤上一页:二进制数据收发演示程序

C++ 爱好者 -- Victor Chen 的个人网站 www.cppfans.com 辽ICP备11016859号