snprintf() 使用 newlib nano 打印垃圾浮点数 [英] snprintf() prints garbage floats with newlib nano

查看:36
本文介绍了snprintf() 使用 newlib nano 打印垃圾浮点数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在运行带有 ARM Cortex-M3 (STM32F205) 的裸机嵌入式系统.当我尝试将 snprintf() 与浮点数一起使用时,例如:

I am running a bare metal embedded system with an ARM Cortex-M3 (STM32F205). When I try to use snprintf() with float numbers, e.g.:

float f;

f = 1.23;
snprintf(s, 20, "%5.2f", f);

我将垃圾放入s.格式似乎受到尊重,即垃圾是一个格式良好的字符串,包含数字、小数点和两个尾随数字.但是,如果我重复 snprintf,字符串可能会在两次调用之间发生变化.

I get garbage into s. The format seems to be honored, i.e. the garbage is a well-formed string with digits, decimal point, and two trailing digits. However, if I repeat the snprintf, the string may change between two calls.

浮点数学似乎不适用,而 snprintf 适用于整数,例如:

Floating point mathematics seems to work otherwise, and snprintf works with integers, e.g.:

snprintf(s, 20, "%10d", 1234567);

我使用带有 -u _printf_float 链接器开关的 newlib-nano 实现.编译器是arm-none-eabi-gcc.

I use the newlib-nano implementation with the -u _printf_float linker switch. The compiler is arm-none-eabi-gcc.

我强烈怀疑内存分配问题,因为整数打印时没有任何问题,但浮点数表现得好像它们在这个过程中被破坏了.printf 系列函数使用浮点数而不是整数调用 malloc.

I do have a strong suspicion of memory allocation problems, as integers are printed without any hiccups, but floats act as if they got corrupted in the process. The printf family functions call malloc with floats, not with integers.

我在这个上下文中使用的唯一不属于 newlib 的代码是我的 _sbrk(),这是 malloc 所需要的.

The only piece of code not belonging to newlib I am using in this context is my _sbrk(), which is required by malloc.

caddr_t _sbrk(int incr)
{
  extern char _Heap_Begin; // Defined by the linker.
  extern char _Heap_Limit; // Defined by the linker.

  static char* current_heap_end;
  char* current_block_address;

  // first allocation
  if (current_heap_end == 0)
      current_heap_end = &_Heap_Begin;

  current_block_address = current_heap_end;

  // increment and align to 4-octet border
  incr = (incr + 3) & (~3);
  current_heap_end += incr;

  // Overflow?
  if (current_heap_end > &_Heap_Limit)
    {
    errno = ENOMEM;
    current_heap_end = current_block_address;
    return (caddr_t) - 1;
    }

  return (caddr_t)current_block_address;
}

据我所知,这应该有效.似乎从来没有人用负增量调用它,但我想这是由于 newlib malloc 的设计.唯一有点奇怪的是第一次调用 _sbrk 的增量为零.(但这可能只是 malloc 对堆起始地址的好奇.)

As far as I have been able to track, this should work. It seems that no-one ever calls it with negative increments, but I guess that is due to the design of the newlib malloc. The only slightly odd thing is that the first call to _sbrk has a zero increment. (But this may be just malloc's curiosity about the starting address of the heap.)

堆栈不应与堆冲突,因为两者大约有 60 KiB RAM.链接描述文件可能很疯狂,但至少堆和堆栈地址似乎是正确的.

The stack should not collide with the heap, as there is around 60 KiB RAM for the two. The linker script may be insane, but at least the heap and stack addresses seem to be correct.

推荐答案

由于其他人可能会被同样的错误咬伤,所以我发布了我自己问题的答案.然而,是@Notlikethat 的评论提出了正确答案.

As it may happen that someone else gets bitten by the same bug, I post an answer to my own question. However, it was @Notlikethat 's comment which suggested the correct answer.

这是不可偷盗的教训.我借用了 STMCubeMX 代码生成器附带的 gcc 链接器脚本.不幸的是,脚本和启动文件一起坏了.

This is a lesson of Thou shall not steal. I borrowed the gcc linker script which came with the STMCubeMX code generator. Unfortunately, the script along with the startup file is broken.

原始链接描述文件的相关部分:

The relevant part of the original linker script:

_estack = 0x2000ffff;

及其在启动脚本中的对应项:

and its counterparts in the startup script:

Reset_Handler:  
  ldr   sp, =_estack     /* set stack pointer */
...

g_pfnVectors:
  .word  _estack
  .word  Reset_Handler
...

第一个中断向量位置(在 0 处)应始终指向启动堆栈顶部.当到达复位中断时,它还加载堆栈指针.(据我所知,后者是不必要的,因为硬件无论如何都会在调用重置处理程序之前从第 0 个向量重新加载 SP.)

The first interrupt vector position (at 0) should always point to the startup stack top. When the reset interrupt is reached, it also loads the stack pointer. (As far as I can say, the latter one is unnecessary as the HW anyway reloads the SP from the 0th vector before calling the reset handler.)

Cortex-M 堆栈指针应始终指向堆栈中的最后一项.启动时堆栈中没有项目,因此指针应指向实际内存上方的第一个地址,在这种情况下为 0x020010000.对于原始链接器脚本,堆栈指针设置为 0x0200ffff,这实际上导致 sp = 0x0200fffc(硬件强制字对齐堆栈).在此之后,堆栈错位了 4.

The Cortex-M stack pointer should always point to the last item in the stack. At startup there are no items in the stack and thus the pointer should point to the first address above the actual memory, 0x020010000 in this case. With the original linker script the stack pointer is set to 0x0200ffff, which actually results in sp = 0x0200fffc (the hardware forces word-aligned stack). After this the stack is misaligned by 4.

我通过删除 _estack 的常量定义并将其替换为 _stacktop 来更改链接器脚本,如下所示.内存定义之前就在那里.我更改了名称只是为了查看该值的使用位置.

I changed the linker script by removing the constant definition of _estack and replacing it by _stacktop as shown below. The memory definitions were there before. I changed the name just to see where the value is used.

MEMORY
{
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 128K
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
}

_stacktop = ORIGIN(RAM) + LENGTH(RAM);

在此之后,_stacktop 的值是 0x20010000,我的数字很好地浮动......任何使用双倍长度参数的外部(库)函数都可能出现同样的问题,正如 ARM Cortex ABI 所述调用外部函数时,堆栈必须对齐到 8 个八位字节.

After this the value of _stacktop is 0x20010000, and my numbers float beautifully... The same problem could arise with any external (library) function using double length parameters, as the ARM Cortex ABI states that the stack must be aligned to 8 octets when calling external functions.

这篇关于snprintf() 使用 newlib nano 打印垃圾浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