当前位置: 首页 > news >正文

ARM(5)-IMX6ULL 裸机开发入门:从启动到点亮第一盏 LED 灯

一、开发板介绍:IMX6ULL-Mini 的硬件基础

正点原子的 IMX6ULL-Mini 开发板 是入门 ARM Cortex-A7 裸机开发的经典平台,核心硬件与结构如下:

1. 核心板(六层板设计)

  • CPU:NXP i.MX6ULL Cortex-A7 单核处理器,工业级主频 528MHz、商业级 800MHz,采用 BGA 封装,平衡性能与功耗。
  • 内存:512MB DDR3L RAM,为程序运行提供高速数据存取能力。
  • 存储:8GB eMMC,支持 SD 卡、NAND、eMMC 等多启动模式,灵活适配不同开发场景。
  • 显示扩展:预留 4.3 寸 800×480 分辨率屏幕接口,便于后续图形界面开发。

2. 底板(以 LED 模块为例)

底板负责外设扩展,其中LED 模块是最基础的 “Hello World” 级外设:

  • 用户 LED:1 颗红色 LED 为 “用户可控灯”(后续通过代码控制其亮灭)。
  • 电源指示灯:1 颗蓝色 LED 为电源状态指示(上电即常亮)。
  • 限流保护:LED 串联 510Ω 限流电阻,防止电流过大烧毁灯珠,体现硬件 “保护性设计”。

二、启动代码:ARM 处理器的 “开机第一步”

裸机开发中,启动代码(Start Code) 是系统上电后执行的第一段程序,负责初始化处理器核心状态(如工作模式、栈、中断)。以下是基于 ARM 汇编的核心逻辑:

1. 异常向量表:处理 ARM 的 8 类核心异常

ARM 处理器规定,不同 “异常”(如复位、中断、指令错误)会跳转到固定内存地址执行处理程序,这些地址组成异常向量表。代码通过 ldr pc, =处理函数 指定各异常的入口:

.global _start__start:; 0x00:复位异常(系统上电/复位时触发,优先级最高)ldr pc, =_start_handler ; 0x04:未定义指令异常(执行处理器不识别的指令时触发)ldr pc, =_undefined_handler; 0x08:管理模式异常(通常由SWI指令触发,用于系统调用)ldr pc, =_supervisor_handler; 0x0C:预取指中止异常(指令读取失败时触发)ldr pc, =_prefetch_handler ; 0x10:数据中止异常(数据读写失败时触发)ldr pc, =_data_abort_handler; 0x14:保留异常(未使用,预留)ldr pc, =_not_use_handler; 0x18:IRQ中断(普通外部中断,如按键)ldr pc, =_irq_handler  ; 0x1C:FIQ中断(快速中断,如定时器,响应优先级更高)ldr pc, =_fiq_handler       

复位异常外,其他异常的处理函数暂为 “死循环”(b 自身)—— 这是最基础的 “防跑飞” 设计,后续可根据需求扩展实际逻辑。

2. 复位处理:初始化处理器状态

_start_handler 是系统上电后第一个执行的函数,负责初始化 CPU 的工作模式、栈、中断状态

_start_handler:; 关闭IRQ中断:防止初始化过程被外部中断打断cpsid i     ; 步骤1:切换到IRQ模式,初始化IRQ栈cps #0x12    ; 0x12是IRQ模式的编码(二进制`10010`)ldr sp, =0x82000000  ; 设置IRQ模式的栈指针(需确保地址在有效内存区间); 步骤2:切换到系统模式,初始化系统栈cps #0x1F    ; 0x1F是系统模式的编码(二进制`11111`,特权模式,用于主逻辑)ldr sp, =0x84000000  ; 设置系统模式的栈指针(与IRQ栈地址分离,避免冲突); 打开---再次关闭IRQ中断,确保初始化完成后中断状态cpsid i     finish:b finish     ; 初始化完成后,进入死循环(后续可扩展应用逻辑)

  • cps 指令:用于切换 ARM 处理器的工作模式(如 IRQ 模式、系统模式)。
  • ldr sp, =地址:为当前模式设置栈指针(SP)—— 栈是函数调用、中断处理的 “临时数据容器”,必须提前初始化。

