使用汇编按照GPIO操作流程点亮LED灯

Posted 行稳方能走远

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用汇编按照GPIO操作流程点亮LED灯相关的知识,希望对你有一定的参考价值。

通过第五章我们对ARM 汇编语法有了初步的认识,在本章我们开始使用汇编编写我们的第一个汇编实验,在开发板的额外众多外设里面,原理最简单,最适合初学者入门的就是gpio 的驱动,在我们的开发板上板载了一个led 发光二极管,他的实现原理就是通过gpio 来控制led 的亮灭(控制gpio 输出高低电平)。

LED汇编程序

按照前面的介绍,我们需要对GPIO1_IO03 做如下的设置:

1.使能GPIO1 的时钟

GPIO1 的时钟由CCM_CCGR1 寄存器的bit27 和26 控制,我们把这两位设置成1,就会使能GPIO1 的时钟了。

2.配置GPIO1_IO03 的复用功能

IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器是GPIO1_IO03 的复用寄存器,地址是0x20e0068,把这个寄存器设置成GPIO 功能。

3.配置GPIO1_IO03 上下拉

IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 寄存器是GPIO1_IO03 的配置寄存器,通过该寄存器配置GPIO1_IO03 为GPIO 的上下拉。

4.设置GPIO1_IO03 输出

在第二步骤我们已经把GPIO1_IO03 复用成GPIO 了,然后我们需要配置GPIO 为输出,以及输出时候的高低电平,在《IMX6ULL 参考手册》的1357 页我们可以看到PIO1_IO03 对应的GPIO 寄存器组,如图所示:
在这里插入图片描述

通过设置寄存器GPIO1_GDIR 的bit3 为1,可以设置GPIO1_IO03 为输出。然后通过设置GPIO1_DR 寄存器的bit3 为0,可以控制GPIO1_IO03 输出低电平,LED 会亮。GPIO1_DR 的bit3 设置为1,可以控制GPIO1_IO03输出高电平,LED 会灭。

本实验的源码在开发板光盘资料的"i.MX6UL 终结者光盘资料\\04_裸机例程源码\\1_leds\\1_leds"目录下,我们可以把这个文件夹拷贝到UBuntu 下,使用交叉编译器编译。

下面我们开始一步步的实现LED 这个实验。首先登录Ubuntu 系统,在用户的根目录下的work 文件夹创建led 文件夹然后在led 目录下新建“led.s”的汇编文件,如图所示:

在这里插入图片描述
然后使用“vi led.s”命令打开led.s 文件,在led.s 文件输入如下代码:

.global _start  /* 全局标号 */

/*
 * 	_start函数,程序从此函数开始执行此函数完成时钟使能、
 *	GPIO初始化、最终控制GPIO输出低电平来点亮LED灯。
 */
_start:
	/* 1、使能所有时钟 */
	ldr r0, =0X020C4068 	/* CCGR0 */
	ldr r1, =0XFFFFFFFF  
	str r1, [r0]		
	
	ldr r0, =0X020C406C  	/* CCGR1 */
	str r1, [r0]

	ldr r0, =0X020C4070  	/* CCGR2 */
	str r1, [r0]
	
	ldr r0, =0X020C4074  	/* CCGR3 */
	str r1, [r0]
	
	ldr r0, =0X020C4078  	/* CCGR4 */
	str r1, [r0]
	
	ldr r0, =0X020C407C  	/* CCGR5 */
	str r1, [r0]
	
	ldr r0, =0X020C4080  	/* CCGR6 */
	str r1, [r0]
	

	/* 设置GPIO1_IO03为GPIO模式 */
	ldr r0, =0X020E0068	/* 把寄存器SW_MUX_GPIO1_IO03_BASE加载到r0中 */
	ldr r1, =0X5		/* 设置寄存器SW_MUX_GPIO1_IO03_BASE的MUX_MODE为5 */
	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	/*寄存器SW_PAD_GPIO1_IO03_BASE */
    ldr r1, =0X10B0
    str r1,[r0]

	/* 设置GPIO1_IO03为输出 */
    ldr r0, =0X0209C004	/*寄存器GPIO1_GDIR */
    ldr r1, =0X0000008		
    str r1,[r0]

	/* 点亮LED0
	 * 设置GPIO1_IO03输出低电平
	 */
	 ldr r0, =0X0209C000	/*寄存器GPIO1_DR */
   ldr r1, =0		
   str r1,[r0]

