极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 24639|回复: 10

软串口发送数据突然出现异常

[复制链接]
发表于 2017-6-3 17:46:37 | 显示全部楼层 |阅读模式
本帖最后由 微凉天 于 2017-6-14 22:42 编辑

使用两个arduino nano,一个发送数据(用固定数据测试),另一个接受数据并将其保存为数组,两个nano使用的均是软串口。之后串口打印出数据。实验中发现数据每隔几十组就会出现一次异常,这是什么原因,求教

红色为异常数据

经过几分钟后接受的数据甚至变成这样
附上代码
发送端:
  1. #include <SoftwareSerial.h>
  2. SoftwareSerial XbeeSerial(2,3);

  3. float PWM_out_A = 110;
  4. float PWM_out_B = 120;
  5. float PWM_out_C = 130;
  6. float PWM_out_D = 140;
  7. String input = "";
  8. float data[20] = {0};

  9. void setup()   
  10. {  
  11.   Serial.begin(9600);
  12.   while(!Serial){;}
  13.   XbeeSerial.begin(9600);
  14. }  

  15. void loop()
  16. {
  17.   float acc[3],ang[3],gyro[3];
  18.   for(int i = 0;i<3;i++)
  19.   {
  20.    acc[i] = i*10+0.11;
  21.    gyro[i] = i*10+0.22;
  22.    ang[i] = i*10+0.33;
  23.   }
  24.   
  25.   XbeeSerial.println(PWM_out_A);
  26.   XbeeSerial.println(PWM_out_B);
  27.   XbeeSerial.println(PWM_out_C);
  28.   XbeeSerial.println(PWM_out_D);

  29.   for(int i = 0;i<3;i++)
  30.   {XbeeSerial.println(acc[i]);}
  31.   for(int i = 0;i<3;i++)
  32.   {XbeeSerial.println(gyro[i]);}
  33.   for(int i = 0;i<3;i++)
  34.   {XbeeSerial.println(ang[i]);}
  35.   delay(550);
  36. }
复制代码


接受端:
  1. #include <SoftwareSerial.h>
  2. SoftwareSerial softSerial(2,3);

  3. String input = "";
  4. float data[20] = {0};

  5. void setup()   
  6. {  
  7.   Serial.begin(9600);
  8.   while(!Serial){;}
  9.   softSerial.begin(9600);
  10.   softSerial.listen();
  11. }  

  12. void loop()
  13. {
  14.   int cun = 0;
  15.   while(softSerial.available())
  16.   {
  17.     if(softSerial.peek()!='\n')
  18.     {input += (char)softSerial.read();}
  19.     else
  20.     {
  21.       softSerial.read();//NOTE:clear the serial date '\n'
  22.       float input_int = input.toFloat();
  23.       data[cun] = input_int;
  24.       input = "";
  25.       cun++;
  26.     }
  27.   }

  28.   //Show on the screen
  29.   for(int i=0;i<20;i++)
  30.   {
  31.     Serial.print(i+1);Serial.print(": ");
  32.     Serial.print(data[i]);Serial.print("  ");
  33.    }
  34.   Serial.println();
  35.   delay(500);
  36. }
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2017-6-4 17:29:17 | 显示全部楼层
程式有点乱来, 特别是读取的部份。

每次发出  14 个数, 硬要设定 20 个的 data 去显示, 有点莫名其妙.  但应该影响不大.

通讯没好的协定 (连最基本的开始结束码也没做), 只是用 delay 的时间去配合.  如果是跟别人的系统连线, 还可以说是逼不得已.  但是自己写的话, 就有点那个了.

接收跟发出一开始就没同步过, 程式又怎能假设它们同步.  输出 delay(550) 跟 接收 delay(500) 更是完全没根据的设定.  这是超级错误, 谁可以保证两边的速度可以配合?  接收端就应该基於接收到的东西去做事, 不需要考虑 delay 的问题, 没输出就自然没东西可接收, 不用画蛇添足.