三、点亮 LED:外设寄存器的 “精准控制”

要控制底板上的红色 LED,需通过寄存器配置完成 “引脚复用→电气特性→GPIO 方向→数据输出” 的流程。

前期准备:查阅技术文档


开发外设前需明确硬件连接和寄存器配置,核心参考文档(开发板购买处可提供)

  • 《IMX6ULL_MINI_V2.2 (Mini 底板原理图).pdf》:确认 LED 连接的 GPIO 引脚(如 GPIO1_IO03)
  • 《IMX6ULL 参考手册.pdf》:查询 GPIO 相关寄存器的地址和配置方式

1. 硬件映射:LED 与 GPIO 的关联

从底板原理图可知:红色用户 LED 连接到 GPIO1_IO03 引脚,因此需配置该引脚的功能与输出状态。

2. 寄存器配置流程

ARM 芯片的外设通过 “内存映射” 的寄存器控制,需按顺序配置以下寄存器(以 GPIO1_IO03 为例):

(1)引脚复用配置:选择 GPIO 功能

i.MX6ULL 的引脚可复用为多种功能(如 GPIO、I2C、UART 等),需通过 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器选择 “GPIO 模式”。

该寄存器的低 4 位决定复用功能,设置为 0101(二进制)时,引脚被配置为 GPIO 功能。

(2)电气特性配置:优化引脚驱动

通过 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 寄存器配置引脚的电气特性(如上下拉电阻、迟滞、驱动能力等)。需根据 LED 的驱动需求(如电流、电平匹配)调整,确保引脚稳定输出。

(3)GPIO 方向配置:设为输出模式

GPIO 引脚的方向由 GPIOx_GDIR(GPIO 方向寄存器)控制。对于 GPIO1 组,寄存器为 GPIO1_GDIR

将 GPIO1_GDIR 的第 3 位(对应 GPIO1_IO03)设为 1,表示 “输出模式”(0 为输入模式)。

(4)GPIO 数据输出:控制 LED 亮灭

GPIO 数据由 GPIOx_DR(GPIO 数据寄存器)控制。对于 GPIO1 组,寄存器为 GPIO1_DR

  • 若硬件为 “低电平点亮”:置 GPIO1_DR 的第 3 位为 0,LED 亮;置为 1,LED 灭。
  • 若硬件为 “高电平点亮”:逻辑相反(需结合原理图电平逻辑确认)。

3.****** 汇编代码实现 *********

围绕ARM 汇编启动代码(异常向量表、工作模式配置)与LED 驱动逻辑展开,结合 Makefile 工程构建流程,梳理嵌入式 ARM 开发的核心环节,适用于 I.MX6ULL 等 ARM Cortex-A 系列开发板入门实践。


A、核心文件结构

实验包含 2 类关键文件:

  • start.S:ARM 汇编源代码(异常向量表、工作模式初始化、LED 控制逻辑)
  • Makefile:编译、链接、烧录脚本(指定工具链、生成可执行文件、下载到 SD 卡)

B、ARM 汇编代码解析(start.S)

代码分为 4 大模块:异常向量表工作模式与栈初始化LED 控制函数主循环,遵循 ARM Cortex-A 汇编语法(AT&T 或 ARM 语法,本文为 ARM 语法)。

(一)1. 异常向量表(ARM 架构核心)

异常向量表是内存固定地址区域(本文链接地址0x87800000,对应 I.MX6ULL 启动地址),存储 8 类异常的处理入口,地址偏移固定(每 4 字节 1 个入口,对应 32 位指令)。

