STM32F2:Makefile、链接器脚本和启动文件组合,无需商业IDE

电器工程 C stm32 海合会
2022-01-04 06:08:41

我一直在使用 STM32F2(特别是开发板上的 STM32F217IGH6)大约两个月。到目前为止,我最大的问题与“设置”有关,它包括生成文件、链接器脚本和启动文件。

特别是,我无法正确设置我的中断向量表并调用中断处理程序。ST 确实提供了针对商业 IDE 量身定制的示例。相反,我使用免费的 Yagarto 重新编译 GCC 工具链(和 OpenOCD 通过 JTAG 加载图像)。

我的板子(或它的近亲)是否有示例项目,其中包含为调用中断处理程序设置的非商业 IDE 的适当 makefile、链接器脚本和启动文件组合?

2个回答

http://github.com/dwelch67

特别是 stm32f4 和 stm32vld,但其他的也可能对您有用。mbed 和 mbed (cortex-m0) 下的 mzero 目录。

我喜欢保持简单愚蠢的方法、最少的链接器脚本、最少的启动代码等。这项工作是由代码完成的,而不是由任何特定的工具链完成的。

大多数形式的 gcc 和 binutils(能够使用拇指)在这些示例中都可以使用,因为我使用编译器进行编译,而不是作为库调用的资源,我不使用股票链接器脚本等。较旧的 gcc 和 binutils 不会知道较新的 thumb2 部件,因此可能需要进行一些更改。

例如,我构建了自己的 gcc、binutils 和 llvm/clang 以及使用 codesourcery(现在是指导图形,但您仍然可以获得免费/精简版)。

特别是在开始为新目标组合项目时,您需要进行一些反汇编。特别是要确保项目在您想要的位置,例如矢量表。

以 stm32f4d/blinker02 为例。它以vectors.s 开始,异常/向量表加上一些asm 支持例程:

/* vectors.s */
.cpu cortex-m3
.thumb

.word   0x20002000  /* stack top address */
.word   _start      /* 1 Reset */
.word   hang        /* 2 NMI */
.word   hang        /* 3 HardFault */
.word   hang        /* 4 MemManage */
.word   hang        /* 5 BusFault */
.word   hang        /* 6 UsageFault */
.word   hang        /* 7 RESERVED */
.word   hang        /* 8 RESERVED */
.word   hang        /* 9 RESERVED*/
.word   hang        /* 10 RESERVED */
.word   hang        /* 11 SVCall */
.word   hang        /* 12 Debug Monitor */
.word   hang        /* 13 RESERVED */
.word   hang        /* 14 PendSV */
.word   hang        /* 15 SysTick */
.word   hang        /* 16 External Interrupt(0) */
.word   hang        /* 17 External Interrupt(1) */
.word   hang        /* 18 External Interrupt(2) */
.word   hang        /* 19 ...   */

.thumb_func
.global _start
_start:
    /*ldr r0,stacktop */
    /*mov sp,r0*/
    bl notmain
    b hang

.thumb_func
hang:   b .

/*.align
stacktop: .word 0x20001000*/

;@-----------------------
.thumb_func
.globl PUT16
PUT16:
    strh r1,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr
;@-----------------------
.thumb_func
.globl GET16
GET16:
    ldrh r0,[r0]
    bx lr

.end

这个例子没有中断,但你需要的其他东西在这里。

blinker02.c 包含带有 C 入口点的 C 代码主体,我将其称为 notmain() 以避免将其称为 main(当您有 main() 时,某些编译器会将垃圾添加到您的二进制文件中)。

将节省您的剪切和粘贴。makefile 讲述了关于编译和链接的故事。请注意,我的一些示例从相同的代码编译两个或多个二进制文件。gcc 编译器、llvm 的 clang 编译器、thumb only 和 thumb2、不同的优化等。

首先从源文件制作目标文件。

vectors.o : vectors.s
    $(ARMGNU)-as vectors.s -o vectors.o

