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和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,该按钮确实支持中断。并且需要一些内存,因此定义了.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都可以正常工作。
不会显示整个vector.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构建。您可以更改all:行,仅在不涉及/不希望使用clang(llvm)时才包含gcc项。我使用binutils汇编并链接clang输出btw。
所有这些项目都使用免费的,现成的开源工具。没有IDE,仅命令行。是的,我只使用Linux而不是Windows,但这些工具也可供Windows用户使用,将rm -f等内容更改为makefile中的某些内容,如在Windows上构建时的内容。或在vmware或virtualbox或qemu上运行linux。不使用IDE意味着您也要选择文本编辑器,我不会说的,我有我的最爱。请注意,gnu make程序的一个非常烦人的功能是它需要makefile中的实际制表符,我非常讨厌看不见的制表符。因此,一个文本编辑器用于保留制表符的makefile文件,另一个文本编辑器用于制造空格的源代码。我不知道窗户,
我希望这会有所帮助,不是确切的芯片/板,而是cortex-m4孔m4而不是m3,对于本次讨论而言足够接近。请参阅mbed或stm32vld目录以获取实际的cortex-m3(与m4的makefile和启动代码等差异不大),但不是由st。制作的。各个供应商的cortex-m3内核应该相同,cortex-m3和cortex-m4都是ARMv7m,并且距离更近而不是不同。cortex-m0是ARMv6m,几乎没有足以烦恼的thumb2指令,编译器还没有赶上它,因此只能使用thumb(如果需要,可以假装您正在构建ARMv4T(仅拇指))。我的拇指模拟器仅是拇指,没有拇指2,它对您也可能有用,我认为我使它以某种形式或方式执行中断。