看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17

目录

    • 前言
    • STM32 知识回顾
    • 完整汇编代码
    • 最终编译验证
    • 结束语

前言

首先我们在开发stm32的时候驱动LED灯会有两种方式,分别是寄存器库函数版本。因为在实际的开发过程中,我们更愿意更习惯用库函数版本来驱动各种外设,但是对于底层的寄存器调用和配置也是我们必须掌握的。由于本篇文章主要讲解在I.MX6U平台上的LED驱动,并且使用汇编语言编写,所以更需要借助寄存器底层来驱动。接下来,我会详细讲解怎么从0开始驱动LED灯。

STM32 知识回顾

我们以最常见的 STM32F103 为例来看一下 STM32 的 GPIO 初始化
看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第1张图片
由上图可知,该GPIO初始化代码时由库函数编写的,贴这段代码的主要目的是为了了解驱动GPIO需要进行哪几步操作。分别是:

  1. 使能GPIO对应端口的时钟
  2. 设置GPIO的复用功能
  3. 设置GPIO的输出/输入模式、上下拉、 速度等相关
  4. 通过设置电平高低来控制LED亮灭

那自然而然,在I.MX6U平台上驱动LED灯大致也是如上几个步骤。

完整汇编代码

/*全局标号*/
.global _start 

_start:

/*使能GPIO1——IO03时钟*/
ldr r0, =0x020C4070 
ldr r1, =0xFFFFFFFF 
str r1, [r0]       

/*设置GPIO1——IO03复用为GPIO1——IO03*/
ldr r0, =0x020E0068
ldr r1, =0x5
str r1,[r0]

/*配置GPIO1——IO03的IO属性*/
ldr r0, =0x020E02F4
ldr r1, =0x10B0
str r1,[r0]

/*设置GPIO1——IO03为输出*/
ldr r0, =0x0209C004
ldr r1, =0x0000008
str r1,[r0]

/*打开LED0*/
ldr r0, =0x0209C000
ldr r1, =0
str r1,[r0]

loop:
    b loop

好!按照老样子,接下来开始详细讲解每行代码的用处,以及为什么这样写!

.global _start 

_start:

//汇编程序的默认入口标号是_start,可以理解为代码从该处开始运行。上面代码中的.global是声明_start是一个全局标号,此时可联想为_start这个变量是全局变量即可。

ldr r0, =0x020C4070 
ldr r1, =0xFFFFFFFF 
str r1, [r0]   

//接下来我们一行行进行分析,在分析之前,我想先引入几个Cortex-A7汇编指令,由于ARM不能直接访问存储器,比如RAM中的数据,所以我们需要借助汇编指令来访问存储器,从而访问并配置I.MX6UL的寄存器。

  1. 存储器访问指令
    在这里插入图片描述
ldr r0, =0x020C4070

//相当于将0x020C4070赋值给r0。那至于为什么是0x020C4070,这个我们就需要根据I.MX6UL的原理图和参考数据手册了。
其中关于LED灯的原理图如下:
看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第2张图片
由上图可知,LED0接到了GPIO_3上,也就是GPIO1_IO03。所以我们需要开启GPIO1_IO03对应的时钟,也就是使能时钟。
看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第3张图片

由上图可知,该寄存器的主要作用为CCM的时钟门控寄存器,其中该寄存器的第26~27位也就是CG13为gpio3的时钟使能位,由于有两位,所以实际上会产生4种模式配置,具体如下图所示。

看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第4张图片

ldr r1, =0xFFFFFFFF

//相当于将0xFFFFFFFF赋值给r1
//即我们开启gpio3的时钟使能,只需将第26~27位设置为11即可。

str r1, [r0]  

//相当于将r1的值赋值给地址为r0的寄存器,即将0xFFFFFFFF赋值给CCM_CCGR2寄存器,成功对gpio3进行时钟使能。

ldr r0, =0x020E0068
ldr r1, =0x5
str r1,[r0]

//这一部分主要是设置GPIO的复用功能为GPIO1_IO03。

ldr r0, =0x020E0068

//相当于将0x020E0068赋值给r0,至于为什么是0x020E0068,请看下图(数据参考手册)

看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第5张图片看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第6张图片

//由上图可知,该寄存器可配置位为5位,分别是0 ~ 4位,其中0 ~ 3位为MUX_MODE,其中0101为将GPIO1复用功能设置为GPIO1_IO03。

ldr r1, =0x5

//将0101赋值给r1

str r1,[r0]

//将r1的值赋值给内存地址为r0的寄存器中。

ldr r0, =0x020E02F4
ldr r1, =0x10B0
str r1,[r0]

//这一部分主要功能是配置GPIO1_IO03的IO属性

*bit 16:0 HYS 关闭
*bit [15:14]: 00 默认下拉
*bit [13]: 0 kepper 功能
*bit [12]: 1 pull/keeper 使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度 100Mhz
*bit [5:3]: 110 R0/6 驱动能力
*bit [0]: 0 低转换率

ldr r0, =0x020E02F4

//相当于将0x020E02F4赋值给r0,至于为什么是0x020E02F4,请看下图(数据参考手册)
看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第7张图片看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第8张图片看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第9张图片

ldr r1, =0x10B0

//配置IO属性,具体配置是什么,请对照上图进行辨认。

str r1,[r0]

//相当于将r1的值赋值给内存地址为r0的寄存器中。

ldr r0, =0x0209C004
ldr r1, =0x00000008
str r1,[r0]

//这一部分主要是设置GPIO1_IO03为输出

ldr r0, =0x0209C004

//相当于将0x0209C004赋值给r0,至于为什么是0x0209C004,请看下图(数据参考手册)
看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第10张图片

ldr r1, =0x00000008

//由上图可知,GDIR寄存器是32位的,每一位对应的一个IO,由于我们设置GPIO1_IO3,所以需要将第3为设置为1即可,即0~3位为0001。

str r1,[r0]

//相当于将r1的值赋值给内存地址为r0的寄存器中。

ldr r0, =0x0209C000
ldr r1, =0
str r1,[r0]
ldr r0, =0x0209C000

//相当于将0x0209C000赋值给r0,至于为什么是0x0209C000,请看下图(数据参考手册)
看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第11张图片//该寄存器是32位的,每一位对应一个IO,当GPIO设置成输出模式时,向指定的位写入数据那么相应的IO就会输出相应的高低电平。

ldr r1, =0

//相当于将GPIO1_IO3的电平设置成低电平,即对应硬件电路中使其电路导通,从而使得LED保持常亮。

str r1,[r0]

//相当于将r1的值赋值给内存地址为r0的寄存器中。

loop:
    b loop

//死循环,通过b指令,CPU重复不断的跳到loop函数执行,进入一个死循环。

最终编译验证

由于我们只是编写了一个led.s的汇编文件,为了编写方便,我们需要创建Makefile文件。具体Makefile文件内容如下:

led.bin:led.s
 arm-linux-gnueabihf-gcc -g -c led.s -o led.o
 arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
 arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
 arm-linux-gnueabihf-objdump -D led.elf > led.dis
clean:
 rm -rf *.o led.bin led.elf led.dis

然后使用make命令生成led.bin文件即最终烧到开发板中的文件,在本次实验中我们采取烧写到SD卡中(至于怎么烧写到SD卡可以参考正点原子文档),最终实验结果如下:
看完这篇文章你就彻底懂啦{保姆级讲解}-----(I.MX6U驱动LED灯《使用汇编语言编写》) 2023.4.17_第12张图片

结束语

如果觉得这篇文章还不错的话,记得点赞 ,支持下!!!

你可能感兴趣的:(嵌入式硬件,arm开发)