关于从串口接收数据包的问题

现有一频率测量仪器,可在定义的时间内(10ms,20ms,50ms,100ms)按照定义的波特率传送一串高精度的频率测量结果过来,其数据格式如下
02 xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx 03 0D 0A
我的程序采用打开串口后用select的方法探测数据是否达到,收到数据后采用
switch(rcvChar_){
case 02:

break;
case 03:

break;
case 0D:

break;
case 0A:

break;
default:

}
这段代码进行分包。
程序运行起来后能正确收到数据并对数据进行解释,但是CPU占用率较高,在10ms传送一次数据的情况下,竟然达到15%的CPU使用率,控制器CPU是Pentium M1.8G。原来的我们系统采用486 133M的cpu,操作系统式vxWorks 5.5的,同样的代码好像cpu占用率没这么高(6%左右)。请教各位在qnx下我该如何提高接收此类数据包的效率,麻烦给出思路即可?_

你的程序会频繁访问内存。
在VxWorks5.5中,由于没有内存分页,所以内存访问速度快一些。
而在QNX中,内存访问速度会慢一些。
所以CPU使用率的差异是正常的。

建议方法:
1.构造一个Union,里面一个unsigned int,一个unsigned char[4]
2.串口来的数据按照4个一组放入unsigned char数组中
3.每次判断union的unsigned int中是否有030D0A
4.如果是,则回硕判断第一个字节是否为02
5.如果也是,则有了完整的一帧。

这样可以将比较次数降低为最多两次。
你现在的比较次数是4次,缩减一倍。当然,效率提升可能不到一倍。

在select()以后,你是怎样“收数据”的?就是一个read()? 还是不断read()一直到收到03 0d 0a 为止? 这是最耗CPU的做法,因为你很可能每次只能读一个字节进来。

看看 readcond() 这个函数,它可以进行“有条件”的读。

从 02 到 03 0D 0A间的字节长度是不是一定的?如果是的话,可以设最少字节数,这样,串口驱动在没收满这些字节前是不会让readcond()返回的。当然,为了保证不会因为数据丢失而永远不返回,可以在readcond()的条件里设个时钟来timeout.

即使字节长度不同,还是可以用 readcond() 来做个延时读。总之,看看那个文档里的min, time, timeout参数组合。你的目标不是让串口驱动每收一个字节就交给你,而是“收到这些字节后”,再一起交给你。

谢谢两位。
1)采用laris的方法在理想的情况下可以实现较高效率的分包,但在有误码和掉字节的情况下我想可能会需要做复杂的异常和同步处理,加上这部分代码后不知效率会如何。
2)xtang提出的readcond()是一条新思路。实际上我现在采用的方法read()一次可以收到6~8个字节,统计大概平均收3~4次能接受到一个包,主要耗时间的步骤是每个字节都需要对比好几次。
readcond()遗憾的是超时设置太粗,T*0.1s步长,如果能够按照ms来设定就好了。其实在每个周期内接收28个字节的时间视波特率不同只有几个到十几个ms,要是能探测出毫秒级的inter-byte传送时间就好,这样就可以采用这个时间来分包。周期数据传送方法如下

transfering empty transfering empty
|-------------|------------…------------|-------------|------------…- -----------|
|< 10/20/50/100ms ->|< 10/20/50/100ms ->|

此外再问问xtang大侠,readcond()能否设定比如说只要收到03 0d 0a就触发,我实在是笨,看例子好像只能设定一个限定符?若能实现收到03 0d 0a就触发那是最好的了,我们的系统其实在收到第一个字节的时候就已经晚了10/20/50/100ms了,这个值是闭环控制的一个主要的参考量。

由于转换格式的问题,刚才的传送方法post后没能表述清楚,特作修改如下:

–transfering------------empty----------transfering-----------empty----------…
|-------------|------------…------------|-------------|------------…- -----------|…
|<----------10/20/50/100ms-------->|<----------10/20/50/100ms----- —>|…

FORWORD是单个字符,不能设成字符串。

你收到的字节不是定长,所以不能用max/min来控制是吗?

看你的图,似乎TIMEOUT设到100ms也能用了?

实在不行的话,可以试试 TimerTimeout()和readcond()混用。

   struct sigevent ev;

   SIGEV_UNBLOCK_INIT(&ev);

   .....

   // 25ms timeout
   TimerTimeout(CLOCK_REALTIME, _NTO_TIMEOUT_REPLY | _NTO_TIMEOUT_SEND, &ev,  25 * 1000000L, 0);
   nbytes = readcond(fd, buf, n, n, 0, 0);
   ....

关键是你降低CPU使用率的目的是什么?
如果就是收数,100%都没关系。

我说的理想情况,是因为对你硬件不了解,不知你的串口硬件是否能判断接收
FIFO为空。如果有这个功能,那么每次读之前先读标志寄存器判断一下即可。

另,如果你的串口出现丢字节,那么只能说明你的硬件问题很大。
串口丢掉一个字节的情况极少极少。
如果出现误码,那你还有校验位啊。每次读出一个字节后判断一下标志寄存器是否有校验错误即可。