当前位置: 首页 > 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.lryc.cn/news/622379.html

相关文章:

  • AI - MCP 协议(一)
  • 备忘录模式C++
  • 线性代数 · 直观理解矩阵 | 空间变换 / 特征值 / 特征向量
  • JavaScript递归
  • nVidia Tesla P40使用anaconda本地重编译pytorch3d成功加载ComfyUI-3D-Pack
  • 磁悬浮轴承“幽灵振动”克星:深度解析同频振动机理与精准打击策略
  • 日常反思总结
  • Layui 语法详解与全功能示例
  • GoLand深度解析:智能开发利器与cpolar内网穿透的协同革命
  • 基于Spring Boot的智能民宿预订与游玩系统设计与实现 民宿管理系统 民宿预订系统 民宿订房系统
  • Linux操作系统从入门到实战(二十二)命令行参数与环境变量
  • Lecture 10: Concurrency 3
  • 【嵌入式硬件实例】-555定时器驱动直流无刷电机
  • kubernetes(序)
  • ESP32-C3_TCP
  • Windows Server存储智能数据校验
  • Spring Boot接口签名校验设计与实现
  • 办公效率提升指南:完成重复任务自动化
  • Docker Compose 入门教程
  • 图片滤镜处理(filters)
  • lidar2imu/auto_caliban以及manual_calib安装过程
  • 线程P5 | 单例模式[线程安全版]~懒汉 + 饿汉
  • 【C#补全计划】委托
  • Vue 侦听器(watch 与 watchEffect)全解析2
  • SSH协议的GIT转换
  • pyecharts可视化图表-pie:从入门到精通(进阶篇)
  • 集成电路学习:什么是Image Segmentation图像分割
  • GPT-5 官方前瞻:它将如何重塑你的数字生活?
  • 艾伦·图灵:计算理论与人工智能的奠基人
  • Linux————网络基础