IMX6ULL学习笔记(13)——GPIO接口使用(汇编方式)

2023-05-16

一、GPIO简介

i.MX6ULL 芯片的 GPIO 被分成 5 组,并且每组 GPIO 的数量不尽相同,例如 GPIO1 拥有 32 个引脚, GPIO2 拥有 22 个引脚, 其他 GPIO 分组的数量以及每个 GPIO 的功能请参考 《i.MX 6UltraLite Applications Processor Reference Manual》 第26章General Purpose Input/Output (GPIO)(P1133)

通过 GPIO 硬件结构框图,就可以从整体上深入了解 GPIO 外设及它的各种应用模式。

1.1 IO命名

打开 i.MX6ULL 参考手册的第 32 章“Chapter 32: IOMUX Controller(IOMUXC)”

i.MX6ULL 的 IO 分为两类:SNVS 域的和通用的,这两类 IO 本质上都是一样的。

“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”的就是 GPIO 命名,命名形式就是“IOMUXC_SW_MUC_CTL_PAD_XX_XX”,后面的“XX_XX”就是 GPIO 命名,比如:GPIO1_IO01、UART1_TX_DATA、JTAG_MOD 等等。他是 根据某个 IO 所拥有的功能来命名的。比如我们一看到 GPIO1_IO01 就知道这个肯定能做 GPIO,看到 UART1_TX_DATA 肯定就知道这个 IO 肯定能做为 UART1 的发送引脚。

IO 复用功能。 i.MX6ULL 除了 GPIO1_IO00~GPIO1_IO09 引脚外,其它 IO 也是可以复用为 GPIO 功能。同样的,GPIO1_IO00~GPIO_IO09 也是可以复用为其它外设引脚。

1.2 IO复用

IOMUX 译为 IO 复用选择器。i.MX6ULL 的芯片每个 GPIO 都通过 IOMUX 支持多种功能, 例如一个 IO 可用于网络外设 ENET 的数据接收引脚,也可以被配置成 PWM 外设的输出引脚, 这样的设计大大增加了芯片的适用性,这样可选的功能就是由 IOMUX 实现的。IOMUX 相当于增加了多根内部信号线与 IO 引脚相连,最多有 8 根,也就是说一个 IO 最多可支持 8 种可选的功能

以“IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00”这个 IO 为例,打开参考手册的 1568 页。

可以看到有个名为:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00 的寄存器,寄存器地址为 0X020E005C,这个寄存器是 32 位的,但是只用到了最低 5 位,其中 bit0~bit3(MUX_MODE) 就是设置 GPIO1_IO00 的复用功能的。GPIO1_IO00 一共可以复用为 9 种功能 IO,分别对应 ALT0~ALT8,其中 ALT5 就是作为 GPIO1_IO00。GPIO1_IO00 还可以作为 I2C2_SCL、GPT1_CAPTURE1、ANATOP_OTG1_ID 等。

1.3 IO配置

IOMUX 由其左侧的 IOMUXC 控制(C表示Controler),IOMUXC 提供寄存器给用户进行配置, 它又分成 MUX Mode(IO模式控制) 以及 Pad Settings(Pad配置) 两个部分:

在 IOMUXC 外设中关于 MUX Mode 和 Pad Settings 寄存器命名格式如下:

IOMUXC控制类型寄存器名称
MUX ModeIOMUXC_SW_MUX_CTL_PAD_XXXX
Pad SettingsIOMUXC_SW_PAD_CTL_PAD_XXXX

每个引脚都包含这两个寄存器,表中的XXXX表示引脚的名字

1.3.1 MUX Mode配置

MUX Mode 就是用来配置引脚的复用功能,即选择引脚具体是用于网络外设 ENET 的数据接收, 还是用于 PWM 外设的输出引脚,当然,也可以配置成普通的 IO 口,仅用于控制输出高低电平。

以 GPIO1_IO04 引脚为例对 MUX 寄存器进行说明,该引脚相应的 MUX 寄存器在参考手册中的描述如下:

该寄存器主要有两个配置域,分别是 SIONMUX_MODE

  • SION: 用于设置引脚在输出模式下同时开启输入通道。
  • MUX_MODE: 使用 4 个寄存器位表示可选的 ALT0~ALT7 这 8 个模式。
    • 如 ALT2 模式就是用于 USB 外设的 USB_OTG1_PWR 信号;
    • 若配置为 ALT5 则引脚会用作普通的 GPIO 功能, 用于输出高、低电平。

1.3.2 Pad Settings配置

Pad Settings 用于配置引脚的属性,例如驱动能力,是否使用上下拉电阻, 是否使用保持器,是否使用开漏模式以及使用施密特模式还是CMOS模式等。

以 GPIO1_IO04 引脚中 PAD 寄存器在参考手册中的描述如下:

相对来说 PAD 寄存器的配置项就更丰富了,而且图中仅是该寄存器的部分说明,如 HYS 设置使用施密特模式的滞后功能,PUS 配置上下拉电阻的阻值, 其它的还包含PUE、PKE、ODE、SPEED、DSE 及 SRE 的配置。

1.3.3 PAD(可跳过不看)

