STM32汇编语言点亮led灯

STM32汇编语言点亮led灯

首先,这篇blog的主要内容是在C语言中调用汇编语言写的函数。即在我们常用的标准库函数工程中,对外设(这里指led)使用汇编语言完成相关的寄存器配置和控制。

一、背景

课程的需要:微机原理课程设计中,需要使用汇编语言/部分汇编语言进行课程设计的开发。

网上资料的稀缺:汇编相对于高级语言,更加偏向底层,编写代码的效率很低,资料较少,stm32汇编语言与教科书上8086汇编有一些差别,资料更加少。以下引用的例子是网上比较常见的关于stm32使用汇编点亮led灯的教程。

STM32用汇编点亮LED灯

基于 MDK 创建 STM32 汇编程序:串口输出 Hello world

虽然上面两个例子给出了点亮led/串口输出的功能代码,但对其原理方面的描述不是很清晰。

二、预备知识

在keil5中,按F1便可调出ARM内核的工具书,里面包含了ARM内核汇编的指导,如下图所示:

STM32汇编语言点亮led灯_第1张图片

stm32相关的汇编相关的汇编指令

三、STM32中汇编与C语言的调用

常用的两种函数相互调用方式是在C语言中调用汇编函数,在汇编语言中调用C函数。这里仅仅是函数的调用,调用指.c文件调用.s文件中的函数,.s文件调用.c文件中的函数,嵌入代码块与这两种不同。(附:C语言中嵌入汇编代码块)

两种调用方式的具体实现教程可以查阅下面的博客:

两种函数调用方式

四、STM32汇编语言点亮led灯

在C语言中,我们点亮led灯首先需要对led灯对应引脚进行配置,即void LED_INIT(void),对led进行开关,即void LED_ON(void)void LED_OFF(void)。然后我们就可以在main函数中调用这些函数,实现对led的控制。

现在我们的目标是,对void LED_INIT(void)void LED_ON(void)void LED_OFF(void)这三个函数用汇编语言编写,这三个函数位于led.s中,在main.c文件中被main函数调用。

实验平台:stm32f103c8t6、MDK5.28

led引脚:C14、C15

4.1汇编语言实现LED相关配置

  1. 查阅数据手册使能相关的外设时钟和端口C,由下图我们可以得到

STM32汇编语言点亮led灯_第2张图片

STM32汇编语言点亮led灯_第3张图片

端口C的基地址是0x4001 1000,再由上图我们可以知道AHB系统总线通过桥接1和桥接2连接到了APB2和APB1总线,而AHB总线包含RCC时钟控制,GPIOC是属于APB2的,因此我们可以找到时钟使能寄存器的地址即复位和时钟控制RCC的地址是从0x4002 1000开始。

STM32汇编语言点亮led灯_第4张图片

通过查阅数据手册,如上图 所示我们可以知道APB2外设时钟使能寄存器的地址为RCC时钟的地址为+0x18,即RCC_APB2ENR= 0x40021018。找到APB2外设时钟使能寄存器的地址后,需要对其进行赋值,根据参考手册里面描述,第四位为端口C的时钟使能位。因此将第四位赋为1便可以使能端口C的时钟。即0001 0000,对于0x10

使能外设时钟的代码如下:

LDR R0,=RCC_APB2ENR;LDR是把地址装载到寄存器中(比如R0)。  
ORR R0,R0,#0x10 ;按位或ORR R0,R1,R2; R0=R1 | R2  
LDR R1,=RCC_APB2ENR;R1存了RCC_APB2ENR的地址  
STR R0,[R1];使能端口C的时钟  

2.接下来配置PC14、PC15为通用输出。

控制LED需要输出高电平或是低电平,所以需要配置为输出。由于STM32的每个IO都需要4个位来配置,所以一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用端口配置低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用端口配置高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。

配置引脚PC14,PC15使用的寄存器是GPIOC_CRH。下面我们来寻找这个寄存器的地址。

STM32汇编语言点亮led灯_第5张图片

从上图中,我们可以推得到端口C的配置高寄存器为端口C的基地址是0x40011000+0x040x40011004 ,即GPIOC_CRH=0x40011004。PC15、PC14对应位31:28,接下来对该寄存器赋值。

