GPIO | i.MX6ULL芯片 | 基础学习
内容覆盖:
- GPIO概述(5组124个引脚,命名规则对比STM32)
- IO配置三步走:CCM时钟 → IOMUXC MUX → IOMUXC PAD
- 8个GPIO寄存器:DR、GDIR、PSR、ICR1/2、EDGE_SEL、IMR、ISR
- 寄存器地址表(5组GPIO基地址+偏移)
- 完整代码示例(LED控制,C语言)
- 与STM32关键区别对比
- 常见问题(原子操作、PSR vs DR、EDGE_SEL优先级)
GPIO 是什么
GPIO = General Purpose Input/Output(通用输入输出)
芯片通过 GPIO 引脚与外部世界交互:
-
输出:芯片控制外部设备(LED、电机、蜂鸣器)
-
输入:芯片读取外部信号(按键、传感器)
GPIO结构示意一:

说明:
1. IOMUXC: io mux controller : IO复用控制器(硬件模块)
SW_MUX: software mux control :软件复用控制寄存器(软件配置接口)
IOMUXC 是模块名,SW_MUX 是其中的寄存器名。
2. Pin指芯片封装好后的管脚,即用户能够看到的管脚,PAD是硅片的管脚,是封装在芯片内部的,用户看不到。PAD到PIN之间还有一段导线连接的。
一、i.MX6ULL GPIO 概述
1.1 GPIO 分组
i.MX6ULL 共有 5组GPIO,总计 124个引脚:
注:除了124个GPIO功能引脚,i.MX6ULL 还有165个其它功能引脚,共计289个物理引脚
| GPIO组 | 引脚数量 | 说明 |
|---|---|---|
| GPIO1 | 32个 | IO0~IO31 |
| GPIO2 | 22个 | IO0~IO21 |
| GPIO3 | 29个 | IO0~IO28 |
| GPIO4 | 29个 | IO0~IO28 |
| GPIO5 | 12个 | IO0~IO11 |
注:GDIR是 GPIO 方向寄存器,每一位对应一个 IO 引脚的 方向:置 1 = 输出,置 0 = 输入。
列:
/*Direction:Output*/
GPIO1 ->GDIR |= (1 << 27); // 第一组GPIO中,第27个引脚(管脚) 设置为 1(输出模式)。
1.2 命名规则(与STM32不同!)
STM32 (命名:端口+编号):PA0~PA15, PB0~PB15...
i.MX6ULL (命名:引脚功能):GPIO1_IO00, GPIO5_IO03, UART1_TX_DATA...
-
命名依据引脚的功能,而不是端口+编号
-
看到
GPIO1_IO00→ 知道这是GPIO功能 -
看到
UART1_TX_DATA→ 知道这是串口发送功能。 -
同一个引脚可以复用作GPIO,具体某个引脚能复用哪些功能,必须查数据手册确认。
GPIO功能:通用输入输出,软件直接控制电平高低,简单灵活。
串口发送功能:硬件专用功能,自动处理时序、波特率、数据格式,高效可靠。
1.3 两类IO
-
SNVS域:SNVS_TAMPER0~SNVS_TAMPER9(低功耗域,始终供电)
-
通用域:其余所有IO(GPIO1~GPIO5)
二、IO配置三步走(与STM32流程对比)
IO配置本质:
1.每个寄存器32位(二进制),每位对应一个IO(管脚)或一个属性。
2.IO配置:即按手册,设置对应寄存器对应位为0或1.
补充:
1.位域: 一个功能可能占多位(如DSE占3位),不是简单的0/1
2.保留位:未定义位必须写0,否则可能出错
3.原子性:修改某位时不能影响其他位(读-改-写)
配置流程对比
| 步骤 | STM32 | i.MX6ULL |
|---|---|---|
| 1. 使能时钟 | RCC寄存器 | CCM_CCGR0~6(可选,默认打开的) |
| 2. 设置复用功能 | MODER/AFR | IOMUXC MUX寄存器 |
| 3. 配置电气属性 | PUPDR/OSPEEDR | IOMUXC PAD寄存器 (可选,可以用默认设置) |
| 4. 配置输入/输出 | MODER | GPIO_GDIR (默认值是0-->输入模式) |
| 5. 读写数据 | ODR/IDR/BSRR | GPIO_DR |
2.1 第一步:使能GPIO时钟(CCM)
CCM(Clock Controller Module)控制所有外设时钟。
7个时钟使能寄存器:CCM_CCGR0 ~ CCM_CCGR6
每个CCM_CCGR是32位,每2位控制一个外设的时钟:
00 = 所有模式关闭
01 = 仅运行模式开启
10 = 保留
11 = 除停止模式外一直开启
各GPIO时钟配置位置:
| GPIO组 | 寄存器 | 位 | 基地址 |
|---|---|---|---|
| GPIO1 | CCGR1 | CG13 (bit26:27) | 0x020C406C |
| GPIO2 | CCGR0 | CG15 (bit30:31) | 0x020C4068 |
| GPIO3 | CCGR2 | CG13 (bit26:27) | 0x020C4070 |
| GPIO4 | CCGR3 | CG6 (bit12:13) | 0x020C4074 |
| GPIO5 | CCGR1 | CG15 (bit30:31) | 0x020C406C |
// 示例:开启GPIO1时钟
// 心脏==>时钟信号 设置为 11 模式 :除了暂停模式,其它模式一直运行
// #define CCM_CCGR1 *((volatile unsigned int *)0x20C406C)
CCM_CCGR1 |= (0x3 << 26); // CCGR1的bit26:27设为11(二进制)
2.2 第二步:设置IO复用功能(IOMUXC MUX)
每个IO都有一个 MUX寄存器,决定这个引脚做什么功能。
寄存器命名:IOMUXC_SW_MUX_CTL_PAD_<PAD NAME> :用来配置管脚的复用功能
寄存器结构(32位,只用低5位):
| 位 | 名称 | 说明 |
|---|---|---|
| bit[3:0] | MUX_MODE | ALT0~ALT8,选择复用功能 |
| bit[4] | SION | 输出模式下开启输入通道(回环) |
MUX_MODE 值对应功能:
| 值 | 模式 | 说明 |
|---|---|---|
| 0000 | ALT0 | 默认功能 |
| 0001 | ALT1 | 功能1 |
| ... | ... | ... |
| 0101 | ALT5 | 作为GPIO功能 |
// 示例:
// *GPIO MODE *:配置管脚的复用功能
// 配置 第一组管脚的 第27个管脚 为GPIO模式(通用输入输出模式)
// #define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO27 *((volatile unsigned int *)0x20E00B0)
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO27 &= ~(0xf << 0); // 最后4位 清零
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO27 |= (0x5 << 0); // 设置为 5(101):GPIO模式
-
查手册第32章(IOMUX Controller)
-
搜索
IOMUXC_SW_MUX_CTL_PAD_GPIOx_IOxx -
找到寄存器地址和ALT值
3. 第三步:配置PAD电气属性(IOMUXC PAD)
每个IO还有一个 PAD寄存器,配置电气特性。
寄存器命名:IOMUXC_SW_PAD_CTL_PAD_XXXX
寄存器结构(32位,用低17位):
| 位 | 名称 | 说明 | 默认 |
|---|---|---|---|
| bit[16] | HYS | 迟滞比较器(施密特触发器),输入整形用 | 0 :滞后功能已禁用 |
| bit[15:14] | PUS | 上下拉电阻选择 | 00 :100 千欧下拉电阻 |
| bit[13] | PUE | 0=状态保持器,1=上下拉 | 0 :状态保持 |
| bit[12] | PKE | 使能上下拉/保持器 | 1 : 上下拉取 / 保持器功能启用 |
| bit[11] | ODE | 开漏输出(0=禁止,1=使能) | 0 :禁止 |
| bit[7:6] | SPEED | 输出速度 | 10 :中速 100MHz |
| bit[5:3] | DSE | 驱动能力(8档) | 110 :6档 驱动能力 |
| bit[0] | SRE | 压摆率控制 | 0 :低转换速率 |
PUS 上下拉选择:
| 值 | 说明 |
|---|---|
| 00 | 100K 下拉电阻 |
| 01 | 47K 上拉电阻 |
| 10 | 100K 上拉电阻 |
| 11 | 22K 上拉电阻 |
SPEED 输出速度:
| 值 | 速度 |
|---|---|
| 00 | 低速 50MHz |
| 01/10 | 中速 100MHz |
| 11 | 高速 200MHz |
DSE 驱动能力(000~111,8档,值越大驱动越强)
// 配置 GPIO1_IO04 引脚 电器属性
// 功能:47K上拉 + 中速 + 中等驱动能力
// 寄存器:SW_PAD_CTL_PAD_GPIO1_IO04 (地址 0x020E02F8)
// 写入值:0x7071 = 0b0 0111 0000 0111 0001
//
// 位定义:
// bit16 HYS=0 (迟滞关闭)
// bit15-14 PUS=01 (47K上拉)
// bit13 PUE=1 (使用上下拉)
// bit12 PKE=1 (keeper使能)
// bit11 ODE=0 (开漏关闭)
// bit7-6 SPEED=01 (中速)
// bit5-3 DSE=110 (R0/6驱动能力)
// bit0 SRE=1 (快速压摆率)
*(volatile unsigned long*)0x020E02F8 = 0x7071;
三、GPIO核心寄存器(8个32位寄存器)
i.MX6ULL 每组GPIO有 8个寄存器:
| 寄存器 | 缩写 | 功能 | 读/写 |
|---|---|---|---|
| Data Register | DR | 输出/输入数据 | R/W |
| Direction Register | GDIR | 设置输入/输出方向 | R/W |
| Pad Sample Register | PSR | 读取引脚当前状态 | 只读 |
| Interrupt Config 1 | ICR1 | 低16位中断配置 | R/W |
| Interrupt Config 2 | ICR2 | 高16位中断配置 | R/W |
| Edge Select Register | EDGE_SEL | 边沿选择(覆盖ICR) | R/W |
| Interrupt Mask Register | IMR | 中断使能/屏蔽 | R/W |
| Interrupt Status Register | ISR | 中断状态标志 | R/W(写1清除) |
3.1 DR(Data Register)数据寄存器
32位,每个bit对应一个GPIO引脚
输出模式(GDIR=1):写DR控制引脚输出高低电平
输入模式(GDIR=0):读DR获取引脚输入电平
// 点亮LED0:设置GPIO1_IO27输出高电平
//
// 原理:
// GPIO1->DR 是数据寄存器(Data Register),地址 0x0209C000
// 每位控制一个引脚:1=高电平(3.3V),0=低电平(0V)
// LED0连接在GPIO1的第27引脚,对应寄存器的第27位
//
// 操作:|= (1 << 27) 表示"只把第27位置1,其他位保持原样"
//
/*
typedef struct
{
__IO uint32_t DR; // GPIO data register, 地址偏移: 0x0 (0个字节)
__IO uint32_t GDIR; // GPIO direction register, 地址偏移: 0x4 (4个字节)
__IO uint32_t PSR; // GPIO pad status register, 地址偏移: 0x8 (8个字节)
__IO uint32_t ICR1; // GPIO interrupt configuration register1, 地址偏移: 0xC (12个字节)
__IO uint32_t ICR2; // GPIO interrupt configuration register2, 地址偏移: 0x10 (16个字节)
__IO uint32_t IMR; // GPIO interrupt mask register, 地址偏移: 0x14 (20个字节)
__IO uint32_t ISR; // GPIO interrupt status register, 地址偏移: 0x18 (24个字节)
__IO uint32_t EDGE_SEL; // GPIO edge select register, 地址偏移: 0x1C (28个字节)
} GPIO_Type;
*/
// uint32_t 占4字节,所以,指针+1,地址+4字节
// #define GPIO1_BASE (0x209C000u)
// #define GPIO1 ((GPIO_Type *)GPIO1_BASE) // 强转 指针类型
GPIO1->DR |= (1 << 27); // 第27位写1 ==> LED0点亮
注意:写DR是直接写整个32位,不像STM32有BSRR原子操作。
3.2 GDIR(GPIO Direction Register)方向寄存器
32位,每个bit对应一个GPIO引脚
| bit值 | 方向 |
|---|---|
| 0 | 输入模式 |
| 1 | 输出模式 |
// 示例:设置GPIO1_IO27为输出模式
// 操作:|= (1 << 27) 表示"只把第27位置1,其他位保持原样"
// 27 : 指的是第27个管脚
GPIO1->GDIR |= (1 << 27); // 方向设置为 输出模式 1 => 27位为1
3.3 PSR(Pad Sample Register)引脚状态寄存器
32位,只读
-
无论GDIR设置为何值,PSR始终反映引脚当前的电平状态
-
电平状态:1 => 高电平 , 0 => 低电平
-
输出模式下,如果MUX开启了SION(回环),可以通过PSR读回输出状态
// 读取GPIO1_IO26的当前电平
// 1 => 高电平 , 0 => 低电平
int level = GPIO1->PSR & (1 << 26);
3.4 ICR1/ICR2(Interrupt Configuration Registers)中断配置
ICR1:配置低16位(IO0~IO15)的中断触发方式
ICR2:配置高16位(IO16~IO31)的中断触发方式
每个GPIO用2个bit配置中断触发方式:
| bit值 | 触发方式 |
|---|---|
| 00 | 低电平触发 |
| 01 | 高电平触发 |
| 10 | 上升沿触发 |
| 11 | 下降沿触发 |
// 示例:设置GPIO1_IO15为上升沿触发
// IO15在ICR1中,占用bit30:31
// 上升沿 = 10(二进制)
GPIO1.ICR1 |= (2 << 30); // 2 = 0b10
3.5 EDGE_SEL(Edge Select Register)边沿选择
32位,每个bit对应一个GPIO
-
当bit=1时,对应GPIO设置为双边沿触发(上升+下降都触发)
-
会覆盖ICR1/ICR2的设置
// 设置GPIO1_IO00为双边沿触发
// EDGE_SEL的bit0 = 1
*(volatile unsigned long*)0x0209C014 |= (1 << 0);
3.6 IMR(Interrupt Mask Register)中断屏蔽
32位,每个bit对应一个GPIO
| bit值 | 中断状态 |
|---|---|
| 0 | 中断被屏蔽(禁用) |
| 1 | 中断使能 |
// 使能GPIO1_IO00的中断
*(volatile unsigned long*)0x0209C010 |= (1 << 0);
3.7 ISR(Interrupt Status Register)中断状态
32位,每个bit对应一个GPIO
-
中断发生时,对应bit被硬件置1
-
清除方法:向对应bit写1(写1清零)
// 检查GPIO1_IO00是否发生中断
uint32_t isr = *(volatile unsigned long*)0x0209C00C;
if (isr & (1 << 0)) {
// 发生中断,清除标志
*(volatile unsigned long*)0x0209C00C = (1 << 0); // 写1清零
}
四、GPIO寄存器地址表
4.1 GPIO基地址
| GPIO组 | 基地址 |
|---|---|
| GPIO1 | 0x0209C000 |
| GPIO2 | 0x020A0000 |
| GPIO3 | 0x020A4000 |
| GPIO4 | 0x020A8000 |
| GPIO5 | 0x020AC000 |
4.2 寄存器偏移
| 寄存器 | 偏移 | 完整地址示例(GPIO5) |
|---|---|---|
| DR | +0x00 | 0x020AC000 |
| GDIR | +0x04 | 0x020AC004 |
| PSR | +0x08 | 0x020AC008 |
| ICR1 | +0x0C | 0x020AC00C |
| ICR2 | +0x10 | 0x020AC010 |
| IMR | +0x14 | 0x020AC014 |
| ISR | +0x18 | 0x020AC018 |
| EDGE_SEL | +0x1C | 0x020AC01C |
五、完整配置示例(LED控制)
5.1 硬件示例
电路图:

如图,信号是UART3_RTS,链接管脚是GPIO1_IO[27](第一组管脚的第27个管脚)
目标:让信号UART3_RTS 输出高低电平 来控制LED灯亮灭。
信号 UART3_RTS 输出 高电平 => LED灯亮,
信号 UART3_RTS 输出 低电平 => LED灯灭
5.2 C语言代码
// imx6ull.h
// #define CCM_CCGR1 *((volatile unsigned int *)0x20C406C)
// #define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO27 *((volatile unsigned int *)0x20E00B0)
/* GPIO - Register Layout Typedef */
/*
typedef struct
{
__IO uint32_t DR; // GPIO data register, 地址偏移: 0x0 (0个字节)
__IO uint32_t GDIR; // GPIO direction register, 地址偏移: 0x4 (4个字节)
__IO uint32_t PSR; // GPIO pad status register, 地址偏移: 0x8 (8个字节)
__IO uint32_t ICR1; // GPIO interrupt configuration register1, 地址偏移: 0xC (12个字节)
__IO uint32_t ICR2; // GPIO interrupt configuration register2, 地址偏移: 0x10 (16个字节)
__IO uint32_t IMR; // GPIO interrupt mask register, 地址偏移: 0x14 (20个字节)
__IO uint32_t ISR; // GPIO interrupt status register, 地址偏移: 0x18 (24个字节)
__IO uint32_t EDGE_SEL; // GPIO edge select register, 地址偏移: 0x1C (28个字节)
} GPIO_Type;
*/
// uint32_t 占4字节,所以,指针+1,地址+4字节
// #define GPIO1_BASE (0x209C000u)
// #define GPIO1 ((GPIO_Type *)GPIO1_BASE) // 强转 指针类型
#include "imx6ull.h"
// LED灯 初始化
void led_init(void)
{
// 1. 开启GPIO1 时钟
// 心脏==>时钟信号 设置为 11 模式 :除了暂停模式,其它模式一直运行
CCM_CCGR1 |= (0x3 << 26); // CCGR1的bit26:27 设为11(二进制)
// 2. 配置GPIO1_IO27管脚的 复用功能
// 配置第一组管脚的 第27个管脚为 GPIO模式(通用输入输出模式)
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO27 &= ~(0xf << 0); // 最后4位 清零
IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO27 |= (0x5 << 0); // 设置为 5(101):GPIO模式
// 3. GPIO1_IO27管脚的电器属性,使用的是默认值。所以,没有配置
// 4. 设置GPIO1_IO27为 输出模式
// 操作:|= (1 << 27) 表示"只把第27位置1,其他位保持原样"
// 27 : 指的是第27个管脚
GPIO1->GDIR |= (1 << 27); // 方向设置为 输出模式 1 => 27位为1
}
// LED灯 亮
void led_on(void)
{
// 点亮LED灯:设置GPIO1_IO27输出高电平
// 信号连接在GPIO1的第27引脚,对应寄存器的第27位
// 操作:|= (1 << 27) 表示"只把第27位置1,其他位保持原样"
GPIO1->DR |= (1 << 27); // 设置为高电平 :27位为1 ==> MOS管连通 ==> LED灯亮
}
// LED灯 灭
void led_off(void)
{
// 熄灭LED灯:设置GPIO1_IO27输出低电平
// 信号连接在GPIO1的第27引脚,对应寄存器的第27位
// 操作:&= ~(1 << 27) 表示"只把第27位置0,其他位保持原样"
GPIO1->DR &= ~(1 << 27); // 设置为低电平 :27位为0 ==> MOS管断开 ==> LED灯灭
}
// 延迟 time 时间
void delay_time(uint32_t time)
{
int i;
int j;
for(i = 0;i < time * 1000;i ++){
for(j = 0;j < 50000;j ++){
}
}
return;
}
// LED灯 测试
void led_test(void)
{
led_init(); // LED灯 初始化
while(1){
led_on(); // LED灯 亮
delay_time(1); // 延迟1秒
led_off(); // LED灯 灭
delay_time(1); // 延迟1秒
}
return;
}
六、i.MX6ULL vs STM32 关键区别总结
| 特性 | STM32 | i.MX6ULL |
|---|---|---|
| GPIO命名 | PA0~PA15 | GPIO1_IO00 |
| 时钟使能 | RCC | CCM_CCGR0~6 |
| 复用配置 | MODER + AFR | IOMUXC MUX寄存器 |
| PAD配置 | PUPDR, OSPEEDR等 | IOMUXC PAD寄存器 |
| 方向设置 | MODER | GDIR寄存器 |
| 输出数据 | ODR | DR寄存器 |
| 输入读取 | IDR | DR寄存器(或PSR) |
| 原子操作 | BSRR(置位/复位分离) | 无,需用 &= ~ 和 |
| 中断配置 | EXTI + NVIC | ICR1/2 + IMR + ISR |
| 边沿选择 | EXTI寄存器 | EDGE_SEL(覆盖ICR) |
| CPU架构 | Cortex-M(单片机) | Cortex-A7(应用处理器) |
| 操作系统 | 裸机/RTOS | 通常跑Linux |
七、常见问题
Q1: i.MX6ULL没有BSRR,怎么原子操作?
原子操作(Atomic Operation) 指的是不可被中断的最小操作单位。
没有。直接读-改-写DR:
// 置高GPIO5_IO03
*GPIO5_DR_BASE |= (1 << 3);
// 置低GPIO5_IO03
*GPIO5_DR_BASE &= ~(1 << 3);
如果在Linux下,需要加锁保护(spin_lock)。
Q2: 为什么有的IO有MUX,有的没有?
所有IO都有MUX。普通IO的MUX在IOMUXC模块,SNVS域的IO的MUX在IOMUXC_SNVS模块(地址不同)。
Q3: PSR和DR读取有什么区别?
-
DR:读的是输出寄存器(输出模式下是你要输出的值)
-
PSR:读的是引脚实际电平(始终反映真实状态)
-
输入模式下,两者都反映输入电平
-
输出模式下,只有开启SION才能在PSR读回输出值
Q4: EDGE_SEL和ICR1/ICR2冲突吗?
EDGE_SEL优先级更高。如果EDGE_SEL某位=1,对应ICR配置被覆盖,强制双边沿触发。
八、寄存器地址速查(GPIO5为例)
GPIO5基地址:0x020AC000
GPIO5_DR = 0x020AC000 // 数据寄存器
GPIO5_GDIR = 0x020AC004 // 方向寄存器
GPIO5_PSR = 0x020AC008 // 引脚状态
GPIO5_ICR1 = 0x020AC00C // 中断配置(低16位)
GPIO5_ICR2 = 0x020AC010 // 中断配置(高16位)
GPIO5_IMR = 0x020AC014 // 中断屏蔽
GPIO5_ISR = 0x020AC018 // 中断状态
GPIO5_EDGE_SEL = 0x020AC01C // 边沿选择
时钟:CCM_CCGR1 (0x020C406C) bit30:31 = 11
MUX :IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 (0x02290014)
PAD :IOMUXC_SNVS_SW_PAD_CTL_PAD_SNVS_TAMPER3 (0x02290058)
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)