当前位置: 首页 > news >正文

【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,它有两大关键设置:

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

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

    • 01:上升沿触发。

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

  2. 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 clockEDIS;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是GPIO12EDIS;EALLOW;	// 修改被保护的寄存器,修改前应添加EALLOW语句PieVectTable.XINT1 = &EXTI1_IRQn;EDIS;   // EDIS的意思是不允许修改被保护的寄存器PieCtrlRegs.PIEIER1.bit.INTx4 = 1;          // 使能PIE组1的INT4XIntruptRegs.XINT1CR.bit.POLARITY = 0;      // 下降沿触发中断XIntruptRegs.XINT1CR.bit.ENABLE= 1;        // 使能XINT1IER |= 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 clockEDIS;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*SYSCLKOUTGpioCtrlRegs.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是GPIO13EDIS;EALLOW;	// 修改被保护的寄存器,修改前应添加EALLOW语句PieVectTable.XINT2 = &EXTI2_IRQn;EDIS;   // EDIS的意思是不允许修改被保护的寄存器PieCtrlRegs.PIEIER1.bit.INTx5 = 1;          // 使能PIE组1的INT5XIntruptRegs.XINT2CR.bit.POLARITY = 0;      // 下降沿触发中断XIntruptRegs.XINT2CR.bit.ENABLE = 1;        // 使能XINT2IER |= 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 与外部世界实时交互的桥梁。掌握了它,你就拥有了构建复杂、高效、可靠嵌入式系统的关键能力。

http://www.dtcms.com/a/333253.html

相关文章:

  • java注释功能
  • ESP32-C3_TCP
  • Linux操作系统从入门到实战(二十二)命令行参数与环境变量
  • 信刻光盘摆渡系统案例——某省纪委
  • 微服务容错与监控体系设计
  • 生存主义:隐形异变 (Survivalist: Invisible Strain)免安装中文版
  • Leetcode 最小生成树系列(1)
  • 解决zabbix图片中文乱码
  • Mac(二)Homebrew 的安装和使用
  • 前端更改浏览器默认滚动条样式
  • 716SJBH高职院校财务收费系统的设计与实现
  • 25. 移动端-uni-app
  • 【论文解读】DDRNet:深度双分辨率网络在实时语义分割中的结构与原理全面剖析
  • LeetCode 905.按奇偶排序数组
  • 【机器学习深度学习】客观评估主观评估:落地场景权重比例
  • Rust 中 i32 与 *i32 的深度解析
  • 大华相机RTSP无法正常拉流问题分析与解决
  • flume实战:从零配置到启动运行的完整指南
  • 【Ubuntu 中安全删除 Windows 分区并优化磁盘空间利用】
  • redis-sentinel基础概念及部署
  • 机械学习---- PCA 降维深度解析
  • Vue响应式系统在超大型应用中的性能瓶颈
  • 深度学习实战115-基于Qwen3的多智能体协同深度数据分析:架构、流程与实现
  • 光伏工单智能管理,故障处理快人一步
  • 金融业务安全增强方案:国密SM4/SM3加密+硬件加密机HSM+动态密钥管理+ShardingSphere加密
  • 不同DuckDB插件对不同工具生成的xlsx文件读取速度的比较
  • sqlsever的sql转postgresql的sql的方言差异
  • 日本CN2服务器租用多少钱
  • Linux -- 线程概念与控制
  • Spring Boot 静态函数无法自动注入 Bean?深入解析与解决方案