ARM架构——用汇编语言点亮 LED

目录

一、开发环境与硬件基础

[1.1 IMX6ULL-Mini 开发板介绍](#1.1 IMX6ULL-Mini 开发板介绍)

[1.2.1 编译工具:gcc-linaro 交叉编译器](#1.2.1 编译工具:gcc-linaro 交叉编译器)

[1.2.2 代码编辑:Visual Studio Code](#1.2.2 代码编辑:Visual Studio Code)

[1.2.3 辅助工具](#1.2.3 辅助工具)

[二、LED 点亮的底层逻辑](#二、LED 点亮的底层逻辑)

[2.1 引脚配置三步骤](#2.1 引脚配置三步骤)

[2.1.1 复用功能配置(IOMUXC)](#2.1.1 复用功能配置(IOMUXC))

[2.1.2 电气特性配置(IOMUXC)](#2.1.2 电气特性配置(IOMUXC))

[2.1.3 引脚方向配置(GPIO)](#2.1.3 引脚方向配置(GPIO))

[2.2 LED 开关逻辑](#2.2 LED 开关逻辑)

[2.3 LDR 与 STR 详解](#2.3 LDR 与 STR 详解)

[三、汇编代码实现 LED 闪烁](#三、汇编代码实现 LED 闪烁)

四、编译与烧写

[4.1 手动编译步骤](#4.1 手动编译步骤)

[4.2 Makefile 简化编译](#4.2 Makefile 简化编译)

[4.3 程序烧写与测试](#4.3 程序烧写与测试)

[4.3.1 SD 卡烧写](#4.3.1 SD 卡烧写)

[4.3.2 开发板测试](#4.3.2 开发板测试)

五、核心概念解答


一、开发环境与硬件基础

1.1 IMX6ULL-Mini 开发板介绍

IMX6ULL-Mini 开发板是由正点原子提供的, 搭载NXP生产i.MX6ULL的SOC, 主打高性能、低功耗与紧凑体积。整个开发板由核心板与底板组成,核心板通过双列直插方式与底板连接。
IMX6ULL-Mini 开发板

  • 核心板:六层板
    • **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)

bash 复制代码
arm-linux-gnueabihf-gcc -c start.S -o start.o -g
  • -c:只汇编不链接
  • -o:指定输出文件
  • -g:保留调试信息

**(2)链接:**将目标文件链接到指定地址(IMX6ULL 的 SD 卡启动地址为 0x87800000)

bash 复制代码
arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf
  • -Ttext:指定代码段起始地址

(3)格式转换:将 ELF 文件转为开发板可执行的二进制文件(.bin)

bash 复制代码
arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin
  • -O binary:输出二进制格式
  • -S:去除符号信息
  • -g:去除调试信息

(4)反汇编(可选):将 ELF 文件转为汇编代码(用于调试)

bash 复制代码
arm-linux-gnueabihf-objdump -D start.elf > start.dis

4.2 Makefile 简化编译

bash 复制代码
# 定义工具链变量,简化后续命令
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 卡烧写

  1. 将 SD 卡插入 Ubuntu 虚拟机(通过 USB 读卡器)
  2. 查看 SD 卡设备名:ls /dev/sdb(通常为 sdb,需确认无其他存储设备冲突)
  3. 执行烧写命令:./imxdownload start.bin /dev/sdb

**注意:**烧写速率需为 KB 级,若显示 MB 级速率则失败,需拔掉读卡器重启 Ubuntu 后重试

4.3.2 开发板测试

  1. 选择启动模式:通过开发板上的拨码开关选择 SD 卡启动(参考开发板原理图)
  2. 插入烧写好的 SD 卡到开发板 TF 卡槽
  3. 上电测试:
    • 确保开发板电源开关弹起,插入电源适配器
    • 按下蓝色电源开关,观察蓝色电源灯亮起
    • 红色 LED 周期性闪烁

熄灭状态 点亮状态

五、核心概念解答

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

  • 复用功能配置:将引脚从默认功能切换为 GPIO 功能;
  • 电气特性配置:设置引脚驱动能力、上下拉等,保证信号稳定;
  • GPIO 方向配置:设置为输出模式,使引脚能主动输出高低电平。

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

  • 编译器(gcc):将汇编源文件(.S)转为目标文件(.o),进行语法检查和指令翻译;
  • 链接器(ld):将目标文件链接到指定内存地址,解决符号引用(如程序入口 _start);
  • 格式转换器(objcopy):将 ELF 文件(包含调试 / 符号信息)转为开发板可直接执行的二进制文件(.bin);
  • 反汇编器(objdump):将 ELF 文件转回汇编代码,用于调试(验证指令是否正确翻译)。
相关推荐
Y1rong3 小时前
STM32之GPIO
stm32·单片机·嵌入式硬件
宵时待雨3 小时前
STM32笔记归纳2:GPIO
笔记·stm32·嵌入式硬件
9稳3 小时前
基于单片机的家庭安全系统设计
开发语言·网络·数据库·单片机·嵌入式硬件
一路往蓝-Anbo4 小时前
第37期:启动流程(二):C Runtime (CRT) 初始化与重定位
c语言·开发语言·网络·stm32·单片机·嵌入式硬件
切糕师学AI4 小时前
ARM Cortex-M 的存储器系统特性
arm开发
longxiangam4 小时前
基于esp32p4 的掌机
单片机·嵌入式硬件
代码游侠4 小时前
ARM嵌入式开发代码实践——LED灯闪烁(C语言版)
c语言·开发语言·arm开发·笔记·嵌入式硬件·学习
Analog1114 小时前
电子秤采用 SIG5530 国产平替 CS5530
人工智能·嵌入式硬件·目标检测·硬件架构·信号处理·智能硬件
Saniffer_SH5 小时前
【高清视频】笔记本电脑出现蓝屏、死机、慢、不稳定是这样连接分析M.2 SSD的
运维·服务器·网络·人工智能·驱动开发·嵌入式硬件·fpga开发