/*
 * 描述:	loop死循环
 */
loop:
	b loop 				

第2 行我们定义了全局标号_start,程序就是从_start 标号处开始顺序往下执行的。

第8 行使用ldr 向寄存器r0 写入0x020c4068,这是CCM_CCGR0 的寄存器地址。

第9 行使用ldr 向寄存器r1 写入0xffffffff,开启所有外设的时钟。

第10 行使用str 命令把r1 中的值写到r0 所保存的地址中,相当于给0x20c4068 这个地址写入0xffffffff,也就是CCM_CCGR0 寄存器设置成0xffffffff,使能CCM_CCGR0 寄存器控制的所有外设时钟。

第11 行到第22 行是向CCM_CCGR1-CCM_CCGR6 寄存器写入0xffffffff,使能所有的外设时钟。

第24 行到第26 行是设置GPIO1_IO03 这个IO 为GPIO 模式,GPIO1_IO03 的复用寄存器是0x20e0068,把该寄存器的MUX_MODE 设置为5(GPIO 模式)

第37 行到第39 行设置GPIO1_IO03 的IOMUX_SW_PAD_CTL_PAD_GPIO1_IO03 配置寄存器

第40 行到第43 行是设置GPIO 为输出功能。

第47 到第49 是设置GPIO 输出0(低电平)

第53 到第54 行是死循环,使用b 跳转指令,程序不断的跳转到loop 执行。

编译LED汇编程序

在这里插入图片描述

前面用汇编写的代码后缀名是.s文件,所以省去了预处理和编译,直接进行汇编就可以了。

我们在Ubuntu 下通过vim 编辑好LED 的汇编程序(6.6 章节),然后保存并退出。然后我们使用arm交叉编译器来编译该程序,我们在终端输入“arm-linux-gnueabihf-gcc -g -c led.s -o led.o”命令,把led.s 编译成led.o,其中的“-g”参数是产生调试信息,可以使用GDB 来调试代码。“-c”参数是编译源文件,不链接。“-o”参数是指定产生的文件名称,我们指定生成led.o,运行效果如图所示:
在这里插入图片描述
我们可以看到通过arm 交叉编译生成了led.o 文件,这个文件相当于中间文件,我们还需要把编译生成的.o 文件链接起来生成可执行文件(我们这里只有一个led.o,对于有的工程可能会生成多个.o 文件)。

接下来我们使用交叉编译器的“arm-linux-gnueabihf-ld”命令来把“.o”文件链接起来。链接的目的就是把我们的程序固定到某个地址,这样cpu 在运行的时候就可以通过链接地址找到我们的程序,并运行它。在链接之前我们需要先了解下i.MX6ULL 的启动,i.MX6ULL 支持SD 卡,EMMC,NAND 等方式启动,i.MX6ULL启动的时候首先将代码从SD 卡,EMMC,NAND 中拷贝到运行地址,然后开始从运行地址处开始运行,i.MX6ULL芯片内部有128K 的RAM(0X900000~0X91FFFF),另外外部扩展了DDR,所以i.MX6ULL 的链接地址可以是内部的RAM,也可以是外部的DDR。我们所有的例程都是链接到DDR 中,其地址为0X87800000。i.MX6ULL终结者开发板的DDR 有两种:256MB 和512MB,起始地址都是0X80000000。256MB 的终止地址是0X8FFFFFFF,
512M 的终止地址是0X9FFFFFFF,之所以选择地址0X87800000,是因为后面我们要学的Uboot 链接地址也是0X87800000。所以为了学习方便,我们统一使用0X87800000 地址。

下面我们开始使用arm-linux-gnueabihf_ld 命令将前面我们生成的“led.o”文件链接到0X87800000地址处,我们在终端输入“arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf”,其中的“-Ttext”指定链接地址(0X87800000),“-o”生成链接文件名,运行效果如图所示:
在这里插入图片描述

我们需要把链接文件转换成“.bin”文件,然后烧写到EMMC,才能运行。
接下来我们使用“arm-linux-gnueabihf-objcopy”命令将链接文件led.elf 转换成“led.bin”文件。
我们在终端输入“arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin”命令,其中“-O”指定以什么格式输出(binary 表示二进制输出),“-S”表示不要复制源文件中的重定位信息和符号信息,“-g”表示不复制源文件中的调试信息,运行效果如图所示:
在这里插入图片描述
至此我们生成了最终的可执行程序“led.bin”文件。
我们可以总结下,为了生成led.bin 文件,我们分别使用了命令:

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