blinker02.gcc.thumb.o : blinker02.c
    $(ARMGNU)-gcc $(COPS) -mthumb -c blinker02.c -o blinker02.gcc.thumb.o

blinker02.gcc.thumb2.o : blinker02.c
    $(ARMGNU)-gcc $(COPS) -mthumb -mcpu=cortex-m3 -march=armv7-m -c blinker02.c -o blinker02.gcc.thumb2.o

blinker02.gcc.thumb.bin : memmap vectors.o blinker02.gcc.thumb.o
    $(ARMGNU)-ld -o blinker02.gcc.thumb.elf -T memmap vectors.o blinker02.gcc.thumb.o
    $(ARMGNU)-objdump -D blinker02.gcc.thumb.elf > blinker02.gcc.thumb.list
    $(ARMGNU)-objcopy blinker02.gcc.thumb.elf blinker02.gcc.thumb.bin -O binary

blinker02.gcc.thumb2.bin : memmap vectors.o blinker02.gcc.thumb2.o
    $(ARMGNU)-ld -o blinker02.gcc.thumb2.elf -T memmap vectors.o blinker02.gcc.thumb2.o
    $(ARMGNU)-objdump -D blinker02.gcc.thumb2.elf > blinker02.gcc.thumb2.list
    $(ARMGNU)-objcopy blinker02.gcc.thumb2.elf blinker02.gcc.thumb2.bin -O binary

链接器 ld 使用我称为 memmap 的链接器脚本,这些可能非常痛苦,有时是有充分理由的,有时不是。我更喜欢少即是多的方法来满足所有人的需求,除了厨房水槽方法。

我通常不使用 .data(几乎从不使用),并且这个示例不需要 .bss,所以这里是链接器脚本,足以将程序(.text)放置在这个处理器需要的地方,就像我一样使用它。

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

SECTIONS
{
    .text : { *(.text*) } > ram
}

我有一个内存区域来定义它,名称 ram 没有什么特别之处,你可以称它为 foo 或 bar 或 bob 或 ted 没关系,它只是将内存项链接到部分。这些部分定义了诸如 .text、.data、.bss、.rodata 之类的内容以及它们在内存映射中的位置。

当你构建这个时,你会看到我反汇编了所有东西(objdump -D)你看到了这个

Disassembly of section .text:

08000000 <_start-0x50>:
 8000000:       20002000        andcs   r2, r0, r0
 8000004:       08000051        stmdaeq r0, {r0, r4, r6}
 8000008:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}
 800000c:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}
 8000010:       08000057        stmdaeq r0, {r0, r1, r2, r4, r6}

需要注意的关键是左边的地址是我们想要的地址,vectors.s 代码在二进制文件中是第一个(因为它在 ld 命令行上是第一个,除非您在链接描述文件中执行某些操作,否则项目将显示按照它们在 ld 命令行上的顺序在二进制文件中向上)。为了正确启动,您必须确保您的向量表位于正确的位置。第一项是我的堆栈地址,这很好。第二项是_start的地址,它应该是一个奇数。在标签之前使用 .thumb_func 会导致这种情况发生,因此您不必做其他难看的事情。

08000050 <_start>:
 8000050:       f000 f822       bl      8000098 <notmain>
 8000054:       e7ff            b.n     8000056 <hang>

08000056 <hang>:
 8000056:       e7fe          

所以 0x08000051 和 0x08000057 是 _start 和 hang 的正确向量条目。开始调用 notmain()

08000098 <notmain>:
 8000098:       b510            push    {

看起来不错(他们没有在反汇编中显示奇数地址)。

一切都很好。

跳到示例 blinker05,这个确实支持中断。并且需要一些 ram,因此定义了 .bss。

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

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

请记住 ram 和 rom 是任意名称,bob 和 ted、foo 和 bar 都可以正常工作。

不会显示整个vectors.s,因为如果你制作一个完整的,cortex-m3 在向量表中有无数个条目(因内核而异,可能在同一个内核内,具体取决于芯片供应商选择的选项)拆解后相关部分在这里:

08000000 <_start-0x148>:
 8000000:       20020000        andcs   r0, r2, r0
 8000004:       08000149        stmdaeq r0, {r0, r3, r6, r8}
 8000008:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
...
8000104:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}
 8000108:       08000179        stmdaeq r0, {r0, r3, r4, r5, r6, r8}
 800010c:       0800014f        stmdaeq r0, {r0, r1, r2, r3, r6, r8}

