arduino 串口通信 读写优化

在使用arduino uno与多个传感器和上位机进行串口通信时,受制于arduino羸弱的性能,常常无法发挥传感器的全部性能.因此,我将简述一些提高arduino串口效率的技巧.
查阅官网后,我们可以看到,目前(2021/1/1),硬串口Serial共有如下函数:

Functions
if(Serial) print()
available() println()
availableForWrite() read()
begin() readBytes()
end() readBytesUntil()
find() readString()
findUntil() readStringUntil()
flush() setTimeout()
parseFloat write()
parseInt() serialEvent()
peek()

串口函数基础

串口初始化阶段常用函数简介

if(Serial)常常被用于在void setup()阶段等待串口设置完成.官方示例代码如下:

void setup() {
   
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
   
    ; // wait for serial port to connect. Needed for native USB
  }
}
void loop() {
   
  //proceed normally
}

Serial.begin()Serial.end()则分别被用于初始化串口以及关闭串口.注意初始化过程运行开销较大,因此如非特殊情况,请尽量不要在void setup()以外的地方使用.
Serial.available()可以获取到接收缓冲区Rx还有多少个字符,它返回的实际上是接收缓冲区的头尾指针的差值:

int HardwareSerial::available(void)
{
   
  return ((unsigned int)(SERIAL_RX_BUFFER_SIZE + _rx_buffer_head - _rx_buffer_tail)) % SERIAL_RX_BUFFER_SIZE;
}

从代码中我们可以看出,当串口接收缓冲区Rx为空,那么Serial.available()的返回值为0.
Serial.availableForWrite()函数则能够在不影响write()这一过程的前提下,获取串口写缓存TX中能够写的字符的数量.
Serial.flush()目前的作用是等待串口完成write()这一过程.当然在不考虑具体输出,而纸上谈兵又要确保兼容性的情况下,write()函数的优化空间并不算大.
接下来最需要强调的就是void serialEvent(),这是串口中断函数,在每次循环之间,如果有数据传入硬串口的Rx端,那么就会调用这个函数.在中断里执行的代码由于不再出现在void loop()当中,因此可以省去调用Serial.available()这类函数进行判断的时间.以下面的代码为例:

String inputString="";
void setup(){
   
      Serial.begin(9600);
}
void loop(){
   
    while(Serial.available()){
   
        inputString=inputString+char(Serial.read());
        delay(2);
    }
    if(inputString.length()>0){
   
        Serial.println(inputString);
        inputString="";
    }
} //项目使用了 3220 字节,占用了 (9%) 程序存储空间.最大为 32256 字节.
  //全局变量使用了204字节,(9%)的动态内存,余留1844字节局部变量.最大为2048字节.
String inputString = "";         // a String to hold incoming data
bool stringComplete = false;  // whether the string is complete
void setup() {
   
  Serial.begin(9600);
}
void loop() {
   
  if (stringComplete) {
   
    Serial.println(inputString);
    inputString = "";
    stringComplete = false;
  }
}
void serialEvent() {
   
  while (Serial.available()) {
   
    char inChar = (char)Serial.read();
    inputString += inChar;
    if (inChar == '\n') {
   
      stringComplete = true;
    }
  } //项目使用了 3080 字节,占用了 (9%) 程序存储空间.最大为 32256 字节.
}   //全局变量使用了205字节,(10%)的动态内存,余留1843字节局部变量.最大为2048字节.

通过把回车符设为字符串的终止符,我们把代码执行的速度和代码量都做到了一定的优化.

Serial.read()与Serial.peek()

接下来,我们来对比一下HardwareSerial中的最后两个自带函数peek()read().先放代码:

int HardwareSerial::peek(void)
{
   
  if (_rx_buffer_head == _rx_buffer_tail) {
   
    return -1;
  } else {
   
    return _rx_buffer[_rx_buffer_tail];
  }
}
int HardwareSerial::read(void)
{
   
  // if the head isn't ahead of the tail, we don't have any characters
  if (_rx_buffer_head == _rx_buffer_tail) {
   
    return -1;
  } else {
   
    unsigned char c = _rx_buffer[_rx_buffer_tail];
    _rx_buffer_tail = (rx_buffer_index_t)(_rx_buffer_tail + 1) % SERIAL_RX_BUFFER_SIZE;
    return c;
  }
}

由此可见,peek()相比read()在C语言代码的层面省去了创建变量Rx缓存尾指针自增这两个过程.在实际代码中,使用peek()会比read()更快吗?不同于我们朴素的直觉,实际效率取决于具体的使用场景.

串口优化实例

有时串口传输的数据具有多种格式,我们来看一个激光测距传感器的例子:

D=1.314m,520#<CR><LF> 表示距离为1.314米,回光量为520
E=258<CR><LF> 表示超出量程,错误码为258

让我们暂且放下吐槽这些谐音梗的想法,如果需要让arduino提取出测距得到的距离信息,并且把发生错误时的距离返回值设为-1,我们可以使用如下写法:

if(Serial.read()=='D')
  distance = GetDistance();//GetDistance指用于获取距离的一段伪代码
else
  distance = -1;

我们可以把上述代码修改为

if(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值