简体   繁体   English

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

[英]Reading serial commands takes too much time

so following problem: I am currently writing a small program for my NUCLEO F207ZG which provides an interface for other services over the serial port. 所以出现以下问题:我目前正在为NUCLEO F207ZG编写一个小程序,该程序通过串行端口为其他服务提供接口。 The goal of my program is to expose data with commands like ?variable and set values with !variable <value> (eg. ?threshold returns 1400 and !threshold 1234 sets threshold to 1234). 我程序的目标是使用?variable等命令公开数据,并使用!variable <value>设置值(例如?threshold返回1400,而!threshold 1234将阈!threshold 1234设置为1234)。 Additonally the variables in my program are synced with the EEPROM to persist the data. 另外,我程序中的变量与EEPROM同步以保留数据。

For this whole process I've been given a codebase which includes a SerialCommands.h library. 在整个过程中,为我提供了一个包含SerialCommands.h库的代码库。

Problem 问题

The main problem is that setting values takes up way too much time and breaks the serial buffer (or something similiar) if other commands are executed immediatly after setting. 主要问题是,如果在设置后立即执行其他命令,则设置值会占用太多时间并破坏串行缓冲区(或类似的东西)。 I've written a few unit tests in python (using pyserial ) that execute get requests (such as ?threshold ) flawlessy, as often as possible. 我已经在python中(使用pyserial )编写了一些单元测试,它们尽可能频繁地完美无误地执行get请求(例如?threshold )。 However, If I execute a set command (such as !threshold 1400 ) I need to wait AT LEAST four seconds before making any other requests, otherwise the serial interface/buffer seems to lose some data. 但是,如果我执行set命令(例如!threshold 1400 ),则需要在发出任何其他请求之前至少等待四秒钟,否则串行接口/缓冲区似乎丢失了一些数据。 The same thing happens if I try to make any set -> get requests on the Arduino Serial Monitor. 如果我尝试在Arduino串行监视器上进行任何设置 -> 获取请求,也会发生相同的情况。 Here is a short example: 这是一个简短的示例:

  1. ?threshold => returns '1400' ?threshold =>返回'1400'
  2. !threshold 1234
  3. ?threshold => nothing happens ?threshold =>没有任何反应
  4. ?threshold => nothing happens ?threshold =>没有任何反应
  5. ?threshold => returns 'unrecognized command ???threshold ' (see my code for this functionality) ?threshold =>返回“无法识别的命令???threshold ”(有关此功能,请参见我的代码)

EDIT: Something important which I forgot to mention. 编辑:重要的事情我忘了提。 After the program has recovered (after step 5) the value can be queried correctly. 程序恢复后(在步骤5之后),可以正确查询该值。

  1. ?threshold => returns '1234' ?threshold =>返回'1234'

I also have a blinking status LED (in 500ms steps) and if I set something the blinking stops noticeably for around a second. 我还具有闪烁的状态LED(以500毫秒为步长),如果设置了某些内容,则闪烁会明显停止约一秒钟。

Code

So here is (non-working) simplified example of my codebase: 因此,这是我的代码库的(不起作用的)简化示例:

#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();
}

I've mostly omitted the code covering the EEPROM functionality as I've confirmed that it is not the source of the slow/unpredictable behaviour. 我已经基本省略了覆盖EEPROM功能的代码,因为我确认它不是缓慢/不可预测行为的根源。

Debugging attempts 调试尝试

So the main problem seems the be concerning the time of the setting process, as it works if I do it slow enough. 因此,主要的问题似乎与设置过程的时间有关,因为如果我做得足够慢,它就会起作用。 I've tried timing all the parts of the codebase and I simply can't explain the delay when setting anything and the required delay for future requests. 我已经尝试对代码库的所有部分进行计时,但我无法解释设置任何内容时的延迟以及将来请求所需的延迟。 Setting the EEPROM values takes at most 75 ms, ReadSerial() is so fast I can barely measure it in 10^-3 seconds. 设置EEPROM值最多需要75毫秒, ReadSerial()是如此之快, ReadSerial()我几乎无法在10 ^ -3秒内对其进行测量。