PAD 代表了一个 i.MX6ULL 的 GPIO 引脚。在它的左侧是一系列信号通道及控制线,如 input_on 控制输入开关,Dir 控制引脚的输入输出方向,Data_out 控制引脚输出高低电平,Data_in 作为信号输入,这些信号都经过一个 IOMUX 的器件连接到左侧的寄存器。

①PAD引脚
代表一个i.MX6ULL的引脚。
②输出缓冲区
当输出缓冲区使能时,引脚被配置为输出模式。在输出缓冲区中,又包含了如下的属性配置:

  • DSE驱动能力
    当IO用作输出的时候用来设置IO的驱动能力。DSE可以调整芯片内部与引脚串联电阻R0的大小,从而改变引脚的驱动能力。例如,R0的初始值为260欧姆,在3.3V电压下其电流驱动能力为12.69mA,通过DSE可以把R0的值配置为原值的1/2、1/3…1/7等。

    位设置速度
    000输出驱动关闭
    001R0(3.3V 下 R0 是 260Ω,1.8V 下 R0 是 150Ω,接 DDR 的时候是 240Ω)
    010R0/2
    011R0/3
    100R0/4
    101R0/5
    110R0/6
    111R0/7
  • SRE压摆率配置
    设置压摆率。压摆率是指电压转换速率,可理解为电压由波谷升到波峰的时间。增大压摆率可减少输出电压的上升时间。i.MX6ULL的引脚通过SRE支持低速和高速压摆率这两种配置。当此位为0的时候是低压摆率,当为1的时候是高压摆率。压摆率是大信号特性,下面的带宽是小信号特性。

  • SPEED带宽配置
    设置IO的带宽。分别可设置为50MHz、100MHz以及200MHz。带宽的意思是能通过这个IO口最高的信号频率,通俗点讲就是方波不失真,如果超过这个频率方波就变正弦波。但是这个带宽要区别于IO的翻转速率,IO的翻转速率的信号来自于GPIO这个外设,而IO的带宽只是限制了IO口引脚的物理特性,IO口的信号可以来自于内部定时器输出的PWM信号,也可以来自于GPIO翻转输出的信号,两者相比之下,PWM信号的频率是远远高于GPIO翻转输出的信号频率。

    位设置速度
    00低速 50M
    01中速 100M
    10中速 100M
    11最大速度 200M
  • ODE开漏输出配置
    设置引脚是否工作在开漏输出模式。在该模式时引脚可以输出高阻态和低电平,此位为0的时候禁止开路输出,当此位为1的时候就使能开路输出功能。输出高阻态时可由外部上拉电阻拉至高电平。开漏输出模式常用在一些通讯总线中,如I2C。

③输入缓冲区
当输入缓冲区使能时,引脚被配置为输入模式。在输入缓冲区中,又包含了如下的属性配置:

  • HYS滞后使能
    用来使能迟滞比较器。i.MX6ULL的输入检测可以使用普通的CMOS检测或施密特触发器模式(滞后模式)。施密特触发器具有滞后效应,对正向和负向变化的输入信有不同的阈值电压。如果需要对输入波形进行整形的话可以使能此位。此位为0的时候禁止迟滞比较器,为1的时候使能迟滞比较器。常被用于电子开关、波形变换等场合,其转换特性和对比如下,如检测按键时,使用施密特模式即可起到消抖的功能。

④Pull/Keeper上下拉、保持器
引脚的控制逻辑中还包含了上下拉、保持器的功能。芯片内部的上拉和下拉电阻可以将不确定的信号钳位在高、低电平,或小幅提高的电流输出能力,上拉提供输出电流,下拉提供输入电流。注意这些上下拉配置只是弱拉,对于类似I2C之类的总线,还是必须使用外部上拉电阻。i.MX6ULL芯片的电源模块中包含转换器,当转换器停止工作时,保持器会保持输入输出电压。

上下拉、保持器可以通过如下属性配置:

  • PUS上下拉配置
    设置上下拉电阻。PUS可配置项可选为100K欧下拉以及22K欧、47K欧及100K欧上拉。
    位设置含义
    00100K 下拉
    0147K 上拉
    10100K 上拉
    1122K 上拉
  • PUE上下拉、保持器选择
    上下拉功能和保持器功能是二选一的,可以通过PUE来选择。当IO作为输入的时候,这个位用来设置 IO 使用上下拉还是状态保持器。当为0的时候使用状态保持器,当为1的时候使用上下拉。状态保持器在IO作为输入的时候才有用,顾名思义,就是当外部电路断电以后此IO口可以保持住以前的状态。
  • PKE上下拉、保持器配置
    用来使能或者禁止上下拉/状态保持器功能。为0时禁止上下拉/状态保持器,为1时使能上下拉和状态保持器。

注意,当引脚被配置为输出模式时,不管上下拉、保持器是什么配置,它们都会被关闭。

1.4 GPIO配置

GPIO 模块是每个 IO 都具有的外设,它具有 IO 控制最基本的功能,如输出高低电平、检测电平输入等。 它也占用 IOMUX 分配的复用信号,也就是说使用 GPIO 模块功能时同样需要使用 IOMUX 选中 GPIO 外设,对其 GPIO 的功能进行配置。

