Days 45i.MX6ULL 裸机开发|从内核切入外设,汇编点亮 LED
前言
学习嵌入式 Linux 驱动的时候,经常会接触到外设操作。但如果不了解底层寄存器是如何工作的,写驱动就像 “黑盒调用”。 裸机开发没有操作系统,CPU 上电后直接执行我们写的汇编代码,所有硬件初始化、模式切换、外设控制全部手动实现。 本次实验:正点原子 IMX6ULL-Mini 开发板,汇编启动代码,操作 GPIO 寄存器点亮 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 回路,限制电流大小,防止电流过大烧毁灯珠
二、开发环境搭建
本次裸机汇编代码使用 VS Code 编写:
- 安装 Visual Studio Code
- 安装插件:Arm Assembly,提供 ARM 汇编语法高亮
- 创建工程目录:
D:\IMX6ULL\led_asm - 创建启动文件:
start.S
⚠️重点:后缀大写
.S,编译时会执行 C 预处理器,可以使用宏;小写.s不会预处理。裸机启动代码一般使用.S。
三、汇编启动代码基础
裸机上电,SOC 内置 ROM 会读取程序,从_start标签开始执行。ROM 起始地址:0x80000000,地址空间大小 512MB。
1. .global _start
.global _start 将_start声明为全局符号,作为程序入口,链接器识别该标签为程序启动地址。
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指向的外设寄存器
六、学习总结
- i.MX6ULL SOC 集成 Cortex‑A7 内核与各类外设,裸机开发需要手动配置外设寄存器,没有操作系统帮忙初始化硬件。
- 裸机启动从汇编开始:切换处理器模式、设置栈、控制中断。
- GPIO 外设操作四步:引脚复用配置 → PAD 电气配置 → 设置 GPIO 方向 → 读写 DR 寄存器输出电平。
- LDR/STR 是 ARM 汇编访问内存映射外设寄存器的核心指令,也是 Linux 驱动底层硬件操作的基础。
后续计划:补全完整 start.S 汇编源码,编写 Makefile 工程,使用交叉编译器编译,SD 卡烧录测试,观察 LED 亮灭效果。
结尾
裸机开发是打通内核和外设之间壁垒的关键。只有亲自操作寄存器,才能真正理解 Linux 驱动框架底层到底做了哪些硬件初始化。 文章如果对你有帮助,欢迎点赞收藏,后续持续更新 i.MX6ULL 裸机系列。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)