需要一些试验和错误才能将该处理程序准确地放置在正确的位置,检查您的芯片需要的位置,它不一定与这个位置相同,并且有如此多的中断,您可能正在寻找不同的中断。cortex-m 处理器,与普通手臂不同,它使您不需要蹦床代码来进行中断,它们保留了一定数量的寄存器并通过链接寄存器内容管理处理器模式的切换。只要编译器的硬件和 abi 足够接近,它就可以工作。在这种情况下,我在 C 中做了处理程序,不像其他平台和过去你不需要对编译器/语法做任何特殊的事情,只需创建一个函数(但不要在函数/处理程序中做愚蠢的事情)

//-------------------------------------------------------------------
volatile unsigned int intcounter;
//-------------------------------------------------------------------
// CAREFUL, THIS IS AN INTERRUPT HANDLER
void tim5_handler ( void )
{
    intcounter++;
    PUT32(TIM5BASE+0x10,0x00000000);
}
// CAREFUL, THIS IS AN INTERRUPT HANDLER
//-------------------------------------------------------------------

blinker05 的 makefile 应该类似于 blinker02 示例,大部分都是剪切和粘贴。将单个源文件转换为对象,然后链接。我确实使用 gcc 和 clang 为 thumb、thumb2 构建。如果您没有/想要clang(llvm)参与,您可以更改 all: 行以仅包含 gcc 项目。我使用 binutils 来组装和链接 clang 输出 btw。

所有这些项目都使用免费、现成的开源工具。没有IDE,只有命令行。是的,我只使用 Linux 而不是 Windows,但是这些工具也可供 Windows 用户使用,更改诸如 rm -f 之类的东西以在 makefile 中删除一些东西,就像在 Windows 上构建时那样。或者在 vmware 或 virtualbox 或 qemu 上运行 linux。不使用 IDE 意味着你也选择了你的文本编辑器,我不会进入那个,我有我最喜欢的。请注意,gnu make 程序的一个非常烦人的特性是它需要 makefile 中的实际选项卡,我非常讨厌不可见的选项卡。因此,一个用于生成文件的文本编辑器会留下选项卡,另一个用于生成空格的源代码。我不知道窗户,

我希望这会有所帮助,它不是确切的芯片/板,而是 cortex-m4 井 m4 而不是 m3,足够接近本次讨论。查看实际 cortex-m3 的 mbed 或 stm32vld 目录(对于 makefile 和引导代码等,与 m4 没有足够的差异),但不是由 st。各个供应商的 cortex-m3 内核应该是相同的,cortex-m3 和 cortex-m4 都是 ARMv7m 并且更接近而不是不同。cortex-m0 是一个 ARMv6m,几乎没有 thumb2 指令足以打扰,编译器还没有赶上它,所以只使用拇指(假装你正在为 ARMv4T(仅限拇指)构建,如果需要的话)。我的拇指模拟器只有拇指,没有拇指2,它也可能对你有用,我想我让它以某种形式或方式执行中断。

您可以查看此站点,该站点尝试解释代码中链接器和 low_level_init 的基础知识。

请注意,该页面专注于描述问题,因此 nvic 向量有点小。

然后在“STM32F2xx 标准外设库”中有更完整的示例,只需查看 gcc 部分(因为 Yagarto 是基于 gcc 的)。那里有示例代码可以帮助您正确设置 nvic(中断向量表)。

因此,即使这不是一个完整的答案,我也希望它对您有所帮助。