1.4.1 GDIR方向寄存器

设置某个 IO 的工作方向。控制一个 GPIO 引脚时,要先用 GDIR 方向寄存器配置该引脚用于输出电平信号还是用作输入检测。 典型的例子是使用输出模式可以控制LED灯的亮灭,输入模式时可以用来检测按键是否按下。

GDIR 寄存器的每一个数据位代表一个引脚的方向,对应的位被设置为0时该引脚为输入模式,被设置为1时该引脚为输出模式。

例如,对 GPIO1 的 GDIR 寄存器的 bit3 位被写入为 1,那么 GPIO1.3 引脚的模式即为输出。

1.4.2 DR数据寄存器

DR 数据寄存器直接代表了引脚的电平状态,它也使用 1 个数据位表示 1 个引脚的电平,每位用 1 表示高电平,用 0 表示低电平。

当 GDIR 方向寄存器设置引脚为输出模式时,写入 DR 数据寄存器对应的位即可控制该引脚输出的电平状态, 如这时 GPIO1 的 DR 寄存器的 bit4 被写入为 1,则引脚为输出高电平。

当 GDIR 方向寄存器设置引脚为输入模式时,读取 DR 数据寄存器对应的位即可获取该引脚当前的输入电平状态,例如这里读取 GPIO1 的DR寄存器的 bit4,得到该位的值为 0,表示当前引脚的输入状态为低电平。

1.4.3 PSR引脚状态寄存器

读取相应的位即可获取对应的 GPIO 的状态,也就是 GPIO 的高低电平值。PSR 引脚状态寄存器相当于 DR 寄存器的简化版,它仅在 GDIR 方向寄存器设置为输入模式时有效,它的每个位表示一个引脚当前的输入电平状态。PSR 寄存器的权限是只读的,对它进行写操作是无效的。

特别地,当引脚被配置成输出模式时,若 IOMUXC 中的 MUX 寄存器使能了 SION 功能(输出通道回环至输入), 可以通过 PSR 寄存器读取回引脚的状态值。

二、引脚确定

我使用的是 野火_EBF6ULL S1 Pro 开发板

从原理图可看到 RGB 灯的三个阴极 R、G、B 连接分别连接至标号 GPIO_4CSI_HSYNCCSI_VSYNC, 这些标号实际上与配套核心板上 i.MX6ULL 芯片的引脚相连。由于引脚功能众多, 绘制原理图时不可避免地无法完全表示引脚信息的所有信息。而无论是具体的引脚名还是复用功能, 我们都无法直接得知这些具体是 i.MX6ULL 芯片的哪个引脚。我们需要知道这些引脚是对应的具体 GPIO,这样我们才能编写程序进行控制。

由于还不清楚标号 GPIO_4CSI_HSYNCCSI_VSYNC 的具体引脚名,我们首先要在核心板原理图中查看它与 i.MX6ULL 芯片的关系。打开 《野火_EBF6ULL S1 邮票孔核心板_V1.0_原理图》,在PDF阅读器的搜索框输入前面的 GPIO_4CSI_HSYNCCSI_VSYNC 标号。


查找到了 GPIO_4 信号的具体引脚名为 GPIO1_IO04。 但是当我们使用同样的方法查找时发现只能找到 CSI_HSYNCCSI_VSYNC, 并没有我们熟悉的 GPIOx_IOx 标注的引脚名。这两个引脚默认情况下不用作 GPIO,而是用作摄像头的某一功能引脚,但是它可以复用为 GPIO,我们怎么找到对应的 GPIO 呢?

  • 方法一:
    《i.MX 6UltraLite Applications Processor Reference Manual》的第4章 External Signals and Pin Multiplexing 搜索引脚名

  • 方法二:
    在官方写好的文件 fsl_iomuxc.h(路径:SDK文件夹/devices/MCIMX6Y2/drivers/fsl_iomuxc.h) 中搜索引脚名

经查阅,我们把以上连接 LED 灯的各个 i.MX6ULL 芯片引脚总结出如表:

LED灯原理图的标号具体引脚名GPIO端口及引脚编号
R灯GPIO_4GPIO1_IO04GPIO1_IO04
G灯CSI_HSYNCCSI_HSYNCGPIO4_IO20
B灯CSI_VSYNCCSI_VSYNCGPIO4_IO19

三、编程流程

1. 开启GPIO时钟
2. 设置引脚的复用功能以及引脚属性
3. 设置引脚方向以及输出电平

四、编程代码

4.1 完整代码

/*************************第一部分*************************/
.text            //代码段
.align 2         //设置2字节对齐
.global _start   //定义一个全局标号

/*************************第二部分*************************/
_start:          //程序的开始
   b reset      //跳转到reset标号处

/*************************第三部分*************************/
reset:
   mrc     p15, 0, r0, c1, c0, 0     /*  将 CP15 协处理器中的寄存器数据读到 ARM 寄存器中   */
   bic     r0,  r0, #(0x1 << 12)     /*  清除第12位(I位)禁用 I Cache  */
   bic     r0,  r0, #(0x1 <<  2)     /*  清除第 2位(C位)禁用 D Cache  */
   bic     r0,  r0, #0x2             /*  清除第 1位(A位)禁止严格对齐   */
   bic     r0,  r0, #(0x1 << 11)     /*  清除第11位(Z位)分支预测   */
   bic     r0,  r0, #0x1             /*  清除第 0位(M位)禁用 MMU   */
   mcr     p15, 0, r0, c1, c0, 0     /*  将 ARM 寄存器的数据写入到 CP15 协处理器寄存器中   */

