正点原子 I.MX6ULL MINI 开发板学习笔记:GPIO 输出与 LED 点灯

这篇笔记记录在正点原子 I.MX6ULL MINI 开发板上完成的最基础实验——GPIO 输出控制 LED 点灯。内容来自实际工程 /home/zhao/code/2026/touch_screen 中的验证代码,适合作为裸机学习的第一站。
1. 完成什么实验?
通过配置 I.MX6ULL 的 GPIO1_IO03 引脚为输出模式,控制板载 LED 的亮灭和翻转。
2. 什么是 GPIO?
GPIO(General Purpose Input/Output,通用输入输出)是 SoC 上最基础的可编程引脚。每个 GPIO 引脚可以被配置为输入或输出:输出时 CPU 可以控制引脚电平(高/低),输入时 CPU 可以读取外部电平。

3. I.MX6ULL GPIO 主要寄存器有哪些?
I.MX6ULL 的每个 GPIO 组(GPIO1~GPIO5)都有一套 32 位寄存器,输出点灯主要用到下面三个:
- DR(Data Register):数据寄存器,写 1/0 控制引脚输出高/低电平。
- GDIR(GPIO Direction Register):方向寄存器,某位为 1 表示对应引脚为输出,为 0 表示输入。
- PSR(Pad Status Register):只读,反映引脚当前真实电平。
4. 什么是引脚复用(IOMUXC)?
I.MX6ULL 的一个物理引脚往往可以承担多种功能(UART、PWM、GPIO 等),具体用哪种由 IOMUXC 的 MUX 寄存器决定。点灯时我们要把 UART1_RTS_B/GPIO1_IO03 这个引脚复用成 GPIO 功能,并配置上拉、驱动能力等电气属性。

5. LED 硬件连接与点灯原理
正点原子 MINI 开发板的 LED0 一端通过限流电阻接到 3.3V,另一端接到 GPIO1_IO03。因此当 GPIO1_IO03 输出低电平时,LED 两端形成压差,LED 点亮;输出高电平时熄灭。也就是说:代码里往 DR 寄存器的 bit3 写 0,灯就亮了。

6. 真实可用的 GPIO/LED 代码
下面是工程中验证过的核心代码,只保留 GPIO 输出相关部分。
#ifndef _GPIO_H_
#define _GPIO_H_
#include "MCIMX6Y2.h"
typedef enum
{
gpio_direction_input,
gpio_direction_output
} gpio_direction_t;
typedef struct
{
gpio_direction_t pin_direction;
int default_vaule;
} gpio_configt_t;
extern void init_gpio(GPIO_Type *gpio, int pin, gpio_configt_t *config);
extern void write_gpio(GPIO_Type *gpio, int pin, int value);
extern int read_gpio(GPIO_Type *gpio, int pin);
#endif
#include "gpio.h"
#include "fsl_iomuxc.h"
#include "MCIMX6Y2.h"
void init_gpio(GPIO_Type *gpio, int pin, gpio_configt_t *config)
{
if (config->pin_direction == gpio_direction_output)
{
gpio->GDIR |= (1 << pin); /* 配置为输出 */
if (config->default_vaule != 0)
gpio->DR |= (1 << pin);
else
gpio->DR &= ~(1 << pin);
}
else
{
gpio->GDIR &= ~(1 << pin); /* 配置为输入 */
}
}
void write_gpio(GPIO_Type *gpio, int pin, int value)
{
if (value)
gpio->DR |= (1 << pin); /* 输出高 */
else
gpio->DR &= ~(1 << pin); /* 输出低 */
}
int read_gpio(GPIO_Type *gpio, int pin)
{
return (gpio->DR & (1 << pin)) != 0;
}
#include "led.h"
#include "fsl_iomuxc.h"
#include "MCIMX6Y2.h"
#include "gpio.h"
void init_led(void)
{
/* 1. 将 UART1_RTS_B 复用为 GPIO1_IO03 */
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0);
/* 2. 配置电气属性 */
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03, 0x10B0);
/* 3. 通过 gpio 驱动配置为输出,默认高电平(熄灭) */
gpio_configt_t t = {
.pin_direction = gpio_direction_output,
.default_vaule = 1
};
init_gpio(GPIO1, 3, &t);
}
void led_off(void) { write_gpio(GPIO1, 3, 1); } /* 高电平熄灭 */
void led_on(void) { write_gpio(GPIO1, 3, 0); } /* 低电平点亮 */
void led_nor(void) { write_gpio(GPIO1, 3, !read_gpio(GPIO1, 3)); }
#include "led.h"
#include "gpt.h"
int main(void)
{
init_gpt1(); /* 提供延时函数 */
init_led();
led_off();
while (1)
{
led_nor(); /* 翻转 LED */
delay_ms(500); /* 延时 500ms */
}
return 0;
}
7. 关键点说明
- 时钟使能:GPIO 模块时钟由 CCM_CCGR1 等寄存器门控,使用 GPIO 前必须使能;本工程启动代码(init_clock/enable_clock)已把所有 CCGR 统一写 0xFFFFFFFF 打开,所以点灯代码里没有体现。
- 引脚复用:必须先通过
IOMUXC_SetPinMux把物理引脚选到 GPIO 功能,否则写 DR 不会体现在引脚上。 - 电气属性:
0x10B0配置了压摆率、驱动能力等;其 PUE=0、PKE=1,打开的是 Keeper(保持器)而不是上下拉,对 LED 这种小电流负载足够。 - 方向寄存器:GDIR 对应位写 1 才表示输出,否则 DR 写入无效。
- 低电平有效:LED 接在 3.3V 与 GPIO 之间,写 0 点亮,写 1 熄灭,不要和“高电平点亮”搞混。
8. 实验现象
程序下载运行后,板载 LED0 会以大约 1Hz 的频率闪烁:亮 500ms、灭 500ms。如果把 <code>delay_ms(500)</code> 改成更小的值,闪烁会变快;改成 <code>led_on()</code> 常亮也可以直接验证高低电平。
总结
| 问题 | 答案 |
|---|---|
| 什么是 GPIO? | 通用输入输出引脚,可配置为输入或输出 |
| 点灯主要用哪两个寄存器? | GDIR(方向)和 DR(数据) |
| 为什么要引脚复用? | 一个物理引脚可承担多种功能,IOMUXC 选择 GPIO |
| LED 是低电平还是高电平点亮? | 低电平点亮(GPIO1_IO03 输出 0) |
| 点灯四步走? | 复用 → 电气属性 → 配置方向 → 输出电平 |
GPIO 是裸机开发的地基,理解了引脚复用、方向寄存器和数据寄存器,后面的按键、蜂鸣器、PWM、I2C 都会反复用到这几个概念。