ARM学习笔记(三)——i.MX6ULL—LED点灯

一、i.MX6ULL开发板介绍

今天开始从 ARM 内核理论逐渐进入到 i.MX6ULL 裸机开发,主要学习如何在开发板上通过 ARM 汇编程序控制硬件。

使用的开发板为 IMX6ULL-Mini 开发板,核心处理器为 NXP 的 i.MX6ULL。

开发板的基本组成:

复制代码
IMX6ULL-Mini开发板
│
├── 核心板
│   ├── CPU
│   ├── DDR3L RAM
│   └── eMMC
│
└── 底板
    ├── LED
    ├── GPIO
    ├── UART
    ├── PWM
    └── 其他外设

二、i.MX6ULL核心板

i.MX6ULL 采用:

复制代码
CPU:NXP i.MX6ULL
架构:ARM
内核:Cortex-A7

主要参数:

  • Cortex-A7 单核处理器;

  • 主频为 528MHz(工业级)或 800MHz(商业级);

  • BGA 封装;

  • 512MB DDR3L RAM;

  • 8GB eMMC;

  • 支持 SD 卡、NAND、eMMC 等启动方式。

因此:

复制代码
Cortex-A7 → ARM处理器内核
i.MX6ULL → NXP生产的SoC
IMX6ULL-Mini → 开发板

三、开发板底板上的LED

今天主要观察和使用了开发板上的 LED 模块。

底板上有:

1. 红色LED

红色 LED 是用户可以控制的 LED,可以通过程序控制其亮灭。

2. 蓝色LED

蓝色 LED 是电源指示灯,用于表示开发板是否正常供电。

3. 510Ω限流电阻

LED 前串联了一个 510Ω 电阻。

主要作用:

复制代码
限制电流
   ↓
防止LED电流过大
   ↓
保护LED灯珠

四、开发环境搭建

今天使用 Visual Studio Code 进行代码编写。

1. 安装VS Code

VS Code 可以用于:

  • 编写 ARM 汇编程序;

  • 编写启动代码;

  • 编写 Makefile;

  • 查看程序源码。

为了方便编写汇编代码,可以安装:

复制代码
Arm Assembly

插件,实现 ARM 汇编代码高亮。


2. 创建工程目录

创建 ARM 裸机工程:

复制代码
D:\IMX6ULL\led_asm

工程主要用于完成 LED 点亮实验。


五、启动代码start.S

ARM 裸机程序启动时,需要首先执行启动代码。

创建:

复制代码
start.S

这里需要注意:

复制代码
.S

是大写的 S

大写 .S 文件会经过预处理,因此后续可以使用一些宏定义等功能。


1. 设置程序入口

启动代码中使用:

复制代码
.global _start

_start 声明为全局符号。

因此:

复制代码
_start
   ↓
程序启动入口

处理器启动后从程序入口开始执行。


六、i.MX6ULL内存空间

当前学习中涉及的 ROM 地址空间:

复制代码
0x8000 0000

大小:

复制代码
512MB

可以理解为:

复制代码
ROM起始地址
      ↓
0x8000 0000
      ↓
程序运行空间

裸机程序最终需要被放置到处理器能够访问的指定地址空间中。


七、CPS指令

启动代码中需要设置 ARM 处理器的工作状态,因此学习了 CPS 指令。

基本格式:

复制代码
CPS<effect> <iflags>{, #<mode>}

也可以写成:

复制代码
CPS #<mode>

1. 禁止中断

例如:

复制代码
cpsid i

其中:

复制代码
i → IRQ中断
f → FIQ中断
a → 异常相关控制

cpsid i 可以用于禁止 IRQ。


2. 设置处理器模式

例如:

复制代码
cps #0x12

以及:

复制代码
cps #0x1F

通过修改处理器的工作模式,可以为不同模式设置相应的运行环境。


3. 设置栈指针

例如:

复制代码
ldr sp, =0x820000000

以及:

复制代码
ldr sp, =0x840000000

通过 LDR 将指定地址加载到 SP,完成栈指针初始化。


4. 开启中断

例如:

复制代码
cpsie i

表示重新允许 IRQ。

因此,启动代码中可以形成:

复制代码
禁止中断
   ↓
设置工作模式
   ↓
初始化SP
   ↓
完成系统基本初始化
   ↓
开启中断

八、点亮LED需要完成哪些工作

要通过程序控制 i.MX6ULL 的 LED,不能直接对 LED 写数据。

首先需要对 GPIO 对应的 SOC 引脚进行配置。

主要包括三个方面:

复制代码
GPIO引脚初始化
│
├── ① 复用功能配置
├── ② 电气特性配置
└── ③ GPIO方向配置

之后才能通过 GPIO 数据寄存器控制 LED。


九、IOMUXC复用功能配置

i.MX6ULL 的一个引脚可能具有多个功能。

因此使用 GPIO 前,需要首先设置该引脚的复用功能。

对应模块:

复制代码
IOMUXC

即:

IOMUX Controller

LED 使用的引脚为:

复制代码
GPIO1_IO03

对应的复用寄存器:

复制代码
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03

需要设置该寄存器的低四位:

复制代码
0101

通过复用功能配置,将对应引脚设置为 GPIO 功能。


十、电气特性配置

除了选择引脚功能之外,还需要设置引脚的电气特性。

对应寄存器:

复制代码
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03

该寄存器用于配置 GPIO 引脚相关的电气属性。

因此 GPIO 初始化不仅包括:

复制代码
功能复用

还包括:

复制代码
电气特性

十一、GPIO方向配置

完成引脚复用和电气特性配置后,还需要设置 GPIO 的输入输出方向。

对应模块:

复制代码
GPIO

方向寄存器:

复制代码
GPIOx_GDIR

主要作用:

复制代码
设置GPIO为输入
或者
设置GPIO为输出

LED 需要由处理器输出控制信号,因此需要将对应 GPIO 设置为输出。


十二、GPIO数据寄存器

完成 GPIO 初始化后,程序运行过程中通过 GPIO 数据寄存器控制 LED。

对应寄存器:

复制代码
GPIOx_DR

即:

GPIO Data Register

因此:

复制代码
GPIOx_GDIR
    ↓
设置GPIO输入/输出方向

GPIOx_DR
    ↓
运行过程中控制GPIO输出数据

十三、LED控制的整体流程

今天的 LED 实验可以总结为:

复制代码
处理器启动
    ↓
执行start.S
    ↓
初始化GPIO引脚
    ↓
设置IOMUXC复用功能
    ↓
设置引脚电气特性
    ↓
设置GPIO方向
    ↓
设置GPIO数据
    ↓
LED亮/灭

因此,对一个 SOC 外设进行操作,首先需要找到对应的:

复制代码
1. 引脚
2. 复用寄存器
3. 电气特性寄存器
4. GPIO方向寄存器
5. GPIO数据寄存器

十四、LDR指令访问外设寄存器

ARM 汇编中可以使用 LDR 获取寄存器地址或读取内存中的数据。

常见形式:

复制代码
LDR Rt, [Rn]

还可以带有立即数偏移:

复制代码
LDR Rt, [Rn, #+/-imm12]

例如:

复制代码
LDR r0, =0x020C4068

表示将:

复制代码
0x020C4068

加载到 r0


十五、STR指令修改外设寄存器

STR 用于将寄存器中的数据写入内存地址。

例如:

复制代码
ldr r0, =0x020C4068
ldr r1, =0xFFFFFFFF
str r1, [r0]

执行过程:

复制代码
r0 = 0x020C4068
       ↓
作为目标地址

r1 = 0xFFFFFFFF
       ↓
作为写入数据

str r1, [r0]
       ↓
将0xFFFFFFFF写入0x020C4068

因此:

复制代码
LDR → 读取/加载
STR → 写入/存储

对于 SOC 外设来说,本质上就是通过这些寄存器访问指令修改外设寄存器。


十六、ARM交叉编译器

由于开发环境通常是:

复制代码
Ubuntu PC

而最终程序运行在:

复制代码
ARM Cortex-A7

所以需要使用 ARM 交叉编译器。

使用:

复制代码
arm-linux-gnueabihf-gcc

进行 ARM 程序编译。


十七、安装ARM交叉编译器

交叉编译器:

复制代码
gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

安装到:

复制代码
/usr/local/arm

解压:

复制代码
sudo tar -xvf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

删除压缩包:

复制代码
sudo rm gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

十八、配置环境变量

进入用户主目录:

复制代码
vi .bashrc

添加:

复制代码
export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/

之后重新启动虚拟机:

复制代码
reboot

检查交叉编译器:

复制代码
arm-linux-gnueabihf-gcc -v

如果能够正常显示版本信息,说明交叉编译器配置成功。


十九、FileZilla文件传输工具

还学习了 FileZilla Client。

FileZilla 是 Windows 下的 FTP 文件传输工具,可以用于:

复制代码
Windows
   ↕
Ubuntu

之间传输代码文件。

如果需要手动配置 Ubuntu FTP 服务,可以安装:

复制代码
sudo apt-get install vsftpd

修改:

复制代码
/etc/vsftpd.conf

确保:

复制代码
local_enable=YES
write_enable=YES

然后重启服务:

复制代码
sudo /etc/init.d/vsftpd restart

查看服务状态:

复制代码
sudo /etc/init.d/vsftpd status

二十、ARM裸机程序编译流程

今天学习了 ARM 裸机程序从源代码到最终二进制文件的完整过程。

整体流程:

复制代码
start.S
  ↓
gcc
  ↓
start.o
  ↓
ld
  ↓
start.elf
  ↓
objcopy
  ↓
start.bin
  ↓
烧写到SD卡
  ↓
开发板运行

二十一、使用gcc进行汇编

命令:

复制代码
arm-linux-gnueabihf-gcc -c start.S -o start.o -g

其中:

复制代码
-c

表示只进行编译/汇编,不进行链接。

生成:

复制代码
start.o

二十二、使用ld进行链接

命令:

复制代码
arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf

作用是:

复制代码
start.o
  ↓
链接
  ↓
start.elf

其中:

复制代码
-Ttext 0x87800000

用于指定代码段的链接地址。


二十三、使用objcopy进行格式转换

命令:

复制代码
arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin

作用:

复制代码
start.elf
    ↓
格式转换
    ↓
start.bin

其中:

复制代码
-O binary

表示输出为二进制格式。

start.bin 是后续烧写到 SD 卡中的主要程序文件。


二十四、使用objdump反汇编

命令:

复制代码
arm-linux-gnueabihf-objdump -D start.elf > start.dis

作用:

复制代码
start.elf
   ↓
反汇编
   ↓
start.dis

反汇编主要用于:

复制代码
查看程序中的机器指令
分析程序执行过程
检查编译、链接结果

它不是烧写程序运行所必须的步骤,主要用于分析和阅读程序。


二十五、编译工具总结

今天接触了四个重要工具:

工具 作用 主要输出
gcc 编译/汇编 .o
ld 链接 .elf
objcopy 格式转换 .bin
objdump 反汇编 .dis

完整过程:

复制代码
.S
 ↓ gcc
.o
 ↓ ld
.elf
 ↓ objcopy
.bin

二十六、将程序烧写到TF卡

首先将 SD 卡连接到 Ubuntu。

查看设备:

复制代码
ls /dev/sdb

确认 SD 卡设备为:

复制代码
/dev/sdb

然后将下载工具:

复制代码
imxdownload

拷贝到 Ubuntu 工程目录。

修改执行权限:

复制代码
chmod +777 imxdownload

然后执行:

复制代码
./imxdownload start.bin /dev/sdb

将程序烧写到 SD 卡。


二十七、程序烧写注意事项

烧写过程中需要注意烧写速度。

如果烧写速度出现异常,例如速度过快导致烧写失败,需要:

复制代码
拔掉USB读卡器
    ↓
重新启动Ubuntu
    ↓
重新连接SD卡
    ↓
再次烧写

二十八、开发板启动方式

i.MX6ULL 支持多种启动方式。

例如:

复制代码
USB启动
SD卡启动
板载eMMC启动
NAND Flash启动

开发板通过 BOOT 模块的拨码开关选择启动方式。

本次 LED 实验选择:

复制代码
SD卡启动

二十九、LED实验测试流程

烧写完成后:

复制代码
1. 选择SD卡启动模式
       ↓
2. 插入TF卡
       ↓
3. 给开发板上电
       ↓
4. 观察蓝色电源指示灯
       ↓
5. 观察红色LED

如果程序正确运行,就可以观察到 LED 的控制效果。


三十、LED闪烁练习

在完成 LED 点亮之后,可以进一步修改汇编程序,实现 LED 闪烁。

基本思路:

复制代码
LED亮
 ↓
延时
 ↓
LED灭
 ↓
延时
 ↓
重新循环

程序结构:

复制代码
初始化GPIO
    ↓
设置LED亮
    ↓
延时函数
    ↓
设置LED灭
    ↓
延时函数
    ↓
循环

因此,LED 闪烁实际上是在 LED 亮灭控制的基础上增加了一个延时函数。


三十一、Makefile

如果每次都手动输入:

复制代码
gcc ...
ld ...
objcopy ...
objdump ...

会比较繁琐。

因此今天学习了使用 Makefile 简化编译过程。

Makefile 可以自动完成:

复制代码
编译
 ↓
链接
 ↓
格式转换
 ↓
反汇编

三十二、Makefile三要素

Makefile 最基本的结构:

复制代码
目标:依赖
    规则

例如:

复制代码
start.bin : start.S
	arm-linux-gnueabihf-gcc -c start.S -o start.o -g
	arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf
	arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin
	arm-linux-gnueabihf-objdump -D start.elf > start.dis

这里:

复制代码
目标:
start.bin

依赖:
start.S

规则:
gcc
ld
objcopy
objdump

三十三、clean伪目标

Makefile 中可以定义:

复制代码
clean:
	rm start.o start.elf start.bin start.dis -f

执行:

复制代码
make clean

即可删除编译生成的文件。

包括:

复制代码
start.o
start.elf
start.bin
start.dis

这样就可以快速清理工程。


三十四、load伪目标

还可以定义:

复制代码
load:
	./imxdownload start.bin /dev/sdb

这样执行:

复制代码
make load

就可以自动执行:

复制代码
./imxdownload start.bin /dev/sdb

完成程序烧写。


三十五、使用变量优化Makefile

为了避免重复书写:

复制代码
arm-linux-gnueabihf-

可以使用变量。

例如:

复制代码
COMPLITER = arm-linux-gnueabihf-

CC = $(COMPLITER)gcc
LD = $(COMPLITER)ld
OBJCOPY = $(COMPLITER)objcopy
OBJDUMP = $(COMPLITER)objdump

之后使用:

复制代码
start.bin : start.S
	$(CC) -c start.S -o start.o -g
	$(LD) -Ttext 0x87800000 start.o -o start.elf
	$(OBJCOPY) -O binary -S -g start.elf start.bin
	$(OBJDUMP) -D start.elf > start.dis

这样可以使 Makefile 更简洁,也方便以后更换编译工具。


三十六、今天的完整开发流程

今天学习的整个过程可以总结成:

复制代码
① 创建工程
      ↓
② 编写start.S
      ↓
③ 初始化ARM启动环境
      ↓
④ 配置IOMUXC
      ↓
⑤ 配置GPIO方向
      ↓
⑥ 配置GPIO数据
      ↓
⑦ 完成LED控制
      ↓
⑧ 使用交叉编译器
      ↓
⑨ 生成start.o
      ↓
⑩ 链接生成start.elf
      ↓
⑪ 转换生成start.bin
      ↓
⑫ 反汇编生成start.dis
      ↓
⑬ 烧写start.bin到SD卡
      ↓
⑭ 开发板SD卡启动
      ↓
⑮ 观察LED运行结果
相关推荐
Awh-4 小时前
ARM 裸机开发 day02 学习笔记
linux·arm开发·arm
M78佐菲5 小时前
ARM学习笔记(四)
linux·arm开发·笔记·嵌入式硬件·学习
昌原的儿子LEO5 小时前
i.MX6ULL 裸机开发 C 语言驱动与 BSP 工程管理学习总结
linux·arm开发·学习
AAA代码批发商1 天前
Days 46 i.MX6ULL 裸机开发|从内核切入外设,汇编点亮 LED(补充)
arm开发·嵌入式·裸机开发
xx~t1 天前
嵌入式——ARM——汇编2
linux·汇编·arm开发·嵌入式硬件·启动代码
M78佐菲1 天前
ARM学习笔记(三)
linux·arm开发·笔记·嵌入式硬件·学习
xx~t2 天前
嵌入式学习——ARM1
linux·arm开发·嵌入式硬件·arm
xx~t2 天前
嵌入式——ARM1(详细篇)
linux·arm开发·嵌入式硬件·arm
Doraemomo2 天前
IMX6ULL裸机开发——I2C通信协议
arm开发·嵌入式硬件·imx6ull