/*************************第四部分*************************/
   /*跳转到light_led函数*/
   bl light_led
   /*进入死循环*/
 /*************************第五部分*************************/
loop:
   b loop


/*************************第六部分*************************/
/*CCM_CCGR1 时钟使能寄存器地址,默认时钟全部开启*/
#define gpio1_clock_enible_ccm_ccgr1  0x20C406C


/*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04
寄存器地址,用于设置GPIO1_iIO04的复用功能*/
#define gpio1_io04_mux_ctl_register  0x20E006C
/*IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04寄存器地址,用于设置GPIO的PAD属性*/
#define gpio1_io04_pad_ctl_register  0x20E02F8


/*GPIO1_GDIR寄存器,用于设置GPIO为输入或者输出*/
#define  gpio1_gdir_register  0x0209C004
/*GPIO1_DR寄存器,用于设置GPIO输出的电平状态*/
#define  gpio1_dr_register  0x0209C000



/*************************第七部分*************************/
light_led:
   /*开启GPIO1的时钟*/
   ldr r0, =gpio1_clock_enible_ccm_ccgr1
   ldr r1, =0xFFFFFFFF
   str r1, [r0]

/*************************第八部分*************************/
   /*将PAD引脚复用为GPIO*/
   ldr r0, =gpio1_io04_mux_ctl_register
   ldr r1, =0x5
   str r1, [r0]

/*************************第九部分*************************/
   /*设置GPIO PAD属性*/
   ldr r0, =gpio1_io04_pad_ctl_register
   ldr r1, =0x1F838
   str r1, [r0]
/*************************第十部分*************************/
   /*将GPIO_GDIR.[4] 设置为1, gpio1_io04设置为输出模式*/
   ldr r0, =gpio1_gdir_register
   ldr r1, =0x10
   str r1, [r0]

/*************************第十一部分*************************/
   /*将GPIO_DR 设置为0, gpio1全部输出为低电平*/
   ldr r0, =gpio1_dr_register
   ldr r1, =0x0
   str r1, [r0]

/*************************第十二部分*************************/
   /*跳出light_led函数,返回跳转位置*/
   mov pc, lr

4.2 分析代码

在 Ubuntu 下创建 led.S 文件用于编写 LED 汇编驱动代码。

  • 第一部分
    .text 定义代码段。
    .align 2 设置字节对齐。
    .global _start 生命全局标号_start。
/*************************第一部分*************************/
.text            //代码段
.align 2         //设置2字节对齐
.global _start   //定义一个全局标号
  • 第二部分
    _start: 定义标号_start: ,它位于汇编的最前面,说以会首先被执行。
    b reset 使用b指令将程序跳转到reset标号处。
/*************************第二部分*************************/
_start:          //程序的开始
   b reset      //跳转到reset标号处
  • 第三部分
    通过修改CP15寄存器(系统控制寄存器) 关闭 I Cache 、D Cache、MMU 等等。
    我们暂时用不到的功能,如果开启可能会影响我们裸机运行,为避免不必要的麻烦暂时关闭这些功能。
/*************************第三部分*************************/
reset:
   mrc     p15, 0, r0, c1, c0, 0     /*  将 CP15 协处理器中的寄存器数据读到 ARM 寄存器中   */
   bic     r0,  r0, #(0x1 << 12)     /*  清除第12位(I位)禁用 I Cache  */
   bic     r0,  r0, #(0x1 <<  2)     /*  清除第 2位(C位)禁用 D Cache  */
   bic     r0,  r0, #0x2             /*  清除第 1位(A位)禁止严格对齐   */
   bic     r0,  r0, #(0x1 << 11)     /*  清除第11位(Z位)分支预测   */
   bic     r0,  r0, #0x1             /*  清除第 0位(M位)禁用 MMU   */
   mcr     p15, 0, r0, c1, c0, 0     /*  将 ARM 寄存器的数据写入到 CP15 协处理器寄存器中   */
  • 第四部分
    bl light_led 执行跳转指令,代码将跳转到函数“light_led”执行。
    “bl”指令是“可返回”跳转,跳转之前的执行地址保存在lr(连接寄存器)中。
    “light_led” 函数实现位于第六到十二部分。
/*************************第四部分*************************/
   /*跳转到light_led函数*/
   bl light_led
   /*进入死循环*/
  • 第五部分
    light_led函数返回后就会执行标号loop处的代码,而标号loop处只有一条指令 b loop, 这个指令是代码再次跳转到loop标号处,所以这是一个死循环
/*************************第五部分*************************/
loop:
   b loop
  • 第六部分
    定义我们用到的寄存器地址。
    配置时钟使能寄存器地址 设置时钟控制寄存器CCM_CCGR1的地址如下:

配置MUX Mode寄存器地址 MUX Mode用于设置GPIO1_IO04的复用功能。配置成普通的IO口,仅用于控制输出高低电平。寄存器地址如下:

配置Pad Settings寄存器地址 Pad Settings用于设置GPIO的PAD属性。例如驱动能力,是否使用上下拉电阻, 是否使用保持器,是否使用开漏模式以及使用施密特模式还是CMOS模式等。寄存器地址如下:

/*************************第六部分*************************/
/*CCM_CCGR1 时钟使能寄存器地址,默认时钟全部开启*/
#define gpio1_clock_enible_ccm_ccgr1  0x20C406C


/*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04
寄存器地址,用于设置GPIO1_IO04的复用功能*/
#define gpio1_io04_mux_ctl_register  0x20E006C
/*IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04寄存器地址,用于设置GPIO的PAD属性*/
#define gpio1_io04_pad_ctl_register  0x20E02F8


/*GPIO1_GDIR寄存器,用于设置GPIO为输入或者输出*/
#define  gpio1_gdir_register  0x0209C004
/*GPIO1_DR寄存器,用于设置GPIO输出的电平状态*/
#define  gpio1_dr_register  0x0209C000
  • 第七部分
    开启GPIO1的时钟。
    设置的时钟控制寄存器 CCM_CCGR1

从上表中可以看出CCM_CCGR1[26:27]用于使能GPIO1的时钟,这里不仅仅设置时钟的开或者关, 还可以设置在芯片在不同工作模式下的时钟状态如下表:

CCM_CCGR1[26:27]的值时钟状态描述
00时钟在所有模式下都是关闭的
01时钟在运行模式下为开,但在等待和停止模式下为关
10保留
11除停止模式外,时钟一直开启

我们将CCM_CCGR1[26:27]设置为11(二进制)即可。仔细观察可以发现发现CCM_CCGR1寄存器默认全为1,即默认开启了时钟。 为了程序规范我们再次使用代码开启时钟。将CCM_CCGR1寄存器设置全为1

ldr r0, =gpio1_clock_enible_ccm_ccgr1 从存储器Rn+offset(即gpio1_clock_enible_ccm_ccgr1)的位置读取数据存放到r0中。
str r1, [r0] 将r1中的数据写入到存储器中的Rn+offset(即r0的指向)位置。

/*************************第七部分*************************/
light_led:
   /*开启GPIO1的时钟*/
   ldr r0, =gpio1_clock_enible_ccm_ccgr1
   ldr r1, =0xFFFFFFFF
   str r1, [r0]
  • 第八部分
    设置引脚复用功能为GPIO。
    这里设置的是GPIO1_04的引脚复用寄存器,我们直接搜索 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04 可以找到如下所示的寄存器。

从上图可知IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04[MUX_MODE]=0101(二进制)时GPIO1_04复用功能是GPIO。所以在程序中我们将0x5写入该寄存即可。

/*************************第八部分*************************/
   /*将PAD引脚复用为GPIO*/
   ldr r0, =gpio1_io04_mux_ctl_register
   ldr r1, =0x5
   str r1, [r0]
  • 第九部分
    设置引脚的PAD属性。
    这里设置的是GPIO1_04的引脚PAD属性寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04”
/*************************第九部分*************************/
   /*设置GPIO PAD属性*/
   ldr r0, =gpio1_io04_pad_ctl_register
   ldr r1, =0x1F838
   str r1, [r0]
  • 第十部分
    设置GPIO为输出模式。
/*************************第十部分*************************/
   /*将GPIO_GDIR.[4] 设置为1, gpio1_io04设置为输出模式*/
   ldr r0, =gpio1_gdir_register
   ldr r1, =0x10
   str r1, [r0]
  • 第十一部分
    设置GPIO输出电平为低电平。
/*************************第十一部分*************************/
   /*将GPIO_DR 设置为0, gpio1全部输出为低电平*/
   ldr r0, =gpio1_dr_register
   ldr r1, =0x0
   str r1, [r0]
  • 第十二部分
    从light_led函数返回。在第四部分说到,我们使用“bl”指令跳转到light_led函数执行, “bl”指令是“可返回”的跳转指令,返回地址保存在“LR”(连接寄存器)里。
    mov pc, lr 将“lr”寄存器的值写入“pc”寄存器即可。
/*************************第十二部分*************************/
   /*跳出light_led函数,返回跳转位置*/
   mov pc, lr

五、编译下载验证

5.1 编译生成.bin文件

5.1.1 编译文件

编译出在ARM开发板上运行的可执行文件,所以要使用我们安装的交叉编译器arm-linux-gnueabihf-gcc来编译。 编译出的led.o文件并不是我们可以下载到开发板中运行的文件,一个工程中所有的C文件和汇编文件都会编译生成一个对应的.o文件,我们需要将这.o文件链接起来组合成可执行文件。

arm-none-eabi-gcc -g -c led.S -o led.o
  • -g :加入GDB能够使用的调试信息,能够使用GDB调试。
  • -c :对源程序example.c进行预处理、编译、汇编操作,生成example.o文件。
  • led.S :要编译的源文件。
  • -o :指定输出文件的文件名,不加“-o led.o”默认会输出led.o。 正常情况下执行该命令后会在当前文件夹下生成led.o文件。

