i.MX 6ULL 裸机开发
一、点灯开发三阶段
1.1 三个核心模块
点灯涉及三个硬件模块,必须按顺序配置:
| CCM(时钟) | 开启外设时钟门 | CCM_CCGR0 ~ CCM_CCGR6 |
| IOMUXC(引脚复用) | 设置引脚复用功能 + 电气属性 | SW_MUX_CTL_PAD_xxx、SW_PAD_CTL_PAD_xxx |
| GPIO(通用输入输出) | 设置引脚方向、输出电平 | GPIOx_GDIR、GPIOx_DR |
1.2 阶段一:汇编点灯
直接操作寄存器地址,通过汇编指令完成配置。
配置步骤:
开时钟:写 CCM_CCGR0 ~ CCM_CCGR6 为 0xFFFFFFFF
引脚复用:配置 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 为 GPIO 功能
电气属性:配置 IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03
GPIO 方向:写 GPIO1_GDIR 设置为输出
输出电平:写 GPIO1_DR 控制亮灭
1.3 阶段二:C 语言点灯
通过宏定义封装寄存器地址,用 C 语言操作。
关键:volatile 关键字
-
变量前加 volatile:编译器对该变量的操作不会被优化
-
指针前加 volatile:通过该指针访问的内存空间不被优化
寄存器地址宏定义示例:
\\#define CCM\\_CCGR0 (\\*((volatile unsigned int \\*)(0x020C4068)))
\\#define CCM\\_CCGR1 (\\*((volatile unsigned int \\*)(0x020C406C)))
// … 其余 CCGR2 \\~ CCGR6
\\#define SW\\_MUX\\_CTL\\_PAD\\_GPIO1\\_IO03 (\\*((volatile unsigned int \\*)(0x020E0068)))
\\#define SW\\_PAD\\_CTL\\_PAD\\_GPIO1\\_IO03 (\\*((volatile unsigned int \\*)(0x020E02F4)))
\\#define GPIO1\\_GDIR (\\*((volatile unsigned int \\*)(0x0209C004)))
\\#define GPIO1\\_DR (\\*((volatile unsigned int \\*)(0x0209C000)))
1.4 阶段三:NXP 固件库点灯
使用 NXP 提供的 SDK 库函数,封装更简洁。
代码示例:
\\#include "MCIMX6Y2.h"
\\#include "fsl\\_iomuxc.h"
\\#include "led.h"
// 开启所有外设时钟
void enable\\_clocks(void)
{
  CCM->CCGR0 = 0xFFFFFFFF;
  CCM->CCGR1 = 0xFFFFFFFF;
  CCM->CCGR2 = 0xFFFFFFFF;
  CCM->CCGR3 = 0xFFFFFFFF;
  CCM->CCGR4 = 0xFFFFFFFF;
  CCM->CCGR5 = 0xFFFFFFFF;
  CCM->CCGR6 = 0xFFFFFFFF;
}
// LED 初始化
void led\\_init(void)
{
  IOMUXC\\_SetPinMux(IOMUXC\\_GPIO1\\_IO03\\_GPIO1\\_IO03, 0); // 引脚复用
  IOMUXC\\_SetPinConfig(IOMUXC\\_GPIO1\\_IO03\\_GPIO1\\_IO03, 0x10B0); // 电气属性
  GPIO1->GDIR |= (1 << 3); // 设置为输出
}
// 点亮 LED(低电平点亮)
void led\\_on(void)
{
  GPIO1->DR &= \\~(1 << 3);
}
二、程序内存段
| .text | 代码、指令(通过 -Ttext 指定起始地址) |
| .data | 已初始化的全局变量和静态变量 |
| .bss | 初始化为 0 的全局变量和静态变量 |
| .common | 未初始化的全局变量 |
| .rodata | 只读常量 |
三、交叉编译工具链(arm-linux-gnueabihf-)
裸机开发完整流水线:
start.S / main.c
  ↓ arm-linux-gnueabihf-gcc -c
start.o / main.o
  ↓ arm-linux-gnueabihf-ld -Ttext 0x87800000
led.elf
  ↓ arm-linux-gnueabihf-objcopy -O binary
led.bin ← 下载到开发板运行
3.1 四个核心工具
| gcc | 编译器:源码 → 目标文件 | -c(只编译不链接) | .o 目标文件 |
| ld | 链接器:合并 .o,分配内存地址 | -Ttext 0x87800000 | .elf 可执行文件 |
| objcopy | 格式转换:提取纯机器码 | -O binary | .bin 原始二进制(烧录用) |
| objdump | 反汇编:机器码 → 汇编代码 | -D | .dis 反汇编文件(调试用) |
区别
:
.elf
带符号表和调试信息,不能直接下载;
.bin
只有纯机器码,是最终烧录文件。
四、GPIO 外设通用控制流程
所有 GPIO 外设(LED、蜂鸣器等)都遵循统一的四步流程:
开启时钟:配置 CCM 时钟门控寄存器
查找引脚:查看底板原理图,找到对应外设的引脚
引脚配置:设置引脚复用功能 + 电气属性
GPIO 配置:设置方向(输入 / 输出)、读写电平
五、外部中断(按键 EINT)
5.1 中断系统架构

外设中断源 → GPIO 中断 → GIC 中断控制器 → ARM 内核
GIC(通用中断控制器):ARM 设计的专门管理 Cortex-A 系列处理器中断的外设。
5.2 GIC 版本
| GIC V1 | 旧款嵌入式芯片(Cortex-A5) |
| GIC V2 | i.MX6ULL、树莓派(Cortex-A7,32 位),支持 1020 个中断 |
| GIC V3-V4 | 64 位处理器(手机芯片) |
| GIC V5 | 大型服务器、车载算力芯片 |
5.3 GIC 中断分类(共 1020 个)
| SPI | Shared Peripheral Interrupt | 共享中断,外设产生的中断,所有 CPU 核都可以处理 |
| PPI | Private Peripheral Interrupt | 私有中断,CPU 核与核之间的通信中断 |
| SGI | Software Generated Interrupt | 软件中断,由软件指令触发 |
i.MX6ULL 实际支持
160 个中断
。
5.4 GPIO 外部中断配置
以 GPIO1_IO18 按键中断为例:
外设端(GPIO 寄存器)
| GPIO1_ICR2 | 配置中断触发方式(下降沿 / 上升沿) |
| GPIO1_IMR | 中断屏蔽寄存器,使能对应引脚的中断 |
| GPIO1_ISR | 中断状态寄存器,查询是否产生中断(写 1 清零) |
| GPIO1_EDGE_SEL | 设置双边沿触发 |
GIC 端寄存器
| C_IAR | 中断通知寄存器(只读),读取中断号,确定中断源 |
| C_EOIR | 中断结束寄存器(只写),处理完后写入中断号,通知 GIC 处理完毕 |
5.5 启动代码中的中断相关配置
在汇编启动代码中,通过 cp15 协处理器配置:
mrc p15, 0, r0, c1, 0 // 读取 SCTLR 控制寄存器
bic r0, r0, #(1 << 13) // 关闭异常向量表重映射
orr r0, r0, #(1 << 12) // 打开 I-Cache
mcr p15, 0, r0, c1, c0, 0 // 写回 SCTLR 寄存器
5.6 内联函数 INLINE
-
本质是函数,调用前必须有定义
-
编译时在调用位置展开,不产生函数调用开销,提高效率
-
适合短小、频繁调用的函数



