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

分享
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 可以读取外部电平。

GPIO 常用寄存器

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,灯就亮了。

LED 控制流程

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 都会反复用到这几个概念。

继续阅读

嵌入式开发面试知识手册

嵌入式开发工程师面试知识手册 适用对象:零基础起步、目标是通过嵌入式软件/驱动/BSP 方向面试的读者 编写依据:赵哲简历全部知识点 + 智能车 AGV 控制平台完整项目代码(i.MX6ULL Linux 端 + STM32F407 固件)+ Linux 驱动练习代码包 + embedded-agent 嵌入式开发智能体仓库 + 真实面试手撕代码与追问记录 使用方法:每篇按"通俗解释 → 原理细节 → 命令/API → 代码实例 → 项目结合 → 记忆口诀 → 面试问答"组织;口诀用于快速记忆,问答用于自测。第9篇是讲项目的演练场,第10篇是手撕代码实战题库。 总目录 * 第1篇 C语言与数据结构 * 1.1 编译链接全过程 * 1.2 数据类型与内存

作者:azhezhezhe

正点原子 I.MX6ULL MINI 开发板学习笔记:Linux 驱动架构详解——编译、树内树外与运行时效果

这是 Linux 驱动小系列的总结篇。前三篇把 LED 驱动从内核模块写到 misc 设备,属于“会用”;这篇把背后的架构讲透:驱动到底怎么编译、内核树内和树外两种方式的区别、以及驱动里每个操作在系统跑起来之后到底产生了什么效果。内容整理自我的学习笔记(note.pdf)和实际工程 code/2026/20260803。 1. 驱动代码到底怎么编译的? 不管哪种方式,最终都靠内核的 kbuild 系统。核心就三个文件的关系: * Kconfig:定义 menuconfig 里的选项(如 CONFIG_MYMODULE),三态 y/m/n——y 编进内核,m 编成模块,n 不编; * .config:menuconfig 的选择结果,保存在源码顶层; * Makefile:

作者:azhezhezhe

正点原子 I.MX6ULL MINI 开发板学习笔记:misc 设备——字符设备驱动的快捷方式

上两篇分别用手动 mknod 和 class_create+device_create 注册字符设备,调用链一长串。这篇用 misc(混杂)设备 一步搞定。代码来自实际工程 code/2026/20260803/05_misc_led,已验证可用。 1. 完成什么实验? 用 misc_register 重写 LED 驱动:一步完成设备号分配、字符设备注册、/dev/myled0 节点创建,同时保留上一篇的 sysfs 属性 led_status。 2. 什么是 misc 设备? misc(混杂)设备是 Linux 给“不好归类的小字符设备”

作者:azhezhezhe