5.1.2 链接文件

arm-linux-gnueabihf-ld用来将众多的.o文件链接到一个指定的链接地址。这里我们要区分“存储地址”和“运行地址”这两个概念,“存储地址”就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。“运行地址”就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。比如I.MX6ULL支持SD卡、EMMC、NAND启动,因此代码可以存储到SD卡、EMMC或者NAND中,但是要运行的话就必须将代码从SD卡、EMMC或者NAND中拷贝到其运行地址(链接地址)处,“存储地址”和“运行地址”可以一样,比如STM32的存储起始地址和运行起始地址都是0X08000000。上电以后I.MX6ULL的内部boot rom程序会将可执行文件拷贝到链接地址处,这个链接地址可以在I.MX6UL的内部128KB RAM中(0X900000~0X91FFFF),也可以在外部的DDR中。led.elf 文件也不是我们最终烧写到SD卡中的可执行文件,我们要烧写的.bin文件,因此还需要将led.elf文件转换为.bin文件。

arm-none-eabi-ld -Ttext 0x80000000 led.o -o led.elf
  • -Ttext 0x80000000设置程序代码段的起始地址为0x80000000。0x80000000是外部内存的起始地址。这个地址是由芯片本身决定的,我们打开 《IMX6ULRM》手册在Chapter 2 Memory Maps章节ARM平台内存映射表 介绍了这部分内容,如下所示。
  • -o :指定输出的文件名。

5.1.3 格式转换

上一步链接生成的.elf文件是带有地址信息的文件,不能放在存储器中执行,要使用格式转换命令转化为二进制文件。

arm-none-eabi-objcopy -O binary -S -g led.elf led.bin
  • -O binary :指定输出文件格式为二进制文件。
  • -S选项 :不从源文件中复制重定位信息和符号信息。
  • -g选项 :不从源文件中复制可调试信息。

5.1.4 反汇编(可跳过)

大多数情况下我们都是用C语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将elf文件反汇编。

arm-linux-gnueabihf-objdump -D led.elf > led.dis
  • -D :表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为led.dis文件。

5.2 代码烧写

编译成功后会在当前文件夹下生成.bin文件,这个.bin文件也不能直接放到开发板上运行, 这次是因为需要在.bin文件缺少启动相关信息。

为二进制文件添加头部信息并烧写到SD卡。查看 IMX6ULL学习笔记(12)——通过SD卡启动官方SDK程序

进入烧写工具目录,执行 ./mkimage.sh <烧写文件路径> 命令,例如要烧写的 led.bin 位于 home 目录下,则烧写命令为 ./mkimage.sh /home/led.bin

执行上一步后会列出linux下可烧写的磁盘,选择你插入的SD卡即可。这一步 非常危险!!!一定要确定选择的是你插入的SD卡!!,如果选错很可能破坏你电脑磁盘内容,造成数据损坏!!! 确定磁盘后SD卡以“sd”开头,选择“sd”后面的字符即可。例如要烧写的sd卡是“sdb”则输入“b”即可。

5.3 实验现象

将开发板设置为SD卡启动,接入SD卡,开发板上电,正常情况下可以看到开发板RGB灯红灯亮。


• 由 Leung 写于 2022 年 12 月 12 日

• 参考:4. 汇编点亮LED灯

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

