我收到了一款MCU(比如STM32F4xx),我需要创建一个闪烁LED的例子。这应该是从零开始完成的,包括自己的启动代码,没有使用外部库等。
我有一个MCU,比如STM32F4xx,想要让PA5上的LED闪烁,不使用任何库,从零开始,没有外部内容。
blinker01.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define RCCBASE 0x40023800
#define RCC_AHB1ENR (RCCBASE+0x30)
#define GPIOABASE 0x40020000
#define GPIOA_MODER (GPIOABASE+0x00)
#define GPIOA_OTYPER (GPIOABASE+0x04)
#define GPIOA_BSRR (GPIOABASE+0x18)
int notmain ( void )
{
unsigned int ra;
unsigned int rx;
ra=GET32(RCC_AHB1ENR);
ra|=1<<0;
PUT32(RCC_AHB1ENR,ra);
ra=GET32(GPIOA_MODER);
ra&=~(3<<10);
ra|=1<<10;
PUT32(GPIOA_MODER,ra);
ra=GET32(GPIOA_OTYPER);
ra&=~(1<<5);
PUT32(GPIOA_OTYPER,ra);
for(rx=0;;rx++)
{
PUT32(GPIOA_BSRR,((1<<5)<<0));
for(ra=0;ra<200000;ra++) dummy(ra);
PUT32(GPIOA_BSRR,((1<<5)<<16));
for(ra=0;ra<200000;ra++) dummy(ra);
}
return(0);
}
flash.s
.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 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 dummy
dummy:
bx lr
linker script flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
所有这些都是使用gcc/gnu工具完成的。
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m4 flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m4 -mthumb -mcpu=cortex-m4 -c blinker01.c -o blinker01.flash.o
arm-none-eabi-ld -o blinker01.flash.elf -T flash.ld flash.o blinker01.flash.o
arm-none-eabi-objdump -D blinker01.flash.elf > blinker01.flash.list
arm-none-eabi-objcopy blinker01.flash.elf blinker01.flash.bin -O binary
为确保启动正确和链接正确,请检查列表文件中的向量表。
08000000 <_start>:
8000000: 20001000
8000004: 08000041
8000008: 08000047
800000c: 08000047
8000010: 08000047
8000014: 08000047
这些应该是奇数,处理器地址加1。
08000040 <reset>:
8000040: f000 f80a bl 8000058 <notmain>
8000044: e7ff b.n 8000046 <hang>
08000046 <hang>:
8000046: e7fe b.n 8000046 <hang>
在这些STM32部件的情况下,它们从0x08000000开始(某些供应商会将其设置为零)。在上电时,零会被镜像到0x08000000,因此向量将带您到flash中的正确位置。关于LED,将GPIO引脚设置为推挽输出并打开或关闭它即可。在这种情况下,通过使用不在blinker01.c中的函数来强制编译器执行这些计数(而不是进行易失性操作),这是一种简单的优化技巧。PUT32/GET32是个人偏好,确保使用正确的指令,编译器并不总是使用正确的指令,如果硬件需要特定大小的操作,则可能会遇到麻烦。抽象化有更多的优点而不是缺点,我个人认为。这些部件配置和使用起来相当简单。这样学习很好,也可以使用库,职业上你可能需要处理两个极端,也许你成为为他人编写库的人,并且需要同时掌握这两种方法。了解你的工具是最重要的事情,是的,大多数人在这项业务中不知道如何做到这一点,他们依赖于工具,绕过工具或库的问题,而不是理解正在发生的事情并/或修复它。这个答案的重点是1)你问了,2)展示了使用工具有多容易。如果我只使用汇编作为创建向量表的非常简单的方法,则可以使它变得更加简单。Cortex-m是这样的,您可以在C中完成除向量表之外的所有操作(虽然您可以,但它很丑),然后使用像经过良好测试和工作的汇编程序之类的东西来创建向量表。注意Cortex-m0与其他部件的区别。
8000074: f420 6140 bic.w r1, r0, #3072 ; 0xc00
8000078: f441 6180 orr.w r1, r1, #1024 ; 0x400
Cortex-M0和(如果你遇到的话)M1是基于ARMv6M的,而其他的都是基于ARMv7M的,后者有大约150个Thumb2扩展到Thumb指令集中(以前是未定义的指令,用于制作可变长指令)。所有的Cortex-M都运行Thumb指令集,但是Cortex-M0不支持ARMv7M特定的扩展,在构建时可以将其修改为Cortex-M0而不是M4,它将在M4上正常工作,像这样的代码(根据需要修补地址,也许GPIO对于你的特定部分是不同的,也许不是),并为M0构建,它将在M0上运行...就像需要定期检查向量表是否被正确构建一样,您可以检查反汇编以查看使用的指令的正确版本。