IMX6ULL 的 LED 操作方法 ------ 从零开始,点亮你的第一颗灯
无论你是学习裸机开发,还是将来写 Linux 驱动,点亮 LED 都是嵌入式世界的 "Hello World"。
这节课,我们就以 IMX6ULL 芯片为例,手把手教你如何通过操作寄存器,让板子上的 LED 亮起来。
一、LED 为什么会亮?
先看一个最简单的电路图(示意图):
-
当 GPIO 引脚输出 低电平 (0V) 时,电流从 VCC 流过 LED、再流入 GPIO 引脚,LED 亮。
-
当 GPIO 引脚输出 高电平 (3.3V) 时,LED 两端电压相等,没有电流,LED 灭。
注意:不同的开发板可能接法不同,有的板子是 高电平点亮 ,有的则是 低电平点亮 。
我们以 100ASK_IMX6ULL 开发板为例,它的 LED 是 低电平点亮。
二、GPIO 是什么?怎么操作它?
GPIO = General Purpose Input/Output,通用输入输出口。
你可以把它理解成芯片上的一个 "开关",通过程序控制它输出高电平或低电平。
操作任何一个 GPIO,都离不开三个步骤:
-
使能时钟 ------ 让 GPIO 模块先通上电(不供电无法工作)。
-
配置引脚功能 ------ 这个引脚可以是普通 GPIO,也可以是串口、I2C 等,我们要把它设为 GPIO 模式。
-
设置方向和数据 ------ 决定它是输入还是输出,再输出高电平或低电平。
三、找到我们要控制的 LED 引脚
打开开发板的原理图(PDF 或纸质),搜索 "LED" 关键字,会看到类似下面的信息:

在 100ASK_IMX6ULL 的原理图中,LED 连接到了 GPIO5_IO03 。
意思是:这个 LED 挂在 GPIO5 组 的 第 3 个引脚 上。
四、一步步操作 IMX6ULL 的寄存器
下面所有的地址和数值,都可以在芯片手册《IMX6ULLRM.pdf》里找到。
4.1 使能 GPIO5 的时钟(其实这一步可以跳过)
在 IMX6ULL 中,GPIO5 的时钟默认就是使能的,但我们还是知道一下它在哪里:
CCM_CCGR1 寄存器,地址 0x020C406C,里面的 bit[31:30] 控制 GPIO5 的时钟。
不过对于我们的板子,不设置它也没问题 ------ 厂家已经在 boot 阶段帮我们开好了。
4.2 配置引脚功能 ------ 让 GPIO5_IO03 变成 GPIO 模式
一个物理引脚可能有多种功能(比如作为 UART 的 TX,或者作为 GPIO)。
我们必须明确告诉芯片:这个引脚,我要当 GPIO 用。
对应的寄存器是 IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 ,地址 0x02290000 + 0x14 = 0x02290014。
| 位域 | 值 | 含义 |
|---|---|---|
| bit[3:0] | 0b0101 (5) | 选择 ALT5 模式,即 GPIO5_IO03 |
所以我们要往这个地址写入 0x5。
小提示:在芯片手册里,这个引脚的名字是
SNVS_TAMPER3,它的 ALT5 模式就是 GPIO5_IO03。

4.3 设置方向 ------ 输出模式
现在引脚已经是 GPIO 了,但它默认是 输入 模式,我们要改为 输出。
方向控制寄存器是 GPIO5_GDIR ,地址 0x020AC004。
每一个 bit 对应一个引脚:
-
bit = 0 → 输入
-
bit = 1 → 输出
我们要控制 GPIO5 的第 3 个引脚,所以需要把 bit3 设置为 1。
cs
c
*((volatile unsigned int *)0x020AC004) |= (1 << 3);

4.4 输出电平 ------ 点亮或熄灭 LED
数据寄存器是 GPIO5_DR ,地址 0x020AC000。
同样,bit3 控制第 3 个引脚的电平:
-
写入 0 → 输出低电平 → LED 亮
-
写入 1 → 输出高电平 → LED 灭
cs
c
// 点亮 LED
*((volatile unsigned int *)0x020AC000) &= ~(1 << 3);
// 熄灭 LED
*((volatile unsigned int *)0x020AC000) |= (1 << 3);
五、把上面的步骤写成代码(伪代码框架)
cs
c
// 定义寄存器地址
#define CCM_CCGR1 (*(volatile unsigned int *)0x020C406C)
#define IOMUX_SNVS_T3 (*(volatile unsigned int *)0x02290014)
#define GPIO5_GDIR (*(volatile unsigned int *)0x020AC004)
#define GPIO5_DR (*(volatile unsigned int *)0x020AC000)
void led_init(void)
{
// 1. 使能 GPIO5 时钟(其实可以省略,但写上也没错)
// CCM_CCGR1 |= (3 << 30); // 如果前面没开,就加上这行
// 2. 设置引脚为 GPIO 模式 (ALT5)
IOMUX_SNVS_T3 = 5;
// 3. 设置为输出方向
GPIO5_GDIR |= (1 << 3);
}
void led_on(void)
{
GPIO5_DR &= ~(1 << 3); // 输出低电平
}
void led_off(void)
{
GPIO5_DR |= (1 << 3); // 输出高电平
}
实际开发中,我们会把这些地址定义放在一个
regs.h头文件里,代码会更整洁。
六、上机实验(裸机方式)
-
编写完整的
led.c和main.c,在main里调用led_init(),然后循环亮灭。 -
使用交叉编译器
arm-linux-gnueabihf-gcc编译。 -
用
mkimage工具制作.imx映像文件。 -
通过 USB 或 SD 卡烧写到开发板。
-
上电,你会看到 LED 按照你的程序闪烁。
如果不想烧写,也可以直接在 Linux 下写一个简单的 GPIO 驱动(后续会讲到)。
七、总结与延伸
今天我们学到的内容,其实已经涵盖了 嵌入式 Linux 驱动开发 的最底层知识:
-
看原理图 → 确定引脚
-
看芯片手册 → 找到对应的寄存器
-
写程序 → 操作寄存器
以后你写的任何驱动程序(LED、按键、UART、I2C...)都离不开这三步。
在 Linux 下,我们会用
ioremap映射物理地址,用device tree描述引脚资源,但最终落到底层,依然是操作这些寄存器。
下一节课,我们将把这个裸机 LED 程序,改造成一个标准的 Linux 字符设备驱动。
到时候你会发现 ------ 框架变了,但点亮 LED 的本质从未改变。