? ?大家好,我是ST。
? ?今天主要和大家聊一聊,如何使用汇编语言来实现芯片外设的初始化功能。
第一步:硬件原理分析
? ? ?观察开发板的原理图,可以知道,如下图所示:
? ? ? 从原理图中可知,硬件时接到了GPIO1_IO03的引脚输出低电平(0)的时候发光二极管LED0就会导通点亮,当GPIO1_IO03输出高电平(1)的时候发光二极管LED0不会导通,因此LED0就不会亮。
第二:实验程序编写方法
1、使能GPIO1时钟
? ? ?GPIO1的时钟由CCM_CCGR1的bit27和bit26这两个位控制,将这两个位设置为11即可。
2、设置GPIO1_IO03的复用功能
? ? ?找到GPIO1_IO03的复用寄存器“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03”的地址为0X020E0068,然后设置此寄存器,将GPIO1_IO03这个IO复用为GPIO功能,也就是ALT5。
3、配置GPIO1_IO03
? ? ?找到GPIO1_IO03的配置寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03”的地址为0X020E02F4,根据实际情况,配置此寄存器。
4、设置GPIO
? ? ? 将GPIO1_IO03复用为GPIO功能,所以我们需要配置GPIO。
? ? ? ? 实验中需要将GPIO1_IO03作为输出功能的,因此GPIO1_GDIR的bit3要设置为1,表示输出。
5、控制GPIO的输出电平
? ? ?经过前面的步骤,GPIO1_IO03已经配置好了,只需要向GPIO1_DR寄存器的bit3写入0即可控制GPIO1_IO03输出低电平,打开 LED,向 bit3 写入 1 可控制 GPIO1_IO03 输出高电平,关闭 LED。
第三:汇编代码具体实现
?
.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] ? ?/*?2、设置?GPIO1_IO03?复用为?GPIO1_IO03?*/ ?ldr?r0,?=0X020E0068?/*?将寄存器?SW_MUX_GPIO1_IO03_BASE?加载到?r0?中?*/ ?ldr?r1,?=0X5?/*?设置寄存器?SW_MUX_GPIO1_IO03_BASE?的?MUX_MODE?为?5?*/ str r1,[r0] ?/*?3、配置?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] ?/*?4、设置?GPIO1_IO03?为输出?*/ ?ldr?r0,?=0X0209C004?/*寄存器?GPIO1_GDIR?*/ ldr r1, =0X0000008 ?str?r1,[r0] ?/*?5、打开?LED0 ?*?设置?GPIO1_IO03?输出低电平 */ ?ldr?r0,?=0X0209C000?/*寄存器?GPIO1_DR?*/ ?ldr?r1,?=0? ?str?r1,[r0] ?/* ?*?描述: loop 死循环 ?*/ ?loop: ?b?loop
?
? ? 分析:第一行定义了一个全局标号_start,代码就是从_start这个标号开始顺序往下执行的。使用ldr 指令向寄存器 r0 写入 0X020C4068,也就是 r0=0X020C4068,这个是CCM_CCGR0 寄存器的地址。使用 ldr 指令向寄存器 r1 写入 0XFFFFFFFF,也就是 r1=0XFFFFFFFF。因为我们要开启所有的外设时钟。使用 str 将 r1 中的值写入到 r0 所保存的地址中去,也就是给 0X020C4068 这个地址写入 0XFFFFFFFF,相当于 CCM_CCGR0=0XFFFFFFFF,就是打开 CCM_CCGR0 寄存器所控制的所有外设时钟。向 CCM_CCGRX(X=1~6)寄存器写入 0XFFFFFFFF。这样我就通过汇编代码使能了芯片的所有外设时钟。设置GPIO1_IO03的复用功能,GPIO1_IO03的复用寄存器地址为0X020E0068,寄 存 器 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的 MUX_MODE 设置为 5 就 是 将GPIO1_IO03 设置为 GPIO。设 置 GPIO1_IO03的 配 置 寄 存 器 , 也 就 是 寄 存 器IOMUX_SW_PAD_CTL_PAD_GPIO1_IO03 的值,此寄存器地址为 0X020E02F4,代码里面已经给出了这个寄存器详细的位设置。设置 GPIO1->GDIR 寄存器,将 GPIO1_IO03 设置为输出模式,也就是寄存器的 GPIO1_GDIR 的 bit3 置 1。设置 GPIO1->DR 寄存器,也就是设置 GPIO1_IO03 的输出,我们要点亮开发板上的 LED0,那么 GPIO1_IO03 就必须输出低电平,所以这里设置 GPIO1_DR 寄存器为 0。
第四:编译与下载
? ??1、arm-linux-gnueabihf-gcc 编译文件
? 编译出在 ARM 开发板上运行的可执行文件,需要使用到对应的交叉编译工具。
?
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
?
? ? 2、arm-linux-gnueabihf-ld 链接文件
? ? ? ? arm-linux-gnueabihf-ld 用来将众多的.o 文件链接到一个指定的链接位置。可以使用 arm-linux-gnueabihf-ld 来将前面编译出来的 led.o 文件链接到 0X87800000 这个地址,使用如下命令:
?
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
?
??led.elf 文件也不是我们最终烧写到 SD 卡中的可执行文件,我们要烧写的.bin 文件,因此还需要将 led.elf 文件转换为.bin 文件,这里我们就需要用到 arm-linux-gnueabihf-objcopy 这个工具了。
3、arm-linux-gnueabihf-objcopy 格式转换
? ?? arm-linux-gnueabihf-objcopy 更像一个格式转换工具,我们需要用它将 led.elf 文件转换为led.bin 文件,命令如下:
?
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
?
上述命令中,“-O”选项指定以什么格式输出,后面的“binary”表示以二进制格式输出,选项“-S”表示不要复制源文件中的重定位信息和符号信息,“-g”表示不复制源文件中的调试信息。
4、arm-linux-gnueabihf-objdump 反汇编
? ? ?C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将 elf 文件反汇编,比如如下命令:
?
arm-linux-gnueabihf-objdump -D led.elf > led.dis
?
第五:创建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
?
最终编译效果:
总结:利用汇编控制底层硬件,是非常经典的实现方式,值得交流学习。
审核编辑:汤梓红
?
评论