极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 19606|回复: 11

如何解析串口接收的字符串

[复制链接]
发表于 2012-5-28 15:52:34 | 显示全部楼层 |阅读模式
我想在PC端接收电子罗盘输出的角度值,下面是我的做法:
1. 首先在Arduino上接上电子罗盘,每隔100ms输出一次角度值(0~359整数)
Serial.print(angle);
Serial.print('$'); //为什么写这一行见下面的说明
delay(100);

2. 然后在PC上写了一个win32串口接收程序,用于读取接收到的字符串并转换为整形角度值。
    设置为重叠I/O方式,当PC串口接收到数据时,每次读取1个字符赋给c,并添加到字符串str中,当接收到'$'时,就把之前接收到的str转为整形。
    举例来说,当Arduino端输出359时,PC端程序首先监测到串口缓冲区接收到字符,于是,先把'3'赋给c,str="3";然后读取串口缓冲区的'5',c='5', str="35";然后是‘9’,c=‘9’,str=”359“;最后是‘$',c=‘$',认为一个角度值已经输出完全,将str转化为int赋给变量angle后,清空str,接着从头开始。

3. 或许诸位同学会觉得这样很麻烦,为什么不每次读取3个字符或者更多字符,而要每次只读一个字符呢?我也这么想过,并试过:当我把每次读取字符个数改成3时,我发现每次读取的str并不是完整的一个”359“字符串,而是分裂成两个”3“和”59“,或者”35“和”9“,即第一次先接收到了”35“,下一次才接收到了”9“。
这也是我上面需要添加'$'和判断的原因。

4. 不过我现在想要增加功能,除了输出电子罗盘的角度之外,Arduino还要输出加速度计三个轴的值,类似于{X:33,Y:35,Z:1034, H:359},我想过可以按照上面的思路输出如下格式的字符串"33X35Y1034Z359$",然后PC端对X、Y、Z、$做判断,不过这样真的很麻烦。

5. 所以我想问问版上有没有同学有更便捷的方法来解析串口接收到的字符串数据?
Any advice will be appreciated!
回复

使用道具 举报

发表于 2012-5-29 23:40:55 | 显示全部楼层
恕我直言,你这个方法相当的笨拙

使用数据帧的方式是通行的做法。设定一个包含几个(一般2byte足亦)特殊字节的组合作为帧头标志。帧的最后一个字节用于存放奇偶校验数据。

因为pc上的串口是个不折不扣的龟速设备,所以你不能指望一次能够从串口中读取到所有你希望得到的数据。解决办法是把串口读取过程放在一个循环当中,当识别到帧头部分时即意味着一帧数据要开始了(当然,要考虑数据帧不完整的情况),因为数据帧的长度是已知的,如果顺利的读取了一帧长度的数据,那么就下来就是进行数据校验。校验通过,这就意味着一帧数据已经完整到手了,可以开始下一帧数据的读取。

数据如何在数据帧中表示,先前我的帖子里已经说过了。用字符来表示数据是对空间的极大浪费。

回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-5-30 13:58:16 | 显示全部楼层
SS15 发表于 2012-5-29 23:40
恕我直言,你这个方法相当的笨拙

使用数据帧的方式是通行的做法。设定一个包含几个(一般2byte足亦)特殊 ...

能否告诉我帖子链接?
回复 支持 反对

使用道具 举报

发表于 2012-5-30 15:12:38 | 显示全部楼层
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-5-30 18:28:54 | 显示全部楼层
SS15 发表于 2012-5-30 15:12
http://www.geek-workshop.com/forum.php?mod=viewthread&tid=264

我不太明白Arduino使用Serial.print()与PC传输的时候int型(比如说5)是以二进制表示的(0x05)还是以ascii码表示的?
回复 支持 反对

使用道具 举报

发表于 2012-6-2 13:16:44 | 显示全部楼层
我也想不通,好象不管用什么方式,PC端仍然以ASCII码接收的么......
回复 支持 反对

使用道具 举报

发表于 2012-6-2 16:59:49 | 显示全部楼层
arduino 里面,Serial.print 写到串口当中的数据是ASCII码,比如字符‘5’,写入串口的实际是0x35
但是,Serial.Write 写入串口的就是该变量的二进制形式,比如
byte a = 5;
Serial.Write(a);
那么此时写入串口的就是0x05
回复 支持 反对

使用道具 举报

发表于 2012-6-2 22:58:01 | 显示全部楼层
SS15 发表于 2012-6-2 16:59
arduino 里面,Serial.print 写到串口当中的数据是ASCII码,比如字符‘5’,写入串口的实际是0x35
但是,S ...


你說用write寫入是二進制形式, 那麼最後怎麼又說是0x05 ?
[0x]這不是代表十六進制的符號嗎?
回复 支持 反对

使用道具 举报

发表于 2012-6-3 12:48:59 | 显示全部楼层
jack4904 发表于 2012-6-2 22:58
你說用write寫入是二進制形式, 那麼最後怎麼又說是0x05 ?
[0x]這不是代表十六進制的符號嗎?

呵呵,严谨一点说应该是这样:写入串口的是在AVR C体系下以二进制形式表示的两字节整形变量5。

写入串口的应该是 0000 0101(不过这个俺也不敢百分之百确定,因为俺也不了解整形变量在AVR C当中的表示方法。之所以我说是这个是因为这好像是从pc端观察得到的结果),一般为了书写简单起见,用十六进制表示为0x05
回复 支持 反对

使用道具 举报

发表于 2012-6-5 00:37:22 | 显示全部楼层
原來如此, 我多想了哈哈
感謝回覆!
(另外請問我以後發言應該轉簡體嗎? 還是你們繁體也OK?)
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-6-5 10:50:23 | 显示全部楼层
SS15 发表于 2012-6-2 16:59
arduino 里面,Serial.print 写到串口当中的数据是ASCII码,比如字符‘5’,写入串口的实际是0x35
但是,S ...

谢谢。我在串口调试工具上测试了一下,使用Serial.write(),串口调试工具设置成接收HEX,可以正确显示接收数字的16进制显示。
另外发现查Arduino.cc发现Serial.write()只有显示write(byte)、write(byte[], n)和write(str),木有write(int),当尝试write(int)时,只会显示int值的低8位,如write(360)时,360=0x0168,所以收取到的是68
回复 支持 反对

使用道具 举报

发表于 2012-6-5 13:26:16 | 显示全部楼层
树·水·风 发表于 2012-6-5 10:50
谢谢。我在串口调试工具上测试了一下,使用Serial.write(),串口调试工具设置成接收HEX,可以正确显示接收 ...

这个简单:

byte arBuff[2];
int    nDat = 360;

memset(arBuff, 0x00, sizeof(arBuff));
memcpy(arBuff, &nDat, 2);

Serial.write(arBuff[0]);
Serial.write(arBuff[1]);

// OK!

评分

参与人数 1 +1 收起 理由
幻生幻灭 + 1 赞一个!

查看全部评分

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-5-4 00:39 , Processed in 0.043557 second(s), 22 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表