所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
优化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

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

#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, 1000, 2000); // 限制最大最小值
    Serial.println(comInt); // 通过串口向电脑发送即将发出的PWM信号大小
    myServo.writeMicroseconds(comInt); // 向PWM口输出PWM型号
   
  }
 
}

 

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

 

#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信号,相信聪明的读者们只要参照第一例代码就能很快完成移植。


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

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

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

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

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

最后是,

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年4个月前 - 2022/07/15 15:40:47]

来自:航空航天 / 航空技术电子信息 / 电子技术动手实践:实验报导严肃内容:教程/课程
8
1
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
WernerPleischner
2年4个月前 修改于 2年4个月前 IP:广东
905703

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

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

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


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

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


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

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


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

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

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

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


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

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

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

想要速度,就别琢磨Arduino了

固定帧长可用DMA中断

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

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

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

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

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

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

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

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:江苏
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}