IMX6ULL学习笔记(13)——GPIO接口使用(汇编方式) 的相关文章

  • 使用python爬虫把自己的CSDN文章爬取下来并保存到MD文件

    导言 爬虫作为一个敏感技术 xff0c 千万要把握好 xff0c 如果人家不让爬那就不要头铁去试了 如何确定某个网站是否允许爬虫 在域名后面加上 robots txt查看即可 xff0c 例如 xff1a https blog csdn n
  • 寻找矩阵中的指定路径

    给定一个矩阵和一个要找的字符串 xff0c 如果有的话找出矩阵中的字符串路径 测试用例 功能测试 xff1a 在多行多列的矩阵中存在或者不存在路径 边界值测试 xff1a 矩阵只有一行或一列 xff1b 矩阵和路径中的所有字母都是相同的 特
  • Kalibr标定Intel D435i相机

    Kalibr标定Intel D435i相机 文章目录 Kalibr标定Intel D435i相机1 相机标定2 IMU标定3 相机 43 IMU联合标定4 标定结果评价 系统环境 xff1a ubuntu16 04 43 roskineti
  • VINS初始化

    VINS初始化 VINS初始化之外参在线标定 前面主要分析了外参标定出来旋转矩阵 xff0c 接下来接着分析初始化 if solver flag 61 61 INITIAL if frame count 61 61 WINDOW SIZE
  • ROS中关于两个话题时间同步遇到的问题 message_filters

    ROS中关于两个话题时间同步遇到的问题 message filters 参考链接 CMakeFiles imu data dir src imu data cpp o xff1a 在函数 message filters Synchroniz
  • ROS之多传感器融合算法实现

    ROS之多传感器融合算法实现 文章目录 1 motivation2 method2 1 订阅ROS的多个话题并对数据进行处理2 2 订阅ROS的多个话题并发布成一个话题 1 motivation IntelRealsenseD435i传感器
  • ignav代码阅读笔记

    整个代码还是根据rtklib进行改的 xff0c 功能很完善 xff0c 但是我主要只关注ppp ins紧组合 代码链接 https github com Erensu ignav 代码功能 可以完成ppp和ins的紧组合 xff0c 把c
  • 【泡泡Docker乐园】使用泡泡Docker基础镜像放心大胆地开发吧!

    泡泡Docker乐园 使用泡泡Docker基础镜像放心大胆地开发吧 xff01 2020 4 7 泡泡推广 amp 编辑组 泡泡Docker乐园 xff0c 带你进入Docker的狂欢派对 简介 xff1a 泡泡Docker乐园 本次将推出
  • 【泡泡Docker乐园】Dockerfile简易教程 & LARVIO镜像

    泡泡Docker乐园 Dockerfile简易教程 amp LARVIO镜像 亲测完美 简介 xff1a 泡泡Docker乐园 第二期来啦 xff01 本期我们将简要介绍使用Dockerfile进行image构建的方法 利用Dockerfi
  • IMU与GPS传感器ESKF融合定位

    IMU与GPS传感器ESKF融合定位 文章目录 IMU与GPS传感器ESKF融合定位1 代码整体框架说明2 主要函数介绍2 1 LocalizationWrapper构造函数2 2 滤波算法进行预测2 3 通过GPS位置测量数据更新系统的状
  • 机器学习 小工具

    python美化打印的标准库 xff1a pprint
  • python数据类型

    python数据类型 数字 xff08 整型 xff0c 浮点型 xff09 字符串列表 xff1a 元组 xff1a 字典 xff1a 列表 元组以及字典的区别 xff1f 列表 元祖以及字典都是容器型数据类型 xff0c 可以对列表中的
  • 实用机器学习(hw1/hw4)

    实用机器学习 hw1 hw4 文章目录 实用机器学习 hw1 hw4 1 环境安装2 baseline 代码分析3 提升精度代码4 机器学习模型 1 环境安装 autogluon 2 baseline 代码分析 span class tok
  • 没有与这些操作数匹配的运算符

    没有与这些操作数匹配的 lt lt 运算符 include与 include lt string h gt 的区别 lt string h gt 的区别 是C 43 43 特化的字符容器 xff0c 内含string类 lt string
  • gazebo模型下载以及配置

    最近在学习ROS xff0c 主要是为了结合SLAM仿真使用 启动gazebo命令 roscore 在另一个终端执行 gazebo 就可以进入清爽的gazebo界面 xff08 如果屏幕出现黑屏并不是安装错误可以稍微等待一会 xff09 x
  • SLAM中常用数据集下载链接(TUM KITTI DSO Mono EuRoC)

    TUM 链接 xff1a https pan baidu com s 1nwXtGqH 密码 xff1a lsgr KITTI 链接 xff1a https pan baidu com s 1htFmXDE 密码 xff1a uu20 KI
  •  windows docker 更改镜像安装目录

    目录 1 问题 1 1 版本信息 2 修改Docker盘位操作 2 1 停止docker 2 2 备份已有的数据 2 3 删除旧数据 数据未备份前请谨慎操作 2 4 导入数据到新盘 2 5 启动Docker START 1 问题 Windo
  • gnssins代码阅读

    这个代码是GNSS和INS紧组合的 xff1a https github com marcoamm gnssins xff0c 实现了ppp和ins紧组合 改变数据需要改代码的地方 xff1a imu tactical 61 fopen 3
  • 深度解析FUTABA的SBUS协议(/天地飞遥控器的WBUS协议/Robomaster接收机的DBUS协议)到底是啥?

    写在前面 xff1a 无论是SBUS xff08 日本FUTABA xff0c 所以航模 xff0c 车模爱好者都知道的公司 xff0c 一个好点遥控器近万了 xff09 xff0c 还是WBUS xff08 天地飞遥控器接收机用 xff0
  • 贝塞尔曲线动画C++简单实践

    目录 贝塞尔曲线简介一阶贝塞尔二阶贝塞尔三阶贝塞尔N阶贝塞尔曲线 贝塞尔曲线在动画中的应用实践求曲线散点坐标将曲线应用到动画动画框架cmd动画窗口动画 完整代码示例代码核心类代码BezierCurve Animator Console 参考

