一、环境搭建代码实践
工具链安装
#!/bin/bash
# 文件名:install_toolchain.sh
# 1. 创建安装目录
sudo mkdir -p /usr/local/arm
# 2. 复制工具链(假设文件在当前目录)
sudo cp gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz /usr/local/arm/
# 3. 进入目录
cd /usr/local/arm
# 4. 解压工具链
sudo tar -xvf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
# 5. 删除压缩包
sudo rm gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
# 6. 配置环境变量
echo 'export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/' >> ~/.bashrc
# 7. 使配置生效
source ~/.bashrc
# 8. 验证安装
arm-linux-gnueabihf-gcc -v
echo "工具链安装完成!"
二、汇编代码实践
完整LED控制程序
/*
* 文件名:start.S
* 功能:控制IMX6ULL开发板的LED灯闪烁
* 作者:[你的名字]
* 日期:[当前日期]
*/
/* 1. 定义全局符号 */
.global _start /* 程序入口点 */
(1)中断向量表 _start: ldr pc, =_reset_handler // 复位中断处理 ldr pc, =_undef_handler // 未定义指令中断 ldr pc, =_software_handler // 软件中断(SWI) ldr pc, =_prefect_handler // 预取中止(指令获取失败) ldr pc, =_data_abort_handler // 数据中止(数据访问失败) nop // 保留位置 ldr pc, =_irq_handler // IRQ(普通中断) ldr pc, =_fiq_handler // FIQ(快速中断)/*
这是 ARM 架构的中断向量表,必须位于内存起始位置(通常是 0x0)
每个表项对应一种异常类型,发生异常时 CPU 会自动跳转到对应地址
nop是保留位置,保持向量表对齐*/
(2)异常处理程序 _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/*
这些是异常处理程序的占位符,实际都是死循环
在完整系统中,需要实现真正的异常处理逻辑
对于简单的 LED 控制程序,不需要处理这些异常
*/
(3)中断和模式设置 _reset_handler: cpsid i // 禁用所有中断(i=IRQ, f=FIQ) cps #0x12 // 切换到 IRQ 模式(模式号为 0x12) ldr sp, =0x82000000 // 设置 IRQ 模式的栈指针 cps #0x1F // 切换到系统(SYS)模式(模式号为 0x1F) ldr sp, =0x84000000 // 设置系统模式的栈指针 cpsie i // 启用 IRQ 中断/*
cpsid i:清除 CPSR 的 I 位,禁用 IRQ 中断
cps #0x12:切换到 IRQ 模式(为中断处理准备独立栈)
栈指针设置:
IRQ 模式栈:0x82000000(用于处理中断)
系统模式栈:0x84000000(用于主程序)
cpsie i:设置 CPSR 的 I 位,启用 IRQ 中断
*/
(4)主循环 bl led_init // 初始化 LED 引脚 finish: bl led_on // 点亮 LED bl led_delay // 延时 bl led_off // 熄灭 LED bl led_delay // 延时 b finish // 无限循环/*
初始化 LED 后进入无限循环
循环内:点亮 → 延时 → 熄灭 → 延时
*/
(5)LED 初始化(led_init) led_init: // 1. 复用功能配置(设置引脚为 GPIO 功能) ldr r0, =0x020E0068 // IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器地址 ldr r1, [r0] // 读取当前值 bic r1, r1, #0x1F // 清零低5位(MUX_MODE 字段) orr r1, r1, #0x05 // 设置为 0101(GPIO 功能) str r1, [r0] // 写回寄存器 // 2. 电气特性配置(驱动能力、上下拉等) ldr r0, =0x020E02F4 // IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 寄存器地址 ldr r2, =0x10B0 // 电气特性配置值 str r2, [r0] // 写入配置 // 3. GPIO 方向设置(设置为输出) ldr r0, =0x0209C004 // GPIO1_GDIR 寄存器地址(方向寄存器) ldr r1, [r0] // 读取当前值 orr r1, r1, #(1 << 3) // 设置第3位为1(GPIO1_IO03 设为输出) str r1, [r0] // 写回寄存器 bx lr // 返回
(6)LED 开(led_on) led_on: ldr r0, =0x0209C000 // GPIO1_DR 寄存器地址(数据寄存器) ldr r1, [r0] // 读取当前值 bic r1, r1, #(1 << 3) // 清除第3位(输出低电平,LED亮) str r1, [r0] // 写回寄存器 bx lr // 返回
(7)LED 关((led_off) led_off: ldr r0, =0x0209C000 // GPIO1_DR 寄存器地址 ldr r1, [r0] // 读取当前值 orr r1, r1, #(1 << 3) // 设置第3位(输出高电平,LED灭) str r1, [r0] // 写回寄存器 bx lr // 返回
(8) 延时函数(led_delay) led_delay: ldr r0, =0x7FFFF // 设置延时计数值(约 524287 次循环) loop: cmp r0, #0 // 比较 r0 和 0 blt fin // 如果 r0 < 0,跳转到 fin sub r0, r0, #1 // r0 = r0 - 1 b loop // 继续循环 fin: bx lr // 返回
注意:IMX6ULL 的 GPIO 输出是低电平有效(0=亮,1=灭)
三、技术要点总结
1. 地址说明
-
0x020E0068:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03(引脚复用控制)
-
0x020E02F4:IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03(引脚电气特性)
-
0x0209C000:GPIO1_DR(GPIO1 数据寄存器)
-
0x0209C004:GPIO1_GDIR(GPIO1 方向寄存器)
2. 寄存器操作技巧
-
bic:位清除(清零特定位)
-
orr:位设置(置位特定位)
-
ldr/str:加载/存储寄存器值
3. ARM 模式说明
-
0x12:IRQ 模式(中断处理)
-
0x1F:系统模式(普通程序运行)
-
cpsid/cpsie:中断禁用/使能
4. 程序执行流程
上电复位 → 跳转到 _reset_handler → 初始化栈和中断 → LED初始化
→ 进入主循环(亮-延时-灭-延时)→ 无限循环
四、编译与烧录
1. 编译流程
(1)预处理
arm-linux-gnueabihf-cpp main.c -o main.i
(2)编译
arm-linux-gnueabihf-gcc -S main.i -o main.s
(3)汇编
arm-linux-gnueabihf-as main.s -o main.o
(4)链接
arm-linux-gnueabihf-ld main.o -o program.elf
2. 具体操作步骤
# 1. 只汇编不链接
arm-linux-gnueabihf-gcc -c start.S -o start.o -g
# 2. 链接到特定地址
arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf
# 3. 格式转换
arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin
# 4. 反汇编(可选)
arm-linux-gnueabihf-objdump -D start.elf > start.dis
3. 烧录到SD卡
-
连接SD卡到Ubuntu
-
查看设备:
ls /dev/sdb -
拷贝烧录工具
imxdownload到工程目录 -
修改权限:
chmod +777 imxdownload -
烧录:
./imxdownload start.bin /dev/sdb
4. 测试
-
设置启动方式为SD卡

-
插入SD卡
-
开发板上电
-
观察红色LED指示灯
五、知识要点总结
1. SoC引脚配置工作
在LED实验中,对SoC引脚配置的主要工作包括:
-
复用功能选择:通过IOMUX控制器配置引脚功能
-
电气特性设置:配置驱动强度、上下拉电阻等
-
GPIO方向设置:配置引脚为输入或输出模式
-
数据寄存器操作:通过读写GPIO数据寄存器控制LED状态
2. 编译工具链组件详解
(1)编译器(Compiler)
-
作用:将高级语言(如C语言)或汇编语言转换为机器能识别的目标文件
-
工作流程:
-
预处理:处理宏定义、头文件包含等
-
编译:将源代码转换为汇编代码
-
汇编:将汇编代码转换为目标文件
-
-
示例 :
arm-linux-gnueabihf-gcc
(2)链接器(Linker)
-
作用:将多个目标文件、库文件合并成一个可执行文件
-
主要任务:
-
地址分配:为代码和数据分配具体的内存地址
-
符号解析:解决不同文件之间的函数和变量引用
-
重定位:根据最终地址调整代码中的地址引用
-
-
示例 :
arm-linux-gnueabihf-ld
(3)格式转换器(Objcopy)
-
作用:在不同目标文件格式之间进行转换
-
常见用途:
-
从ELF格式提取纯二进制文件(如.bin文件)
-
去除调试信息、符号表等
-
修改文件段布局
-
-
示例 :
arm-linux-gnueabihf-objcopy
(4)反汇编器(Objdump)
-
作用:将机器码反向转换为汇编代码,便于分析和调试
-
功能特点:
-
显示可执行文件的各个段信息
-
反汇编机器指令
-
显示符号表和调试信息
-
-
示例 :
arm-linux-gnueabihf-objdump