Linux学习第三天

嵌入式linux学习第三天汇编语言点灯

今天学习如何在linux板子上点灯。

I.MX6U GPIO 详解

我们发现I.MX6U GPIO是分为两类的,:SNVS 域的和通用的。在讨论i.MX6U或类似的复杂微处理器时,了解其GPIO(通用输入输出)引脚的不同分类是很重要的。i.MX6U的GPIO引脚被分为两大类:SNVS(Secure Non-Volatile Storage)域的GPIO和通用GPIO。这种分类反映了它们不同的用途和特性。

IO:Input Output,用于CPU与外界进行信息交互
GPIO:General Purpose IO ports,通用IO口
SNVS:Secure Non-Volatile Storage 安全的非易失性存储
IOMUX:Input Output Multiplexer 输入/输出多路复用器
IOMUXC:Input Output Multiplexer Controller 输入/输出多路复用控制器
SW:Switch 开关

SNVS域的GPIO

SNVS域是指与安全和非易失性存储相关的功能区域。这个域主要关注的是安全功能,比如加密、安全启动、电源管理和保护敏感数据等。

  • 用途:SNVS域的GPIO通常用于安全相关的功能,例如检测篡改事件、管理安全启动过程中的信号或者控制与安全相关的外设。
  • 特性:这些GPIO在系统的低功耗模式下仍然可以工作,因此它们特别适合于那些需要在系统休眠或低功耗状态下监视外部事件的应用。此外,它们还可能支持一些特定的安全功能,比如硬件加密引擎的控制信号。
  • 电源域:SNVS域的GPIO与特定的电源域相关联,即使在系统其它部分关闭电源的情况下,这部分电源域仍然保持供电。这样可以确保安全功能在系统的各种电源状态下都能正常工作。

通用GPIO

与SNVS域的GPIO相比,通用GPIO没有那么多特定的安全或电源管理功能。它们被设计用于更广泛的目的,从简单的LED控制到与外部设备的复杂通信等。

  • 用途:通用GPIO用于各种标准输入输出任务,如数据收发、信号触发、电机控制等。
  • 灵活性:这些GPIO通常提供高度的配置灵活性,包括但不限于方向控制(输入或输出)、上拉/下拉电阻配置、中断触发设置等。
  • 电源域:通用GPIO通常随主系统电源一起工作,这意味着它们在系统进入低功耗模式时可能不可用。

在这里插入图片描述
STM32 的很多 IO 是可以复用为其它功能的,那么 I.MX6ULL 的其它 IO 也 是 可 以 复 用 为 GPIO 功能。同样的,GPIO1_IO00~GPIO_IO09 也是可以复用为其它外设引脚的,
一般来说配置GPIO步骤分为以下几步:
①、使能 GPIO 对应的时钟。
②、设置寄存器 IOMUXC_SW_MUX_CTL_PAD_XX_XX,设置 IO 的复用功能,使其复用
为 GPIO 功能。
③、设置寄存器 IOMUXC_SW_PAD_CTL_PAD_XX_XX,设置 IO 的上下拉、速度等等。
④、第②步已经将 IO 复用为了 GPIO 功能,所以需要配置 GPIO,设置输入/输出、是否使
用中断、默认输出电平等

1:使能对应GPIO时钟

CCM 里 面 的 外 设 时 钟 使 能 寄 存 器 。 CMM 有CCM_CCGR0~CCM_CCGR6 这 7 个寄存器,这 7 个寄存器控制着 I.MX6U 的所有外设时钟开关,
在这里插入图片描述
在这里插入图片描述
CCM_CCGR0 是个 32 位寄存器,其中每 2 位控制一个外设的时钟,比如 bit31:30 控制着GPIO2 的外设时钟,两个位就有 4 种操作方式.

在这里插入图片描述

2:设置寄存器 IOMUXC_SW_MUX_CTL_PAD_XX_XX,设置 IO 的复用功能,使其复用 为 GPIO 功能。

在这里插入图片描述
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 的寄存器,寄存器地址为 0X020E005C,这个寄存器是 32 位的,但是只用到了最低 5 位,其中bit0~bit3(MUX_MODE)就是设置 GPIO1_IO00 的复用功能的。GPIO1_IO00 一共可以复用为 9种功能 IO,分别对应 ALT0~ALT8,

3: 设置寄存器 IOMUXC_SW_PAD_CTL_PAD_XX_XX,设置 IO 的上下拉、速度等等。

在这里插入图片描述

IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO00 也是个寄存器,寄存器地址为 0X020E02E8。这也是个 32 位寄存器,但是只用到了其中的低 17 位,下面是GPIO功能图的具体配置。

