加载中
加载中
表情图片
评为精选
鼓励
加载中...
分享
加载中...
文件下载
加载中...
修改排序
加载中...
所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
优化arduino的.readString函数,以减少PWM信号延迟
小丛雨天下第一2022/07/15原创 航空技术电子技术 IP:江苏
中文摘要
在航模中,PWM信号是绕不过去的一部分。在这里为了篇幅考虑,不具体进行介绍,各位读者可自行在网上寻找介绍。在使用arduino进行电子制作的时候,.readString函数会经常被用于读取串口信号,然而为了通用性的考虑,该函数通常会需要至少1秒钟的时间进行读取。在本文中,作者将介绍一种针对PWM信号专门设计的算法。
关键词
无人机PWMarduino单片机算法

前情提要:

在航模中,PWM信号是绕不过去的一部分。在这里为了篇幅考虑,不具体进行介绍,各位读者可自行在网上寻找介绍。在使用arduino进行电子制作的时候,.readString函数会经常被用于读取串口信号,然而为了通用性的考虑,该函数通常会需要至少1秒钟的时间进行读取。在本文中,作者将介绍一种针对PWM信号专门设计的算法。

 

正文:
为了输出PWM型号,我们可以借用arduino官方的Servo库,输出50Hz的PWM信号,并且其每个周期内高电平时间的范围在1ms~2ms。 20210228211803396.png

下面展示一个标准的实现方式:

C++
#include <Servo.h> Servo myServo;   String comStr = ""; int comInt=0;   void setup() {   Serial.begin(9600);   while(Serial.read()>=0){} // 清除串口缓存     myServo.attach(9); // 定义PWM脚为pin9   myServo.writeMicroseconds(1000); // 设置PWM信号到最小值 }   void loop() {   if (Serial.available() > 0){     // 监听电脑串口     delay(10);     comStr = Serial.readString(); // 读取电脑输入的数字     comInt = comStr.toInt(); // 字符串格式转换为int格式     comInt = constrain(comInt, 10002000); // 限制最大最小值     Serial.println(comInt); // 通过串口向电脑发送即将发出的PWM信号大小     myServo.writeMicroseconds(comInt); // 向PWM口输出PWM型号       }   }

 

在作者的实际测试中,由于.readString的存在,在arduino收到信号的约1秒后才能发出修改后的PWM信号。这个延迟对于航模来说是绝对不可接受的,因此我们专门针对PWM信号开发了一直全新的算法。

 

C++
#include <SoftwareSerial.h>   uint16_t temp;   void setup() {   Serial.begin(115200);   RCSerial.begin(115200); }   int16_t readInt() {   char rec = '0';   int16_t num = 0;   while (true) {     if (Serial.available()) {       rec = Serial.read();       if (rec < '0' || rec > '9') {         if (num < 1000 || num > 2000) {           num = 1000;           return num;         }         return num;       }       num = num * 10 + (rec - '0');     }   } }   void loop() {   if (Serial.available()) {     Serial.println("Received");      temp = readInt();     Serial.println(temp);   } }

注意:上述代码仅仅是实现了读取和再次发送到电脑的功能,至于怎么将读取的信息输出为PWM信号,相信聪明的读者们只要参照第一例代码就能很快完成移植。


因为作者本人并没有写注释的习惯,因此我们挑出其中的精华部分专门讲解一下。

C++
      if (rec < '0' || rec > '9') {         if (num < 1000 || num > 2000) {           num = 1000;           return num;         }         return num;       }       num = num * 10 + (rec - '0');     }   } }

由于这段是从上文直接复制过来的,所以{}的嵌套关系稍微有点怪,不过大家应该都能理解。

我们串口接收到的信息是一个个字符。因此,第一个if语句是判断接收到的数据是否是数字,如果不是,那就结束。

然后,第二个if语句就是判断输入数据的大小,如果不在范围内,那就统统返回1000。

最后是,

C++
num = num * 10 + (rec - '0');

这句,首先是rec - '0'。由于本质上我们得到的是char类型数据,是ASCII码,具体而言,'0'的ascii码是48,'1'的ascii码是49。

所以,rec - '0',可以将它转换为具体的数字,就比如说当rec是'1'的时候,ASCII码相减49 - 48 = 1。现在你1 * 10 + 2 = 12        12 * 10 + 3 =123        123 * 10 + 4 =1234,就可以还原出原来的数字了。

 

在作者的实际测试下,用此方法时,从arduino接收信号到再次发出的延迟可以忽略不计,与.readString方法产生巨大差距。

图片 1.jpg

总结:

上述介绍的方法很巧妙,使用前一定要完全理解,不然容易出现很多意料之外的错误。第一次没看懂没关系,遇到不会的名词可以先上网查一下,要是多读几次还是读不懂,可以在下方进行提问,只要有时间,我很乐意解答。


[修改于 2年9个月前 - 2022/07/15 15:40:47]

来自:航空航天 / 航空技术电子信息 / 电子技术动手实践:实验报导严肃内容:教程/课程
8
1
新版本公告
~~空空如也
WernerPleischner
2年9个月前 修改于 2年9个月前 IP:广东
905703

readString()本来的逻辑就是读到超时为止

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
小丛雨天下第一作者
2年9个月前 IP:江苏
905704
引用WernerPleischner发表于1楼的内容
readString()本来的逻辑就是读到超时为止

唔,不太懂,能稍微展开说下嘛


引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
小丛雨天下第一作者
2年9个月前 IP:江苏
905734
引用WernerPleischner发表于1楼的内容
readString()本来的逻辑就是读到超时为止

我去查了一下,确实是你说的这样的。但也正是如此,我们需要一个新的算法来读取PWM信号。


引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
NullPeople
2年8个月前 IP:安徽
907683

很有钻研精神,但是方向错了,这种情况可以使用serial.readStringUntil(结束符)解决,即读到结束符快速结束本次读取,readString会默认等待防止不能完全读取缓冲区内容


引用
评论
1
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
小丛雨天下第一作者
2年8个月前 IP:江苏
907833
引用NullPeople发表于4楼的内容
很有钻研精神,但是方向错了,这种情况可以使用XXXXXXXXXadStringUntil(结束符)解...

感谢指导,之前确实不知道这个,大家或许可以当做一个扩宽思路的方法看看吧哈哈

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
量子隧道
2年8个月前 IP:广东
907862

不需要这么做。串口可以发送停止符。arduino读串口时读到停止符可以立刻退出。可以读一读官方例程


引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
warmonkey
2年7个月前 IP:广东
908479
引用小丛雨天下第一发表于3楼的内容
我去查了一下,确实是你说的这样的。但也正是如此,我们需要一个新的算法来读取PWM信号。

正常都是先读到缓冲区,确定得到了一个有效数据帧,再做解析.

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
amo
2年7个月前 IP:广东
908482

想要速度,就别琢磨Arduino了

固定帧长可用DMA中断

不固定帧长可用DMA+串口超时中断

没有DMA的话,串口中断保存到缓冲区,超时中断触发处理

即使是啥特殊性能都没有的老51,也可以用定时器来做串口超时,实现ModBUS那种3.5字节断帧的性能。

引用
评论
1
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

小丛雨天下第一
机友 笔友
文章
5
回复
20
学术分
0
2020/08/24注册,7个月5天前活动

在学校努力学习,在家努力玩的高中生

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:江苏
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的