STM32裸机开发(3) — 使用汇编点亮LED灯

STM32裸机开发(3) — 使用汇编点亮LED灯

一、启动流程

对于STM32F103从flash的启动流程如下:

  • 首先设置栈:CPU会从0x08000000读取值,用来设置SP(不使用C语言可以不设置,或者在程序里设置SP)
  • 然后跳转:CPU从0x08000004得到地址值,根据它的BIT0切换为ARM状态或Thumb状态,然后跳转
    • 对于cortex M3/M4,它只支持Thumb状态,所以0x08000004上的值bit0必定是1
    • 0x08000004上的值 = Reset_Handler + 1
  • 接着就从Reset_Handler继续执行

二、在keil-MDK下编写程序

打开keil,新建工程,选择STM32F103ZE
在这里插入图片描述
新建start.s文件,编写如下代码;



                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)数据,值为0x00000000,实际是应该填入栈顶地址 
                DCD     Reset_Handler              	;当前地址写入一个字(32bit)数据,值为Reset_Handler的值,即程序入口地址

				AREA    |.text|, CODE, READONLY		;定义代码段,标记为.text

; Reset handler	;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰
Reset_Handler   PROC								;过程的开始 
				EXPORT  Reset_Handler	[WEAK]		;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。

				; 1、使能 GPIOB 
				LDR R0, =(0x40021000 + 0x18)
				LDR R1, [R0]
				ORR R1, R1, #(1<<3)
				STR R1, [R0]
				
				; 2、把GPIOB5设置为输出引脚
				LDR R0, =(0x40010C00 + 0x00)
				LDR R1, [R0]
				ORR R1, R1, #(1<<20)
				STR R1, [R0]
				
				; 3、设置GPIOB5的输出寄存器
				LDR R2, =(0x40010C00 + 0x0C)
				
				;4、loop循环
Loop				
				; 5、设置GPIOB5输出高
				LDR R1, [R2]
				ORR R1, R1, #(1<<5)
				STR R1, [R2]
				
				LDR R0, =1000000
				BL delay
				
				; 6、设置GPIOB5输出低
				LDR R1, [R2]
				BIC R1, R1, #(1<<5)
				STR R1, [R2]
				
				LDR R0, =1000000
				BL delay
				
				B Loop
                ENDP								;过程的结束

delay
				SUBS R0, R0, #1
				BNE delay
				BX LR

                ALIGN 								;填充字节使地址对齐
                END									;整个汇编文件结束

然后添加如下两行在编译中执行的命令:

fromelf  --bin  --output=led.bin  Objects\led.axf
fromelf  --text  -a -c  --output=led.dis  Objects\led.axf

或者如下通用命令,和上面的命令是等效的

fromelf --bin -o "$L@L.bin" "#L"
fromelf --text -a -c --output="$L@L.dis" "#L"

第一行生成.bin文件,第二行生成反汇编.dis文件
在这里插入图片描述
然后点击构建,可以看到,已经生成了led.bin文件和反汇编led.dis文件
在这里插入图片描述
打开led.disled.bin文件,可以看到,因为stm32f103的cotex-m3内核使用的是Thumb指令集,所以其指令长度既有16位又有32位;按stm32在flash模式下的启动顺序,先在0x0800000地址下获取栈地址,在0x08000000地址下获取程序入口地址+1的值,其中最后一位1表示thumb指令。
在这里插入图片描述
将其烧写到开发板,可以看到其LED灯闪烁
在这里插入图片描述

三、使用gcc编译程序

编写在gcc下使用的start.s汇编代码


  .syntax unified                   /* 指明当前汇编文件的指令是ARM和THUMB通用格式 */
  .cpu cortex-m3                    /* 指明cpu核为cortex-m3 */
  .fpu softvfp                      /* 软浮点 */
  .thumb                            /* thumb指令 */

.global  _reset                     /* .global表示_start是一个全局符号 */

