2015-02-26 2 views
5

Я запускаю голую металлическую встроенную систему с ARM Cortex-M3 (STM32F205). Когда я пытаюсь использовать snprintf() с номерами с плавающей точкой, например .:snprintf() отпечатки мусорных поплавков с newlib nano

float f; 

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

Я получаю мусор в s. Формат, по-видимому, заслуживает внимания, т. Е. Мусор представляет собой хорошо сформированную строку с цифрами, десятичной точкой и двумя конечными цифрами. Однако, если я повторяю snprintf, строка может меняться между двумя вызовами.

с плавающей точкой математике, кажется, работает иначе, и snprintf работы с целыми числами, например .:

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

Я использую newlib-nano реализации с помощью переключателя -u _printf_float линкера. Компилятор: arm-none-eabi-gcc.

У меня есть сильное подозрение в проблемах с распределением памяти, поскольку целые числа печатаются без каких-либо икота, но поплавки действуют так, как если бы они были повреждены в процессе. Семейные функции printf вызывают 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 's любопытство об исходном адресе кучи.)

Стек не должен сталкиваться с кучей, так как вокруг них около 60 KiB RAM. Сценарий компоновщика может быть безумным, но, по крайней мере, адреса кучи и стека кажутся правильными.

+2

Обратите внимание, что это 'double', а не 'float'. Не знаю, если это имеет значение, вы не можете передавать 'float' в' snprintf() 'в любом случае. – unwind

+4

Прототипом для '' snprintf() '] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html) является' int snprintf (char * ограничивать s, size_t n, const char * ограничивать формат , ...); 'Вы не вызываете функцию в соответствии с прототипом. Вы '#include ' ** и скомпилированы с включенными предупреждениями **? – pmg

+1

Уверен, что ваш истинный код использует 'snprintf()', а не 'sprintf()'? – chux

ответ

9

Как это может случиться так, что кто-то получает укусил же ошибка, я отправляю ответ на свой вопрос. Тем не менее, это комментарий @Notlikethat, который предложил правильный ответ.

Это урок Нельзя красть. Я позаимствовал сценарий компоновщика gcc, который пришел с генератором кода STMCubeMX. К сожалению, скрипт вместе с файлом запуска не работает.

Соответствующая часть оригинального линкера сценарий:

_estack = 0x2000ffff; 

и его коллеги в сценарии запуска:

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

g_pfnVectors: 
    .word _estack 
    .word Reset_Handler 
... 

вектор положения первого прерывания (при 0) всегда должен указывать на старт сверху. Когда достигается прерывание сброса, оно также загружает указатель стека. (Насколько я могу сказать, последний не нужен, поскольку HW в любом случае перезагружает SP из 0-го вектора перед вызовом обработчика сброса.)

Указатель стека 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 октетами.

1

snprintf принимает размер как второй аргумент. Вы можете пройти через этот пример http://www.cplusplus.com/reference/cstdio/snprintf/

/* snprintf example */ 
#include <stdio.h> 

int main() 
{ 
    char buffer [100]; 
    int cx; 

    cx = snprintf (buffer, 100, "The half of %d is %d", 60, 60/2); 

    snprintf (buffer+cx, 100-cx, ", and the half of that is %d.", 60/2/2); 

    puts (buffer); 

    return 0; 
} 
+0

Хороший ответ, и да, я мог бы действительно совершить эту ошибку ... Но в этом случае 'gcc' бы (еще раз) сказал мне, что я глуп. Итак, на этот раз проблема, кажется, лежит где-то глубже в 'newlib' - или, возможно, в сценариях компоновщика. – DrV

+0

Lol .... но u может дать ему попробовать atleast .... – theadnangondal

Смежные вопросы