一、开发板介绍:正点原子 IMX6ULL-Mini
IMX6ULL-Mini 是正点原子推出的基于 NXP i.MX6ULL SOC 的嵌入式开发板,主打低功耗,结构分为核心板 + 底板,核心板采用双列直插和底板相连,方便替换。
1. 核心板(六层 PCB)
- CPU:NXP i.MX6ULL,Cortex‑A7 单核处理器,BGA 封装;工业级主频 528MHz,商业级 800MHz
- 内存:512MB DDR3L RAM,提供高速数据访问
- 存储:8GB eMMC,支持 SD 卡、NAND、eMMC 多种启动方式
- 屏幕:支持 4.3 寸屏幕,分辨率 800*480
概念区分(必记) Cortex-A7:ARM 设计的 CPU 内核 i.MX6ULL:NXP 出品的 SOC(片上系统,内核 + 外设集成在一块芯片) IMX6ULL-Mini:正点原子整套开发板(核心板 + 底板)
2. 底板(LED 模块)
本次实验使用底板 LED 电路:
- 红色 LED:用户可控 LED,本次实验的控制对象
- 蓝色 LED:电源指示灯,上电常亮,不可软件控制
- 510Ω 限流电阻:串联在 LED 回路,限制电流大小,防止电流过大烧毁灯珠
二、开发环境搭建
2.1 VS Code 代码编辑器
- 安装 Visual Studio Code
- 安装插件:Arm Assembly,提供 ARM 汇编语法高亮
- 创建工程目录:
D:\IMX6ULL\led_asm - 创建启动文件:
start.S
⚠️重点:后缀大写
.S,编译时会执行 C 预处理器,可以使用宏;小写.s不会预处理。裸机启动代码一般使用.S。
2.2 交叉编译器安装(Ubuntu)
我们在 Ubuntu 虚拟机编译 ARM 平台的程序,需要交叉编译器gcc-linaro-4.9.4,安装步骤:
-
将压缩包
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
-
配置环境变量,修改家目录下
.bashrcvi ~/.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
2.3 FileZilla(可选,Windows 与 Ubuntu 传文件)
FileZilla Client 是 Windows 免费 FTP 工具,用于 Windows 和 Ubuntu 虚拟机互传代码文件。
-
Ubuntu 安装 vsftpd 服务
sudo apt-get install vsftpd
-
修改 ftp 配置文件
/etc/vsftpd.conf取消注释,保证两行生效:local_enable=YES
write_enable=YES -
重启 ftp 服务:
sudo /etc/init.d/vsftpd restart
-
查看 ftp 服务状态:
sudo /etc/init.d/vsftpd status
绿灯代表服务正常运行
- Windows 安装 FileZilla Client,新建站点
- 主机:Ubuntu 的 ens33 IP:
192.168.71.134 - 用户:
linux - 密码:
1连接后,左侧为 Ubuntu 远程目录,右侧是 Windows 本地目录,右键上传工程文件夹。
三、汇编启动代码基础
裸机上电,SOC 内置 ROM 会读取程序,从_start标签开始执行。ROM 起始地址:0x80000000,地址空间大小 512MB。
3.1 .global _start
.global _start 将_start声明为全局符号,作为程序入口,链接器识别该标签为程序启动地址。
3.2 CPS 指令:切换处理器模式,开关中断
CPS 全称 Change Processor State,用于修改 CPSR 程序状态寄存器,完成 CPU 模式切换、中断开关控制。 语法格式:
CPS<effect> <iflags>{, #<mode>}
CPS #<mode>
示例代码:
cpsid i ; 关闭IRQ中断,i=IRQ,f=FIQ,a=Abort
cps #0x12 ; 切换到IRQ模式
ldr sp, =0x82000000 ; 设置IRQ模式栈指针
cps #0x1F ; 切换到系统模式
ldr sp, =0x84000000 ; 设置系统模式栈指针
cpsie i ; 开启IRQ中断
裸机启动流程要点:上电后切换不同处理器模式,为每种模式单独分配栈空间,为后续 C 语言代码运行做准备。
四、LED 点亮原理:操作外设寄存器
点亮 LED 本质就是配置 SOC 内部外设寄存器,需要两份参考手册:
- IMX6ULL_MINI_V2.2 (Mini 底板原理图).pdf:查看硬件电路
- IMX6ULL 参考手册.pdf:查找外设寄存器地址、位定义
引脚配置完整工作
- 引脚复用配置(IOMUXC) 章节:Chapter32 IOMUX Controller(IOMUXC) 寄存器:
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03将寄存器低四位设置为0101,把引脚复用为 GPIO 功能,而不是 UART、SPI 等其他外设。 - PAD 电气属性配置(IOMUXC) 寄存器:
IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03配置引脚的上下拉、驱动能力、信号速率、滞回等电气特性。 - GPIO 方向寄存器 GDIR 章节:Chapter28 GPIO 模块
GPIOx_GDIR:GPIO 方向寄存器。对应 bit 置 1 为输出模式,置 0 为输入模式。控制 LED 需要设置为输出。 - GPIO 数据寄存器 DR
GPIOx_DR:GPIO 数据寄存器。对应 bit 写 1 输出高电平,写 0 输出低电平,控制 LED 亮灭。
ARM 汇编 LDR / STR:访问外设寄存器
i.MX6ULL 外设寄存器采用内存映射方式,把寄存器映射到内存地址空间。我们使用LDR和STR读写寄存器,这是裸机操作外设的核心指令。
- LDR:加载,把内存 / 寄存器地址的数据读到 CPU 通用寄存器
- STR:存储,把 CPU 通用寄存器的数据写入内存 / 外设寄存器地址
LDR、STR 常用指令格式
LDR<c> <Rt>, [<Rn>{, #+/-<imm12>}]
LDR<c> <Rt>, [<Rn>], #+/-<imm12>]
LDR<c> <Rt>, [<Rn>, #+/-<imm12>]!
LDR<c> <Rt>, <label>
LDR<c> <Rt>, [PC, #-0]
LDR<c> <Rt>, [<Rn>,+/-<Rm>{, <shift>}]{!}
LDR<c> <Rt>, [<Rn>],+/-<Rm>{, <shift>}
STR<c> <Rt>, [<Rn>{, #+/-<imm12>}]
STR<c> <Rt>, [<Rn>], #+/-<imm12>]
STR<c> <Rt>, [<Rn>, #+/-<imm12>]!
STR<c> <Rt>, [<Rn>,+/-<Rm>{, <shift>}]{!}
STR<c> <Rt>, [<Rn>],+/-<Rm>{, <shift>}
STRB<c> <Rt>, [<Rn>{, #+/-<imm12>}]
STRB<c> <Rt>, [<Rn>], #+/-<imm12>]
STRB<c> <Rt>, [<Rn>, #+/-<imm12>]!
示例代码:往指定寄存器地址写入全 1
ldr r0, =0x020C4068 ; 将寄存器地址加载到r0
ldr r1, =0xFFFFFFFF ; 准备写入的值存入r1
str r1, [r0] ; 将r1的值写入r0指向的外设寄存器
五、交叉编译全套流程
5.1 编译分步命令
-
编译器(arm-linux-gnueabihf-gcc):汇编,不链接 将
.S汇编源码编译成目标文件.oarm-linux-gnueabihf-gcc -c start.S -o start.o -g
-
链接器(arm-linux-gnueabihf-ld):链接 将目标文件链接,指定程序运行地址,生成 elf 可执行文件
arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf
-
格式转换器(arm-linux-gnueabihf-objcopy):elf 转 bin 剥离符号、调试信息,转换成裸机可以直接运行的二进制文件
arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin
-
反汇编器(arm-linux-gnueabihf-objdump):反汇编 把 elf 文件反汇编,生成 dis 汇编清单,方便代码查看调试,属于可选步骤
arm-linux-gnueabihf-objdump -D start.elf > start.dis
概念解释:
- 编译器:把汇编 / C 源码翻译成目标文件 (.o)
- 链接器:合并多个目标文件,指定链接地址,生成 elf 文件
- 格式转换器 objcopy:转换文件格式,去除多余信息,得到纯二进制 bin 文件(裸机运行)
- 反汇编器 objdump:将 elf 程序反向翻译成汇编代码,用于代码分析调试
5.2 Makefile 简化编译
手动敲命令很麻烦,使用 Makefile 自动化编译,使用变量优化。
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
# 伪目标:清理编译产物
clean:
rm start.o start.elf start.bin start.dis -f
# 伪目标:一键烧录
load:
./imxdownload start.bin /dev/sdb
make:执行编译,生成 start.binmake clean:清理所有编译生成文件make load:一键烧录 bin 文件到 SD 卡
六、SD 卡烧录与开发板测试
-
将 SD 卡读卡器接入 Ubuntu 虚拟机,查看设备号:
ls /dev/sdb -
把 imxdownload 工具拷贝到工程目录,添加执行权限
chmod +777 imxdownload
-
烧写程序到 SD 卡
./imxdownload start.bin /dev/sdb
⚠️注意:如果烧录速度为 MB/s 级别代表烧录失败,需要拔掉读卡器重启 Ubuntu 重试。
- 开发板拨码设置为 SD 卡启动模式
- SD 卡插入开发板 TF 卡槽,上电
- 观察底板红色 LED 灯是否点亮
课后练习:修改汇编代码,增加软件延时函数,实现 LED 闪烁效果。
七、课后思考题
1、在 LED 实验中,在对 Soc 引脚配置时都做了哪些工作?
- 配置 IOMUXC 复用寄存器,将引脚设置为 GPIO 功能,解除其他外设复用;
- 配置 PAD 电气寄存器,设置引脚上下拉、驱动能力、电气参数;
- 配置 GPIO 方向寄存器 GDIR,将 GPIO 引脚设置为输出模式;
- 配置 GPIO 数据寄存器 DR,输出高低电平,控制 LED 亮灭。
2、什么是编译器、连接器、格式转换器和反汇编器?
- 编译器 (gcc) :读取汇编或者 C 源码,翻译成机器码,生成目标文件
.o,只编译不链接。 - 链接器 (ld):将多个目标文件合并,分配程序运行地址,生成 ELF 格式可执行文件。
- 格式转换器 (objcopy):对 ELF 文件进行格式转换,剔除符号表、调试信息,生成裸机直接运行的二进制 bin 文件。
- 反汇编器 (objdump):读取 ELF 文件,把机器码反向还原成汇编代码,方便开发者查看、调试程序。
八、学习总结
- i.MX6ULL SOC 集成 Cortex‑A7 内核与各类外设,裸机开发需要手动配置外设寄存器,没有操作系统帮忙初始化硬件。
- 裸机启动从汇编开始:切换处理器模式、设置栈、控制中断。
- GPIO 外设操作四步:引脚复用配置 → PAD 电气配置 → 设置 GPIO 方向 → 读写 DR 寄存器输出电平。
- 交叉编译工具链分为 gcc、ld、objcopy、objdump,各司其职,Makefile 可以简化重复编译烧录工作。
- 裸机程序烧录到 SD 卡,通过拨码开关选择 SD 卡启动,上电即可运行我们写的汇编代码。
后续计划:修改汇编代码,添加延时,实现 LED 闪烁实验,学习循环汇编指令。
结尾
裸机开发是打通内核和外设之间壁垒的关键。只有亲自操作寄存器,才能真正理解 Linux 驱动框架底层到底做了哪些硬件初始化。 文章如果对你有帮助,欢迎点赞收藏,后续持续更新 i.MX6ULL 裸机系列。