在这里插入图片描述
HYS(bit16):对应图 8.1.4.2 中 HYS,用来使能迟滞比较器,当 IO 作为输入功能的时候有效,用于设置输入接收器的施密特触发器是否使能。如果需要对输入波形进行整形的话可以使能此位。此位为 0 的时候禁止迟滞比较器,为 1 的时候使能迟滞比较器。
PUS(bit15:14):对应图 8.1.4.2 中的 PUS,用来设置上下拉电阻的,一共有四种选项可以选择,如表 8.1.4.1 所示:
在这里插入图片描述
PUE(bit13):图 8.1.4.2 没有给出来,当 IO 作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器。当为 0 的时候使用状态保持器,当为 1 的时候使用上下拉。状态保持器在IO 作为输入的时候才有用,顾名思义,就是当外部电路断电以后此 IO 口可以保持住以前的状态。
PKE(bit12):对应图 8.1.4.2 中的 PKE,此位用来使能或者禁止上下拉/状态保持器功能,为0 时禁止上下拉/状态保持器,为 1 时使能上下拉和状态保持器。
ODE(bit11):对应图 8.1.4.2 中的 ODE,当 IO 作为输出的时候,此位用来禁止或者使能开路输出,此位为 0 的时候禁止开路输出,当此位为 1 的时候就使能开路输出功能。
SPEED(bit7:6):对应图 8.1.4.2 中的 SPEED,当 IO 用作输出的时候,此位用来设置 IO 速度,设置如表所示:
在这里插入图片描述
DSE(bit5:3):对应图 8.1.4.2 中的 DSE,当 IO 用作输出的时候用来设置 IO 的驱动能力,总共有 8 个可选选项,如表 8.1.4.3 所示:
在这里插入图片描述
SRE(bit0):对应图 8.1.4.2 中的 SRE,设置压摆率,当此位为 0 的时候是低压摆率,当为 1的时候是高压摆率。这里的压摆率就是 IO 电平跳变所需要的时间,比如从 0 到 1 需要多少时间,时间越小波形就越陡,说明压摆率越高;反之,时间越多波形就越缓,压摆率就越低。如果你的产品要过 EMC 的话那就可以使用小的压摆率,因为波形缓和,如果你当前所使用的 IO做高速通信的话就可以使用高压摆率。

4: I.MX6U GPIO 配置

IOMUXC_SW_MUX_CTL_PAD_XX_XX 和 IOMUXC_SW_PAD_CTL_PAD_XX_XX 这两种寄存器都是配置 IO 的,注意是 IO!不是 GPIO,GPIO 是一个 IO 众多复用功能中的一种。如果我们要用 GPIO1_IO00 来点个灯、作为按键输入啥的就是使用其 GPIO(通用输入输出)的功能。将其复用为 GPIO 以后还需要对其 GPIO 的功能进行配置,
在这里插入图片描述
这两种寄存器前面说了用来设置 IO 的复用功能和 IO 属性配置。左上角部分的 GPIO 框图就是,当 IO 用作 GPIO 的时候需要设置的寄存器,一共有八个:DR、GDIR、PSR、ICR1、ICR2、EDGE_SEL、IMR 和 ISR。前面我们说了 I.MX6U 一共有
GPIO1~GPIO5 共五组 GPIO,每组 GPIO 都有这 8 个寄存器。
在这里插入图片描述此寄存器是 32 位的,一个 GPIO 组最大只有 32 个 IO,因此 DR 寄存器中的每个位都对应一个 GPIO。当 GPIO 被配置为输出功能以后,向指定的位写入数据那么相应的 IO 就会输出相应的高低电平,比如要设置 GPIO1_IO00 输出高电平,那么就应该设置 GPIO1.DR=1。被配置为输入模式以后,此寄存器就保存着对应 IO 的电平值,每个位对对应一个 GPIO,例如,当 GPIO1_IO00 这个引脚接地的话,那么 GPIO1.DR 的 bit0 就是 0。
在这里插入图片描述
GDIR 寄存器也是 32 位的,此寄存器用来设置某个 IO 的工作方向,是输入还是输出。同样的,每个 IO 对应一个位,如果要设置 GPIO 为输入的话就设置相应的位为 0,如果要设置为输出的话就设置为 1。比如要设置 GPIO1_IO00 为输入,那么 GPIO1.GDIR=0;

在这里插入图片描述
接下来来看ICR1和ICR2寄存器,ICR1 用于 IO0~15 的配置, ICR2 用于 IO16~31 的配置。ICR1 寄存器中一个 GPIO 用两个
位,这两个位用来配置中断的触发方式,和 STM32 的中断很类似,可配置的选线如图所示:
在这里插入图片描述
在这里插入图片描述
IMR 寄存器,这是中断屏蔽寄存器。

在这里插入图片描述
IMR 寄存器也是一个 GPIO 对应一个位,IMR 寄存器用来控制 GPIO 的中断禁止和使能,如果使能某个 GPIO 的中断,那么设置相应的位为 1 即可,反之,如果要禁止中断,那么就设置相应的位为 0 即可。例如,要使能 GPIO1_IO00 的中断,那么就可以设置 GPIO1.MIR=1 即可。
接下来看寄存器 ISR,ISR 是中断状态寄存器。

