title: GPIO与中断系统 tags: [GPIO, 中断, IMX6ULL, 裸机开发, Cortex-A7, GIC] created: 2026-09-16 updated: 2026-09-17
💡 关联知识: [[STM32学习笔记/02-GPIO寄存器详解与三步进化法]] | [[STM32学习笔记/04-GPIO输入与NVIC中断系统]] | [[嵌入式Linux驱动开发实战/01-ARM架构与裸机编程/01-ARM架构概述]]
GPIO(General Purpose Input/Output)是通用输入输出接口,是处理器与外部设备交互的基础通道。IMX6ULL芯片提供5组GPIO(GPIO1~GPIO5),每组包含32个IO引脚,总共160个GPIO引脚。
flowchart LR
accTitle: GPIO功能架构
accDescr: GPIO引脚通过IOMUXC复用控制器连接到处理器内核,支持输入输出和中断功能
subgraph 外设
A[🔴 LED]
B[🔘 按键]
C[📟 LCD]
D[📡 传感器]
end
subgraph GPIO控制器
E[🔌 IOMUXC复用]
F[⚙️ GPIO控制器]
end
subgraph 处理器内核
G[🧠 Cortex-A7]
end
A & B & C & D --> E
E --> F
F --> G
| 特性 | 描述 |
|---|---|
| 输入模式 | 读取外部电平状态(0/1) |
| 输出模式 | 控制引脚输出高/低电平 |
| 中断功能 | 支持边沿触发和电平触发 |
| 引脚复用 | 通过IOMUXC配置引脚功能 |
| 电气特性 | 可配置驱动强度、上下拉电阻 |
/* GPIO基地址定义 - IMX6ULL参考手册 Section 28 */
#define GPIO1_BASE 0x0209C000
#define GPIO2_BASE 0x020A0000
#define GPIO3_BASE 0x020A4000
#define GPIO4_BASE 0x020A8000
#define GPIO5_BASE 0x020AC000
/* GPIO寄存器偏移 - IMX6ULL参考手册 Table 28-3 */
#define GPIO_DR 0x000 /* 数据寄存器 */
#define GPIO_GDIR 0x004 /* 方向寄存器 */
#define GPIO_PSR 0x008 /* 状态寄存器 */
#define GPIO_ICR1 0x00C /* 中断配置寄存器1 (pin0~15) */
#define GPIO_ICR2 0x010 /* 中断配置寄存器2 (pin16~31) */
#define GPIO_IMR 0x014 /* 中断屏蔽寄存器 */
#define GPIO_ISR 0x018 /* 中断状态寄存器 */
#define GPIO_EDGE_SEL 0x01C /* 边沿选择寄存器 */
/* IOMUXC基地址 - IMX6ULL参考手册 Section 8 */
#define IOMUXC_BASE 0x020E0000
/* 示例:GPIO1_IO03复用配置 */
/* MUX_MODE寄存器:选择引脚功能 */
#define SW_MUX_GPIO1_IO03 (*(volatile unsigned int *)(IOMUXC_BASE + 0x068))
/* PAD属性寄存器:配置电气特性 */
#define SW_PAD_GPIO1_IO03 (*(volatile unsigned int *)(IOMUXC_BASE + 0x2F4))
/* MUX_MODE值定义 - IMX6ULL参考手册 Table 8-23 */
#define MUX_MODE_GPIO 0x5 /* GPIO功能 */
#define MUX_MODE_ALT0 0x0 /* 复用功能0 */
#define MUX_MODE_ALT1 0x1 /* 复用功能1 */
/* ... 其他复用功能 */
/* PAD属性配置 */
#define PAD_CTL_SPEED_LOW 0x0 /* 低速 */
#define PAD_CTL_SPEED_MED 0x4 /* 中速 */
#define PAD_CTL_SPEED_HIGH 0x8 /* 高速 */
#define PAD_CTL_PULL_UP 0x20000 /* 上拉 */
#define PAD_CTL_PULL_DOWN 0x10000 /* 下拉 */
/* GPIO初始化函数 */
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
/* 1. 禁用中断 */
base->IMR &= ~(1U << pin);
/* 2. 设置方向 */
if (config->direction == kGPIO_DigitalInput)
{
base->GDIR &= ~(1 << pin); /* 输入 */
}
else
{
base->GDIR |= 1 << pin; /* 输出 */
gpio_pinwrite(base, pin, config->outputLogic);
}
/* 3. 配置中断触发方式 */
gpio_intconfig(base, pin, config->interruptMode);
}
/* GPIO输出控制 */
void gpio_pinwrite(GPIO_Type *base, int pin, int value)
{
if (value == 0U)
base->DR &= ~(1U << pin); /* 输出低电平 */
else
base->DR |= (1U << pin); /* 输出高电平 */
}
/* GPIO输入读取 */
int gpio_pinread(GPIO_Type *base, int pin)
{
return (((base->DR) >> pin) & 0x1);
}
DR寄存器是GPIO的数据寄存器,用于读取引脚状态(输入模式)或设置输出电平(输出模式)。
/* DR寄存器结构 - IMX6ULL参考手册 Figure 28-4 */
/*
* 位[31:0]:每个位对应一个GPIO引脚
* 输入模式:读取引脚实际电平
* 输出模式:写入值控制引脚输出
*/
#define GPIO_DR_BASE 0x0209C000
/* 示例代码 */
volatile unsigned int *gpio_dr = (volatile unsigned int *)GPIO_DR_BASE;
/* 读取GPIO1_IO18的值 */
int value = (*gpio_dr >> 18) & 0x1;
/* 设置GPIO1_IO18输出高电平 */
*gpio_dr |= (1 << 18);
/* 设置GPIO1_IO18输出低电平 */
*gpio_dr &= ~(1 << 18);
GDIR寄存器控制GPIO引脚的数据传输方向,0表示输入,1表示输出。
/* GDIR寄存器结构 - IMX6ULL参考手册 Figure 28-5 */
/*
* 位[31:0]:每个位对应一个GPIO引脚
* 0:输入模式
* 1:输出模式
*/
/* 示例代码 */
volatile unsigned int *gpio_gdir = (volatile unsigned int *)(GPIO1_BASE + GPIO_GDIR);
/* 设置GPIO1_IO18为输入模式 */
*gpio_gdir &= ~(1 << 18);
/* 设置GPIO1_IO18为输出模式 */
*gpio_gdir |= (1 << 18);
PSR寄存器用于读取GPIO引脚的当前状态,无论引脚被配置为输入还是输出。
/* PSR寄存器结构 - IMX6ULL参考手册 Figure 28-6 */
/*
* 位[31:0]:只读,每个位对应一个GPIO引脚的实际电平
* 0:低电平
* 1:高电平
*/
/* 示例代码 */
volatile unsigned int *gpio_psr = (volatile unsigned int *)(GPIO1_BASE + GPIO_PSR);
/* 读取GPIO1_IO18的实际电平 */
int pin_state = (*gpio_psr >> 18) & 0x1;
ICR寄存器用于配置GPIO中断的触发方式,分为ICR1(pin0~15)和ICR2(pin16~31)两个寄存器。
/* ICR寄存器结构 - IMX6ULL参考手册 Figure 28-7 */
/*
* 每个引脚占用2位,配置4种触发方式:
* 00:低电平触发
* 01:高电平触发
* 10:上升沿触发
* 11:下降沿触发
*/
/* 中断触发类型枚举 */
typedef enum _gpio_interrupt_mode
{
kGPIO_NoIntmode = 0U, /* 无中断功能 */
kGPIO_IntLowLevel = 1U, /* 低电平触发 */
kGPIO_IntHighLevel = 2U, /* 高电平触发 */
kGPIO_IntRisingEdge = 3U, /* 上升沿触发 */
kGPIO_IntFallingEdge = 4U, /* 下降沿触发 */
kGPIO_IntRisingOrFallingEdge = 5U, /* 上升沿和下降沿都触发 */
} gpio_interrupt_mode_t;
/* 配置GPIO中断触发方式 */
void gpio_intconfig(GPIO_Type *base, unsigned int pin,
gpio_interrupt_mode_t pin_int_mode)
{
volatile uint32_t *icr;
uint32_t icrShift;
icrShift = pin;
base->EDGE_SEL &= ~(1U << pin); /* 清除边沿选择 */
if (pin < 16) /* 低16位使用ICR1 */
{
icr = &(base->ICR1);
}
else /* 高16位使用ICR2 */
{
icr = &(base->ICR2);
icrShift -= 16;
}
switch (pin_int_mode)
{
case (kGPIO_IntLowLevel):
*icr &= ~(3U << (2 * icrShift)); /* 00:低电平 */
break;
case (kGPIO_IntHighLevel):
*icr = (*icr & (~(3U << (2 * icrShift)))) |
(1U << (2 * icrShift)); /* 01:高电平 */
break;
case (kGPIO_IntRisingEdge):
*icr = (*icr & (~(3U << (2 * icrShift)))) |
(2U << (2 * icrShift)); /* 10:上升沿 */
break;
case (kGPIO_IntFallingEdge):
*icr |= (3U << (2 * icrShift)); /* 11:下降沿 */
break;
case (kGPIO_IntRisingOrFallingEdge):
base->EDGE_SEL |= (1U << pin); /* 边沿选择寄存器 */
break;
default:
break;
}
}
IMR寄存器用于控制GPIO中断的使能和禁止,0表示禁止中断,1表示使能中断。
/* IMR寄存器结构 - IMX6ULL参考手册 Figure 28-8 */
/*
* 位[31:0]:每个位对应一个GPIO引脚的中断屏蔽
* 0:禁止中断
* 1:使能中断
*/
/* 使能GPIO中断 */
void gpio_enableint(GPIO_Type *base, unsigned int pin)
{
base->IMR |= (1 << pin);
}
/* 禁止GPIO中断 */
void gpio_disableint(GPIO_Type *base, unsigned int pin)
{
base->IMR &= ~(1 << pin);
}
ISR寄存器用于指示中断是否发生,写1清除对应的中断状态标志。
/* ISR寄存器结构 - IMX6ULL参考手册 Figure 28-9 */
/*
* 位[31:0]:只读,每个位对应一个GPIO引脚的中断状态
* 0:中断未发生
* 1:中断已发生
* 写1清除对应中断状态
*/
/* 清除GPIO中断标志位 */
void gpio_clearintflags(GPIO_Type *base, unsigned int pin)
{
base->ISR |= (1 << pin); /* 写1清除 */
}
Cortex-A7内核有8个异常中断,中断向量表位于程序最前面:
flowchart LR
accTitle: Cortex-A7中断向量表
accDescr: 显示Cortex-A7处理器的8个异常中断及其向量地址
subgraph 中断向量表
A[0x00: 复位中断] --> B[0x04: 未定义指令中断]
B --> C[0x08: 软中断SWI]
C --> D[0x0C: 指令预取中止]
D --> E[0x10: 数据访问中止]
E --> F[0x14: 未使用]
F --> G[0x18: IRQ中断]
G --> H[0x1C: FIQ中断]
end
| 向量地址 | 中断类型 | 中断模式 | 描述 |
|---|---|---|---|
| 0x00 | 复位中断(Reset) | SVC | CPU复位后进入,完成初始化 |
| 0x04 | 未定义指令中断 | Undef | 指令不能识别时触发 |
| 0x08 | 软中断(SWI) | SVC | Linux系统调用使用此中断 |
| 0x0C | 指令预取中止 | Abort | 预取出错时触发 |
| 0x10 | 数据访问中止 | Abort | 数据访问出错时触发 |
| 0x14 | 未使用 | — | 保留 |
| 0x18 | IRQ中断 | IRQ | 外部中断,最常用 |
| 0x1C | FIQ中断 | FIQ | 快速中断,需要快速处理 |
IMX6ULL使用GIC-400中断控制器(ARM Generic Interrupt Controller),支持多核中断管理。
flowchart LR
accTitle: GIC中断控制器架构
accDescr: GIC控制器分为分发器端和CPU接口端,接收外部中断并传递给ARM内核
subgraph 中断源
A[SPI共享中断]
B[PPI私有中断]
C[SGI软件中断]
end
subgraph GIC控制器
D[分发器端Distributor]
E[CPU接口端Interface]
end
subgraph ARM内核
F[IRQ信号]
G[FIQ信号]
end
A & B & C --> D
D --> E
E --> F & G
F --> H[Cortex-A7]
| 中断类型 | ID范围 | 数量 | 描述 |
|---|---|---|---|
| SGI (Software-generated) | ID0~ID15 | 16 | 软件触发中断,用于多核通信 |
| PPI (Private Peripheral) | ID16~ID31 | 16 | 私有中断,每个核独有 |
| SPI (Shared Peripheral) | ID32~ID1019 | 988 | 共享中断,所有核共享 |
IMX6ULL总共使用160个中断ID(ID0~ID159),主要包括:
/* 中断ID定义 - 核心中断 */
typedef enum IRQn {
/* SGI中断 (ID0~ID15) */
Software0_IRQn = 0,
Software1_IRQn = 1,
// ... Software15_IRQn = 15
/* PPI中断 (ID16~ID31) */
VirtualMaintenance_IRQn = 25,
HypervisorTimer_IRQn = 26,
VirtualTimer_IRQn = 27,
LegacyFastInt_IRQn = 28,
SecurePhyTimer_IRQn = 29,
NonSecurePhyTimer_IRQn = 30,
LegacyIRQ_IRQn = 31,
/* SPI中断 (ID32~ID159) */
IOMUXC_IRQn = 32, /* IOMUXC中断 */
DAP_IRQn = 33, /* 调试端口中断 */
SDMA_IRQn = 34, /* SDMA中断 */
TSC_IRQn = 35, /* 触摸屏中断 */
SNVS_IRQn = 36, /* SNVS中断 */
// ... 其他中断
GPIO1_Combined_16_31_IRQn = 99, /* GPIO1_IO16~IO31组合中断 */
// ... 更多中断
PMU_IRQ2_IRQn = 159, /* PMU中断 */
} IRQn_Type;
#define NUMBER_OF_INT_VECTORS 160
/* GIC分发器端寄存器 - IMX6ULL参考手册 Section 3.2 */
#define GICD_BASE 0x01880000
/* 分发器控制寄存器 */
#define GICD_CTLR (*(volatile unsigned int *)(GICD_BASE + 0x000))
/* 中断使能寄存器 (16个,每个控制32个中断) */
#define GICD_ISENABLER(n) (*(volatile unsigned int *)(GICD_BASE + 0x100 + (n) * 4))
/* 中断禁止寄存器 */
#define GICD_ICENABLER(n) (*(volatile unsigned int *)(GICD_BASE + 0x180 + (n) * 4))
/* 中断优先级寄存器 */
#define GICD_IPRIORITYR(n) (*(volatile unsigned int *)(GICD_BASE + 0x400 + (n) * 4))
/* 中断目标CPU寄存器 */
#define GICD_ITARGETSR(n) (*(volatile unsigned int *)(GICD_BASE + 0x800 + (n) * 4))
/* 中断配置寄存器 */
#define GICD_ICFGR(n) (*(volatile unsigned int *)(GICD_BASE + 0xC00 + (n) * 4))
/* GIC CPU接口端寄存器 - IMX6ULL参考手册 Section 3.2 */
#define GICC_BASE 0x01840000
/* CPU接口控制寄存器 */
#define GICC_CTLR (*(volatile unsigned int *)(GICC_BASE + 0x000))
/* 优先级掩码寄存器 */
#define GICC_PMR (*(volatile unsigned int *)(GICC_BASE + 0x004))
/* 二进制点寄存器 */
#define GICC_BPR (*(volatile unsigned int *)(GICC_BASE + 0x008))
/* 中断应答寄存器 (读取中断号) */
#define GICC_IAR (*(volatile unsigned int *)(GICC_BASE + 0x00C))
/* 中断结束寄存器 (写入中断号结束中断) */
#define GICC_EOIR (*(volatile unsigned int *)(GICC_BASE + 0x010))
/* 运行优先级寄存器 */
#define GICC_RPR (*(volatile unsigned int *)(GICC_BASE + 0x014))
/* 最高优先级中断状态寄存器 */
#define GICC_HPPIR (*(volatile unsigned int *)(GICC_BASE + 0x018))
CP15协处理器用于系统管理,在中断系统中主要用于获取GIC基地址和设置中断向量表偏移。
/* CP15协处理器访问指令 */
/* MRC: 读取CP15寄存器到ARM寄存器 */
/* MCR: 写入ARM寄存器到CP15寄存器 */
/* 获取GIC基地址 */
/* GIC基地址保存在CP15的CBAR寄存器中 */
uint32_t gic_base;
__asm__ __volatile__(
"mrc p15, 4, %0, c15, c0, 0\n"
: "=r"(gic_base)
:
: "memory"
);
/* gic_base即为GIC基地址 */
/* 设置中断向量表偏移 */
/* 中断向量表基地址保存在CP15的VBAR寄存器中 */
void set_vbar(uint32_t vbar_addr)
{
__asm__ __volatile__(
"mcr p15, 0, %0, c12, c0, 0\n"
:
: "r"(vbar_addr)
: "memory"
);
}
/* 将中断向量表设置到0x87800000 */
set_vbar(0x87800000);
GPIO中断支持5种触发方式:
| 触发方式 | ICR值 | EDGE_SEL | 描述 |
|---|---|---|---|
| 低电平触发 | 00 | 0 | 引脚为低电平时持续触发 |
| 高电平触发 | 01 | 0 | 引脚为高电平时持续触发 |
| 上升沿触发 | 10 | 0 | 引脚电平从低到高跳变时触发 |
| 下降沿触发 | 11 | 0 | 引脚电平从高到低跳变时触发 |
| 双边沿触发 | — | 1 | 任意跳变都触发 |
flowchart LR
accTitle: GPIO中断配置流程
accDescr: 从引脚复用配置到中断服务函数注册的完整流程
A[1️⃣ 配置引脚复用] --> B[2️⃣ 设置GPIO方向]
B --> C[3️⃣ 配置中断触发方式]
C --> D[4️⃣ 使能GPIO中断]
D --> E[5️⃣ 使能GIC中断]
E --> F[6️⃣ 注册中断服务函数]
/* GPIO中断配置函数 */
void gpio_intconfig(GPIO_Type *base, unsigned int pin,
gpio_interrupt_mode_t pin_int_mode)
{
volatile uint32_t *icr;
uint32_t icrShift;
icrShift = pin;
base->EDGE_SEL &= ~(1U << pin); /* 清除边沿选择 */
if (pin < 16) /* 低16位使用ICR1 */
{
icr = &(base->ICR1);
}
else /* 高16位使用ICR2 */
{
icr = &(base->ICR2);
icrShift -= 16;
}
switch (pin_int_mode)
{
case (kGPIO_IntLowLevel):
*icr &= ~(3U << (2 * icrShift)); /* 00:低电平 */
break;
case (kGPIO_IntHighLevel):
*icr = (*icr & (~(3U << (2 * icrShift)))) |
(1U << (2 * icrShift)); /* 01:高电平 */
break;
case (kGPIO_IntRisingEdge):
*icr = (*icr & (~(3U << (2 * icrShift)))) |
(2U << (2 * icrShift)); /* 10:上升沿 */
break;
case (kGPIO_IntFallingEdge):
*icr |= (3U << (2 * icrShift)); /* 11:下降沿 */
break;
case (kGPIO_IntRisingOrFallingEdge):
base->EDGE_SEL |= (1U << pin); /* 边沿选择寄存器 */
break;
default:
break;
}
}
/* 使能GPIO中断 */
void gpio_enableint(GPIO_Type *base, unsigned int pin)
{
base->IMR |= (1 << pin);
}
/* 禁止GPIO中断 */
void gpio_disableint(GPIO_Type *base, unsigned int pin)
{
base->IMR &= ~(1 << pin);
}
/* 清除GPIO中断标志位 */
void gpio_clearintflags(GPIO_Type *base, unsigned int pin)
{
base->ISR |= (1 << pin); /* 写1清除 */
}
/* 中断使能函数 */
void int_init(void)
{
/* 1. 初始化GIC */
GIC_Init();
/* 2. 初始化中断服务函数表 */
system_irqtable_init();
/* 3. 设置中断向量表偏移 */
__set_VBAR((uint32_t)0x87800000);
}
/* 使能GIC中断 */
void irq_enable(int irq)
{
/* 使能分发器端中断 */
GICD_ISENABLER(irq / 32] = 1 << (irq % 32);
}
/* 禁止GIC中断 */
void irq_disable(int irq)
{
/* 禁止分发器端中断 */
GICD_ICENABLER(irq / 32] = 1 << (irq % 32);
}
/* 中断向量表 - start.S */
.global _start
_start:
ldr pc, =Reset_Handler /* 复位中断 */
ldr pc, =Undefined_Handler /* 未定义指令中断 */
ldr pc, =SVC_Handler /* SVC中断 */
ldr pc, =PrefAbort_Handler /* 预取终止中断 */
ldr pc, =DataAbort_Handler /* 数据终止中断 */
ldr pc, =NotUsed_Handler /* 未使用中断 */
ldr pc, =IRQ_Handler /* IRQ中断 */
ldr pc, =FIQ_Handler /* FIQ中断 */
/* 复位中断服务函数 */
Reset_Handler:
cpsid i /* 关闭全局中断 */
/* 关闭I/D Cache和MMU */
mrc p15, 0, r0, c1, c0, 0 /* 读取CP15的C1寄存器 */
bic r0, r0, #(0x1 << 12) /* 清除I位,关闭I Cache */
bic r0, r0, #(0x1 << 2) /* 清除C位,关闭D Cache */
bic r0, r0, #0x2 /* 清除A位,关闭对齐检查 */
bic r0, r0, #(0x1 << 11) /* 清除Z位,关闭分支预测 */
bic r0, r0, #0x1 /* 清除M位,关闭MMU */
mcr p15, 0, r0, c1, c0, 0 /* 写入CP15的C1寄存器 */
/* 设置各模式栈指针 */
/* IRQ模式栈 */
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x12 /* IRQ模式 */
msr cpsr, r0
ldr sp, =0x80600000 /* IRQ栈首地址 */
/* SYS模式栈 */
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x1f /* SYS模式 */
msr cpsr, r0
ldr sp, =0x80400000 /* SYS栈首地址 */
/* SVC模式栈 */
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x13 /* SVC模式 */
msr cpsr, r0
ldr sp, =0X80200000 /* SVC栈首地址 */
cpsie i /* 打开全局中断 */
b main /* 跳转到main函数 */
/* IRQ中断服务函数 - 重点 */
IRQ_Handler:
push {lr} /* 保存lr地址 */
push {r0-r3, r12} /* 保存r0-r3,r12寄存器 */
mrs r0, spsr /* 读取spsr寄存器 */
push {r0} /* 保存spsr寄存器 */
/* 获取当前中断号 */
mrc p15, 4, r1, c15, c0, 0 /* 获取GIC基地址 */
add r1, r1, #0X2000 /* GIC基地址+0x2000=CPU接口端基地址 */
ldr r0, [r1, #0XC] /* 读取GICC_IAR寄存器,获取中断号 */
push {r0, r1} /* 保存中断号和基地址 */
cps #0x13 /* 进入SVC模式,允许其他中断 */
push {lr} /* 保存SVC模式lr寄存器 */
ldr r2, =system_irqhandler /* 加载C语言中断处理函数 */
blx r2 /* 调用C语言中断处理函数 */
pop {lr} /* SVC模式lr出栈 */
cps #0x12 /* 进入IRQ模式 */
pop {r0, r1}
str r0, [r1, #0X10] /* 写EOIR,中断处理完成 */
pop {r0}
msr spsr_cxsf, r0 /* 恢复spsr */
pop {r0-r3, r12} /* r0-r3,r12出栈 */
pop {lr} /* lr出栈 */
subs pc, lr, #4 /* 返回被中断点 */
/* 中断处理函数表 */
static sys_irq_handle_t irqTable[NUMBER_OF_INT_VECTORS];
/* 默认中断处理函数 */
void default_irqhandler(unsigned int giccIar, void *userParam)
{
while(1) {}
}
/* C语言中断处理函数 */
void system_irqhandler(unsigned int giccIar)
{
uint32_t intNum = giccIar & 0x3FFUL; /* 获取中断号 */
/* 检查中断号是否有效 */
if ((intNum == 1020) || (intNum >= NUMBER_OF_INT_VECTORS))
{
return;
}
irqNesting++; /* 中断嵌套计数器加一 */
/* 调用对应的中断处理函数 */
irqTable[intNum].irqHandler(intNum, irqTable[intNum].userParam);
irqNesting--; /* 中断嵌套计数器减一 */
}
/* 注册中断处理函数 */
void system_register_irqhandler(IRQn_Type irq,
system_irq_handler_t handler,
void *userParam)
{
irqTable[irq].irqHandler = handler;
irqTable[irq].userParam = userParam;
}
/* 按键中断初始化 */
void exit_init(void)
{
gpio_pin_config_t key_config;
/* 1. 配置引脚复用 */
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);
/* 2. 初始化GPIO为中断模式 */
key_config.direction = kGPIO_DigitalInput;
key_config.interruptMode = kGPIO_IntFallingEdge; /* 下降沿触发 */
key_config.outputLogic = 1;
gpio_init(GPIO1, 18, &key_config);
/* 3. 使能GIC中断 */
GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);
/* 4. 注册中断服务函数 */
system_register_irqhandler(GPIO1_Combined_16_31_IRQn,
(system_irq_handler_t)gpio1_io18_irqhandler,
NULL);
/* 5. 使能GPIO中断 */
gpio_enableint(GPIO1, 18);
}
/* 按键中断处理函数 */
void gpio1_io18_irqhandler(void)
{
static unsigned char state = 0;
/* 延时消抖(实际项目中应使用定时器) */
delay(10);
/* 检查按键是否按下 */
if (gpio_pinread(GPIO1, 18) == 0)
{
state = !state;
beep_switch(state); /* 控制蜂鸣器 */
}
/* 清除中断标志位 */
gpio_clearintflags(GPIO1, 18);
}
/* 主函数 */
int main(void)
{
unsigned char state = OFF;
int_init(); /* 初始化中断(必须最先调用!) */
imx6u_clkinit(); /* 初始化系统时钟 */
clk_enable(); /* 使能所有时钟 */
led_init(); /* 初始化LED */
beep_init(); /* 初始化蜂鸣器 */
key_init(); /* 初始化按键 */
exit_init(); /* 初始化按键中断 */
while(1)
{
state = !state;
led_switch(LED0, state); /* LED闪烁 */
delay(500);
}
return 0;
}
# 交叉编译工具链
CROSS_COMPILE ?= arm-linux-gnueabihf-
# 编译命令
make clean
make
# 生成文件
# int.elf - 可执行文件
# int.bin - 二进制文件
# int.dis - 反汇编文件
# 赋予imxdownload可执行权限
chmod 777 imxdownload
# 烧写到SD卡(不能烧写到/dev/sda或sda1)
./imxdownload int.bin /dev/sdd
| 测试项 | 预期结果 | 实际结果 | 状态 |
|---|---|---|---|
| LED闪烁 | 周期约500ms | — | ☐ |
| 按键中断触发 | 蜂鸣器开关 | — | ☐ |
| 中断清除标志 | 无重复触发 | — | ☐ |
| 系统稳定性 | 无死机 | — | ☐ |
| 特性 | IMX6ULL (Cortex-A7) | STM32F103 (Cortex-M3) | RK3568 (Cortex-A55) |
|---|---|---|---|
| 中断控制器 | GIC-400 | NVIC | GIC-500 |
| 中断数量 | 1024 (SPI+PPI+SGI) | 81/240 | 1024 |
| 优先级级数 | 256级 | 16级 | 256级 |
| 中断嵌套 | 支持 | 支持 | 支持 |
| 中断向量表 | 可重定位(VBAR) | 固定在Flash | 可重定位(VBAR) |
| GPIO中断分组 | 16个IO共用一个IRQ | 每个IO独立IRQ | 16个IO共用一个IRQ |
| 中断触发配置 | ICR寄存器(2bit) | EXTI_IMR/RTSR/FTSR | ICR寄存器 |
flowchart LR
accTitle: 中断处理流程对比
accDescr: 对比Cortex-A和Cortex-M中断处理的主要差异
subgraph Cortex-M NVIC
A1[硬件自动保存上下文] --> B1[读取中断向量表]
B1 --> C1[调用中断服务函数]
C1 --> D1[写ICPR清除中断]
D1 --> E1[硬件自动恢复上下文]
end
subgraph Cortex-A GIC
A2[保存现场到栈] --> B2[读取GICC_IAR获取中断号]
B2 --> C2[调用C函数system_irqhandler]
C2 --> D2[写GICC_EOIR结束中断]
D2 --> E2[恢复现场]
end
| 差异点 | IMX6ULL (GIC) | STM32 (NVIC) |
|---|---|---|
| 中断向量表位置 | 可通过VBAR寄存器设置 | 固定在0x08000000 |
| 中断号获取 | 需读取GICC_IAR寄存器 | 硬件自动获取 |
| 中断结束处理 | 需写入GICC_EOIR寄存器 | 硬件自动完成 |
| 中断分组 | 16个GPIO共用一个IRQ | 每个EXTI独立IRQ |
| 中断服务函数 | 需软件分发到具体处理函数 | 硬件直接跳转 |
| 中断嵌套 | 支持,通过优先级控制 | 支持,通过优先级控制 |
考察点: 中断控制器架构理解
参考答案:
考察点: GPIO中断配置流程
参考答案:
考察点: 中断编程规范
参考答案: 应该做:
不应该做:
考察点: 中断资源管理
参考答案:
考察点: 中断向量表机制
参考答案:
set_vbar(0x87800000); 将中断向量表重定位到0x87800000| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 中断不触发 | GIC未使能 | 检查GICD_CTLR寄存器 | 确保GICD_CTLR[0]=1 |
| 中断无响应 | GPIO中断未使能 | 检查IMR寄存器 | 确保IMR对应位=1 |
| 中断无响应 | GIC中断未使能 | 检查GICD_ISENABLER | 确保对应中断ID已使能 |
| 中断丢失 | 未清除中断标志 | 检查ISR寄存器 | 在ISR中写1清除 |
| 中断重复触发 | 中断源未处理 | 检查中断处理函数 | 确保处理所有中断源 |
| 系统死机 | 栈溢出 | 检查栈大小 | 增大栈空间 |
| 系统死机 | 中断嵌套过深 | 检查irqNesting | 优化中断处理逻辑 |
代码来源: 裸机例程 9_int, bsp/int/, bsp/exit/ 最后更新: 2026-09-17