地址偏移(相对链接地址)异常类型触发场景处理函数功能说明
0x00复位异常(Reset)系统上电 / 复位按钮按下_start_handler优先级最高,是程序第一个执行的入口
0x04未定义指令异常执行处理器不识别的机器码指令_undefined_handler本文暂用 “忙等” 实现(无实际处理逻辑)
0x08管理模式异常(SVC)执行SWI指令(用户态→内核态切换)_supervisor_handler暂用 “忙等” 实现
0x0C预取指中止异常读取指令时地址无效 / 权限不足(如访问 ROM 越界)_prefetch_handler暂用 “忙等” 实现
0x10数据中止异常读写数据时地址无效 / 权限不足(如访问 RAM 越界)_data_abort_handler暂用 “忙等” 实现
0x14保留异常未定义用途(ARM 架构预留扩展)_not_use_handler暂用 “忙等” 实现
0x18IRQ 中断普通外部中断(如按键、传感器、串口)_irq_handler暂用 “忙等” 实现(后续可扩展中断服务函数)
0x1CFIQ 中断快速中断(如定时器、高速外设)_fiq_handler暂用 “忙等” 实现(响应速度比 IRQ 快)
关键语法说明:
  • .global _start:定义全局符号_start,作为链接器的入口(告诉链接器程序从这里开始执行)。
  • ldr pc,=label:将label的地址加载到程序计数器pc,实现 “跳转到异常处理函数”(ARM 汇编中pc指向当前执行指令的下两条,此伪指令可正确加载目标地址)。
  • b label:无条件分支跳转(“忙等” 逻辑,即异常触发后循环等待,避免程序跑飞)。

(二)2. 系统初始化(_start_handler)

复位异常触发后,进入_start_handler,完成工作模式切换栈指针设置中断关闭(防止初始化被干扰),为后续 LED 控制做准备。

_start_handler:cpsid i     // 关闭IRQ中断(禁止外部中断干扰初始化,i=IRQ)cps #0x12   // 切换到IRQ模式(模式码0x12:ARM Cortex-A中IRQ模式的cpsr配置值)ldr sp, =0x82000000  // 设置IRQ模式栈指针(需在I.MX6ULL的RAM范围内,如0x80000000~0x90000000)cps #0x1F   // 切换到SYS模式(系统模式,特权模式,用于后续函数调用)ldr sp, =0x84000000  // 设置SYS模式栈指针(与IRQ栈地址不重叠,避免栈冲突)// cpsie i   //打开 IRQ 中断(ie=enable IRQ),初始化完成后可按需开启。bl led_init // 调用LED初始化函数(bl=分支并链接,将返回地址存入lr寄存器)b finish    // 跳转到主循环(不再返回)
核心知识点:
  1. 工作模式码(Cortex-A 系列):

    • 0x12:IRQ 模式(中断模式,处理外部中断)
    • 0x1F:SYS 模式(系统模式,特权模式,支持访问所有硬件资源,适合主程序运行)
    • 切换模式用cps #模式码(CPSR 控制指令,直接修改程序状态寄存器)。
  2. 栈指针(sp)设置

    • 不同工作模式需独立栈空间(避免模式切换时栈数据覆盖)。
    • 栈地址需在开发板的 RAM 范围内(I.MX6ULL 的 RAM 基地址为0x80000000,大小通常为 256MB/512MB)。
  3. 中断控制

    • cpsid i:关闭 IRQ 中断(id=disable IRQ)。
    • cpsie i:打开 IRQ 中断(ie=enable IRQ),初始化完成后可按需开启。

(三)3. LED 控制函数

通过操作 I.MX6ULL 的GPIO 寄存器,实现 LED 的初始化、点亮、熄灭,核心是 “配置 GPIO 功能→设置 GPIO 方向→操作 GPIO 数据”。

(1)LED 初始化(led_init)

配置 GPIO 的复用功能(将引脚设为 GPIO 模式)、电气特性(上下拉、驱动能力)、方向(输出模式)。

