第四章:GPIO与中断系统

各位工程师朋友,今天我们来聊聊嵌入式开发中最基础、也最容易被忽视的两个模块——GPIO和中断系统。说实话,我见过不少开发者,GPIO点灯玩得飞起,但一到中断嵌套就翻车。嗯,这章我们就把它彻底讲透。

4.1 GPIO模块架构

i.MX RT系列的GPIO模块,说白了就是一堆寄存器在背后撑着。每个GPIO组有32个引脚,支持独立配置方向、上下拉、驱动能力等。我个人习惯把GPIO分成三类:普通IO、复用功能和中断源。

核心寄存器一览:

  • DR(数据寄存器):读写引脚电平,简单粗暴
  • GDIR(方向寄存器):0输入,1输出,别搞反了
  • PSR(电平状态寄存器):只读,反映实际引脚电平
  • ICR(中断控制寄存器):配置触发方式,后面细说

我在项目中遇到过一个问题:某次调试电机驱动板,GPIO输出波形总是不对。查了半天,发现是驱动能力没配够,导致上升沿变缓。你想想看,一个简单的GPIO配置,背后藏着这么多坑。

4.2 中断控制器:GIC vs NVIC

i.MX RT系列比较特殊,它有两种中断控制器。Cortex-M7内核用的是NVIC,而Cortex-A系列用的是GIC。嗯,这里要分清楚。

特性 NVIC(M7) GIC(A系列)
中断数量 最多240个 最多1020个
优先级位数 8位(可配置) 16位
中断嵌套 硬件自动处理 软件辅助管理
典型应用 实时控制 多核/复杂系统

为什么会有这种差异?说白了,M7主打实时性,NVIC的硬件嵌套机制能保证低延迟。而GIC要处理多核间的中断分发,复杂度自然上去了。

4.3 中断优先级与嵌套

中断优先级这块,我建议你记住一个原则:紧急且短的任务优先级高,耗时长的任务优先级低。为什么?因为高优先级中断会抢占低优先级,如果你把耗时任务放高位,整个系统就卡死了。

我的经验:在工业控制中,通常把定时器中断设为最高优先级,GPIO外部中断次之,UART接收中断再次之。这样能保证控制周期不被破坏。

中断嵌套的机制其实很简单:当一个低优先级中断正在执行,来了个高优先级中断,CPU会暂停当前ISR,去处理高优先级任务。处理完再回来继续。我曾经在某个项目中,因为没处理好嵌套,导致按键中断把电机控制中断给抢了,电机直接飞车。嗯,那场面,至今难忘。

4.4 GPIO中断实战:按键检测

理论讲完了,我们来点实际的。按键检测是GPIO中断最经典的应用。我习惯用下降沿触发,配合软件消抖。

// 按键中断初始化
void KEY_Init(void)
{
    // 1. 使能GPIO时钟
    CLOCK_EnableClock(kCLOCK_Iomuxc);
    
    // 2. 配置引脚为GPIO功能
    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, 0U);
    
    // 3. 配置GPIO方向为输入
    GPIO1->GDIR &= ~(1U << 9);
    
    // 4. 配置中断触发方式:下降沿
    GPIO1->ICR1 |= (2U << 18);  // ICR1对应bit18-19
    
    // 5. 清除中断标志
    GPIO1->ISR = (1U << 9);
    
    // 6. 使能NVIC中断
    NVIC_EnableIRQ(GPIO1_Combined_0_15_IRQn);
    NVIC_SetPriority(GPIO1_Combined_0_15_IRQn, 2);
}

// 中断服务函数
void GPIO1_Combined_0_15_IRQHandler(void)
{
    // 检查中断源
    if(GPIO1->ISR & (1U << 9))
    {
        // 软件消抖:延时10ms再检测
        delay_ms(10);
        if(GPIO_ReadPinInput(GPIO1, 9) == 0)
        {
            // 按键确实被按下
            Key_Flag = 1;
        }
        // 清除中断标志
        GPIO1->ISR = (1U << 9);
    }
}

避坑指南:我曾经在中断服务函数里直接调用printf,结果系统直接死机。记住,ISR里不要做耗时操作,只设标志位,具体处理放到主循环里。

这里有个细节要注意:i.MX RT的GPIO中断是组合的,GPIO1的0-15引脚共享一个中断号。所以你在ISR里必须判断具体是哪个引脚触发的。嗯,这个坑我踩过,当时调试了半天才发现。

4.5 实战中的几个要点

最后,我总结几个实战中容易忽略的点:

  • 中断优先级分组:NVIC支持抢占优先级和子优先级,配置前先调用NVIC_SetPriorityGrouping()
  • 中断延迟:从引脚电平变化到CPU执行ISR,大约需要12-15个时钟周期,实时性要求高的场合要考虑
  • 中断嵌套深度:不要超过8层,否则栈空间可能溢出
  • 中断屏蔽:临界区代码记得关中断,但时间要短,否则影响系统实时性

好了,GPIO与中断系统就讲到这里。下一章我们会聊定时器,那又是一个充满细节的话题。记住,嵌入式开发没有捷径,多动手、多踩坑,才能成长。