Days 46 i.MX6ULL 裸机开发|从内核切入外设,汇编点亮 LED(补充)

一、开发板介绍:正点原子 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 代码编辑器

  1. 安装 Visual Studio Code
  2. 安装插件:Arm Assembly,提供 ARM 汇编语法高亮
  3. 创建工程目录:D:\IMX6ULL\led_asm
  4. 创建启动文件:start.S

⚠️重点:后缀大写.S,编译时会执行 C 预处理器,可以使用宏;小写.s不会预处理。裸机启动代码一般使用.S

2.2 交叉编译器安装(Ubuntu)

我们在 Ubuntu 虚拟机编译 ARM 平台的程序,需要交叉编译器gcc-linaro-4.9.4,安装步骤:

  1. 将压缩包 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz 拷贝到 /usr/local/arm

  2. 解压:

    sudo tar -xvf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

  3. 删除压缩包节省空间:

    sudo rm gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

  4. 配置环境变量,修改家目录下.bashrc

    vi ~/.bashrc

    在文件末尾添加

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

  5. 重启虚拟机生效:reboot

  6. 验证编译器是否安装成功:

    arm-linux-gnueabihf-gcc -v

2.3 FileZilla(可选,Windows 与 Ubuntu 传文件)

FileZilla Client 是 Windows 免费 FTP 工具,用于 Windows 和 Ubuntu 虚拟机互传代码文件。

  1. Ubuntu 安装 vsftpd 服务

    sudo apt-get install vsftpd

  2. 修改 ftp 配置文件 /etc/vsftpd.conf 取消注释,保证两行生效:

    local_enable=YES
    write_enable=YES

  3. 重启 ftp 服务:

    sudo /etc/init.d/vsftpd restart

  4. 查看 ftp 服务状态:

    sudo /etc/init.d/vsftpd status

绿灯代表服务正常运行

  1. 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 内部外设寄存器,需要两份参考手册:

  1. IMX6ULL_MINI_V2.2 (Mini 底板原理图).pdf:查看硬件电路
  2. IMX6ULL 参考手册.pdf:查找外设寄存器地址、位定义

引脚配置完整工作

  1. 引脚复用配置(IOMUXC) 章节:Chapter32 IOMUX Controller(IOMUXC) 寄存器:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 将寄存器低四位设置为0101,把引脚复用为 GPIO 功能,而不是 UART、SPI 等其他外设。
  2. PAD 电气属性配置(IOMUXC) 寄存器:IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 配置引脚的上下拉、驱动能力、信号速率、滞回等电气特性。
  3. GPIO 方向寄存器 GDIR 章节:Chapter28 GPIO 模块GPIOx_GDIR:GPIO 方向寄存器。对应 bit 置 1 为输出模式,置 0 为输入模式。控制 LED 需要设置为输出。
  4. GPIO 数据寄存器 DR GPIOx_DR:GPIO 数据寄存器。对应 bit 写 1 输出高电平,写 0 输出低电平,控制 LED 亮灭。

ARM 汇编 LDR / STR:访问外设寄存器