复位值是0x4444 4444,并不是0x0000 0000。所谓的复位值,就是指如果没有操作这个寄存器时,寄存器存放的默认值。复位值按位拆分0x4 = 0b0100,0x表示16进制,0b表示二进制,也就是默认CNF 01,MODE 00,是浮空输入。

我们需要的是输出高低电平,所以要设置为输出。输出模式设置为推挽输出,速度为50MHZ。那么PC15、PC14所对应的位31:28应赋值为:0011 0011

代码如下:

;设置pc14、pc15
LDR R0,=GPIOC_CRH
BIC R0,R0,#0xffffffff ;R0清零
ORR R0,#0x33000000
LDR R1,=GPIOC_CRH
STR R0,[R1]

0x33000000也代表是0011 0011 0000 0000 0000 0000 0000 0000

这里对GPIOC_CRH寄存器进行赋值时可能会改变其他位,进而修改到其他引脚的配置,因此尽可能只改变相应的位,不改变其他位。

2.接下来设置PC14、PC15的初始电平状态。(端口输出数据寄存器)

STM32汇编语言点亮led灯_第6张图片

中文数据手册有一个小小的BUG,0x0C写成了0Ch,如上图所示,可以参考英文原版。得知地址的偏移是0x0C,所以引脚PC15的端口输出数据寄存器的地址就是GPIOC_ODR =0x4001 100C。对 位15 置 1 ,引脚PC15便可以输出高电平。如下代码所示:

LDR R1,=GPIOC_ODR 
LDR R0,[R1] ;加载GPIOC_ODR寄存器的值
MOV R1,#~(1<<15) 
AND R0,R0,R1 
LDR R1,=GPIOC_ODR 
STR R0,[R1] 

因为修改GPIOC_ODR寄存器的值可能会修改到其他位,进而改变其他引脚的电平,因此尽可能只改变相应的位,不修改其他位。这里使用#~(1<<15)R0进行与操作,保证只对 位15 进行操作将 位15 置0,输出低电平。

对C14进行重复操作,这时候是#~(1<<14)

这样就完成了对C14、C15的初始化。即完成了LED_INIT函数

4.2 LED_ON、LED_OFF函数

以PC15为例:

LED3_ON;亮灯
            PUSH {R0,R1, LR}    
            LDR R1,=GPIOC_ODR
            LDR R0,[R1];加载GPIOC_ODR寄存器的值
            MOV R1,#~(1<<15)
            AND R0,R0,R1
            LDR R1,=GPIOC_ODR
            STR R0,[R1]
            POP {R0,R1,PC}

具体是#~(1<<15)还是#(1<<15)需要查看电路的解法,是高电平亮还是低电平亮。

LED3_OFF;熄灯
                PUSH {R0,R1, LR}    
                LDR R1,=GPIOC_ODR
				LDR R0,[R1]
				MOV R1,#(1<<15)
                ORR R0,R0,R1
                LDR R1,=GPIOC_ODR
                STR R0,[R1]
                POP {R0,R1,PC}  

4.3 在C中调用汇编语言

在.c文件中声明函数

extern void LED_INIT(void);
extern void LED3_ON(void);
extern void LED3_OFF(void);

然后像正常函数那样调用就可以了。

五、总结

stm32汇编跟8086汇编有些不同,使用stm32汇编进行代码逻辑编写确实有些难度,可以使用stm32汇编进行底层代码的编写,底层涉及到的是寄存器,也就是这篇文章所讲的内容。

代码工程附在最下面。实现的功能是将PS2遥控手柄的数据通过串口发送给上位机。因此可以当做3个模块进行学习:汇编实现LED底层,PS2游戏手柄操控,汇编实现串口底层。

本教程的代码仓库:(路径:user/led.s)

STM32汇编语言点亮led灯

拓展资料:

纯汇编实现led、LCD、串口、按键的代码工程

有问题可以评论/发邮件,[email protected]

你可能感兴趣的:(STM32,stm32,单片机,嵌入式硬件)