云计算百科
云计算领域专业知识百科平台

【DSP28335 事件驱动】唤醒沉睡的 CPU:外部中断 (XINT) 实战

大家好!在前面的教程中,我们的 main 函数总是在一个 while(1) 循环里忙碌地查询(轮询)按键状态。这种方式虽然可行,但 CPU 就像一个不停打电话问“你好了吗?”的人,既浪费了大量的计算资源,也无法保证响应的实时性。

今天,我们将学习一种革命性的工作方式——外部中断 (External Interrupt, XINT)。我们将教会 DSP 在没有事件发生时“休息”(执行主循环任务),只在外部引脚电平发生跳变(如按键被按下)的瞬间,才被唤醒去执行特定任务。这是构建高效、低功耗、高响应速度系统的核心技术。

一、硬件基础:外部中断的信号通路

[图1 & 图2: F28335 外部中断逻辑结构图]

DSP28335 提供了一个专门用来处理外部引脚电平变化的硬件中断系统。

  • 7+1 个外部中断通道: F28335 共有 7 个可屏蔽外部中断 (XINT1 ~ XINT7) 和 1 个不可屏蔽外部中断 (XNMI),其中 XNMI 和 XINT13 共用中断源。

  • 灵活的 GPIO 映射: 并非所有 GPIO 都能触发所有外部中断。它们的分工如下:

    • XINT1, XINT2: 只能由 GPIO0 ~ GPIO31 中的任意一个引脚来触发。

    • XINT3 ~ XINT7: 只能由 GPIO32 ~ GPIO63 中的任意一个引脚来触发。

    • XNMI/XINT13: 可由 GPIO0 ~ GPIO31 中的任意一个引幕脚来触发。

信号的完整旅程是:GPIO 引脚 -> GPIO 中断选择器 (GPIOXINTnSEL) -> XINTn 中断控制器 -> PIE 模块 -> C28x 内核。我们软件配置的核心,就是打通这条路径上的所有关卡。

二、核心寄存器:配置中断的“扳机”

[图3: 外部中断控制寄存器 XINTnCR 位定义]

