Serial.println()会影响Serial1读数

我无法将一些C ++代码转换为Arduino。 任何帮助,将不胜感激。

编辑我已经成功完成了上述。 然而,现在唯一的问题是,我已经准确正确地读取了电压的Arduino代码,但没有其他的寄存器。 我也可以写油门。 如果我调用不同数量的Serial.println()语句,则其他寄存器上的读数会发生变化,并且在某些情况下,电压寄存器也会停止工作。在我的代码中可以找到这个代码

Serial.print("Voltage: );

如果我打印出所有这些寄存器,答案都会改变。 我无法弄清楚为什么会发生这种情况。

/* DEFINITIONS */
#include <math.h>

/* FLOATS */

uint8_t command[5];
uint8_t response[3];

/* INTEGERS */
byte deviceId = 0x17;
double throttleOut = 0;
double voltage = 0;
double rippleVoltage = 0;
double current = 0;
double power = 0;
double throttle = 0;
double pwm = 0;
double rpm = 0;
double temp = 0;
double becVoltage = 0;
double safeState = 0;
double linkLiveEnabled = 0;
double eStopStatus = 0;
double rawNTC = 0;

/* SETUP */
void setup() {
  Serial1.begin(115200);
  Serial.begin(115200);

}
void loop() {
  flushPort();
  ReadWriteRegister(128, 1000, true);//_throttleOut is 0[0%] to 65535[100%]
  voltage = ReadWriteRegister(0, 0, false) / 2042.0 / 0.05;
  rippleVoltage = ReadWriteRegister(1, 0, false) / 2042 / 0.25;
  current = ReadWriteRegister(2, 0, false) / 204200 * 50;
  power = voltage * current;
  throttle = (ReadWriteRegister(3, 0, false) / 2042.0 / 1.0);
  pwm = ReadWriteRegister(4, 0, false) / 2042.0 / 3.996735;
  rpm = ReadWriteRegister(5, 0, false) / 2042.0 / 4.89796E-5;
  int poleCount = 20;//Motor pole count
  rpm = rpm / (poleCount / 2);
  temp = ReadWriteRegister(6, 0, false) / 2042.0 * 30.0;
  becVoltage = ReadWriteRegister(7, 0, false) / 2042 / 0.25;
  safeState = ReadWriteRegister(26, 0, false);
  linkLiveEnabled = ReadWriteRegister(25, 0, false);
  eStopStatus = ReadWriteRegister(27, 0, false) == 0 ? false : true;
  rawNTC = ReadWriteRegister(9, 0, false) / 2042.0 / 0.01567091;
  rawNTC = 1.0 / (log(rawNTC * 10200.0 / (255.0 - rawNTC) / 10000.0 ) / 3455.0 + 1.0 / 298.0) - 273.0;
  Serial.print("Voltage: ");
  Serial.println(voltage);
  Serial.print("Current: ");
  Serial.println(current);
}
void flushPort() {

  command[0] = command[1] = command[2] = command[3] = command[4] = 0;
Serial1.write(command, 5);
  while (Serial1.available() > 0) {
    Serial1.read();
  }
}
double ReadWriteRegister(int reg, int value, bool writeMode) {
  // Send read command

  command[0] = (byte)(0x80 | deviceId);
  command[1] = (byte)reg;
  command[2] = (byte)((value >> 8) & 0xFF);
  command[3] = (byte)(value & 0xFF);
  command[4] = (byte)(0 - command[0] - command[1] - command[2] - command[3]);
  Serial1.write(command, 5);


  // Read response
  if(Serial1.available() == 3) {
  response[0] = (byte)Serial1.read();
  response[1] = (byte)Serial1.read();
  response[2] = (byte)Serial1.read();
  }

  if ((byte)(response[0] + response[1] + response[2]) == 0)
  {
    return (double)((response[0] << 8) + (response[1]));
  }
  else
  {
    Serial.println("Error communicating with device!");
  }
}

编辑2一些照片的USB逻辑分析器镜头。 [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 五 ] [ 6 ] [ 7 ]和所有的数据包在一起:[ 他们全部 ]也许这将有助于超时等。这是我所有的信息:。


ReadWriteRegister无法工作。 在115200,每个角色大约需要87us的发送或接收。 那时,Arduino可以执行大约100行代码!

看看这个片段:

  Serial1.write(command, 5);

  // Read response
  if(Serial1.available() == 3) {

write功能只将命令放入输出缓冲区并开始发送第一个字符。 它在所有字符传输之前返回。 这将需要500us!

然后,你看看是否收到了3个字符的回应。 但是命令还没有完成传输,你肯定没有等待258us(3次86us)。 如果设备需要时间来执行您的命令,则甚至可能需要更长的时间。

你必须做两件事:等待命令被发送,并等待接收到响应。 尝试这个:

  Serial1.write(command, 5);
  Serial1.flush(); // wait for command to go out

  // Wait for response to come back
  while (Serial1.available() < 3)
    ; // waitin'....

  // Read response
  response[0] = (byte)Serial1.read();
  response[1] = (byte)Serial1.read();
  response[2] = (byte)Serial1.read();

这就是所谓的“阻塞”,因为当你等待响应时,Arduino不会做任何事情。

但是,如果错过了某个字符,程序可能会“挂起”,如果第二个字符没有正确发送/接收(等待),则等待第四个字符。 所以你应该在while循环中放置一个500us的超时时间:

  // Wait for response
  uint32_t startTime = micros();
  while ((Serial1.available() < 3) && (micros() - startTime < 500UL))
    ; // waitin'...

...或更长时间,如果您知道设备响应速度有多快。 然后你可以确定你是否真的得到了回应:

完整程序更新(v2):

/* DEFINITIONS */
#include <math.h>

/* INTEGERS */

byte deviceId = 0x17;
uint8_t command[5];
uint8_t response[3];

/* FLOATS */

double  throttleOut     = 0.0;
double  voltage         = 0.0;
double  rippleVoltage   = 0.0;
double  current         = 0.0;
double  power           = 0.0;
double  throttle        = 0.0;
double  pwm             = 0.0;
double  rpm             = 0.0;
double  temp            = 0.0;
double  becVoltage      = 0.0;
uint8_t safeState       = 0;
uint8_t linkLiveEnabled = 0;
bool    eStopStatus     = 0;
double  rawNTC          = 0.0;

/* SETUP */
void setup() {
  Serial1.begin(115200);
  Serial.begin(115200);
  Serial.println( F("---------------------------") );

  // According to the spec, you can synchronize with the device by writing
  // five zeroes.  Although I suspect this is mostly for the SPI and I2c
  // interfaces (not TTL-level RS-232), it won't hurt to do it here.
  Serial1.write( command, 5 );
  delay( 250 ); // ms
  while (Serial1.available())
    Serial1.read(); // throw away

  // Set the throttle just once
  ReadWriteRegister(128, 1000);//_throttleOut is 0[0%] to 65535[100%]
}

//  For 12-bit A/D conversions, the range is 0..4096.  Values at
//  the top and bottom are usually useless, so the value is limited
//  to 6..4090 and then shifted down to 0..4084.  The middle of this
//  range will be the "1.0" value: 2042.  Depending on what is being 
//  measured, you still need to scale the result.
const double ADC_FACTOR = 2042.0;

void loop() {

  uint32_t scanTime = millis(); // mark time now so we can delay later

  voltage             = ReadWriteRegister(  0, 0 ) / ADC_FACTOR * 20.0;
  rippleVoltage       = ReadWriteRegister(  1, 0 ) / ADC_FACTOR * 4.0;
  current             = ReadWriteRegister(  2, 0 ) / ADC_FACTOR * 50.0;
  power               = voltage * current;
  throttle            = ReadWriteRegister(  3, 0 ) / ADC_FACTOR * 1.0;
  pwm                 = ReadWriteRegister(  4, 0 ) / ADC_FACTOR * 0.2502;
  rpm                 = ReadWriteRegister(  5, 0 ) / ADC_FACTOR * 20416.66;
  const int poleCount = 20;//Motor pole count
  rpm                 = rpm / (poleCount / 2);
  temp                = ReadWriteRegister(  6, 0 ) / ADC_FACTOR * 30.0;
  becVoltage          = ReadWriteRegister(  7, 0 ) / ADC_FACTOR * 4.0;
  safeState           = ReadWriteRegister( 26, 0 );
  linkLiveEnabled     = ReadWriteRegister( 25, 0 );
  eStopStatus         = ReadWriteRegister( 27, 0 );
  rawNTC              = ReadWriteRegister(  9, 0 ) / ADC_FACTOR * 63.1825;

  const double R0 =  1000.0;
  const double R2 = 10200.0;
  const double B  =  3455.0;
  rawNTC          = 1.0 / (log(rawNTC * R2 / (255.0 - rawNTC) / R0 ) / B + 1.0 / 298.0) - 273.0;

  Serial.print( F("Voltage: ") );
  Serial.println(voltage);
  Serial.print( F("Current: ") );
  Serial.println(current);
  Serial.print( F("Throttle: ") );
  Serial.println(throttle);
  Serial.print( F("RPM: ") );
  Serial.println(rpm);

  // These prints do not actually send the characters, they only queue
  // them up to be sent gradually, at 115200.  The characters will be
  // pulled from the output queue by a TX interrupt, and given to the
  // UART one at a time.
  //
  // To prevent these interrupts from possibly interfering with any other
  // timing, and to pace your program, we will wait *now* for all the
  // characters to be sent to the Serial Monitor.
  Serial.flush();

  // Let's pace things a little bit more for testing: delay here until
  // it's time to scan again.
  const uint32_t SCAN_INTERVAL = 1000UL; // ms
  while (millis() - scanTime < SCAN_INTERVAL)
    ; // waitin'
}

int16_t ReadWriteRegister(int reg, int value) {
  // Flush input, as suggested by Gee Bee
  while (Serial1.available() > 0)
    Serial1.read();

  // Send command (register number determines whether it is read or write)

  command[0] = (byte)(0x80 | deviceId);
  command[1] = (byte)reg;
  command[2] = (byte)((value >> 8) & 0xFF);
  command[3] = (byte)(value & 0xFF);
  command[4] = (byte)(0 - command[0] - command[1] - command[2] - command[3]);
  Serial1.write(command, 5);

  // The command bytes are only queued for transmission, they have not
  // actually gone out.  You can either wait for command to go out
  // with a `Serial1.flush()`  *OR*  add the transmission time to the
  // timeout value below.  However, if anything else has queued bytes
  // to be sent and didn't wait for them to go out, the calculated 
  // timeout would be wrong.  It is safer to flush now and guarantee
  // that *all* bytes have been sent: anything sent earlier (I don't 
  // see anything else, but you may change that later) *plus* 
  // these 5 command bytes.

  Serial1.flush();

  // Now wait for response to come back, for a certain number of us
  //   The TIMEOUT could be as short as 3 character times @ the Serial1
  //   baudrate: 3 * (10 bits/char) / 115200bps = 261us.  This is if
  //   the device responds immediately.  Gee Bee says 20ms, which would 
  //   be 20000UL.  There's nothing in the spec, but 1ms seems generous
  //   for reading the raw NTC value, which may require an ADC conversion.
  //   Even the Arduino can do that in 100us.  Try longer if you get
  //   timeout warnings.

  const uint32_t TIMEOUT = 2000UL;

  uint32_t startTime = micros();
  while ((Serial1.available() < 3) && (micros() - startTime < TIMEOUT))
    ; // waitin'...

  int16_t result;

  if (Serial1.available() >= 3) {
    response[0] = (byte)Serial1.read();
    response[1] = (byte)Serial1.read();
    response[2] = (byte)Serial1.read();

    // Verify the checksum
    if (response[0] + response[1] + response[2] != 0) {
      Serial.print( reg );
      Serial.println( F(" Checksum error!") );
      Serial.flush(); // optional, use it for now to stay synchronous
    }

    //  Cast to 16 bits *first*, then shift and add
    result = (((int16_t) response[0]) << 8) + (int16_t) response[1];

  } else {
    //  Must have timed out, because there aren't enough characters
    Serial.print( reg );
    Serial.println( F(" Timed out!") );
    Serial.flush(); // optional, use it for now to stay synchronous

    result = 0;
  }

  return result; // You must always return something
}

注释:

  • 结果计算中有一个错误(可能)在上面的答案中得到修复。 我想,在外线投中double让你失去前8位。 如上计算应该给出正确的答案。
  • 经过一些Google搜索,我发现这是一个Castle Serial Link控制器。 这将是有益的知道。 它描述了我在上面的ReadWriteRegister函数中使用的校验和。 该函数可以告诉你它是否超时或者校验和是否错误。 这也意味着可能需要更长的超时时间。 目前尚不清楚您的设备是否等待480毫秒以获取最新价值,或者是否持续缓存它们,并立即响应从ESC接收到的最后一个值。 但是,由于ESC接收命令并发送新值需要花费的时间,因此写操作不会反映在读取值中,最长可达480ms。 请参阅ESC Castle Link协议。
  • ReadWriteRegister函数返回一个16位整数,效率更高。 比较浮点数是不行的。 顺便说一句, double是8位Arduinos中的单float
  • ReadWriteRegister函数不需要writemode参数,因为寄存器编号决定了您是在写入还是在读取设备。
  • 写入throttle值只能在设置中执行。

  • 更新2

    nalyzer镜头出现,以示对胚胎干细胞“扫描”。 它正在尝试每个设备ID,并且其中一些用非零电压回复。 此外,它似乎在9600运行,而不是 115200.这是从一个不同的设置?

    无论如何,它确认了控制器规格说的内容:写入5个字节,读取3.校验和值与预期一致。 然而,它比你的程序运行速度慢10倍,所以它没有提供关于超时的很多新信息。 这可能意味着在设备响应之前有一小段延迟,可能是〜1位时间,或大约100us。

    你有没有读过控制器规格? 您应该将该程序与规格进行比较,以确保您了解控制器的工作方式。

    我已将上述程序修改为:

  • setup的控制器同步(写入5个零字节并等待250ms),
  • 使用规范中的缩放数字(而不是它们的倒数?),
  • 使用有意义的常量而不是“魔术”数字(例如2042),
  • 对于一些寄存器而不是double使用整数或布尔类型(请参阅safeStatelinkLiveStatuseStopStatus ),
  • 将超时时间增加到2毫秒(如果继续频繁超时,请继续增加),以及
  • 发生错误时输出reg号。
  • 如果你想在这个领域取得成功,你必须学会​​阅读规范并将其要求转化为符合规范的代码。 您开始使用的程序在最坏情况下是非保形的,或者最好是误导性的。 我对“INTEGERS”和“FLOATS”的评论感到特别有趣,但这些部分却恰恰相反。

    也许这是修复别人代码的一个教训? 它确实有许多问题,你会遇到。 如果我每次说镍时都会说:

  • “那是什么数字?”
  • “那个评论是错误的!”
  • “规范说你应该......”
  • “为什么这么难读,我只是增加一些间距。”
  • ......我会成为一个非常有钱的人。 :)

    (更新结束)


    PS

    这也符合您描述的症状:因为您没有等待传输完成,所以首次读取0个字节( read将返回-1或0xFF字节)。

    在你多次调用这个例程(并将几个命令排入输出缓冲区)之后,500us已经过去了,并且第一个命令最终被发送了。 设备响应开始发送3个字符。 87us后,Arduino终于收到了第一个字符。 它被读取的一条语句read ,但是谁知道哪一条? 根据经过的时间,它将是随机的。

    更多的命令被发送,并且单个字符被这些语句之一接收和读取,直到64个字节的命令或者Serial.println字符被排队。 然后write命令OR Serial.print阻塞,直到输出为最新的命令出空间。 (这解决了您的问题的标题。)

    当最终传输足够的命令字节或调试消息字符时,将返回Serial1.writeSerial.print 。 同时,收到的字符将进入输入缓冲区。 (这是他们存储的位置,直到你打电话read 。)

    此时,一行中的三个read语句实际上会获取设备发送的字符。 但由于之前随机消费的字符,它可能是一个响应的最后一个字符,后面是下一个响应的前两个字符。 您与3字节响应“不同步”。

    为了保持“在同步”与设备,你必须等待发送与完成flush ,然后等待响应回来与while


    对不起,但这段代码有一些问题。 你假设

  • 代码运行时不会收到垃圾
  • 连接到Serial1的硬件在0.00ns内回答3个字节
  • 在ReadWriteRegister中使用以下模式:

  • 丢弃来自Serial1的任何未决输入
  • 发出命令
  • 等待至少收到3个字节,或者发生一些合理的超时(参见slash-dev的详细解答)
  • 读取3个字节(如果有东西收到)
  • 在代码中:

    double ReadWriteRegister(int reg, int value, bool writeMode) {
      // Send read command
    
      command[0] = (byte)(0x80 | deviceId);
      command[1] = (byte)reg;
      command[2] = (byte)((value >> 8) & 0xFF);
      command[3] = (byte)(value & 0xFF);
      command[4] = (byte)(0 - command[0] - command[1] - command[2] - command[3]);
    
      // discard any input
      while (Serial1.available() > 0) {
        Serial1.read();
      }
    
      // write out command
      Serial1.write(command, 5);
    
      // wait for 3 bytes
      uint32_t startTime = micros();
      while ((Serial1.available() < 3) && (micros() - startTime < 20000UL))
        ; // waiting for 20 ms for the answer
    
      // Read response
      if(Serial1.available() >= 3) {  // >=3 accept also more bytes
          response[0] = (byte)Serial1.read();
          response[1] = (byte)Serial1.read();
          response[2] = (byte)Serial1.read();
      }
    
      if ((byte)(response[0] + response[1] + response[2]) == 0)
      {
        return (double)((response[0] << 8) + (response[1]));
      }
      else
      {
        Serial.println("Error communicating with device!");
        return 0;
      }
    }
    
    链接地址: http://www.djcxy.com/p/32179.html

    上一篇: Serial.println() affects Serial1 readings

    下一篇: Spring redirect url issue when behind Zuul proxy