0

StringTLDR:当通过 arduinos类将浮点数打印到字符串时,我得到了一致的冻结。以前我用 sprintf 和 %f 得到了同样的冻结。我已经使用 PString 类解决了这个问题,但我想了解这个问题。

完整故事:我有一个相当大的用于 arduino SAMD 架构(MKRZero)的 C++ 代码库,最近在我很久没有接触的一行代码上开始冻结。sprintf这是对with的调用%f,它在以前的预期中奇怪地工作。在对 SO 和谷歌进行一些研究之后,我意识到 Arduino 不支持通过 sprintf 进行浮点格式化,并且在排除 dtostr (由于不在 AVR 上)之后,我尝试了 ArduinosString类。这暂时解决了问题,但在使用一些不同的外部外围设备(可以连接和断开的 I2C 从机)测试系统后,冻结又出现了。所以完全相同的代码,但是由于不同的外围设备,它的部分被执行了一些差异。

代码库非常大(几 1000 行),我无法用一个简单的例子来重现。所以不幸的是,没有太多上下文,这些是失败的行:

for (int fieldIndex = 0; fieldIndex < totalFullDataPoints; fieldIndex++) {
      char buffer[14];
      Serial.println("foo");
      // String floatString = String((float)data[fieldIndex], 2); // causes system to freeze
      // String floatString = String((float)1024.46, 2); // causes system to freeze
      String floatString = String((float)1024.46); // causes system to freeze      
      // String floatString = String("1024.46"); // works
      Serial.println("bar"); // freezes before this
}

该错误非常不稳​​定,因为我可以通过修改代码中其他位置不相关的内容或从我的 arduino 断开传感器(I2C 从站)来使其无法触发。但是当它出现时,它是一致的,因为它每次运行都会发生。我什至有一个可以运行的代码版本 - 但是删除其中三行会导致它再次冻结:

String floatString = "14123.123";
Serial.println("Float String: ");
Serial.println(floatString);

我很确定这不是内存问题,据我所知,这不是指针或非终止字符串爆炸的情况。

由于这篇文章https://forum.arduino.cc/t/use-pstring-to-avoid-crashes-due- ,我最终使用了PStrings(https://github.com/boseji/PString-Arduino-lib ) to-string-sprintf-or-dtostrf-float-issues/230946但我很沮丧和好奇,为什么它在String应该支持创建浮动时以这种看似随机的方式冻结。

4

1 回答 1

0

经过大量调试,似乎问题确实与 Arduino 将浮点数打印到字符串的能力有关,而不是与我的代码中的指针或未终止的字符串问题有关。

它似乎在dtostrf()String 构造函数的调用中停滞不前:

// ~/.platformio/packages/framework-arduino-samd/cores/arduino/api/String.cpp
String::String(float value, unsigned char decimalPlaces)
{
    static size_t const FLOAT_BUF_SIZE = FLT_MAX_10_EXP + FLT_MAX_DECIMAL_PLACES + 1 /* '-' */ + 1 /* '.' */ + 1 /* '\0' */;
    init();
    char buf[FLOAT_BUF_SIZE];
    decimalPlaces = min(decimalPlaces, FLT_MAX_DECIMAL_PLACES);
    *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);     // <-- HERE
}

它似乎与函数_print_float内的汇编程序调用有关dtostrf()

// ~/.platformio/packages/framework-arduino-samd/cores/arduino/api/deprecated-avr-comp/avr/dtostrf.c.impl
char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
  asm(".global _printf_float");   // If this line is uncommented, the stall wont happen
  char fmt[20];
  sprintf(fmt, "%%%d.%df", width, prec);
  sprintf(sout, fmt, val);   // If the above line is not commented out, the system will freeze here
  return sout;
}

我意识到对于遇到此线程的任何人来说,这可能是一个非常不令人满意的答案......但是对于它的价值,我们的解决方案将是使用PStringhttps://github.com/boseji/PString-Arduino-lib)作为到目前为止,它们至少看起来很稳定。

如果问题再次浮出水面,我会跟进这个线程,尽管使用了PStrings

于 2021-04-16T13:12:29.030 回答