放进 data 之前, 没检查 cum 的值, 程式出大问题, 只是好运吧.

每次都没有清除接收区, 只要有一次读了两份, 之後都会显示出两份来.
回复 支持 1 反对 0

使用道具 举报

发表于 2017-6-4 17:40:13 | 显示全部楼层
本帖最后由 Super169 于 2017-6-4 17:55 编辑
zjz5717 发表于 2017-6-4 16:21
其实我现在不明白的是,原来数据里面的0是怎么来的。。。


不好意思,  想按回覆, 按错了反对.  怎麽没法取消的.

他最初的数据, 问题在於乱来 delay 做法.

两边执行再 delay 结果不同, 在一开始亦从没同步, 之後出现了一次积存了一份未读的数据, 在第2份发送同时, 开始读取.
结果就收到多於 14个数, 而因为读取时没 delay, 结果数值就分拆的读了.
再加上他没有在开始时清除 data, 就是没填满也会显示出 20 个数了.

重点在於写接收端时, 并不是根据接收的东西去处理, 而是用 delay 碰运气.  

补充一点: 接收端每接收一个字符之后delay(2)保证数据不会遗漏
这不一定是好的, 而且也要计算因应发送速度改变.

以 9600bps 计算, 发送 8 bit 大约是 1ms, delay 1-2 ms 影响不大.
如果是再快一点的通讯, 以 115200 为例, 发送一个 8 bit 只需 0.07ms, 如果每个 bit 停 2ms, 发送端就已发出了28个 byte, 要看你的 buffer size, 如果是 64 byte 的 buffer, 而且发送是连续的, 你接收 3 个 byte 後就会出现 buffer overflow.  不能再接收了.  
比较好方法, 是不作任何延迟, 而基於接收到的资料决定是否要再读下去.
比如数据的结束码是 "#", 在没收到 "#" 在前, 都是同一笔数据, 一直把接收到的填进 data 中就可以了.  直到收到 "#" 就进行处理, 次後重设一次,  准备等待下一次数据的到来.


另外, 发送端是不用 delay 的, 接收端加 delay, 是因为想多点时间让连续的数据可以一拼读取.
那发送端的 delay, 又有什麽作用?  只会把数据分散了, 读取的就更难处理.  
回复 支持 1 反对 0

使用道具 举报

发表于 2017-6-4 16:21:09 | 显示全部楼层
微凉天 发表于 2017-6-4 15:45
感谢解答,加上delay(2)后确实没有异常情况了,但是如图所示数据读取了两遍,除了设置延迟以外还有什么 ...

其实我现在不明白的是,原来数据里面的0是怎么来的。。。
回复 支持 0 反对 1

使用道具 举报

发表于 2017-6-3 18:31:52 | 显示全部楼层
接收端每接收一个字符之后delay(2)保证数据不会遗漏
回复 支持 反对

使用道具 举报

发表于 2017-6-3 18:32:07 | 显示全部楼层
发送端其实也可以加delay2
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-4 15:45:46 | 显示全部楼层
zjz5717 发表于 2017-6-3 18:32
发送端其实也可以加delay2

感谢解答,加上delay(2)后确实没有异常情况了,但是如图所示数据读取了两遍,除了设置延迟以外还有什么方法能避免这种情况呢。谢了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2017-6-6 10:28:11 | 显示全部楼层
Super169 发表于 2017-6-4 17:40
不好意思,  想按回覆, 按错了反对.  怎麽没法取消的.

他最初的数据, 问题在於乱来 delay 做法.

讲的非常好,受用了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-13 19:18:10 | 显示全部楼层
本帖最后由 微凉天 于 2017-6-13 21:41 编辑
Super169 发表于 2017-6-4 17:29
程式有点乱来, 特别是读取的部份。

每次发出  14 个数, 硬要设定 20 个的 data 去显示, 有点莫名其妙.   ...