随机推荐

  • package.xml文件介绍

    package xml文件介绍 在ROS中创建功能包时 xff0c 会自动生成package xml文件 xff0c pacakge xml 包含了package的名称 版本号 内容描述 维护人员 软件许可 编译构建工具 编译依赖 运行依赖
  • ubuntu误修改了bashrc文件的解决办法

    在安装Pycharm的过程中配置JAVA的JDK环境变量时 xff0c 将bashrc内的内容不小心修改了 xff0c 导致命令窗口中的很多命令不能执行 xff0c 并且su及sudo这些权限的命令也用不了 xff0c 问题信息如下图所示
  • docker安装canal1.1.5监控mysql的binlog日志并配置rocketmq进行数据同步到elasticsearch(超级大干货)

    直接来 xff0c 不逼逼 xff08 canal官网说的很明白 xff0c 伪从节点请求dump 然后这个那个的 xff0c 自行查阅资料 xff09 1 直接拉取canal镜像 docker pull canal canal serve
  • 相机定位、相机重定位和视觉里程计的概念定义

    相机定位 相机重定位和视觉里程计的概念定义 什么是相机定位 xff1f 什么是相机重定位 xff1f 什么是视觉里程计 xff1f 相机定位 相机定位 xff08 Camera Localization xff09 是求解基于基本坐标系下的
  • vscode使用restClient实现各种http请求

    vscode使用restClient实现各种http请求 一 xff0c 安装插件 首先 xff0c 我们要在vscode的扩展中 xff0c 搜索rest Client xff0c 然后安装它 xff0c 这里我已经安装过了 安装后 xf
  • akka-2 利用模式匹配,实现worker节点向master报告本机配置信息

    1 创建本机配置信息类 WorkInfo Worker的本机信息类 id 主机名称 momery 内存大小 cores CPU核数 直接使用默认构造创建消息类 Worker的本机信息类 id 主机名称 momery 内存大小 cores C
  • 多路归并排序-Python实现大文件排序,合并排序

    使用python实现多 K 路归并外部排序 xff0c 解决小内存排序大文件问题 上一篇中 xff0c 我们实现了一般的归并排序 归并排序递归与非递归 Python实现 在实际工作中 xff0c 多个有序数列合并成一个 xff0c 大文件或
  • GINAV使用

    在配置文件中 xff0c 绝对不能 psd gyro 61 8 46e 14 等号后面必须要有空格才能区分开 psd gyro 61 8 46e 14 运行谷歌数据直接是空的图像
  • ZED双目相机(c++程序实现)

    1 前提条件 SDK最新版 xff08 从官网直接下载安装 xff0c 默认安装路径 xff09 CUDA xff08 安装对应版本 xff0c 此步需要电脑GPU支持 xff08 此步劝退一部人 xff09 xff09 下载zed示例包
  • fork()使用详解

    其他参考 xff1a linux中fork xff08 xff09 函数详解 一 fork入门知识 进程的定义 xff1a 进程是一个执行中的程序的实例 xff0c 是系统进行资源分配和调度的一个独立单位 PCB是进程存在的唯一标识 PCB
  • conda 安装包出现问题 :Collecting package metadata (current_repodata.json): failed

    如果有重装过anaconda的经历的同学 xff0c 应该注意到会有一个 condarc文件会自动生成 xff0c 所以当使用conda install和conda create命令会出现问题 删除 condarc文件之后 xff0c 这个
  • DockerFile+Python2安装和python3和Pip2安装request

    dockerfile 的使用步骤 sudo docker build t cve 11890 cve 11890是文件名 docker images 查看文件是否成功创建 sudo docker run d p 8089 80 cve 11
  • Linux关闭启用图形界面

    Linux关闭启用图形界面 关闭图形界面 sudo su root systemctl set default multi user target sudo service lightdm stop shutdown r 重启命令 xff0
  • Prometheus&Grafana

    Prometheus是一个全能选手 原生支持容器监控 也支持传统应用程序 它具备所有监控系统都具备的流程 数据采集 数据处理 数据存储 数据展示 警告 prometheus中文名字普罗米修斯最初在soundCloud上构建的监控系统 xff
  • QGC - QGroundControl启动问题解决

    若打开qgc QGroundControl 弹出窗口提示我们应该如下操作 sudo usermod a G dialout USER 和 sudo apt get remove modemmanger xff0c 但输入命令后依旧启动不了
  • matlab中im2bw

    在MATLAB中 xff0c im2bw是一个用于将灰度图像转换为二值图像的函数 im2bw函数将灰度图像中的像素值转换为二值图像中的0或1值 xff0c 其中0代表黑色或背景 xff0c 1代表白色或前景 这个函数的语法如下 xff1a
  • STM32CubeMX学习笔记(48)——USB接口使用(MSC基于外部Flash模拟U盘)

    一 USB简介 USB xff08 Universal Serial BUS xff09 通用串行总线 xff0c 是一个外部总线标准 xff0c 用于规范电脑与外部设备的连接和通讯 是应用在 PC 领域的接口技术 USB 接口支持设备的即
  • pppBayesTree工程在ubuntu16.04的vscode下调试

    从下面这个链接中获取代码 xff0c 其他链接有些make操作的错误 xff0c 在下面这个链接中我改好了 bryantaoli PPP BayesTree 参考下面这个配置vscode VScode单步调试 fb 941219的博客 CS
  • STM32CubeMX学习笔记(49)——USB接口使用(MSC基于SD卡模拟U盘)

    一 USB简介 USB xff08 Universal Serial BUS xff09 通用串行总线 xff0c 是一个外部总线标准 xff0c 用于规范电脑与外部设备的连接和通讯 是应用在 PC 领域的接口技术 USB 接口支持设备的即
  • IMX6ULL学习笔记(13)——GPIO接口使用(汇编方式)

    一 GPIO简介 i MX6ULL 芯片的 GPIO 被分成 5 组 并且每组 GPIO 的数量不尽相同 xff0c 例如 GPIO1 拥有 32 个引脚 xff0c GPIO2 拥有 22 个引脚 xff0c 其他 GPIO 分组的数量以