为了编译方便,我们可以使用Makefile 来编译我们的led 汇编程序,首先我们在led 工程目录下使用“touch Makefile”命令创建Makefile 文件,如图所示:
在这里插入图片描述
然后使用vim 编辑器打开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
clean:
rm -rf *.o led.bin led.elf

添加完上面的命令,保存并退出,然后在led 工程目录下执行“make”命令编译led.S,过程如图所示:
在这里插入图片描述
如果要清除工程,我们在终端执行“make clean”即可。
至此关于arm 交叉编译器的使用,我们就先介绍到这里。编译生成了led.bin 执行文件,下一步我们还需要给led.bin 文件添加一些数据头才能执行。我们为用户提供了添加数据头的工具“create_imx”(光盘资料的“i.MX6UL 终结者光盘资料\\01_开发及烧写工具\\2.裸机镜像制作工具”目录下),我们通过ssh工具拷贝该文件到led 工程目录下,然后在终端输入命令“./create_imx led.bin”,生成bare.imx 文件,如图所示:
在这里插入图片描述
现在我们开始使用MFG 烧写工具来烧写(光盘资料的“i.MX6UL 终结者光盘资料\\01_开发及烧写工具\\3.mfgtools_for_6ULL”文件夹),首先我们进到该文件夹,我们修改“cfg.ini”文件,如果您的板子是EMMC 版本(8G flash 容量),按照图的方式修改:
在这里插入图片描述
如果您的板子是NAND 版本(512MB flash 容量)(NAND 版本的裸机验证我们需要使用一张TF 卡),按照图所示修改:
在这里插入图片描述
修改完“cfg.ini”配置文件,然后我们在Ubuntu 系统生成的“bare.imx”文件通过ssh 工具拷贝到MFG 烧写工具的“Profiles\\Linux\\OS Firmware\\files\\linux\\”目录下,如图9.7.9 所示:

在这里插入图片描述
然后我们鼠标双击打开MFG 烧写工具,如图所示:
在这里插入图片描述
MFG 烧写工具打开以后,我们使用开发板配带的USB 数据线,连接开发板的OTG 接口和PC 的USB 接口,使用开发板配带的电源连接到开发板的电源接口,然后开发板的拨码开关设置成USB 启动,如图9.7.11 所示:
在这里插入图片描述

如果我们使用的是NAND 版本的开发板,我们需要先拔掉TF 卡)然后按下开发板的电源开关,使开发板上电,此时我们会看到MFG 烧写工具识别到开发板,如图所示:
在这里插入图片描述
(如果我们使用的是NAND 版本的开发板,我们需要插入TF 卡),然后我们点击MFG 烧写工具的“Start”按钮,开始烧写镜像,如图所示:
在这里插入图片描述
等到进度条显示绿色,烧写完成,如图所示:
在这里插入图片描述
然后我们在按下开发板的电源按键给开发板断电,然后修改拨码开关设置正常启动。
如果您的板子是EMMC 版本(8GB Flash 存储),拨码开关如图所示:
在这里插入图片描述
如果您的开发板是NAND 版本(512M Flash 存储),拨码开关如图所示(设置成TF 卡启动模式):
在这里插入图片描述
最后我们在按下开发板的电源开关,给开发板上电,此时我们会看到开发板的LED2 被点亮了,如图所示:
在这里插入图片描述
本节我们详细的介绍了如何编译代码,并且如何使用MFG 烧写生成的镜像到开发板的Flash 里面,并最终运行测试。后面我们的程序都是用MFG 工具进行代码的烧写。

以上是关于使用汇编按照GPIO操作流程点亮LED灯的主要内容,如果未能解决你的问题,请参考以下文章

在一个 STM32 点亮 LED 的程序中,部分代码如下?

树莓派实验1:GPIO点亮led灯

汇编点亮LED I.MX6U-ALPHA

汇编点亮LED I.MX6U-ALPHA

汇编语言编一个八个灯,由左至右循环闪烁的程序~~~谢谢

BlinkLED 点亮第一个LED灯