不好意思回帖晚了,这两天有事情耽搁了。
感谢批评指正,我搜了一下数据包的发送和解析,参照网上的一些例程尝试编写了一下;
发送程序如下:
  1. #include <SoftwareSerial.h>
  2. SoftwareSerial softSerial(2,3);

  3. float PWM_out_A = 110;
  4. char tr_buf[20];
  5. void setup()   
  6. {  
  7.   Serial.begin(9600);
  8.   while(!Serial){;}
  9.   softSerial.begin(9600);
  10. }  
  11. void loop()
  12. {  
  13. int pwm_a = -PWM_out_A*100;
  14.   sprintf(tr_buf,"@%6d,%6d!",pwm_a,4567);
  15.   softSerial.print(tr_buf);
  16.   Serial.println(tr_buf);
  17. }
复制代码
接收端如下:
  1. #include <SoftwareSerial.h>
  2. SoftwareSerial softSerial(2,3);

  3. int num = 15;
  4. char re_buf[15];    //串口缓冲区的字符数组
  5. int counter=0;
  6. int sign = 0;

  7. void setup()      //设定串口和引脚模式
  8. {
  9.   Serial.begin(9600);
  10.   Serial.flush();   //请客串口缓存
  11.   while(!Serial){;}
  12.   softSerial.begin(9600);
  13.   softSerial.flush();
  14. }

  15. void loop()
  16. {
  17.     while(sign)
  18.   {
  19.     if(re_buf[num-1]=='!')
  20.     {
  21.     Serial.print(re_buf);
  22.     Serial.println();
  23. //    splitString(re_buf);
  24.     softSerial.flush();
  25.     Serial.println();
  26.     }
  27.     sign = 0;
  28.   }
  29. }

  30. void serialEvent() {
  31.   while (softSerial.available())
  32.   {
  33.     re_buf[counter]=(char)softSerial.read();
  34.     if(counter==0&&re_buf[0]!='@') return;      //第0号数据不是帧头              
  35.     counter++;      
  36.     if(counter==num)             //接收到数据
  37.     {   
  38.        counter=0;               //重新赋值,准备下一帧数据的接收
  39.        sign=1;
  40.     }
  41.   }
  42. }
复制代码
我尝试以发送两个数据,其中一个为负的浮点型数据,乘以100做整形发送,包头为‘@’,包尾为‘!’
发送端串口显示正常,如下图

下面是打印出来的数据:

可以看出数据能够接收到,但是后面会有一些奇怪的符号,应该不影响数据的处理吧?
本人确实是没什么工程经验的初学者,程序中若有不妥的地方还请不吝赐教,谢了~

另:我发现在串口事件里面用softSerial.availiable()时,直接打开串口助手是没有任何反应的,必须在串口调试窗口随便输入一个数值或符号(任意输入),之后串口助手才会打印出软串口接收到的数据。这是为什么呢,是要激活serialEvent()吗,有什么好的方法不用人工输入数据直接可以在串口事件中使用软串口呢?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2017-6-14 01:55:23 | 显示全部楼层
你現在的  interrupt 的作用, 只是一個 buffer, Serial 本身就有 buffer.
而你的 loop 內亦沒有其他特別的程序, 沒必要多加一層 buffer 去處理.
乾脆用回原來的方法, 在 loop 內讀取 serial 的數據, 只要處理好首尾碼就行了.

有一點要注意, 就是接收用的 buffer, 每次用之前或用完, 必須要清理一次.
用 char array 當 string 顯示時, 必須要自行在最後一個 character 後加 0.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-14 23:06:37 | 显示全部楼层
Super169 发表于 2017-6-14 01:55
你現在的  interrupt 的作用, 只是一個 buffer, Serial 本身就有 buffer.
而你的 loop 內亦沒有其他特別的 ...

感谢建议,现在程序已经差不多了~
回复 支持 反对

使用道具 举报

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

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

Archiver|联系我们|极客工坊

GMT+8, 2024-4-20 18:14 , Processed in 0.041238 second(s), 22 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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