在这里插入图片描述
ISR 寄存器也是 32 位寄存器,一个 GPIO 对应一个位,只要某个 GPIO 的中断发生,那么ISR 中相应的位就会被置 1。所以,我们可以通过读取 ISR 寄存器来判断 GPIO 中断是否发生,相当于 ISR 中的这些位就是中断标志位。当我们处理完中断以后,必须清除中断标志位,清除方法就是向 ISR 中相应的位写 1,也就是写 1 清零。
EDGE_SEL 寄存器,这是边沿选择寄存器,
在这里插入图片描述

硬件原理图

以上就是配置GPIO的过程。接下来看具体的硬件原理图。LED0 接到了 GPIO_3 上,GPIO_3 就是 GPIO1_IO03,当 GPIO1_IO03输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发
光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03
的输出电平,输出 0 就亮,输出 1 就灭。
在这里插入图片描述

汇编程序编写

1、使能 GPIO1 时钟
GPIO1 的时钟由 CCM_CCGR1 的 bit27 和 bit26 这两个位控制,将这两个位都设置位 11 即可。本教程所有例程已经将 I.MX6U 的所有外设时钟都已经打开了,因此这一步可以不用做。
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。找到 GPIO3 对应的 GPIO 组寄存器地址,
在这里插入图片描述
具体汇编程序如下:
我们在虚拟机系统进行程序编写以及编译。

.global _start
_start:
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
ldr r1,=0x5
str r1,[r0]

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

ldr r0, =0X0209C004 /*寄存器 GPIO1_GDIR */
ldr r1, =0X0000008 
str r1,[r0]

ldr r0, =0X0209C000 /*寄存器 GPIO1_DR */
ldr r1, =0 
str r1,[r0]

loop:
    b loop

上面汇编的操作并不复杂,只是按照上面已经列出的步骤对对应寄存器进行赋值。

编译下载验证

1、arm-linux-gnueabihf-gcc 编译文件
我们是要编译出在 ARM 开发板上运行的可执行文件,所以要使用安装的交叉编译器 arm-linux-gnueabihf-gcc 来编译。就一个 led.s 源文件,所以编译比较简单。先将 led.s 编译为对应的.o 文件,在终端中输入如下命令:

arm-linux-gnueabihf-gcc -g -c led.s -o led.o

上述命令就是将 led.s 编译为 led.o,其中“-g”选项是产生调试信息,GDB 能够使用这些调试信息进行代码调试。“-c”选项是编译源文件,但是不链接。“-o”选项是指定编译产生的文件名字,这里我们指定 led.s 编译完成以后的文件名字为 led.o。执行上述命令以后就会编译生成一个 led.o 文件。
在这里插入图片描述
2、arm-linux-gnueabihf-ld 链接文件
arm-linux-gnueabihf-ld 用来将众多的.o 文件链接到一个指定的链接位置。因此我们现在需要做的就是确定一下本试验最终的可执行文件其运行起始地址,也就是链接地址。这里我们要区分“存储地址”和“运行地址”这两个概念,“存储地址”就是可执
行文件存储在哪里,可执行文件的存储地址可以随意选择。“运行地址”就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。比如 I.MX6U 支持 SD 卡、EMMC、NAND 启动,因此代码可以存储到 SD 卡、EMMC 或者 NAND 中,但是要运行的话就必须将代码从 SD 卡、EMMC 或者
NAND 中拷贝到其运行地址(链接地址)处,“存储地址”和“运行地址”可以一样,比如STM32 的存储起始地址和运行起始地址都是 0X08000000。
上电以后 I.MX6U 的内部 boot rom 程序会将可执行文件拷贝到链接地址处,这个链接地址可以在 I.MX6U 的内部 128KB RAM 中(0X900000~0X91FFFF),也可以在外部的 DDR 中。本教程所有裸机例程的链接地址都在 DDR中,链接起始地址为 0X87800000。I.MX6U-ALPHA 开发板的 DDR 容量有两种:512MB 和256MB,起始地址都为 0X80000000,只不过 512MB 的终止地址为 0X9FFFFFFF,而 256MB 容量的终止地址为 0X8FFFFFFF。之所以选择 0X87800000 这个地址是因为后面要讲的 Uboot 其链接地址就是 0X87800000,

arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf

在这里插入图片描述

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

在这里插入图片描述
5:创建Makefile文件:

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

代码烧写

烧写使用正点原子提供的
1、将 imxdownload 拷贝到工程根目录下
2、给予 imxdownload 可执行权限
3、确定要烧写的 SD 卡。
在这里插入图片描述
比如我们插入sb多出了几个,烧写的确认的就是/dev/sdc。

./imxdownload led.bin /dev/sd

在这里插入图片描述
烧写速度226kb每秒证明烧写成功。
然后还要设置板子的启动方式为sd卡启动。拨码开关组合为:
在这里插入图片描述
插入板子按下复位,发现这个灯亮起,证明点灯成功!!!
在这里插入图片描述

posted @ 2024-05-07 22:21  Bathwind_W  阅读(4)  评论(0编辑  收藏  举报