STM32裸机开发(4) — 编写C语言点亮LED灯

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来说,即初始化为08字节对齐
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

猜你喜欢

转载自blog.csdn.net/qq_38113006/article/details/112101467