led_init:// 1. IO复用配置:将引脚复用为GPIO(I.MX6ULL的复用寄存器地址0x020E0068)ldr r0, =0x020E0068  // 加载复用寄存器地址到r0ldr r1, =0x05        // 配置值0x05:将对应引脚复用为GPIO(需结合硬件原理图,如LED接GPIO1_IO03)str r1, [r0]         // 将r1的值写入r0指向的寄存器(完成复用配置)// 2. 引脚电气特性配置:上下拉、驱动能力、 slew rate(地址0x020E02F4)ldr r0, =0x020E02F4  // 加载电气特性寄存器地址ldr r1, =0x10B0      // 配置值0x10B0:如20K上拉、驱动能力等级、慢 slew rate(需参考芯片手册)str r1, [r0]         // 写入配置// 3. GPIO方向配置:设为输出模式(GPIO1方向寄存器地址0x0209C004)ldr r0, =0x0209C004  // 加载GPIO1方向寄存器地址ldr r1, [r0]         // 读取当前配置orr r1, r1, #(1 << 3)// 第3位置1:GPIO1_IO03设为输出(1=输出,0=输入)str r1, [r0]         // 写入配置bx lr                // 返回调用处(bx=分支并交换指令集,lr存返回地址)
(2)LED 点亮(led_on)

操作 GPIO数据寄存器,将对应引脚置低电平(假设 LED 为 “低电平点亮”,需结合硬件原理图)。

led_on:ldr r0, =0x0209C000  // 加载GPIO1数据寄存器地址(数据寄存器控制引脚电平)ldr r1, [r0]         // 读取当前电平状态bic r1, r1, #(1 << 3)// 第3位清0:GPIO1_IO03输出低电平(LED点亮)str r1, [r0]         // 写入数据寄存器bx lr                // 返回

(3)LED 熄灭(led_off)

将 GPIO 对应引脚置高电平,实现 LED 熄灭。

led_off:ldr r0, =0x0209C000  // 加载GPIO1数据寄存器地址ldr r1, [r0]         // 读取当前电平状态orr r1, r1, #(1 << 3)// 第3位置1:GPIO1_IO03输出高电平(LED熄灭)str r1, [r0]         // 写入数据寄存器bx lr                // 返回

(4)延时函数(led_delay)

通过循环计数实现简单延时(软件延时,精度较低,适合入门)。

led_delay:ldr r0, =0x7FFFF     // 加载延时计数初值(值越大,延时越长)
loop:sub r0, r0, #1       // r0 = r0 - 1(计数减1)cmp r0, #0           // 比较r0与0(设置cpsr的标志位)bgt loop             // 若r0 > 0,跳回loop继续循环(bgt=大于则分支)bx lr                // 延时结束,返回
关键寄存器说明(I.MX6ULL):
寄存器功能地址关键配置位作用
IO 复用寄存器0x020E0068低 4 位(如 0x05=GPIO 模式)决定引脚功能(是 GPIO、UART 还是 SPI)
电气特性寄存器0x020E02F4上下拉、驱动能力位确保引脚电气性能稳定(如避免电平漂移)
GPIO 方向寄存器0x0209C004每 1 位对应 1 个引脚(1 = 输出)配置 GPIO 为输入 / 输出模式
GPIO 数据寄存器0x0209C000每 1 位对应 1 个引脚电平控制引脚输出高 / 低电平

(四)4. 主循环(finish)

初始化完成后,进入无限循环,实现 “LED 点亮→延时→熄灭→延时” 的往复逻辑。

finish:bl led_on    // 点亮LEDbl led_delay // 延时(保持点亮状态)bl led_off   // 熄灭LEDbl led_delay // 延时(保持熄灭状态)b finish     // 循环执行

C、Makefile 工程构建

Makefile 用于自动化编译、链接、生成可执行文件(start.bin),并提供烧录脚本,核心是指定ARM 交叉编译工具链(适用于 PC 编译嵌入式代码)。

(一)1. Makefile 代码解析

C、核心知识点总结

  1. ARM 异常处理:异常向量表是入口,复位异常是程序启动点,其他异常需后续扩展实际处理逻辑(如 IRQ 中断服务函数)。
  2. 工作模式与栈:不同模式独立栈空间,SYS 模式适合主程序,IRQ 模式用于中断处理。
  3. GPIO 控制流程:复用配置→电气特性→方向设置→数据操作,需结合芯片手册和硬件原理图。
  4. 交叉编译流程:汇编→编译(.o)→链接(.elf)→格式转换(.bin)→烧录,Makefile 自动化管理流程。

****************************************************

四、编译与烧写:让代码在开发板运行

裸机程序需经过 “编译→链接→格式转换→烧写” 流程,才能在开发板上运行。

