ARM嵌入式开发代码实践——LED灯闪烁(汇编版)

一、环境搭建代码实践

工具链安装

复制代码
#!/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卡

  1. 连接SD卡到Ubuntu

  2. 查看设备:ls /dev/sdb

  3. 拷贝烧录工具imxdownload到工程目录

  4. 修改权限:chmod +777 imxdownload

  5. 烧录:./imxdownload start.bin /dev/sdb

4. 测试

  1. 设置启动方式为SD卡

  2. 插入SD卡

  3. 开发板上电

  4. 观察红色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

相关推荐
不怕犯错,就怕不做2 小时前
RK3562+RK817在关机状态下提升充电电流至2A解决方案
linux·驱动开发·嵌入式硬件
wheelmouse77882 小时前
Python 装饰器函数(decoratots) 学习笔记
笔记·python·学习
电饭叔2 小时前
案例证明法--内容学习
学习
li星野2 小时前
OpenCV4X学习-轮廓检测、水印、凸缺陷检测、数字水印
学习
yang011110012 小时前
论文总结 HVI: A New Color Space for Low-light Image Enhancement
图像处理·人工智能·学习·计算机视觉
LYS_06182 小时前
寒假学习(2)(C语言2+模数电2)
c语言·学习·算法
shanghaichutai2 小时前
Valosin (porcine) (Peptide VQY (porcine)) ;VQYPVEHPDKFLKFGMTPSKGVLFFY
笔记
listhi5202 小时前
压缩感知信号重构的块稀疏贝叶斯学习(BSBL)算法:原理、实现与应用
学习·算法·重构
三伏5222 小时前
Cortex-M3权威指南Cn第三章——笔记
笔记·cortex-m3