为什么 stm32f103 的 elf 文件可以正常运行,但二进制文件不行?

Why stm32f103's elf file works well, but binary not?

我正在尝试 运行 取出我在 stm32f103 中的固件。所以当我用opencod+gdb上传和调试elf文件时,一切正常,我的固件可以正常工作,我可以设置和删除断点。

但是当我尝试使用 st-flash 上传此固件(与 elf 文件一起构建)并将其写入 0x8000000 时,它不起作用。虽然我收到 'the firmware was uploaded successfully'.

的消息

我可以看到我的代码是否 运行 当我的 LED 开始闪烁时 link。

BOOT0 通过 npn 晶体管连接到 cp2102 的 DTR 引脚,根据数据表启用引导加载程序。我必须将 BOOT0 设置为高电平。但是当我通过 st-link 上传我的 fw 时,我的序列号 (cp2102) 没有连接。所以我认为 DTR 引脚悬空或下拉。我的错误在哪里?

我试图在上传之前批量擦除我的闪存,它给出了相同的结果

这是我的 link 儿的 ld 文件:

MEMORY
{
  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
  CCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 0
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
  FLASHB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  MEMORY_ARRAY (xrw)  : ORIGIN = 0x00000000, LENGTH = 0
}

和章节 https://pastebin.ubuntu.com/p/N32zQf9sCm/

您尝试过使用 STM32CubeProgrammer 吗?

它允许您使用 UART、SWD、JTAG 和 USB 对固件进行编程。请先尝试SWD,再尝试UART模式,看看是ST的bug还是你的UART接线问题。

试试这个

.globl _start
_start:

.word 0x20001000
.word reset
.word loop
.word loop

.thumb_func
reset:
    add r0,#1
    b reset

.thumb_func
loop:
    b loop

构建,可以使用 arm-whatever-whatever(arm-none-eabi、arm-linux-gnueabi 等)

arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -Ttext=0x08000000 so.o -o so.elf
arm-none-eabi-objcopy -O binary so.elf so.bin
arm-none-eabi-objdump -D so.elf

so.elf:     file format elf32-littlearm


Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000011    stmdaeq r0, {r0, r4}
 8000008:   08000015    stmdaeq r0, {r0, r2, r4}
 800000c:   08000015    stmdaeq r0, {r0, r2, r4}

08000010 <reset>:
 8000010:   3001        adds    r0, #1
 8000012:   e7fd        b.n 8000010 <reset>

08000014 <loop>:
 8000014:   e7fe        b.n 8000014 <loop>

并不是向量是奇数,它们是处理程序的地址与一个或。如果你没有看到这个,处理器将不会启动。

你说过你有 openocd+gdb 工作,所以要么通过那个路径,要么通过 openocd+telnet,或者如果你有其他方式使用 uart 引导加载程序。但是使用重置或开机并为应用程序设置 boot0,然后在不重置的情况下使用 openocd 连接,然后暂停并检查 r0,恢复,暂停并再次检查,它是否在计数,此代码是否加载并从闪存 运行 .

如果您有蓝色药丸,则可以使用此代码使 LED 灯闪烁。

flash.s

.cpu cortex-m0
.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang

.thumb_func
reset:
    bl notmain
    b hang
.thumb_func
hang:   b .

.align

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.thumb_func
.globl dummy
dummy:
    bx lr

.end

blinker01.c

void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );

#define GPIOCBASE 0x40011000
#define RCCBASE 0x40021000

int notmain ( void )
{
    unsigned int ra;
    unsigned int rx;

    ra=GET32(RCCBASE+0x18);
    ra|=1<<4; //enable port c
    PUT32(RCCBASE+0x18,ra);
    //config
    ra=GET32(GPIOCBASE+0x04);
    ra&=~(3<<20);   //PC13
    ra|=1<<20;      //PC13
    ra&=~(3<<22);   //PC13
    ra|=0<<22;      //PC13
    PUT32(GPIOCBASE+0x04,ra);

    for(rx=0;;rx++)
    {
        PUT32(GPIOCBASE+0x10,1<<(13+0));
        for(ra=0;ra<200000;ra++) dummy(ra);
        PUT32(GPIOCBASE+0x10,1<<(13+16));
        for(ra=0;ra<200000;ra++) dummy(ra);
    }
    return(0);
}

flash.ld

MEMORY
{
    rom : ORIGIN = 0x08000000, LENGTH = 0x1000
    ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}

SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
    .bss : { *(.bss*) } > ram
}

建设

arm-none-eabi-as --warn --fatal-warnings  flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding  -mthumb -c blinker01.c -o blinker01.o
arm-none-eabi-ld -o blinker01.elf -T flash.ld flash.o blinker01.o
arm-none-eabi-objdump -D blinker01.elf > blinker01.list
arm-none-eabi-objcopy blinker01.elf blinker01.bin -O binary

检查向量 table

Disassembly of section .text:

08000000 <_start>:
 8000000:   20001000    andcs   r1, r0, r0
 8000004:   08000041    stmdaeq r0, {r0, r6}
 8000008:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 800000c:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000010:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000014:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000018:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 800001c:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000020:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000024:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000028:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 800002c:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000030:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000034:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 8000038:   08000047    stmdaeq r0, {r0, r1, r2, r6}
 800003c:   08000047    stmdaeq r0, {r0, r1, r2, r6}

08000040 <reset>:
 8000040:   f000 f80a   bl  8000058 <notmain>
 8000044:   e7ff        b.n 8000046 <hang>

08000046 <hang>:
 8000046:   e7fe        b.n 8000046 <hang>

看起来不错。现在编程并重置。

您的原理图不是蓝色药丸,因此请相应调整以启用 gpio 时钟并使引脚成为输出等等。

无论是使用这些程序还是您自己的程序,我都不使用 gdb,对它没有任何用处我使用 openocd+telnet(如果有的话)。但要么让你用 telnet

转储闪存
mdw 0x00000000 20
mdw 0x08000000 20

两者应该有相同的数据。如果不是,那么你有一个 boot0 问题。

编辑

如果 DTR 为高那么 boot0 为 0/GND 是吗?这就是正常启动所需要的。 boot0 连接低电平,复位连接高电平,电源打开或从低到高。

您可以编写一个程序以一种或另一种方式强制执行 DTR。

int dtr_bit=TIOCM_DTR;
...
dtr_bit=TIOCM_DTR;
ioctl(ser_hand,TIOCMBIC,&dtr_bit);
...
dtr_bit=TIOCM_DTR;
ioctl(ser_hand,TIOCMBIS,&dtr_bit);

用通常的 termios 东西打开手柄。

或者假设您的哑终端(minicom 等)支持 DTR,连​​接到 uart,然后开机 and/or 重置电路板(回形针,或任何您手边的东西)。

因为我经常使用串行引导加载程序来加载我的 stm32 部件,或者即使我使用 SWD,我总是为自己提供一个控制 boot0 和重置的解决方案,无论是按钮、跳线、焊盘还是一些组合。

@old_timer非常感谢您的详细解答。我发现问题出在过大的固件中。我的芯片只有64k的flash,fw是68k的,所以eclipse没有告诉我固件过大,试过刷。此外,我的项目所基于的模板使用了 newlib,其中 startup.s 被 C 文件替换,我想这有一些问题。所以我从 openstm32 tamplate 创建了一个新项目(它从 stdperiph lib、cmsis 等 + ld 复制了所有必要的文件),之后我添加了我的文件,通过删除未使用的文件和设置 -Os 标志来优化构建,并且它似乎对我有用,但固件大小几乎太大了。我会尝试你的解释)