尝试printf数字> = 10时STM32硬错误

时间:2014-10-13 02:29:45

标签: arm printf stm32 cortex-m3 newlib

临时解决方法

我用微小的printf解决了这个问题:

可能newlib printf只占用太多内存。在此之后,PC会改变一些奇怪的东西,理论上它应该是数组char[100]的结尾。

cp = buf + BUF

稍后它会尝试执行

*--cp = something

它崩溃了。记忆错误?有很多事情我不明白。例如,我不确定链接器脚本是否正确,或者是syscall函数。在我进一步了解之前,我必须坚持使用这个小小的printf。


原始

我有一块STM32F103RB板(Nucleo),我刚刚使用了USART1。此外,我测试了Newlib函数,puts()按预期工作。但是,当我尝试将printf()用于整数时,如下所示:

printf("ADC: %d\r\n", test);

如果test < 10程序有效,但如果test >= 10生成了硬故障。经过一些GDB调试后,我发现它是从vfprintf生成的:

#0  HardFault_Handler () at main.c:136
#1  <signal handler called>
#2  0x08005af4 in _vfprintf_r (data=<optimized out>, fp=0x20000384 <impure_data+852>, fmt0=fmt0@entry=0x20004f57 " \254\264", ap=...,
    ap@entry=...) at ../../../../../../newlib/libc/stdio/vfprintf.c:1601
#3  0x08004fd0 in printf (fmt=0x800b4ac "ADC: %d\n\r") at ../../../../../../newlib/libc/stdio/printf.c:52
#4  0x080004e8 in main () at main.c:168

我想不出任何解决方案。这是我的_sbrk()

caddr_t _sbrk(int nbytes) {
  static caddr_t heap_ptr = NULL;
  caddr_t base;

  if (heap_ptr == NULL){
    heap_ptr = (caddr_t) &_end;
  }

  if ((caddr_t) _stackend > heap_ptr + nbytes) {
    base = heap_ptr;
    heap_ptr += nbytes;
    return (base);
  }
  else {
    errno = ENOMEM;
    return ((caddr_t) -1);
  }
}

我的最小堆栈大小配置为1024字节。我试图增加到10k,但它仍然会产生这个难题。我不知道如何调试或找到问题。我该如何解决?

我注意到了一件事。失败发生在vfprintf.c:1601,在那里我检查了GDB中的指针cp

(gdb) x 0x20004f57
0x20004f57:     0x00b4ac20
(gdb) x 0x00b4ac20
0xb4ac20:       0x20004f80
(gdb) x 0x20004f58
0x20004f58:     0x0800b4ac
(gdb)

我不知道为什么地址0x20004f57指向一个不存在的地址。

另外,p _stackend给出了

$6 = (caddr_t) 0xb33ea8a6 <error: Cannot access memory at address 0xb33ea8a6>

显然不存在

似乎执行_VFPRINTF_R()时,执行goto number时,指针cp已损坏:

3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb) n
1057                            base = DEC;
3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb)
1400    number:                 if ((dprec = prec) >= 0)
3: cp = <optimized out>
2: fmt = 0x800b453 "\n\r"
(gdb)
1409                            if (_uquad != 0 || prec != 0) {
3: cp = 0x20004b58 "\245ۊ\256\211W{\325\326\377Y\352\224\t x\207\220\230&-\031\032~\337\032\371\024\254\"(\214\354\363\b\241\365\022\035\037\252\026\243\206\235P\005OZn\245c\n\352\244E^ά\246\301Ӕ\271L\264"
2: fmt = 0x800b453 "\n\r"
(gdb)

链接脚本:

/*
Linker script for STM32F10x_128K_20K

modified from

http://www.codesourcery.com/archives/arm-gnu/msg02972.html
http://communities.mentor.com/community/cs/archives/arm-gnu/msg02972.html
*/

/*
There will be a link error if there is not this amount of RAM free at the
end.
*/

/* _Minimum_Stack_Size = 256; */
_Minimum_Stack_Size = 1024;

ENTRY(Reset_Handler)


/* Memory Spaces Definitions */

MEMORY
{
  RAM (rwx)  : ORIGIN = 0x20000000, LENGTH = 20K
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
}

__ram_start__ = ORIGIN(RAM);
__ram_size__  = LENGTH(RAM);
__ram_end__   = __ram_start__ + __ram_size__;
_estack = __ram_end__;
/* highest address of the user mode stack */



PROVIDE ( _Stack_Limit = _estack - _Minimum_Stack_Size );

/* Sections Definitions */

SECTIONS
{
    .text :
    {
        KEEP(*(.isr_vector))            /* Startup code */
        *(.text)                   /* code */
        *(.text.*)                 /* remaining code */
        *(.rodata)                 /* read-only data (constants) */
        *(.rodata.*)
        *(.glue_7)
        *(.glue_7t)
        *(.vfp11_veneer)
        *(.v4_bx)
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } >FLASH

    /* for exception handling/unwind - some Newlib functions (in
    common with C++ and STDC++) use this. */
    .ARM.extab :
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)

    } > FLASH

     __exidx_start = .;
    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > FLASH
        __exidx_end = .;

    . = ALIGN(4);
     _etext = .;
    /* This is used by the startup in order to initialize the .data secion
*/
    _sidata = _etext;

    /* This is the initialized data section
    The program executes knowing that the data is in the RAM
    but the loader puts the initial values in the FLASH (inidata).
    It is one task of the startup to copy the initial values from FLASH to
RAM. */
    .data  : AT ( _sidata )
    {
        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .data
secion */
        _sdata = . ;

        *(.data)
        *(.data.*)

        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .data
secion */
        _edata = . ;
    } >RAM


    /* This is the uninitialized data section */
    .bss :
    {
        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .bss
secion */
        _sbss = .;
    __bss_start__ = _sbss;
        *(.bss)
        *(.bss.*)
        *(COMMON)

        . = ALIGN(4);
        /* This is used by the startup in order to initialize the .bss
secion */
        _ebss = . ;
    __bss_end__ = _ebss;
    } >RAM

    PROVIDE ( end = _ebss );
    PROVIDE ( _end = _ebss );
    PROVIDE ( _exit = _ebss );
    PROVIDE (_stackend = ORIGIN(RAM) + LENGTH(RAM) - _Minimum_Stack_Size);

    /* This is the user stack section
    This is just to check that there is enough RAM left for the User mode
stack
    It should generate an error if it's full.
     */
    ._usrstack :
    {
        . = ALIGN(4);
        _susrstack = . ;

        . = . + _Minimum_Stack_Size ;

        . = ALIGN(4);
        _eusrstack = . ;
    } >RAM



    /* after that it's only debugging information. */

    /* remove the debugging information from the standard libraries */
