gpt4 book ai didi

c++ - Serial.println() 影响 Serial1 读数

转载 作者:行者123 更新时间:2023-12-01 19:49:13 26 4
gpt4 key购买 nike

我在将一些 C++ 代码转换为 Arduino 时遇到问题。任何帮助,将不胜感激。

编辑
我已经成功完成了上述操作。然而,现在唯一的问题是我的 Arduino 代码准确而正确地读取了电压,但没有其他寄存器。我也可以写 throttle 。如果我拨打不同数量的 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 ]
[ 5 ]
[ 6 ]
[ 7 ]
以及所有的数据包在这一个中:
[ All of them ]
也许这将有助于超时等。这是我拥有的所有信息:。

最佳答案

没办法,ReadWriteRegister将工作。在 115200 处,发送或接收每个字符大约需要 87us。那时,Arduino 可以执行大约 100 行代码!

看看这个片段:

  Serial1.write(command, 5);

// Read response
if(Serial1.available() == 3) {
write函数只将命令放入输出缓冲区并开始发送第一个字符。它在传输所有字符之前返回。这将需要 500us!

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

您必须做两件事:WAITING发送命令,并等待收到响应。尝试这个:
  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 位。如上所述计算应该给出正确的答案。
  • 稍微谷歌搜索后,我看到这是一个 Castle Serial Link controller .知道这会很有用。它描述了我在 ReadWriteRegister 中使用的校验和。以上功能。该函数可以告诉您它是否超时或校验和是否错误。这也意味着可能需要更长的超时时间。不清楚您的设备是等待 480 毫秒来获取最新值,还是持续缓存它们并立即使用从 ESC 接收到的最后一个值进行响应。但是,在长达 480 毫秒的时间内,写入不会反射(reflect)在读取值中,因为 ESC 需要时间来接收命令然后发送新值。见 ESC Castle Link protocol .
  • ReadWriteRegister函数返回一个 16 位整数,这样效率会更高。比较浮点数从来都不是好事。顺便说一句,double只是单例float在 8 位 Arduinos 上。
  • ReadWriteRegister函数不需要 writemode参数,因为寄存器编号决定了您是在写入还是读取设备。
  • throttle值仅在设置中执行。


  • 更新 2

    您的 L 奥奇 一个 分析仪镜头似乎显示了对 ESC 的“扫描”。它正在尝试每个设备 ID,其中一些以非零电压进行回复。此外,它似乎运行在 9600, 不是 115200. 这是来自不同的设置吗?

    无论如何,它证实了 Controller 规范所说的:写入 5 个字节,读取 3 个。校验和值符合预期。但是,它的运行速度比您的程序慢 10 倍,因此它没有提供太多有关超时的新信息。这可能意味着在设备响应之前有一个小的延迟,可能是 ~1 位时间,或大约 100us。

    你读过 Controller 规范吗?您应该将程序与规范进行比较,以确保您了解 Controller 的工作原理。

    我已经将上面的程序修改为:
  • 与 Controller 同步 setup (写入 5 个零字节并等待 250 毫秒),
  • 使用规范中的缩放数字(而不是它们的倒数?),
  • 使用有意义的常量而不是“神奇”数字(例如 2042),
  • 对少数寄存器使用整数或 bool 类型而不是 double (参见 safeStatelinkLiveStatuseStopStatus )、
  • 将超时增加到 2ms(如果您继续频繁超时,请继续增加它),以及
  • 输出 reg发生错误时的编号。

  • 如果您想在该领域取得成功,您必须学会阅读规范并将其需求转换为符合要求的代码。您开始的程序在最坏的情况下是不规范的,或者在最好的情况下具有误导性。我对说“整数”和“浮点数”的评论特别感兴趣,但这些部分包含相反的内容。

    也许这是修复别人代码的教训?它确实有很多你会遇到的问题。如果我每次说的时候都有一枚镍币:
  • “那个号码是干什么的?”
  • “那个评论是错误的!”
  • “规范说你应该……”
  • “为什么这么难读?我就加一些间距吧。”

  • ......我会成为一个非常富有的人。 :)

    (更新结束)

    附言

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

    在您多次调用此例程(并将多个命令排入输出缓冲区)后,500us 已过去,第一个命令终于被发送。设备通过开始发送 3 个字符来响应。 87us 之后,Arduino 终于收到了第一个字符。它会被您的一个人读取 read声明,但谁知道是哪一个?它将是随机的,基于耗时。

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

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

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

    要与设备保持“同步”,您必须等待发送完成 flush ,然后等待响应返回 while .

    关于c++ - Serial.println() 影响 Serial1 读数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35451657/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com