首、尾、控制符的格式的串口数据包
数据包协议是为了在连续发送多条命令时,接收方能够容易判断从哪里开始是命令的开始,到哪里结束。
当属性 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);
}
}
//--------------------------------------------------------------------------- |
|