/*
    DISCARD :
    {
     libc.a ( * )
     libm.a ( * )
     libgcc.a ( * )
     }
*/

    /* Stabs debugging sections.  */
    .stab          0 : { *(.stab) }
    .stabstr       0 : { *(.stabstr) }
    .stab.excl     0 : { *(.stab.excl) }
    .stab.exclstr  0 : { *(.stab.exclstr) }
    .stab.index    0 : { *(.stab.index) }
    .stab.indexstr 0 : { *(.stab.indexstr) }
    .comment       0 : { *(.comment) }
    /* DWARF debug sections.
       Symbols in the DWARF debugging sections are relative to the beginning
       of the section so we begin them at 0.  */
    /* DWARF 1 */
    .debug          0 : { *(.debug) }
    .line           0 : { *(.line) }
    /* GNU DWARF 1 extensions */
    .debug_srcinfo  0 : { *(.debug_srcinfo) }
    .debug_sfnames  0 : { *(.debug_sfnames) }
    /* DWARF 1.1 and DWARF 2 */
    .debug_aranges  0 : { *(.debug_aranges) }
    .debug_pubnames 0 : { *(.debug_pubnames) }
    /* DWARF 2 */
    .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
    .debug_abbrev   0 : { *(.debug_abbrev) }
    .debug_line     0 : { *(.debug_line) }
    .debug_frame    0 : { *(.debug_frame) }
    .debug_str      0 : { *(.debug_str) }
    .debug_loc      0 : { *(.debug_loc) }
    .debug_macinfo  0 : { *(.debug_macinfo) }
    /* SGI/MIPS DWARF 2 extensions */
    .debug_weaknames 0 : { *(.debug_weaknames) }
    .debug_funcnames 0 : { *(.debug_funcnames) }
    .debug_typenames 0 : { *(.debug_typenames) }
    .debug_varnames  0 : { *(.debug_varnames) }
}

我没有很好地理解这个链接器脚本,我修改了随附的#34;发现STM32微控制器&#34;。

3 个答案:

答案 0 :(得分:2)

好的,经过各种测试,我发现它是我用过的工具链。我自己使用GCC 4.8构建了工具链,根据http://forum.chibios.org/phpbb/viewtopic.php?f=16&t=2241,GCC 4.8存在一些问题。我切换回CodeSourcery工具链,它再次工作。

答案 1 :(得分:2)

我有同样的问题(也是arm-none-eabi-gcc 4.8),经过一些调试我发现_vfprintf_f使用64位除法导致Hardfaults。事实证明,编译器正在链接错误的libgcc,因为在编译期间指定了执行状态(thumb,ARM),但没有链接。

根据您的处理器指定-mcpu=cortex-m3 -mthumb-mcpu=cortex-m4 -mthumb后,对于编译器链接器,问题就会消失。

成功打印小于10的数字的原因是_vfprintf_r函数在转换为十进制格式时具有单位数字的特殊情况,该格式不使用64位除法,因此没有HardFault。

答案 2 :(得分:1)

Building with ARM gcc toolchain on an ATMEL E70, I solved the issue it by adding -Dprintf=iprintf to the compiler (and linker) arguments. Atmel examples seemed to use this and it worked for me. I had tried to augment the stack size (to 32k) and made sure -mcpu=cortex-m7 -mthumb was passed to both the compiler and the linker but the issue remained.