STM32F103C8 裸机与链接器脚本 GCC
STM32F103C8 bare metal with linker scripts GCC
我正在尝试在汇编中编写裸机代码并使用 GCC 工具链编译 + link 它。
据我所知,正确的步骤是遵循以下步骤:
- 重启后 - MCU 必须检查向量 table 并执行重置处理程序,我在此处初始化堆栈指针。
- 执行主要代码。
为了完成这个任务,我还需要相应的 linker 脚本。当我尝试执行 linker 时,它抛出语法错误。
请指教:
- linker 脚本中需要更正的内容
- 更正 vtable 和处理程序执行顺序。
代码:
stack_size = 0x400
stack_start = 0x20000000+stack_size
gpiob_base = 0x40010C00
rcc_base = 0x40021000
rcc_apb2enr = rcc_base+0x18
gpio_crl = gpiob_base
gpiob_odr = gpiob_base+0x0C
.syntax unified
.cpu cortex-m3
.thumb
.global main
.global vtable
main:
LDR R0, =rcc_apb2enr
LDR R1, [R0]
LDR R2, =0x8 // Activate 3rd bit in registry
ORR R1, R2
STR R1, [R0]
// Configure GPIO_CRL
LDR R0, =gpio_crl
LDR R1, [R0]
LDR R2, =0xFFFFFF00
AND R1,R1,R2
ORR R1, R1, #0x20
STR R1, [R0] // Reset register
//Configure GPIOB_ODR
LDR R0, =gpiob_odr
LDR R1, [R0]
ORR R1, #0x2
STR R1, [R0]
B .
vtable:
.word stack_start
.word reset_handler
reset_handler:
B main
链接描述文件:
/* - STM32F103C8T6 - Medium Density device
* - RAM: 20K, Flash:64K CPU: 72MHz
*/
ENTRY(reset_handler);
MEMORY {
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
}
SECTIONS
{
/* Section that stores program instructions (code) */
.text : {
. = ALIGN(4);
KEEP(*(.vtable))
*(.text)
*(.text*)
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} > FLASH
_data_flash = .;
// Section that store initialized data - variables
.data : AT(_data_flash){
. = ALIGN(4);
_data_begin = .;
*(.data)
*(.data*)
. = ALIGN(4);
_data_end = .;
} > RAM
/* Section that stores uninitialized data */
.bss :{
_bss_begin = .;
_bss_start_ = _bss_begin;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_bss_end = .;
_bss_end_ = _bss_end;
} > RAM
/* Here we define stack */
_stack_size = 1024;
_stack_end = ORIGIN(RAM)+LENGTH(RAM);
_stack_begin = _stack_end - _stack_size;
. = _stack_begin;
._stack :{
. = . + _stack_size;
} > RAM
}
._stack :{
. = . + _stack_size;
} > RAM
}
程序反汇编:
pi@mylab:~/assembly $ arm-none-eabi-objdump --disassemble bp.o
bp.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <main>:
0: 480d ldr r0, [pc, #52] ; (38 <reset_handler+0x4>)
2: 6801 ldr r1, [r0, #0]
4: f04f 0208 mov.w r2, #8
8: ea41 0102 orr.w r1, r1, r2
c: 6001 str r1, [r0, #0]
e: 480b ldr r0, [pc, #44] ; (3c <reset_handler+0x8>)
10: 6801 ldr r1, [r0, #0]
12: f06f 02ff mvn.w r2, #255 ; 0xff
16: ea01 0102 and.w r1, r1, r2
1a: f041 0120 orr.w r1, r1, #32
1e: 6001 str r1, [r0, #0]
20: 4807 ldr r0, [pc, #28] ; (40 <reset_handler+0xc>)
22: 6801 ldr r1, [r0, #0]
24: f041 0102 orr.w r1, r1, #2
28: 6001 str r1, [r0, #0]
2a: e7fe b.n 2a <main+0x2a>
0000002c <isr_vector>:
2c: 20000400 .word 0x20000400
30: 00000034 .word 0x00000034
00000034 <reset_handler>:
34: f7ff bffe b.w 0 <main>
38: 40021018 .word 0x40021018
3c: 40010c00 .word 0x40010c00
40: 40010c0c .word 0x40010c0c
对于你正在做的事情,你可以从更简单的开始。
flash.s
stack_size = 0x400
stack_start = 0x20000000+stack_size
gpiob_base = 0x40010C00
rcc_base = 0x40021000
rcc_apb2enr = rcc_base+0x18
gpio_crl = gpiob_base
gpiob_odr = gpiob_base+0x0C
.syntax unified
.cpu cortex-m3
.thumb
vtable:
.word stack_start
.word reset_handler
.thumb_func
reset_handler:
B main
main:
LDR R0, =rcc_apb2enr
LDR R1, [R0]
LDR R2, =0x8 // Activate 3rd bit in registry
ORR R1, R2
STR R1, [R0]
// Configure GPIO_CRL
LDR R0, =gpio_crl
LDR R1, [R0]
LDR R2, =0xFFFFFF00
AND R1,R1,R2
ORR R1, R1, #0x20
STR R1, [R0] // Reset register
//Configure GPIOB_ODR
LDR R0, =gpiob_odr
LDR R1, [R0]
ORR R1, #0x2
STR R1, [R0]
B .
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
}
建造
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin
检查(应该始终对新项目执行此操作)
so.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <vtable>:
8000000: 20000400 andcs r0, r0, r0, lsl #8
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <reset_handler>:
8000008: e7ff b.n 800000a <main>
0800000a <main>:
800000a: 480b ldr r0, [pc, #44] ; (8000038 <main+0x2e>)
800000c: 6801 ldr r1, [r0, #0]
800000e: f04f 0208 mov.w r2, #8
8000012: ea41 0102 orr.w r1, r1, r2
8000016: 6001 str r1, [r0, #0]
...
因此,只需将向量 table 放在正确的位置,并且重置处理程序地址正确(地址与 1 或),这就有一半的机会工作。
还可以看到它正在生成 thumb2 代码(如果你最终使用 cortex-m0 并且不修复你的 build/code 最容易从 cortex-m0 开始,它会咬你,它适用于所有这些然后如果需要 select thumb2.YMMV)
现在,如果您有其他对象,在这种情况下,带有向量 table、flash.o 的对象,需要成为命令行上的第一个对象,才能成为第一个 .text解析并放入二进制文件中。
您可以将事情复杂化以标记向量 tables 的特殊部分(请注意,您可以随意调用部分但有一些限制,不必将其称为向量或 vector_table,等等除非你愿意)。然后,您将需要在源代码等中添加更多链接器内容和更多代码。因为您必须在命令行上获得正确的内容才能开始,并且您必须投入工作以获取正确的代码,包括指令。为什么要创建更多没有价值的工作,只需将向量 table 放在正确的位置,靠近重置处理程序。 YMMV.
所以你的矢量 table 接近代码的末尾,而不是第一件事。基本上你要求 table 在错误的地方。它是。您首先需要它以适应此代码的其余部分。接下来,由于您在 table 中引用重置处理程序的地址,您需要函数地址而不仅仅是地址,您需要将该标签声明为一个函数,以便它为 thumb/arm 工作。对于缩略图函数,您可以在标签前的某处使用快捷方式 .thumb_func(不必是之前的行),它将触发 gnu 汇编程序找到的下一个标签作为缩略图函数。因为这是靠近顶部的单个 .thumb 下的所有单个文件,所以到 main 的分支是可以的。理想情况下,您可能也想声明一个函数,当然,如果您正在为非 cortex-m 手臂进行 arm/thumb 交互操作,则需要在全局 asm 函数中进行此操作。
同时适用于 arm 和 thumb gnu 汇编器的另一种方法是
.type functionname, %function
functionname:
您的链接描述文件对于此任务过于复杂。我确定您正在努力解决这个问题。
您使用了正确的地址,但您拆解的是对象而不是小精灵。但是根据你的代码,它仍然会构建错误。
您正在链接描述文件中声明一个堆栈(我倾向于非常讨厌它,但有些人喜欢它,YMMV)。但是您没有在向量 table 中使用该链接描述文件变量,您使用的是计算值。这个程序没有坏掉,但有一点需要注意。
堆栈正在由向量table设置,因此您可以使用它(可以调用函数)。我推荐
bl main
b .
在reset handler中,然后main可以选择无限循环或者return你是safe/fine.
如果您没有在向量中正确设置堆栈地址 table 那么您可能会在那里遇到麻烦。
注意 odr 很好,但您可能需要查看 bsrr 寄存器。对于单个端口位更灵活。并且将位“打开”并不会打开所有板上的 LED“打开”,一些 gpio 连接到另一端并且您将其接地以将其打开。所以请注意这一点并查看原理图或文档。
我正在尝试在汇编中编写裸机代码并使用 GCC 工具链编译 + link 它。 据我所知,正确的步骤是遵循以下步骤:
- 重启后 - MCU 必须检查向量 table 并执行重置处理程序,我在此处初始化堆栈指针。
- 执行主要代码。 为了完成这个任务,我还需要相应的 linker 脚本。当我尝试执行 linker 时,它抛出语法错误。 请指教:
- linker 脚本中需要更正的内容
- 更正 vtable 和处理程序执行顺序。
代码:
stack_size = 0x400
stack_start = 0x20000000+stack_size
gpiob_base = 0x40010C00
rcc_base = 0x40021000
rcc_apb2enr = rcc_base+0x18
gpio_crl = gpiob_base
gpiob_odr = gpiob_base+0x0C
.syntax unified
.cpu cortex-m3
.thumb
.global main
.global vtable
main:
LDR R0, =rcc_apb2enr
LDR R1, [R0]
LDR R2, =0x8 // Activate 3rd bit in registry
ORR R1, R2
STR R1, [R0]
// Configure GPIO_CRL
LDR R0, =gpio_crl
LDR R1, [R0]
LDR R2, =0xFFFFFF00
AND R1,R1,R2
ORR R1, R1, #0x20
STR R1, [R0] // Reset register
//Configure GPIOB_ODR
LDR R0, =gpiob_odr
LDR R1, [R0]
ORR R1, #0x2
STR R1, [R0]
B .
vtable:
.word stack_start
.word reset_handler
reset_handler:
B main
链接描述文件:
/* - STM32F103C8T6 - Medium Density device
* - RAM: 20K, Flash:64K CPU: 72MHz
*/
ENTRY(reset_handler);
MEMORY {
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
}
SECTIONS
{
/* Section that stores program instructions (code) */
.text : {
. = ALIGN(4);
KEEP(*(.vtable))
*(.text)
*(.text*)
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} > FLASH
_data_flash = .;
// Section that store initialized data - variables
.data : AT(_data_flash){
. = ALIGN(4);
_data_begin = .;
*(.data)
*(.data*)
. = ALIGN(4);
_data_end = .;
} > RAM
/* Section that stores uninitialized data */
.bss :{
_bss_begin = .;
_bss_start_ = _bss_begin;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_bss_end = .;
_bss_end_ = _bss_end;
} > RAM
/* Here we define stack */
_stack_size = 1024;
_stack_end = ORIGIN(RAM)+LENGTH(RAM);
_stack_begin = _stack_end - _stack_size;
. = _stack_begin;
._stack :{
. = . + _stack_size;
} > RAM
}
._stack :{
. = . + _stack_size;
} > RAM
}
程序反汇编:
pi@mylab:~/assembly $ arm-none-eabi-objdump --disassemble bp.o
bp.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <main>:
0: 480d ldr r0, [pc, #52] ; (38 <reset_handler+0x4>)
2: 6801 ldr r1, [r0, #0]
4: f04f 0208 mov.w r2, #8
8: ea41 0102 orr.w r1, r1, r2
c: 6001 str r1, [r0, #0]
e: 480b ldr r0, [pc, #44] ; (3c <reset_handler+0x8>)
10: 6801 ldr r1, [r0, #0]
12: f06f 02ff mvn.w r2, #255 ; 0xff
16: ea01 0102 and.w r1, r1, r2
1a: f041 0120 orr.w r1, r1, #32
1e: 6001 str r1, [r0, #0]
20: 4807 ldr r0, [pc, #28] ; (40 <reset_handler+0xc>)
22: 6801 ldr r1, [r0, #0]
24: f041 0102 orr.w r1, r1, #2
28: 6001 str r1, [r0, #0]
2a: e7fe b.n 2a <main+0x2a>
0000002c <isr_vector>:
2c: 20000400 .word 0x20000400
30: 00000034 .word 0x00000034
00000034 <reset_handler>:
34: f7ff bffe b.w 0 <main>
38: 40021018 .word 0x40021018
3c: 40010c00 .word 0x40010c00
40: 40010c0c .word 0x40010c0c
对于你正在做的事情,你可以从更简单的开始。
flash.s
stack_size = 0x400
stack_start = 0x20000000+stack_size
gpiob_base = 0x40010C00
rcc_base = 0x40021000
rcc_apb2enr = rcc_base+0x18
gpio_crl = gpiob_base
gpiob_odr = gpiob_base+0x0C
.syntax unified
.cpu cortex-m3
.thumb
vtable:
.word stack_start
.word reset_handler
.thumb_func
reset_handler:
B main
main:
LDR R0, =rcc_apb2enr
LDR R1, [R0]
LDR R2, =0x8 // Activate 3rd bit in registry
ORR R1, R2
STR R1, [R0]
// Configure GPIO_CRL
LDR R0, =gpio_crl
LDR R1, [R0]
LDR R2, =0xFFFFFF00
AND R1,R1,R2
ORR R1, R1, #0x20
STR R1, [R0] // Reset register
//Configure GPIOB_ODR
LDR R0, =gpiob_odr
LDR R1, [R0]
ORR R1, #0x2
STR R1, [R0]
B .
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
}
建造
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-ld -nostdlib -nostartfiles -T flash.ld flash.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy -O binary so.elf so.bin
检查(应该始终对新项目执行此操作)
so.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <vtable>:
8000000: 20000400 andcs r0, r0, r0, lsl #8
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <reset_handler>:
8000008: e7ff b.n 800000a <main>
0800000a <main>:
800000a: 480b ldr r0, [pc, #44] ; (8000038 <main+0x2e>)
800000c: 6801 ldr r1, [r0, #0]
800000e: f04f 0208 mov.w r2, #8
8000012: ea41 0102 orr.w r1, r1, r2
8000016: 6001 str r1, [r0, #0]
...
因此,只需将向量 table 放在正确的位置,并且重置处理程序地址正确(地址与 1 或),这就有一半的机会工作。
还可以看到它正在生成 thumb2 代码(如果你最终使用 cortex-m0 并且不修复你的 build/code 最容易从 cortex-m0 开始,它会咬你,它适用于所有这些然后如果需要 select thumb2.YMMV)
现在,如果您有其他对象,在这种情况下,带有向量 table、flash.o 的对象,需要成为命令行上的第一个对象,才能成为第一个 .text解析并放入二进制文件中。
您可以将事情复杂化以标记向量 tables 的特殊部分(请注意,您可以随意调用部分但有一些限制,不必将其称为向量或 vector_table,等等除非你愿意)。然后,您将需要在源代码等中添加更多链接器内容和更多代码。因为您必须在命令行上获得正确的内容才能开始,并且您必须投入工作以获取正确的代码,包括指令。为什么要创建更多没有价值的工作,只需将向量 table 放在正确的位置,靠近重置处理程序。 YMMV.
所以你的矢量 table 接近代码的末尾,而不是第一件事。基本上你要求 table 在错误的地方。它是。您首先需要它以适应此代码的其余部分。接下来,由于您在 table 中引用重置处理程序的地址,您需要函数地址而不仅仅是地址,您需要将该标签声明为一个函数,以便它为 thumb/arm 工作。对于缩略图函数,您可以在标签前的某处使用快捷方式 .thumb_func(不必是之前的行),它将触发 gnu 汇编程序找到的下一个标签作为缩略图函数。因为这是靠近顶部的单个 .thumb 下的所有单个文件,所以到 main 的分支是可以的。理想情况下,您可能也想声明一个函数,当然,如果您正在为非 cortex-m 手臂进行 arm/thumb 交互操作,则需要在全局 asm 函数中进行此操作。
同时适用于 arm 和 thumb gnu 汇编器的另一种方法是
.type functionname, %function
functionname:
您的链接描述文件对于此任务过于复杂。我确定您正在努力解决这个问题。
您使用了正确的地址,但您拆解的是对象而不是小精灵。但是根据你的代码,它仍然会构建错误。
您正在链接描述文件中声明一个堆栈(我倾向于非常讨厌它,但有些人喜欢它,YMMV)。但是您没有在向量 table 中使用该链接描述文件变量,您使用的是计算值。这个程序没有坏掉,但有一点需要注意。
堆栈正在由向量table设置,因此您可以使用它(可以调用函数)。我推荐
bl main
b .
在reset handler中,然后main可以选择无限循环或者return你是safe/fine.
如果您没有在向量中正确设置堆栈地址 table 那么您可能会在那里遇到麻烦。
注意 odr 很好,但您可能需要查看 bsrr 寄存器。对于单个端口位更灵活。并且将位“打开”并不会打开所有板上的 LED“打开”,一些 gpio 连接到另一端并且您将其接地以将其打开。所以请注意这一点并查看原理图或文档。