每个外部中断通道(XINTn)都有一个对应的控制寄存器 XINTnCR,它有两大关键设置:

  • Polarity (位 3~2):触发极性

    • 00 或 10:下降沿触发。非常适合按键按下(电平从高到低)的场景。

    • 01:上升沿触发。

    • 11:上升沿和下降沿均触发。

  • Enable (位 0):中断使能

    • 0:禁止该外部中断通道。

    • 1:使能该外部中断通道。这是打开 XINTn 模块自身功能的总开关。

  • 三、软件配置八步法:让中断跑起来

    配置一个外部中断,虽然涉及多个模块,但遵循一个清晰、固定的流程。下面我们以“配置 GPIO12 作为 XINT1 的触发源”为例,拆解这八个步骤。

    (1) 初始化 PIE 模块
    这是所有中断配置的起点。我们需要初始化 PIE 控制器和向量表,为中断系统搭建好基础框架。

    InitPieCtrl(); // 初始化PIE控制器寄存器
    IER = 0x0000; // 清零CPU级中断使能寄存器
    IFR = 0x0000; // 清零CPU级中断标志寄存器
    InitPieVectTable(); // 初始化PIE中断向量表,将其所有入口指向一个默认ISR

    (2) 配置 GPIO 为输入模式
    既然要接收外部信号,那么对应的 GPIO 引脚必须被配置为输入模式。

    EALLOW;
    // 使能GPIO时钟…
    GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0; // 设为通用GPIO
    GpioCtrlRegs.GPADIR.bit.GPIO12 = 0; // 设为输入
    EDIS;

    (3) 映射 GPIO 到中断线
    告诉系统,XINT1 这条中断专线应该去“监听”哪个 GPIO 引脚。

    EALLOW;
    // XINT1 的源选择寄存器,.GPIOSEL 字段写入引脚号
    GpioIntRegs.GPIOXINT1SEL.bit.GPIOSEL = 12; // XINT1 由 GPIO12 触发
    EDIS;

    (4) 注册中断服务函数 (ISR)
    将我们自己编写的 ISR 函数地址,写入到 PIE 向量表中 XINT1 对应的位置。

    EALLOW;
    PieVectTable.XINT1 = &EXTI1_IRQn; // EXTI1_IRQn 是我们函数的函数名
    EDIS;

    (5) 使能 PIE 级中断
    根据 PIE 中断向量表,XINT1 属于 PIE 第 1 组的第 4 个中断 (INT1.4)。我们需要打开这个通道。

    PieCtrlRegs.PIEIER1.bit.INTx4 = 1; // 使能 PIE 组1 的 INT4

    (6) 配置 XINTn 的触发方式并使能
    设置 XINTnCR 寄存器,定义我们需要的触发边沿,并正式启用 XINT1 模块。

    XIntruptRegs.XINT1CR.bit.POLARITY = 0; // 0: 下降沿触发
    XIntruptRegs.XINT1CR.bit.ENABLE = 1; // 1: 使能 XINT1

    (7) 使能 CPU 级中断
    打开 CPU 级的总开关。因为 XINT1 最终汇集到 CPU 的 INT1 总线上,所以我们要使能 INT1,并打开全局中断。

    IER |= M_INT1; // 使能CPU的INT1
    EINT; // 使能全局中断(Clear INTM bit)
    ERTM; // 使能调试事件

    (8) 编写中断服务函数 (ISR)
    这是中断发生后,CPU 最终要执行的代码。

    // 使用 'interrupt' 关键字声明这是一个中断服务函数
    interrupt void EXTI1_IRQn(void)
    {
    // … 在这里编写处理事件的代码 …

    // !! 关键:向 PIE 发送“确认”信号,表示中断已处理
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
    }

    PIEACK 寄存器非常重要,它告诉 PIE 控制器,该组的中断已经得到响应,可以清除标志,以便下一次中断能正常触发。

    四、代码实战:按键触发 LED 翻转

    下面我们通过完整的代码,实现用 K1 键 (连接 GPIO12) 触发 XINT1 中断来翻转 LED2,用另一个按键 (连接 GPIO13) 触发 XINT2 中断来翻转 LED3。

    1. exti.h (头文件)

    #ifndef EXTI_H_
    #define EXTI_H_

    #include "DSP2833x_Device.h" // DSP2833x 头文件
    #include "DSP2833x_Examples.h" // DSP2833x 例子相关头文件

    void EXTI1_Init(void);
    interrupt void EXTI1_IRQn(void);

    void EXTI2_Init(void);
    interrupt void EXTI2_IRQn(void);

    #endif /* EXTI_H_ */

    2. exti.c (源文件)

    #include "exti.h"
    #include "leds.h"
    #include "key.h"

    void EXTI1_Init(void)
    {
    EALLOW;
    SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1; // GPIO input clock
    EDIS;

    EALLOW;
    //KEY端口配置
    GpioCtrlRegs.GPAMUX1.bit.GPIO12=0;
    GpioCtrlRegs.GPADIR.bit.GPIO12=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO12=0;
    GpioCtrlRegs.GPAQSEL1.bit.GPIO12 = 0; // 外部中断1(XINT1)与系统时钟SYSCLKOUT同步

    GpioCtrlRegs.GPBMUX2.bit.GPIO48=0;
    GpioCtrlRegs.GPBDIR.bit.GPIO48=1;
    GpioCtrlRegs.GPBPUD.bit.GPIO48=0;
    GpioDataRegs.GPBCLEAR.bit.GPIO48=1;
    EDIS;

    EALLOW;
    GpioIntRegs.GPIOXINT1SEL.bit.GPIOSEL = 12; // XINT1是GPIO12
    EDIS;

    EALLOW;// 修改被保护的寄存器,修改前应添加EALLOW语句
    PieVectTable.XINT1 = &EXTI1_IRQn;
    EDIS; // EDIS的意思是不允许修改被保护的寄存器

    PieCtrlRegs.PIEIER1.bit.INTx4 = 1; // 使能PIE组1的INT4

    XIntruptRegs.XINT1CR.bit.POLARITY = 0; // 下降沿触发中断
    XIntruptRegs.XINT1CR.bit.ENABLE= 1; // 使能XINT1

    IER |= M_INT1; // 使能CPU中断1(INT1)
    EINT; // 开全局中断
    ERTM;
    }

    interrupt void EXTI1_IRQn(void)
    {
    Uint32 i;
    for(i=0;i<10000;i++); //键盘消抖动
    while(!KEY_H1);
    LED2_TOGGLE;
    PieCtrlRegs.PIEACK.bit.ACK1=1;
    }

    void EXTI2_Init(void)
    {
    EALLOW;
    SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1; // GPIO input clock
    EDIS;

    EALLOW;
    //KEY端口配置
    GpioCtrlRegs.GPAMUX1.bit.GPIO13=0;
    GpioCtrlRegs.GPADIR.bit.GPIO13=0;
    GpioCtrlRegs.GPAPUD.bit.GPIO13=0;
    GpioCtrlRegs.GPAQSEL1.bit.GPIO13 = 2; // 外部中断2(XINT2)输入限定6个采样窗口
    GpioCtrlRegs.GPACTRL.bit.QUALPRD1 = 0xFF; // 每个采样窗口的周期为510*SYSCLKOUT

    GpioCtrlRegs.GPBMUX2.bit.GPIO48=0;
    GpioCtrlRegs.GPBDIR.bit.GPIO48=1;
    GpioCtrlRegs.GPBPUD.bit.GPIO48=0;
    GpioDataRegs.GPBCLEAR.bit.GPIO48=1;
    EDIS;

    EALLOW;
    GpioIntRegs.GPIOXINT2SEL.bit.GPIOSEL = 13; // XINT2是GPIO13
    EDIS;

    EALLOW;// 修改被保护的寄存器,修改前应添加EALLOW语句
    PieVectTable.XINT2 = &EXTI2_IRQn;
    EDIS; // EDIS的意思是不允许修改被保护的寄存器

    PieCtrlRegs.PIEIER1.bit.INTx5 = 1; // 使能PIE组1的INT5

    XIntruptRegs.XINT2CR.bit.POLARITY = 0; // 下降沿触发中断
    XIntruptRegs.XINT2CR.bit.ENABLE = 1; // 使能XINT2

    IER |= M_INT1; // 使能CPU中断1(INT1)
    EINT; // 开全局中断
    ERTM;
    }

    interrupt void EXTI2_IRQn(void)
    {
    Uint32 i;
    for(i=0;i<10000;i++); //键盘消抖动
    while(!KEY_H2);
    LED3_TOGGLE;
    PieCtrlRegs.PIEACK.bit.ACK1=1;
    }

    3. mian.c (源文件)

    #include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
    #include "DSP2833x_Examples.h" // DSP2833x Examples Include File

    #include "leds.h"
    #include "exti.h"

    /*******************************************************************************
    * 函 数 名 : main
    * 函数功能 : 主函数
    * 输 入 : 无
    * 输 出 : 无
    *******************************************************************************/
    void main()
    {
    int i=0;

    InitSysCtrl();

    InitPieCtrl();
    IER = 0x0000;
    IFR = 0x0000;
    InitPieVectTable();

    LED_Init();
    EXTI1_Init();
    EXTI2_Init();

    while(1)
    {
    i++;
    if(i%2000==0)
    {
    LED1_TOGGLE;
    }
    DELAY_US(100);
    }
    }

    可以看到,主循环 while(1) 里不再有 KEY_Scan() 函数了。CPU 只是在悠闲地闪烁着 LED1,完全不用关心按键何时按下。一旦按键按下,硬件会自动打断 CPU,去执行对应的 ISR,执行完毕后再无缝返回,这正是中断的魅力所在。

    总结

    通过本次实战,我们不仅将中断理论落地,还掌握了配置和使用外部中断的全流程。你现在应该深刻理解了:

    • 外部中断信号从 GPIO 到 CPU 的完整硬件路径。

    • 软件配置外部中断的 8 大关键步骤,缺一不可。

    • 如何编写一个规范的中断服务函数,尤其是 PIEACK 的重要性。

    • 中断驱动的编程思想,如何将 CPU 从繁忙的轮询中解放出来。

    外部中断是 DSP 与外部世界实时交互的桥梁。掌握了它,你就拥有了构建复杂、高效、可靠嵌入式系统的关键能力。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【DSP28335 事件驱动】唤醒沉睡的 CPU:外部中断 (XINT) 实战
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!