At this point I am pretty sure that I am doing something wrong with reading/writing the serial buffer/interface. 在这一点上,我很确定我在读取/写入串行缓冲区/接口时做错了什么。 I've tried flush() ing it after every set call which does nothing. 我试过在每次设置调用之后都执行flush() ,它什么也不做。

I've also thought that maybe my PCs serial interface (usb port) is somehow causing this problem as this would explain why no part of the codebase takes up much time. 我还认为也许我的PC串行接口(usb端口)以某种方式引起了此问题,因为这可以解释为什么代码库的任何部分都不会占用很多时间。 However, this does not really fit with the fact that the loop() is delayed and the LED stops blinking. 但是,这实际上与loop()延迟并且LED停止闪烁这一事实不符。

One of the strangest things for me about this whole problem is how the serial interface receives the cmds after spamming them a few times. 对于我来说,关于整个问题最奇怪的事情之一是,串行接口在对cmds进行几次垃圾邮件处理之后如何接收它们。 The cmd_unrecognized(...) function does not receive ?threshold?threshold?threshold but ???threshold , which makes no sense to me. cmd_unrecognized(...)函数不会接收?threshold?threshold?threshold ???threshold ,而是“ ???threshold ,这对我来说毫无意义。

I know this post has been quite long but I hope I've provided a relatively clear picture of my problem and I hope any of you guys have got an idea how to fix this time-consuming mess of a problem. 我知道这篇文章已经很长了,但是我希望我对我的问题有一个比较清晰的了解,并且希望你们中的每个人都有一个想法来解决这个耗时的问题。

EDIT2: After playing around with in the SerialCommands.h debug mode I've gotten the following output when quickly entering !pidp 10 and then ?pidp : 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=[?]

This somehow looks like the full ?pidp didn't fit the buffer, however, there is a buffer full message and it is not shown. 这看起来有点像完整的?pidp不适合缓冲区,但是,有一个buffer full消息,并且未显示。

It sounds like it could be caused by your Python program. 听起来可能是您的Python程序引起的。 Did you remember to add a null-terminator ( \\0 ) after the 1234 integer? 您是否记得在1234整数后添加一个空终止符( \\0 )? Serial commands generally require strings, which must end with a null-terminator. 串行命令通常需要字符串,这些字符串必须以空终止符结尾。 If not your Arduino might keep on waiting for the terminator, and only stops when a timeout occurs or watchdog causes a reset (which could be 4 seconds). 如果不是这样,您的Arduino可能会继续等待终止程序,并且仅在发生超时或看门狗导致复位(可能为4秒)时才停止。 That would explain the hold-up. 这可以解释这种情况。

Ultimatively I've not really found out/understood the problem with the serial buffer but I've managed to implement a basic workaround. 最终,我还没有真正发现/理解串行缓冲区的问题,但是我设法实现了一个基本的解决方法。 Instead of always writing to the EEPROM (and therefore taking up to 75ms), I only set the serial input to the respective variables (which takes at most 3ms). 而不是总是写入EEPROM(因此最多需要75ms),我只将串行输入设置为各个变量(最多需要3ms)。 In order to persist changes I've added a !commit command which saves all values. 为了保留更改,我添加了!commit命令,该命令保存所有值。 With this change I've centralized the issue of messing with the serial interface if my program takes too long, therefore, I will have to handle this in the instance that is calling the serial interface. 随着这一变化,我集中与串行接口搞乱,如果我的程序耗时过长,所以的问题,我会在呼吁串行接口的实例来处理这个问题。

To summarize the problem to my knowledge: If my code is blocking/working while any commands are sent to the serial interface, the serial buffer is filled in a strange, non-predictable way (instead of queuing the buffer input as expected). 总结一下问题,据我所知:如果在将任何命令发送到串行接口时我的代码正在阻塞/正在工作,则串行缓冲区将以一种奇怪的,不可预测的方式填充(而不是按预期方式对缓冲区输入进行排队)。 At the end of the day I just have to avoid this case. 最后,我只需要避免这种情况。 This is definitely not perfect, but it works. 这绝对不是完美的,但可以。 Thanks for the help to anyone. 感谢您对任何人的帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM