STM32裸机开发(4) — 编写C语言点亮LED灯
一、前言
使用c语言时,要用到的一个很重要的就是栈,所以我们要在汇编中就设置好栈;
而对于当下的stm32f103来说,我们可以在0x08000000处写入栈顶地址,也可以在汇编程序中使用指令设置SP
二、在MDK中实现
修改start.s,在0x08000000处写入栈顶地址,如下所示:
Stack_Size EQU 0x00000400 ;定义堆栈大小为1024byte
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;定义一个数据段,标记为STACK,即栈,不写入初始值初,对RAM来说,即初始化为0,8字节对齐
Stack_Mem SPACE Stack_Size ;保留Stack_Size大小的栈空间
__initial_sp ;标号,代表堆栈顶部地址,后面有用
PRESERVE8 ;指示编译器8字节对齐
THUMB ;指示编译器以后的指令为THUMB指令
; Vector Table Mapped to Address 0 at Reset
AREA RESET, CODE, READONLY ;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址
EXPORT __Vectors ;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用
__Vectors DCD __initial_sp ;当前地址写入一个字(32bit)数据,值为__initial_sp指向的地址值,即栈顶地址
DCD Reset_Handler ;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址
AREA |.text|, CODE, READONLY ;定义代码段,标记为.text
; Reset handler ;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰
Reset_Handler PROC ;过程的开始
EXPORT Reset_Handler [WEAK] ;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。
IMPORT led ;通知编译器要使用的标号在其他文件
BL led ;跳转去执行led函数
B . ;原地跳转,即处于循环状态
ENDP
ALIGN ;填充字节使地址对齐
END ;整个汇编文件结束
在汇编程序中使用指令设置SP,如下所示:
PRESERVE8 ;指示编译器8字节对齐
THUMB ;指示编译器以后的指令为THUMB指令
; Vector Table Mapped to Address 0 at Reset
AREA RESET, CODE, READONLY ;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址
EXPORT __Vectors ;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用
__Vectors DCD 0 ;当前地址写入一个字(32bit)数据,值应该为栈顶地址
DCD Reset_Handler ;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址
AREA |.text|, CODE, READONLY ;定义代码段,标记为.text
; Reset handler ;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰
Reset_Handler PROC ;过程的开始
EXPORT Reset_Handler [WEAK] ;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。
IMPORT led ;通知编译器要使用的标号在其他文件
LDR SP, =(0x20000000+0x400) ;设置栈顶地址为0x20000400
BL led ;跳转去执行led函数
B . ;原地跳转,即处于循环状态
ENDP
ALIGN ;填充字节使地址对齐
END ;整个汇编文件结束
然后新建led.c
文件,如下所示
int delay(int ndelay)
{
volatile int n = ndelay;
while(n--);
return 0;
}
int led(void)
{
unsigned int *pReg;
/* 1、使能GPIOB */
pReg = (unsigned int *)(0x40021000 + 0x18);
*pReg |= (1<<3);
/* 2、设置GPIOB5为输出引脚 */
pReg = (unsigned int *)(0x40010C00 + 0x00);
*pReg |= (1<<20);
pReg = (unsigned int *)(0x40010C00 + 0x0C);
while (1)
{
/* 3、设置GPIOB5输出1 */
*pReg |= (1<<5);
delay(1000000);
/* 4、设置GPIOB5输出0 */
*pReg &= ~(1<<5);
delay(1000000);
}
}
编译链接烧写到开发板,可以看到LED闪烁
三、在GCC中实现
修改start.s如下所示:
.syntax unified /* 指明当前汇编文件的指令是ARM和THUMB通用格式 */
.cpu cortex-m3 /* 指明cpu核为cortex-m3 */
.fpu softvfp /* 软浮点 */
.thumb /* thumb指令 */
.global _start /* .global表示Reset_Handler是一个全局符号 */
.word 0x00000000 /* 当前地址写入一个字(32bit)数据,值为0x00000000,实际上应为栈顶地址 */
.word _start+1 /* 当前地址写入一个字(32bit)数据, 值为_reset标号代表的地址+1,即程序入口地址*/
_start: /* 标签_start,汇编程序的默认入口是_start */
/* 1、设置栈 */
LDR SP, =(0x20000000+0x400)
/* 2、跳转到led函数 */
BL led
/* 3、原地循环 */
B .
使用同样的led.c
文件,如下所示
int delay(int ndelay)
{
volatile int n = ndelay;
while(n--);
return 0;
}
int led(void)
{
unsigned int *pReg;
/* 1、使能GPIOB */
pReg = (unsigned int *)(0x40021000 + 0x18);
*pReg |= (1<<3);
/* 2、设置GPIOB5为输出引脚 */
pReg = (unsigned int *)(0x40010C00 + 0x00);
*pReg |= (1<<20);
pReg = (unsigned int *)(0x40010C00 + 0x0C);
while (1)
{
/* 3、设置GPIOB5输出1 */
*pReg |= (1<<5);
delay(1000000);
/* 4、设置GPIOB5输出0 */
*pReg &= ~(1<<5);
delay(1000000);
}
}
再修改Makefile如下,注意要有-mcpu=cortex-m3 -mthumb
参数,这个很重要
all : start.S led.c
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c start.s -o start.o
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c led.c -o led.o
arm-none-eabi-ld start.o led.o -Ttext 0X8000000 -o led.elf
arm-none-eabi-objcopy led.elf -O ihex led.hex
arm-none-eabi-objcopy led.elf -O binary -S led.bin
arm-none-eabi-objdump -D -m cortex-m3 led.elf > led.dis
clean:
rm -rf *.o led.elf led.hex led.bin led.dis
使用make执行后如下
编译烧录后可以看到LED闪烁
四、优化Makefile
可以将Makefile优化为如下所示
TARGET = led
OBJECTS = start.o led.o
CPU = -mcpu=cortex-m3
MCU = $(CPU) -mthumb
CFLAGS = $(MCU) -Wall
all : $(TARGET).elf $(TARGET).hex $(TARGET).bin $(TARGET).dis
arm-none-eabi-size $<
%.o: %.c
arm-none-eabi-gcc -c $(CFLAGS) $< -o $@
%.o: %.s
arm-none-eabi-gcc -c $(CFLAGS) $< -o $@
$(TARGET).elf: $(OBJECTS)
arm-none-eabi-ld $(OBJECTS) -Ttext 0X8000000 -o $@
%.hex: %.elf
arm-none-eabi-objcopy -O ihex $< $@
%.bin: %.elf
arm-none-eabi-objcopy -O binary -S $< $@
%.dis: %.elf
arm-none-eabi-objdump -D -m cortex-m3 $< > $@
clean:
rm -rf *.o led.elf led.hex led.bin led.dis
五、附录
上一篇:STM32裸机开发(3) — 使用汇编点亮LED灯
下一篇:STM32裸机开发(5) — 在Keil-MDK下编写uart串口打印程序
代码存放:https://gitee.com/william_william/stm32f103_noos.git