1. 交叉编译工具链

开发板 CPU 为 ARM 架构,需在 x86 的 Ubuntu 上使用交叉编译工具链 arm-linux-gnueabihf-*(包含gccldobjcopy等工具)。

2. 编译步骤(以start.S为例)

(1)汇编:将汇编代码转为目标文件
  • -c:只编译不链接。
  • -o start.o:输出目标文件。
  • -g:包含调试信息(可选)。
(2)链接:将目标文件链接到指定地址

i.MX6ULL 的启动地址通常为 0x87800000(需与芯片手册、Bootloader 匹配),因此链接时指定入口地址:

arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf
  • -Ttext 地址:指定代码段起始地址。
  • start.elf:输出可链接的 ELF 格式文件
(3)格式转换:生成二进制镜像

开发板运行需要纯二进制文件,因此用objcopy转换格式:

arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin
  • -O binary:输出二进制格式。
  • -S:去除符号表。
  • -g:去除调试信息。
  • start.bin:最终可烧写的二进制文件。
(4)反汇编(可选):查看汇编与机器码对应

为调试或学习,可将 ELF 文件反汇编为可读的汇编代码:

arm-linux-gnueabihf-objdump -D start.elf > start.dis

3. 烧写至 SD 卡

i.MX6ULL 支持从 SD 卡启动,需将start.bin烧写到 SD 卡的特定位置,借助正点原子的imxdownload工具实现:

(1)连接 SD 卡到 Ubuntu

将 SD 卡插入 USB 读卡器,连接到 Ubuntu 后,通过 ls /dev/sd* 确认 SD 卡设备名(如/dev/sdb)。

(2)赋予烧写工具权限

imxdownload拷贝到工程目录,执行:

chmod +777 imxdownload
(3)烧写二进制文件
./imxdownload start.bin /dev/sdb
  • 若烧写速度异常(如上 MB/s),需重新拔插读卡器或重启 Ubuntu,确保 SD 卡被正确识别。

4. 开发板测试

(1)设置启动模式

通过开发板的拨码开关选择 “SD 卡启动”(参考底板原理图的 BOOT 模块说明)。

(2)启动开发板

插入烧写好的 SD 卡,给开发板上电,观察红色 LED 是否按代码逻辑亮灭,验证程序是否运行成功。

5. 命令说明

  • 执行 make:自动编译生成start.bin
  • 执行 make clean:删除所有编译生成的文件,清理工程。
  • 执行 make load:自动调用imxdownload,将程序烧写到 SD 卡。
  • 板子需配置启动方式

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

(1)引脚复用配置:选择 GPIO 功能

操作寄存器:IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03

将引脚的低 4 位设置为 0101,将引脚复用为 GPIO 功能

(2)电气特性配置:优化引脚驱动

操作寄存器:IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03

设置引脚的电气参数

(3)GPIO 方向配置:设为输出模式

GPIO 引脚的方向由 GPIOx_GDIR(GPIO 方向寄存器)控制。对于 GPIO1 组,寄存器为 GPIO1_GDIR

将 GPIO1_GDIR 的第 3 位(对应 GPIO1_IO03)设为 1,表示 “输出模式”(0 为输入模式)。

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

1.编译器是将源代码转换为目标代码(机器语言指令)的工具

arm-linux-gnueabihf-gcc -c -g start.S -o start.o

  • -c:只编译不链接,生成目标文件(start.o)

  • -g:保留调试信息,方便后续调试

  • -o start.o:指定输出的目标文件名

2.链接器将多个目标文件(start.o)组合成一个完整的可执行文件(.elf文件),并分配内存地址

arm-linux-gnueabihf-ld -Ttext 0x87800000 start.o -o start.elf

  • -Ttext 0x87800000:指定程序代码段(text 段)的起始地址(IMX6ULL 开发板的常用加载地址)

  • -o start.elf:指定输出的可执行文件名

3.格式转换器将一种文件格式(ELF 格式的start.elf)转换为另一种格式(二进制格式的start.bin)

