读取串行命令会花费太多时间

时间:2018-07-12 09:43:39

标签: c++ arduino serial-port microcontroller stm32

以下问题:我目前正在为NUCLEO F207ZG编写一个小程序,该程序通过串行端口为其他服务提供接口。我程序的目标是使用?variable之类的命令公开数据,并使用!variable <value>设置值(例如?threshold返回1400,而!threshold 1234将阈值设置为1234)。另外,程序中的变量与EEPROM同步以保留数据。

在整个过程中,我得到了一个包含SerialCommands.h库的代码库。

问题

主要问题是,如果设置后立即执行其他命令,则设置值会占用过多时间并破坏串行缓冲区(或类似的东西)。我用python(使用pyserial)编写了一些单元测试,这些单元测试尽可能完美地执行 get 请求(例如?threshold)。但是,如果我执行 set 命令(例如!threshold 1400),则我需要等待至少四秒钟,然后再发出任何其他请求,否则串行接口/缓冲区似乎丢失了一些数据。如果我尝试在Arduino串行监视器上发出任何 set -> get 请求,也会发生同样的事情。这是一个简短的示例:

  1. ?threshold =>返回'1400'
  2. !threshold 1234
  3. ?threshold =>什么都没发生
  4. ?threshold =>什么都没发生
  5. ?threshold =>返回“无法识别的命令???threshold”(有关此功能,请参见我的代码)

编辑:重要的事情我忘了提。程序恢复后(在第5步之后),可以正确查询该值。

  1. ?threshold =>返回'1234'

我还有一个闪烁的状态LED(以500毫秒为步长),如果设置了某些内容,闪烁会明显停止约一秒钟。

代码

这是我的代码库的(不起作用的)简化示例:

#include <SimpleTimer.h>    // https://github.com/marcelloromani/Arduino-SimpleTimer
#include <SerialCommands.h> // https://github.com/ppedro74/Arduino-SerialCommands/
#include <EEPROM.h>
#include "EEPROMAnything.h"
char serial_command_buffer_[64];

SerialCommands serial_commands_(&Serial, serial_command_buffer_, sizeof(serial_command_buffer_), "\r\n", " ");

SimpleTimer timer;
int filter_threshold;

void cmd_unrecognized(SerialCommands* sender, const char* cmd) {
  sender->GetSerial()->print("Unrecognized command [");
  sender->GetSerial()->print(cmd);
  sender->GetSerial()->println("]");
}

void set_arg(SerialCommands* sender, int& injectedVar) {
  char* temp = sender->Next();
  injectedVar = atoi(temp);
}

void set_arg(SerialCommands* sender, int& injectedVar, int address) {
  set_arg(sender, injectedVar);
  EEPROM_writeAnything(address, injectedVar);
}

void echo(SerialCommands* sender, int var) { sender->GetSerial()->println(var); }
void echo(SerialCommands* sender, String var) { sender->GetSerial()->println(var); }

void get_pressure_threshold(SerialCommands* sender) { echo(sender, filter_threshold); } //?threshold
void set_pressure_threshold(SerialCommands* sender) { set_arg(sender, filter_threshold, ADDR_THRESHOLD); } //!threshold <int>

void main_timer() {
  mock_changes();
}

SerialCommand cmd_getpressthreshold("?threshold", get_pressure_threshold);
SerialCommand cmd_setpressurethreshold("!threshold", set_pressure_threshold);

void add_serial_commands() {
  serial_commands_.SetDefaultHandler(cmd_unrecognized);
  serial_commands_.AddCommand(&cmd_getpressthreshold);
  serial_commands_.AddCommand(&cmd_setpressurethreshold);
}

void setup() {
  pinMode(PB0, OUTPUT);
  pinMode(PB7, OUTPUT);
  pinMode(PB14, OUTPUT);

  Serial.begin(9600);

  timer.setInterval(500, main_timer);
  add_serial_commands();
}

void loop() {
  serial_commands_.ReadSerial();
  timer.run();
}

由于我已经确认它不是缓慢/不可预测行为的根源,因此我基本上省略了覆盖EEPROM功能的代码。

调试尝试

所以主要的问题似乎与设置过程的时间有关,因为如果我做得足够慢,它就会起作用。我已经尝试对代码库的所有部分进行计时,但我无法解释设置任何内容时的延迟以及将来请求所需的延迟。设置EEPROM值最多需要75毫秒,ReadSerial()是如此之快,以至于我几乎无法在10 ^ -3秒内对其进行测量。

在这一点上,我非常确定我在读取/写入串行缓冲区/接口时做错了什么。我尝试过在每次 set 调用后flush()都执行此操作,但此操作无济于事。

我还认为也许我的PC串行接口(usb端口)以某种方式引起了此问题,因为这可以解释为什么代码库的任何部分都不会占用很多时间。但是,这与loop()延迟并且LED停止闪烁这一事实并不合适。

对我来说,关于整个问题最奇怪的事情之一是,串行接口在对cmds进行几次垃圾邮件处理之后如何接收它们。 cmd_unrecognized(...)函数不会收到?threshold?threshold?threshold,而会收到???threshold,这对我来说毫无意义。

我知道这篇文章已经很长了,但是我希望我对我的问题有一个比较清晰的了解,希望大家都知道如何解决这个耗时的问题。

EDIT2:在SerialCommands.h调试模式下试玩之后,快速进入!pidp 10然后进入?pidp时,我得到以下输出:

Read: bufLen=63 bufPos=0 termPos=0 ch=[!]
Read: bufLen=63 bufPos=1 termPos=0 ch=[p]
Read: bufLen=63 bufPos=2 termPos=0 ch=[i]
Read: bufLen=63 bufPos=3 termPos=0 ch=[d]
Read: bufLen=63 bufPos=4 termPos=0 ch=[p]
Read: bufLen=63 bufPos=5 termPos=0 ch=[ ]
Read: bufLen=63 bufPos=6 termPos=0 ch=[1]
Read: bufLen=63 bufPos=7 termPos=0 ch=[0]
Read: bufLen=63 bufPos=8 termPos=0 ch=#13
Read: bufLen=63 bufPos=9 termPos=1 ch=#10
Received: [!pidp 10]
Matched #30
Read: bufLen=63 bufPos=0 termPos=0 ch=[?]

这似乎有点像完整的?pidp不适合缓冲区,但是,有一条buffer full消息却没有显示。

2 个答案:

答案 0 :(得分:0)

听起来可能是您的Python程序引起的。您还记得在\0整数后面添加一个空终止符(1234)吗?串行命令通常需要字符串,该字符串必须以空终止符结尾。如果不是这样,您的Arduino可能会继续等待终止程序,并且仅在发生超时或看门狗导致复位(可能为4秒)时才停止。那可以解释这种坚持。

答案 1 :(得分:0)

最终,我还没有真正发现/理解串行缓冲区的问题,但是我设法实现了一个基本的解决方法。而不是总是写入EEPROM(因此最多需要75ms),我只将串行输入设置为各个变量(最多需要3ms)。为了保留更改,我添加了一个!commit命令来保存所有值。进行此更改后,如果程序花费的时间太长,我就会集中与串行接口混淆的问题,因此,我将不得不在调用串行接口的实例中处理此问题。

据我所知总结问题:如果在将任何命令发送到串行接口时我的代码正在阻塞/正在工作,则串行缓冲区将以一种奇怪的,不可预测的方式填充(而不是按预期方式对缓冲区输入进行排队) )。最后,我只需要避免这种情况。这绝对不是完美的,但可以。感谢任何人的帮助。