Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2349209
  • 博文数量: 816
  • 博客积分: 10000
  • 博客等级: 上将
  • 技术积分: 5010
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-17 17:57
文章分类

全部博文(816)

文章存档

2011年(1)

2008年(815)

分类:

2008-12-17 18:00:15

 

-

串口数据包

数据包协议是为了在连续发送多条命令时,接收方能够容易判断从哪里开始是命令的开始,到哪里结束。
当属性 UsePackage=true 时,会启动数据包支持,你可以随时启动和停止数据包支持。

数据包有几种,例如:
1. 延时,如果持续有多长时间没接收到数据,再接收到数据时认为是命令的开始,要想可靠,需要长时间的延时,所以速度很慢,因为实现简单,很多人都在用;
2. 标识 + 长度 + 数据,如果接收方收到误码,把长度收错,后面的数据就乱套了,如果由于误码长度变大,再接收到的标识还以为是数据呢;
3. Victor控件采用的方法:头标识 + 数据 + 尾标识,这种方法发送的数据中,如果有作为头尾标识的字节,要转换为:控制符 + 字节,这种方法的优点:
① 数据可以连续发送,不用间断;
② 接收到误码不串位,遇到包尾和包首会很容易同步;
③ 如果数据中包含CRC(循环冗余校验码),很容易判断数据是否有误码。
Victor的方法看起来有些复杂,在对数据可靠程度要求比较高的时候用的比较多。
实际上在单片机上实现这个算法也很简单,编码和解码程序都不超过30条汇编语言语句(8051汇编)。

所有常见的 CRC校验 的函数已经包含在 Victor 控件里面了,在 yb_base.h 头文件中。
CRC校验码需要自己计算,并且包含在数据中。

发送自定义的数据包时,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

例:
//---------------------------------------------------------------------------
#pragma pack(push,1) //开始定义数据包, 采用字节对齐方式
const MyPkgSize = 1024; //数据包最大长度
typedef struct
{
    long nBytes; //第一个参数必须是 32 位整数, 表示数据的字节数
    char Data[MyPkgSize]; //数据, 可以是任意的
} TMyPackage; //自定义的数据包
#pragma pack(pop) //结束定义数据包, 恢复原来对齐方式

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
    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 = MyPkgSize; //这个数据必须正确!
    YbCommDevice1->UsePackage = true; //启动数据包 (可以随时启动和停止, 与 Active 属性无关)
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonSetClick(TObject *Sender)
{
    YbCommDevice1->SettingsDialog(this,true);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ButtonSendClick(TObject *Sender)
{
    TMyPackage pkg;
    pkg.nBytes = 0;
    char *EndPtr=0; //指示数据转换错误 if(EndPtr){if(*EndPtr){ 转换失败 }}

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

    YbCommDevice1->WritePackage(&pkg); //发送数据包
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
    TMyPackage pkg;

    while(YbCommDevice1->ReadPackage(&pkg))
    {
        AnsiString s;
        for(int i=0; i         s += IntToHex((unsigned char)pkg.Data[i],2) + " ";
        s = s.Trim();

        if(!s.IsEmpty())
            Memo1->Lines->Add(s);
    }
}
//---------------------------------------------------------------------------




--------------------next---------------------

阅读(1395) | 评论(0) | 转发(0) |
0

上一篇:C++爱好者

下一篇:C++爱好者 - 网友留言

给主人留下些什么吧!~~