arm-linux-gnueabihf-objcopy -O binary -S -g start.elf start.bin

  • -O binary:指定输出格式为纯二进制

  • -S:去除符号表和重定位信息

  • -g:去除调试信息

4.反汇编器将二进制可执行文件(start.elf)(类似于a.out) 反编译为可读的汇编代码(start.dis)

arm-linux-gnueabihf-objdump -D start.elf > start.dis

  • -D:对整个文件进行反汇编(包括所有段)

  • > start.dis:将反汇编结果输出到文本文件


文章转载自:

http://5WmHlRh5.qjxxc.cn
http://01uRZvD9.qjxxc.cn
http://exl60iud.qjxxc.cn
http://RQvUyTWJ.qjxxc.cn
http://bphFKMvS.qjxxc.cn
http://PNlA7rWL.qjxxc.cn
http://Z6iCNtxF.qjxxc.cn
http://V0Q2Coht.qjxxc.cn
http://3k5XfOgg.qjxxc.cn
http://la2H7d8u.qjxxc.cn
http://HUoHFcsH.qjxxc.cn
http://2RGHoO5Z.qjxxc.cn
http://9gXKKWSJ.qjxxc.cn
http://jP8w8aU3.qjxxc.cn
http://RGhmoFUf.qjxxc.cn
http://jkGOqqXi.qjxxc.cn
http://JK5459se.qjxxc.cn
http://0WV44X6k.qjxxc.cn
http://SttQydOk.qjxxc.cn
http://BGFeCtvv.qjxxc.cn
http://yMsRsM0W.qjxxc.cn
http://ZoJMSJii.qjxxc.cn
http://awZqnRDz.qjxxc.cn
http://myh5Y8Oo.qjxxc.cn
http://us2RTBuI.qjxxc.cn
http://Qi1duF5c.qjxxc.cn
http://off7kaKk.qjxxc.cn
http://RwelfkHp.qjxxc.cn
http://oE5Rw45z.qjxxc.cn
http://2qLMyDXE.qjxxc.cn
http://www.dtcms.com/a/376132.html

相关文章:

  • 2025.9.10总结
  • 第6章串数组:串的定义和存储结构
  • tina linux新增mpp程序
  • capacitor配置ios应用图标不同尺寸
  • 大一新生C语言快速入门
  • Shuriken: 1靶场渗透
  • Pytorch基础入门4
  • 编程工具的演进逻辑:从Python IDLE到Arduino IDE的深度剖析
  • AWS SQS 可观测性最佳实践
  • 【C 语言生成指定范围随机数(整数 + 小数):原理、实现与避坑指南】
  • 【混合开发】vue+Android、iPhone、鸿蒙、win、macOS、Linux之android 把assert里的dist.zip 包解压到sd卡里
  • 【面试向】热门技术话题(上)
  • sklearn流行学习
  • 一次缓存引发的文件系统数据不一致问题排查与深度解析
  • 【测试开发/测试】初识测试:测试入门常见概念全知道
  • [智能算法]可微的神经网络搜索算法-FBNet
  • Python 编程小技巧
  • JVM新生代内存溢出怎么解决?
  • 《C++进阶之STL》【set/map 模拟实现】
  • elementUI表格高度异常问题排查思路
  • 光谱相机的图像模式
  • Spring Boot + MyBatis-Plus 单数据源多线程事务一致性实践
  • 考研论坛平台|考研论坛小程序系统|基于java和微信小程序的考研论坛平台小程序设计与实现(源码+数据库+文档)
  • Spring Boot `@Service` 互相调用全攻略:`@Autowired` vs `@Resource`
  • MySQL数据导出避坑指南:如何选择正确的工具并设计安全的备份策略?
  • 《算法闯关指南:优选算法-双指针》--01移动零,02复写零
  • ACD智能分配:轮流分配和排序上限分配的设置
  • DevOps实战(6) - 使用Arbess+GitHub+SonarQube实现Java项目自动化部署
  • 《WINDOWS 环境下32位汇编语言程序设计》第15章 注册表和INI文件
  • 【硬件-笔试面试题-81】硬件/电子工程师,笔试面试题(知识点:详细讲讲同步时钟与异步时钟通信)