.word 0x00000000                    /* 当前地址写入一个字(32bit)数据,值为0x00000000,实际上应为栈顶地址 */
.word _reset+1                      /* 当前地址写入一个字(32bit)数据, 值为_reset标号代表的地址+1,即程序入口地址*/

_reset:                             /* 标签_start,汇编程序的默认入口是_start */
    /* 1、使能 GPIOB */
    LDR R0, = (0x40021000 + 0x18)   /* 将APB2外设时钟使能寄存器的地址值写入R0 */
    LDR R1, [R0]                    /* 读取该寄存器的值 */      
    ORR.W R1, R1, #(1<<20)             /* 修改读出的值 */
    STR R1, [R0]                    /* 写入修改后的值到该寄存器 */

    /* 2、把GPIOB5设置为输出引脚 */
    LDR R0, = (0x40010c00 + 0x00)
    LDR R1, [R0]
    ORR.W R1, R1, #(1<<20)
    STR R1, [R0]

    /* 3、设置GPIOB5的输出寄存器 */
    LDR R2, = (0x40010c00 + 0x0c)

    /* 4、loop循环 */
loop:
    /* 5、设置GPIOB5输出高 */
    LDR R1, [R2]
    ORR.W R1, R1, #(1<<5)
    STR R1, [R2]

    LDR R0, =1000000
    BL delay

    /* 6、设置GPIOB5输出低 */
    LDR R1, [R2]
    BIC.W R1, R1, #(1<<5)
    STR R1, [R2]

    LDR R0, =1000000
    BL delay 

    b loop

delay:
    SUBS R0,R0,#1
    BNE delay
    BX LR

以及Makefile文件如下


led.bin:start.S
	arm-none-eabi-gcc -c start.s -o led.o
	arm-none-eabi-ld led.o -Ttext 0X80000000 -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灯闪烁
在这里插入图片描述

四、比较MDK和GCC编译差别

分别比较两个.dis文件,如下所示,按stm32在flash模式下的启动顺序,先在0x0800000地址下获取栈地址,在0x08000000地址下获取程序入口地址+1的值,其中最后一位1表示thumb指令。
另外发现其中有一个位置不同,但在汇编文件中使用的指令是相同的,这是因为gcc编译器会强制将SUBS有两个相同寄存器指令转换为单个寄存器操作。
在这里插入图片描述
参考链接如下:强制GNU AS使用替代方法

五、附录

上一篇:STM32裸机开发(2) — 点亮第一个LED
下一篇:STM32裸机开发(4) — 编写C语言点亮LED灯

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
点亮STM32F103C8T6开发板上的PC13引脚对应的LED,你需要进行以下步骤: 1. 首先,确保你已经正确连接了开发板和LED。将LED的正极连接到PC13引脚,将LED的负极连接到开发板的地(GND)引脚。 2. 在编程环境中,选择适合的开发工具,如Keil MDK或STM32CubeIDE。 3. 创建一个新的工程,并选择正确的芯片型号(STM32F103C8T6)。 4. 在代码中,需要包含适当的头文件,如"stm32f1xx.h"。 5. 初始化PC13引脚为输出模式。可以使用GPIO_InitTypeDef结构体来配置引脚的参数,例如设置引脚为推挽输出模式、设置输出速度等。 6. 在主循环中,通过设置PC13引脚的电平状态来控制LED的亮灭。可以使用GPIO_WriteBit函数将PC13引脚设置为高电平或低电平。 下面是一个简单的示例代码: ```c #include "stm32f1xx.h" int main(void) { // 初始化PC13引脚 GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStruct); while (1) { // 点亮LED GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET); // 延时一段时间 for (int i = 0; i < 1000000; i++); // 熄灭LED GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET); // 延时一段时间 for (int i = 0; i < 1000000; i++); } } ``` 请注意,以上代码仅供参考,具体的实现可能会因为使用开发工具和库的不同而有所差异。在实际开发中,你可能需要根据自己的需求进行适当的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值