【问题标题】:Is there a less-space taking way than using sprintf to format floats to strings?与使用 sprintf 将浮点数格式化为字符串相比,是否有更小的空间占用方式?
【发布时间】:2021-04-11 04:36:34
【问题描述】:

我正在为微控制器开发 C 代码,该代码从传感器获取输入,并在字母数字字符 LCD 上输出来自传感器的数据以及其他字符串。 我通常为此使用 sprintf,但我注意到当使用 sprintf 将 floats 格式化为字符串时,它占用了太多的程序内存空间,这在微控制器上是相当低的。 (我的意思是从程序内存的 34% 直接跳到 99.2%)

所以我的问题是,有没有一种占用空间较小的方法将浮点数格式化为字符串? 我只关心方法有多简单。

我在 PIC16F877a 8 位 MCU 上使用 MPLABX IDE 和 XC8 编译器。

非常感谢。

【问题讨论】:

  • 通常的解决方案是为了更小的代码占用空间而牺牲范围和最后几位数字的正确性和速度。在您发布允许的输出有多不准确和 FP 的范围有限之前,这都是猜测。 OTHO,简单的解决方案是像"%a"(十六进制有效数)一样打印FP。这需要很少的代码。请提供详细需求。
  • @chux-ReinstateMonica,我只需要小数点后一位。数字的范围是 -40 到 +50。不幸的是,我不希望它采用十六进制格式,这是要输出到 LCD 供用户阅读的。我真的想打印这个:温度 23.4C
  • 相反,将ADC结果转换为浮点数确实是个大错误。
  • 小数点后只有一位,范围为-40到50,温度只有900个可能的值。这很容易用定点数学处理。
  • 您在此处发布的是XY problem。您需要开始一个新问题,在其中描述您要解决的实际问题。具体来说,您从 ADC 获得了哪些值,以及这些值与温度的对应关系。

标签: c printf microcontroller


【解决方案1】:

有没有比使用 sprintf 将浮点数格式化为字符串更节省空间的方法?
...代码从传感器获取输入并从传感器输出数据以及字母数字字符上的其他字符串

根本不要使用浮点数。 @user3386109

传感器的读数肯定是整数。使用整数数学将该读数转换为摄氏度,然后打印。

TMP235 示例

Temperature  Output
-40 C         100
  0 C         500
150 C        2000

#define SCALE_NUM ((int32_t)(150 - -40) * 10)
#define SCALE_DEM (2000 - 100)
#define OFFSET    (500)

int temperature_raw = temperature_sensor();
int temperature_decidegreesC = (temperature_raw - OFFSET)*SCALE_NUM/SCALE_DEN;
send_integer(temperature_decidegreesC/10);
send_char('.');
send_char(abs(temperature_decidegreesC/10) + '0');

可以进行其他改进,但避免 FP 变量和数学并使用整数数学是关键。

【讨论】:

    【解决方案2】:

    有很多可用的 printf 替代品,但它们都不完全符合标准,缺少某些功能以减小代码大小。

    我用过的有Mpaland printfMenie printf

    还有Chan printf,但根本不支持浮动。

    【讨论】:

      【解决方案3】:

      有没有更节省空间的方法来将浮点数格式化成字符串?

      只需写入字节并在阅读器端进行转换。了解微控制器上浮点数的字节顺序和格式后,读者需要开发一种从字节中读取浮点数的软件方法。

      XC8文档中你知道浮点数的格式:

      浮点是使用 IEEE 754 32 位格式或截断的 24 位格式实现的。

      你会在微控制器方面做:

      void send_byte(unsigned char b) {
          // send the bytes as is as binary - the simplest there is
          hardware_send(b);
          // or as hex readable number
          // depending on if you want it human readable or not.
          char buf[10];
          int len = snprintf(buf, sizeof(buf), "%#02x", b);
          for (unsigned char i = 0; i < len; ++i) {
             hardware_send(buf[i]);
          }
      }
      
      void send_float(float data) {
          const unsigned char *b = (const unsigned char*)&data;
          for (unsigned char i = 0; i < sizeof(data); ++i) {
              send_byte(b[i]);
          }
      }
      
      int main() {
          float data = get_data();
          send_float(data);
      }
      

      这将花费,嗯,转换数据几乎没有。编写自己的字节->十六进制转换,根本不使用sprintf 以节省更多内存。

      在远程端,您将编写软件转换为浮点数。将字节累积到缓冲区中,修复输入的字节序。使用按位运算提取符号、尾数和指数。然后,在 C 中,您将使用 scalb 将尾数和指数转换为浮点数,然后乘以符号。但更好的选择是尽可能在 PC 端使用更灵活的编程语言 - 我会使用 python。

      但是从务实的角度来看……

      在 PIC16F877a 8 位 MCU 上。

      您永远不会在如此小的 MCU 上使用浮点数。当您似乎想要传递温度时,以毫摄氏度表示的 32 位 long 数字将为您提供无限的温度范围。但即使是用摄氏度表示的 16 位 short 数字也足够了。根本不要使用浮点数。将所有代码转换为仅使用整数。

      @subjective-side-note:我在 XC8 上的旅程并不愉快。 XC8 的免费版本会生成非常糟糕且未优化的代码,我更喜欢 sdcc。如果这是一个业余项目,我建议转移到 STM32(例如 blue-pill)或 Arduino(例如 ESP8266,即使有 wifi ......),它们更便宜,更容易使用,现代和 gcc 可以在它们上面工作。

      【讨论】:

        猜你喜欢
        • 2020-07-28
        • 1970-01-01
        • 1970-01-01
        • 2013-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-06
        • 1970-01-01
        相关资源
        最近更新 更多