用户登陆 用户注册
您的位置:首页> 技术文章>内容正文
如何在串口通讯程序中处理数据包
[正文]:在串口通讯程序中,经常要收到数据包,常有网友问及如何从这些数据包中提取需要的数据,如何处理校验等,在这篇文章里我举两个例子予以说明,程序说明为vc++6.0。
关于串口编程建立程序的细节,请参阅我主页上的其它文章。
同时,此文也适于其它通讯程序中艰数据报文的处理。
    首先,应该指出的是,所有这些处理均在串口事件处理函数oncommunication()中进行。
每当串口缓冲区中有一个或一个以上字符时触发串口通讯事件,该事件就驱动(调用)串口事件通讯处理函数oncommunication(),在这里就可以对接收到的数据进行处理,提取需要的数据。
举两个例子,一个是较为简单的位数据格式的处理,另一个是nmea无线通讯格式的处理,最后回答一位网友提出的问题,大家也可以探讨一下。
1.问题: 一个数据包,其串头为一个字符,字符值为7eh(16进制)'~',其后紧跟一字符‘e’,然后是数据串,串尾也为字符值为7eh的一个字符:即 ~exxxxxx...~ 如何处理这些数据?我们仍以串口调试助手源程序及其详细编程过程之一 中的oncomm()处理为例:void cscommtestdlg::oncomm() 123;// tod add your control notification handler code herevariant variant_inp;colesafearray safearray_inp;long len,k;byte rxdata[2048]; //设置byte数组 an 8-bit integerthat is not signed.cstring strtemp;if(m_ctrlcomm.getcommevent()==2) //事件值为2表示接收缓冲区内有字符123; ////////以下你可以根据自己的通信协议加入处理代码variant_inp=m_ctrlcomm.getinput(); //读缓冲区safearray_inp=variant_inp; //variant型变量转换为colesafearray型变量len=safearray_inp.getonedimsize(); //得到有效数据长度for(k=0;k<len;k++)safearray_inp.getelement(&k,rxdata+k);//转换为byte型数组for(k=0;k<len;k++) //将数组转换为cstring型变量123;byte bt=*(char*)(rxdata+k); //字符型strtemp.format("%c",bt); //将字符送入临时变量strtemp存放m_strrxdata+=strtemp; //加入接收编辑框对应字符串,在这儿,编辑框不是必须的,可做相应处理char ch=(char)bt;if(ch=='e')123; //在此处设置一个可以接收数据的全局标志,说明接收到数据前的‘e’标志了,下一步可以读数据了,同时将m_strrxdata清空        flag=2;         m_strrxdata.empty(); //下一次接收的便为有用的数据125;if(ch==0x7e) 123;      flag=1; //下面可以提取数据了125;if(flag==1) //标志为1,123;     ...//提取数据        flag=0; //提取完后,置标志为0125;125;125;//updatedata(false); //更新编辑框内容125;2 nmea无线通讯格式的处理2.1 nmea-0183报文格式字符串(ascii字符)格式如下:36;xxxx,xx,xx,xx,……*hh<cr><lf>36;:串头xxxx: 串头xx:数据字段,字母或数字xx:数据字段,字母或数字xx:数据字段,字母或数字,:逗号……*:星号,串尾hh:36;与*之间所有字符代码的校验和,(注意:校验和h为半byte校验,*后第1个h表示高4位校验和,第2个h表示低4位校验和。
得到校验值后,再转换成ascii字符。
)<cr>:0dh,回车控制符<lf>:0ah,换行控制符2.2 校验处理由于数据是动态接收,所以数据的处理也是动态进行,尽管有时会收到几个字符才触发一个串口事件,但字符的接收是一个一个接收的,因此就可以在程序中先判断串头36;是否到达,若串头到达,就可以开始计算校验,直至串尾*到达,这时*号后面的两个字符就是校验码,收到这两个校验字符,就可以与自己计算的校验值比较,若不正确,就报错,并继续处理下面的数据,若正确,则处理接收的字符,提取需要的数据。
2.3 程序cstring m_strreceived;cstring m_strchecksum;int flag;char ch为每次收到的字符m_strreceived += (char)ch;switch(ch)123;case '36;':checksum=0; //开始计算checksumflag=0;break;case '*':flag=2;c2=checksum & 0x0f; c1=((checksum >> 4) & 0x0f);if (c1 < 10) c1+= '0'; else c1 += 'a' - 10;if (c2 < 10) c2+= '0'; else c2 += 'a' - 10;break;case cr:break;case lf:m_strreceived[port-1].empty();break;default:if(flag>0) 123;m_strchecksum += ch;if(flag==1) 123;strcheck=strcheck+c1+c2;if(strcheck!=m_strchecksum) 123;m_strreceived.empty();125;else123;strinstruction=m_strreceived[port-1].left(6);if(strinstruction=="36;qgoku") //如果串头正确123;char *temp=(char*)((lpctstr)m_strreceived);//转换int speed=(atoi(temp+7));// 提取int 型数据char splevel=*(temp+25); //提取 char 型数据125;125;m_strchecksum.empty();125;flag--;125;elsechecksum=checksum^ch;break;125;3 网友的问题另外,我回答了一位网友的问题,大家也可以探讨一下:问题如下3:我用你的串口程序收来的十六进制数据是这个样的:00 10 10 c0 00 f0 f0 ab ac ad我现在要将高四位取出来,也就是011c0ffaaa(这点我不会,但我用left实现了,可得到的是字符,不是我要的数值)我只要011c0ff.我要把011c0ff进行如下的处理011转化成十进制c不变0ff也变成十进制后显示,成 17 c 255答:右移得到011c0ff后,可将其放在一个字符型变量cstring m_strreceive中:然后将其转换:char *temp=(char*)((lpctstr)m_strreceive;char tbuf[6]; //temporary viable tbuf[0]=temp[1]; tbuf[1]=temp[2]; tbuf[2]=temp[3]; tbuf[3]=0; //011 最后为0表示结束int data1=atoi(tbuf);char chdata2==temp[4]; //ctbuf[0]=temp[5]; tbuf[1]=temp[6]; tbuf[2]=temp[7]; tbuf[3]=0;int data3=atoi(tbuf); //0ff以上data1,chdata2,data3即为你要的数据


网站首页 培训课程 维修指南
技术文章 家电专栏 供应信息
求购信息 培训资讯 展会信息
电脑专栏 教程下载 资料下载
常用软件 PLC教程 PLC资料
变频伺服 低压电器 维修资料
人机界面 自控仪表 工控机类
文章标题: 搜文章
中国工控资源网手机版 2012
电话:010-67577139 13811659603
培训咨询QQ:657167934 471895637 销售咨询QQ:623769457
联系邮箱:zggkzyw@163.com
 京ICP备11002135号
报时(2026-04-06 06:42:23) 流量统计