c - snprintf() 使用 newlib nano 打印垃圾 float

标签 c arm embedded printf newlib

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

float f;

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

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

float 学似乎以其他方式工作,snprintf 与整数一起工作,例如:

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

我将 newlib-nano 实现与 -u _printf_float 链接器开关结合使用。编译器是 arm-none-eabi-gcc

我确实非常怀疑内存分配问题,因为打印整数时没有任何问题,但 float 的表现就好像它们在这个过程中被破坏了一样。 printf 系列函数使用 float 调用 malloc,而不是整数。

我在此上下文中使用的唯一不属于 newlib 的代码是我的 _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;
}

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

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

最佳答案

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

这是不可偷窃的教训。我借用了 STMCubeMX 代码生成器附带的 gcc 链接描述文件。不幸的是,脚本连同启动文件已损坏。

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

_estack = 0x2000ffff;

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

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

g_pfnVectors:
  .word  _estack
  .word  Reset_Handler
...

第一个中断 vector 位置(0)应该总是指向启动栈顶。当到达复位中断时,它还会加载堆栈指针。 (据我所知,后者是不必要的,因为 HW 在调用重置处理程序之前无论如何都会从第 0 个 vector 重新加载 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);

在这之后 _stacktop 的值是 0x20010000,我的数字漂浮得很漂亮...同样的问题可能出现在使用双倍长度参数的任何外部(库)函数中,正如 ARM Cortex ABI 声明的那样调用外部函数时,堆栈必须对齐到 8 个八位字节。

关于c - snprintf() 使用 newlib nano 打印垃圾 float ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28746062/

相关文章:

c - 使用写函数时出现未初始化字节的 valgrind 错误

c - 仅使用递归如何实现 O(log n) 幂函数 a^n ?

具有位字段的结构的 C++ 大小

c - DMA在STM32的什么地方存放ADC的值?

c - C 编程中的感叹号和 `strcmp` 函数

c - 寄存器的 I2C 读取和 I2C 写入行为,设备无应答

performance - 是否有任何与英特尔 IPP 相当的 ARM?

android - 为什么 gcc emmiting 代码与 ARM 指令集的 2 字节边界对齐?

管脚映射代码

c++ - 如何表示指向地址空间开头的指针?