目录
一、开发环境与硬件基础
1.1 IMX6ULL-Mini 开发板介绍
1.2.1 编译工具:gcc-linaro 交叉编译器
1.2.2 代码编辑:Visual Studio Code
1.2.3 辅助工具
二、LED 点亮的底层逻辑
2.1 引脚配置三步骤
2.1.1 复用功能配置(IOMUXC)
2.1.2 电气特性配置(IOMUXC)
2.1.3 引脚方向配置(GPIO)
2.2 LED 开关逻辑
2.3 LDR 与 STR 详解
三、汇编代码实现 LED 闪烁
四、编译与烧写
4.1 手动编译步骤
4.2 Makefile 简化编译
4.3 程序烧写与测试
4.3.1 SD 卡烧写
4.3.2 开发板测试
五、核心概念解答
一、开发环境与硬件基础
1.1 IMX6ULL-Mini 开发板介绍
IMX6ULL-Mini 开发板是由正点原子提供的, 搭载NXP生产i.MX6ULL的SOC, 主打高性能、低功耗与紧凑体积。整个开发板由核心板与底板组成,核心板通过双列直插方式与底板连接。
- 核心板:六层板
- CPU:NXP i.MX 6ULL Cortex-A7 单核处理器,主频 528MHz(工业级) 或 800MHz(商业级)467, BGA 封装
- 内存:512MB DDR3L RAM,支持高速数据存取。
- 存储:8GB eMMC,支持多种启动模式(SD卡、NAND、eMMC)
- 屏幕:4.3寸屏 800*480分辨率
- 底板
- 红色 led 灯提供用户可控的的 led 灯
- 蓝色 led 灯是电源指示灯
- 510 欧姆限流电阻作用防止电流过大保护灯珠。
1.2.1 编译工具:gcc-linaro 交叉编译器
- 拷贝 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
1.2.2 代码编辑:Visual Studio Code
- 安装Visual Studio Code
- 汇编高亮插件:Arm Assembly
- 创建工程文件夹 \IMX6ULL\led_asm
- 创建启动代码 start.S(注意大写S后缀,会进行预处理,后期用到一些宏)
1.2.3 辅助工具
- 烧写工具:imxdownload(IMX6ULL 专用 SD 卡烧写工具)
- 串口工具(可选):用于调试输出,本次实验暂不涉及
二、LED 点亮的底层逻辑
IMX6ULL 的 LED 控制本质是GPIO 外设寄存器操作,需完成 3 步初始化 + 1 步运行时控制,核心依赖汇编的 LDR(读取寄存器)和 STR(写入寄存器)指令。
2.1 引脚配置三步骤
参考 IMX6ULL 参考手册和底板原理图,本次控制GPIO1_IO03 引脚(对应红色 LED):
2.1.1 复用功能配置(IOMUXC)
GPIO 引脚默认可能不是 GPIO 功能,需通过复用寄存器配置为 GPIO 模式:
- 寄存器地址:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 0x020C4068
- 配置值:低 4 位设为 0101(对应 GPIO 功能)
2.1.2 电气特性配置(IOMUXC)
设置引脚的驱动能力、上下拉等电气参数,避免信号不稳定:
- 寄存器地址:IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03(需查阅手册确认具体地址)
- 核心配置:根据 LED 驱动需求设置(如默认配置可满足基础点灯)
2.1.3 引脚方向配置(GPIO)
设置 GPIO 为输出模式(LED 需要主动输出高低电平):
- 寄存器地址:GPIO1_GDIR(GPIO1 端口方向寄存器)
- 配置逻辑:对应位设为 1(输出),0 为输入;GPIO1_IO03 对应第 3 位,故设置GPIO1_GDIR |= (1<<3)
2.2 LED 开关逻辑
LED 灯珠采用低电平点亮(原理图设计),通过 GPIO 数据寄存器控制:
- 寄存器地址:GPIO1_DR(GPIO1 端口数据寄存器)
- 点亮:对应位设为 0(GPIO1_DR &= ~(1<<3))
- 熄灭:对应位设为 1(GPIO1_DR |= (1<<3))
2.3 LDR 与 STR 详解
| 指令 | 功能 | 示例 |
|---|---|---|
| LDR <Rt>, =<addr> | 将寄存器地址加载到目标寄存器 Rt | ldr r0, =0x020C4068(r0 = 复用寄存器地址) |
| STR <Rt>, [<Rn>] | 将 Rt 的值写入 Rn 指向的内存地址(寄存器) | str r1, [r0](将 r1 的值写入 r0 指向的复用寄存器) |
三、汇编代码实现 LED 闪烁
要实现闪烁,需在 "点亮 - 延时 - 熄灭 - 延时" 之间循环,延时通过汇编空指令实现(利用 CPU 执行指令的时间差):
.global _start ; ============================================== ; ARM异常向量表 (Exception Vector Table) ; ARM架构规定从0x00000000开始的32字节为异常向量表 ; 每个异常占4字节,依次对应8种异常类型 ; ============================================== _start: ldr pc, =_reset_handler ldr pc, =_undef_handler ldr pc, =_software_handler ldr pc, =_prefect_handler ldr pc, =_data_abort_handler nop ldr pc, =_irq_handler ldr pc, =_fiq_handler ; ============================================== ; 异常处理函数(默认原地死循环,防止异常逃逸) ; 新手阶段暂不处理具体异常,仅做兜底 ; ============================================== _undef_handler: b _undef_handler _software_handler: b _software_handler _prefect_handler: b _prefect_handler _data_abort_handler: b _data_abort_handler _irq_handler: b _irq_handler _fiq_handler: b _fiq_handler ; ============================================== ; 复位处理函数(系统上电/复位后第一个执行的核心函数) ; ============================================== _reset_handler: cpsid i ; 失能irq cps #0x12 ldr sp, =0x82000000 cps #0x1F ldr sp, =0x84000000 cpsie i ; 使能irq bl led_init ; ============================================== ; 主循环:LED闪烁逻辑 ; ============================================== finish: bl led_on bl led_delay bl led_off bl led_delay b finish ; ============================================== ; LED初始化函数:配置GPIO引脚(以IMX6ULL为例) ; 步骤:1.复用功能 2.配置电气特性 3.设置引脚为输出 ; ============================================== led_init: ; 复用功能 ldr r0, =0x020E0068 ldr r1, [r0] bic r1, r1, #0x1F orr r1, r1, #0x05 str r1, [r0] ; 电气特性 ldr r0, =0x020E02F4 ldr r2, =0x10B0 str r2, [r0] ; 引脚方向 ldr r0, =0x0209C004 ldr r1, [r0] orr r1, r1, #(1 << 3) str r1, [r0] bx lr ; ============================================== ; LED点亮函数:拉低GPIO1_IO03引脚(低电平点亮) ; ============================================== led_on: ldr r0, =0x0209C000 ldr r1, [r0] bic r1, r1, #(1 << 3) str r1, [r0] bx lr ; ============================================== ; LED熄灭函数:拉高GPIO1_IO03引脚(高电平熄灭) ; ============================================== led_off: ldr r0, =0x0209C000 ldr r1, [r0] orr r1, r1, #(1 << 3) str r1, [r0] bx lr ; ============================================== ; 简单延时函数:通过循环递减实现延时 ; ============================================== led_delay: ldr r0, =0x7FFFF loop: cmp r0, #0 ble fin sub r0, r0, #1 b loop fin: bx lr四、编译与烧写
4.1 手动编译步骤
执行以下命令:
(1)汇编:将.S 文件转为目标文件(.o)
arm-linux-gnueabihf-gcc -c start.S -o start.o -g- -c:只汇编不链接
- -o:指定输出文件
- -g:保留调试信息
(2)链接:将目标文件链接到指定地址(IMX6ULL 的 SD 卡启动地址为 0x87800000)
arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf- -Ttext:指定代码段起始地址
(3)格式转换:将 ELF 文件转为开发板可执行的二进制文件(.bin)
arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin- -O binary:输出二进制格式
- -S:去除符号信息
- -g:去除调试信息
(4)反汇编(可选):将 ELF 文件转为汇编代码(用于调试)
arm-linux-gnueabihf-objdump -D start.elf > start.dis4.2 Makefile 简化编译
# 定义工具链变量,简化后续命令 COMPILER = arm-linux-gnueabihf- CC = $(COMPILER)gcc LD = $(COMPILER)ld OBJCOPY = $(COMPILER)objcopy OBJDUMP = $(COMPILER)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 -f start.o start.elf start.bin start.dis # 伪目标:烧写程序到SD卡 load: ./imxdownload start.bin /dev/sdb使用方法:
- 编译:make(一键生成 start.bin)
- 清理:make clean(删除所有编译产物)
- 烧写:make load(需提前将 imxdownload 工具拷贝到工程目录并添加执行权限:chmod +777 imxdownload)
4.3 程序烧写与测试
4.3.1 SD 卡烧写
- 将 SD 卡插入 Ubuntu 虚拟机(通过 USB 读卡器)
- 查看 SD 卡设备名:ls /dev/sdb(通常为 sdb,需确认无其他存储设备冲突)
- 执行烧写命令:./imxdownload start.bin /dev/sdb
注意:烧写速率需为 KB 级,若显示 MB 级速率则失败,需拔掉读卡器重启 Ubuntu 后重试
4.3.2 开发板测试
- 选择启动模式:通过开发板上的拨码开关选择 SD 卡启动(参考开发板原理图)
- 插入烧写好的 SD 卡到开发板 TF 卡槽
- 上电测试:
- 确保开发板电源开关弹起,插入电源适配器
- 按下蓝色电源开关,观察蓝色电源灯亮起
- 红色 LED 周期性闪烁
五、核心概念解答
1. 在 LED 实验中,在对 SOC 引脚配置时都做了哪些工作?
- 复用功能配置:将引脚从默认功能切换为 GPIO 功能;
- 电气特性配置:设置引脚驱动能力、上下拉等,保证信号稳定;
- GPIO 方向配置:设置为输出模式,使引脚能主动输出高低电平。
2. 什么是编译器、连接器、格式转换器和反汇编器?
- 编译器(gcc):将汇编源文件(.S)转为目标文件(.o),进行语法检查和指令翻译;
- 链接器(ld):将目标文件链接到指定内存地址,解决符号引用(如程序入口 _start);
- 格式转换器(objcopy):将 ELF 文件(包含调试 / 符号信息)转为开发板可直接执行的二进制文件(.bin);
- 反汇编器(objdump):将 ELF 文件转回汇编代码,用于调试(验证指令是否正确翻译)。