i.MX6ULL 外设寄存器采用内存映射方式,把寄存器映射到内存地址空间。我们使用LDRSTR读写寄存器,这是裸机操作外设的核心指令。

  • 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 编译分步命令

  1. 编译器(arm-linux-gnueabihf-gcc):汇编,不链接.S汇编源码编译成目标文件.o

    arm-linux-gnueabihf-gcc -c start.S -o start.o -g

  2. 链接器(arm-linux-gnueabihf-ld):链接 将目标文件链接,指定程序运行地址,生成 elf 可执行文件

    arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf

  3. 格式转换器(arm-linux-gnueabihf-objcopy):elf 转 bin 剥离符号、调试信息,转换成裸机可以直接运行的二进制文件

    arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin

  4. 反汇编器(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.bin
  • make clean:清理所有编译生成文件
  • make load:一键烧录 bin 文件到 SD 卡

六、SD 卡烧录与开发板测试

  1. 将 SD 卡读卡器接入 Ubuntu 虚拟机,查看设备号:ls /dev/sdb

  2. 把 imxdownload 工具拷贝到工程目录,添加执行权限

    chmod +777 imxdownload

  3. 烧写程序到 SD 卡

    ./imxdownload start.bin /dev/sdb

⚠️注意:如果烧录速度为 MB/s 级别代表烧录失败,需要拔掉读卡器重启 Ubuntu 重试。

  1. 开发板拨码设置为 SD 卡启动模式
  2. SD 卡插入开发板 TF 卡槽,上电
  3. 观察底板红色 LED 灯是否点亮

课后练习:修改汇编代码,增加软件延时函数,实现 LED 闪烁效果。

七、课后思考题

1、在 LED 实验中,在对 Soc 引脚配置时都做了哪些工作?

  1. 配置 IOMUXC 复用寄存器,将引脚设置为 GPIO 功能,解除其他外设复用;
  2. 配置 PAD 电气寄存器,设置引脚上下拉、驱动能力、电气参数;
  3. 配置 GPIO 方向寄存器 GDIR,将 GPIO 引脚设置为输出模式;
  4. 配置 GPIO 数据寄存器 DR,输出高低电平,控制 LED 亮灭。

2、什么是编译器、连接器、格式转换器和反汇编器?

  1. 编译器 (gcc) :读取汇编或者 C 源码,翻译成机器码,生成目标文件.o,只编译不链接。
  2. 链接器 (ld):将多个目标文件合并,分配程序运行地址,生成 ELF 格式可执行文件。
  3. 格式转换器 (objcopy):对 ELF 文件进行格式转换,剔除符号表、调试信息,生成裸机直接运行的二进制 bin 文件。
  4. 反汇编器 (objdump):读取 ELF 文件,把机器码反向还原成汇编代码,方便开发者查看、调试程序。

八、学习总结

  1. i.MX6ULL SOC 集成 Cortex‑A7 内核与各类外设,裸机开发需要手动配置外设寄存器,没有操作系统帮忙初始化硬件。
  2. 裸机启动从汇编开始:切换处理器模式、设置栈、控制中断。
  3. GPIO 外设操作四步:引脚复用配置 → PAD 电气配置 → 设置 GPIO 方向 → 读写 DR 寄存器输出电平。
  4. 交叉编译工具链分为 gcc、ld、objcopy、objdump,各司其职,Makefile 可以简化重复编译烧录工作。
  5. 裸机程序烧录到 SD 卡,通过拨码开关选择 SD 卡启动,上电即可运行我们写的汇编代码。

后续计划:修改汇编代码,添加延时,实现 LED 闪烁实验,学习循环汇编指令。

结尾

裸机开发是打通内核和外设之间壁垒的关键。只有亲自操作寄存器,才能真正理解 Linux 驱动框架底层到底做了哪些硬件初始化。 文章如果对你有帮助,欢迎点赞收藏,后续持续更新 i.MX6ULL 裸机系列。

相关推荐
xx~t3 小时前
嵌入式——ARM——汇编2
linux·汇编·arm开发·嵌入式硬件·启动代码
M78佐菲4 小时前
ARM学习笔记(三)
linux·arm开发·笔记·嵌入式硬件·学习
嵌入式阿蔡5 小时前
嵌入式安全认证2026:PSA Certified从Arm主导到全球标准
嵌入式
阿源-14 小时前
PCI 总线协议解析
嵌入式·x86·固件
xx~t16 小时前
嵌入式学习——ARM1
linux·arm开发·嵌入式硬件·arm
xx~t16 小时前
嵌入式——ARM1(详细篇)
linux·arm开发·嵌入式硬件·arm
2301_8150910116 小时前
ARM汇编入门笔记(二):启动代码、指令详解与C语言混合编程
嵌入式·arm汇编
DaTou大头18 小时前
STM32第三篇
单片机·嵌入式·电路基础
Doraemomo1 天前
IMX6ULL裸机开发——I2C通信协议
arm开发·嵌入式硬件·imx6ull