snprintf()使用newlib nano打印垃圾浮动

DrV*_*DrV 6 c embedded printf arm newlib

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

float f;

f = 1.23;
snprintf(s, 20, "%5.2f", f);
Run Code Online (Sandbox Code Playgroud)

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

浮点数学似乎在其他方面起作用,并且snprintf与整数一起工作,例如:

snprintf(s, 20, "%10d", 1234567);
Run Code Online (Sandbox Code Playgroud)

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

我确实怀疑内存分配问题,因为整数打印没有任何打嗝,但浮动表现就好像它们在过程中被破坏一样.该printf系列函数调用malloc用浮漂,不是整数.

唯一不属于newlib我在此上下文中使用的代码是my _sbrk(),这是必需的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;
}
Run Code Online (Sandbox Code Playgroud)

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

堆栈不应该与堆冲突,因为这两个堆栈大约有60 KiB RAM.链接器脚本可能是疯了,但至少堆和堆栈地址似乎是正确的.

DrV*_*DrV 10

由于可能发生其他人被同一个bug咬伤,我会回答我自己的问题.然而,正是@Notlikethat的评论提出了正确答案.

这是你不会偷的教训.我借用了STMCubeMX代码生成器附带的gcc链接器脚本.不幸的是,脚本和启动文件都被破坏了.

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

_estack = 0x2000ffff;
Run Code Online (Sandbox Code Playgroud)

和它在启动脚本中的对应物:

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

g_pfnVectors:
  .word  _estack
  .word  Reset_Handler
...
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

_stacktop = ORIGIN(RAM) + LENGTH(RAM);
Run Code Online (Sandbox Code Playgroud)

在此之后,值为_stacktop0x20010000,并且我的数字漂浮得很漂亮......使用双长度参数的任何外部(库)函数都会出现同样的问题,因为ARM Cortex ABI声明堆栈在调用时必须对齐到8个八位字节外部功能.