1.中断介绍
F28335的中断:
中断源可分为片内中断源和片外中断源,这些外设中断源需要把中断请求传递给内核需要中断线。
14个课屏蔽中断是通过相应的使能寄存器控制是否进行中断。
28335总共的中断源有58个,可是只有12个中断线,如何进行分配呢?即需要PIE中断扩展模块来完成,
2.F28335的中断机制
这一组叫做PIE响应外设中断的一个过程
CPU 响应12组PIE中断的一个过程
外设中断比如定时器时间到了或者串口接收到数据那么外设对应中断 标志寄存器IF会被置位(自动置位),如果改外设对应的使能寄存器IE中断响应的使能之后(相当于开关打开),之后向我们PIE级的一个控制器发出中断申请,如果没有被使能,那么中断相当于屏蔽了。
之后要进行一定的中断标志寄存器复位,不然会一直申请中断,需要软件编程来消除,如果产生中断之后,我们对应的中断标志IF会自动置位,
x表示我们需要的12条中断线。
这些开关都是通过配置寄存器打开即可
mux是多路选择,既可以选择我们58个中断源任意选择哪一个,
PIE级外设中断分成12个组,分别对应CPU的12条可屏蔽中断线,每一个组又由8个外设及中断组成,这8个外设级中断,分别对应了一些相应的一些外设的一些接口中断引脚,通过8选1的多路复用寄存器来进行选择。具体的连接关系如图所示
PIEACKx是我们的一个响应的一个标志,为0取反变为1,这里就响应PIE中断
这个是CPU的总中断开关,
3 F28335中断向量
CPU响应中断,就是CPU要去执行相应的中断服务程序,其响应过程是CPU将现执行程序的指令地址压入堆栈 ,跳转到中断服务程序入口地址 ,中断服务程序的入口地址就是中断向量,这个中断向量用2个16位寄存器存放。入口地址是22位的,地址的低16位保存在该向量的低16位;地址的高16位则保存在它的高6位,更高的10位保留。
4 中断向量映射方式(了解一下即可)
在F28335中,中断向量表可以被映射到4个不同的存储区域,在实际应用中,F28335只能使用PIE中断向量表映射区域。中断向量表映射主要由以下型号控制。
①VMAP:该位在状态寄存器1(ST1)的第3位,复位后值为1。可以通过改变ST1值或使用SETC/CLRC VMAP指令改变VMAP的值,正常操作时该位置1。
②MOM1MAP:该位在状态寄存器1(ST1)的第11位,复位后该位置1.可以通过改变ST1的值或使用SETC/CLRC M0M1MAP指令改变M0M1MAP的值,正常操作该位置1。M0M1MAP=0是厂家测试时使用。
③ENPIE:该位在PIECTRL寄存器的第0位,复位的默认值为0(PIE被屏蔽)。器件复位后,可以通过调整PIECTRL寄存器的值进行修改。
根据上述控制位的不同设置,中断向量表有不同的映射方式,如图
5.中断操作
PIE模块8个中断分成一组与外部中断一起共用一个CPU中断,总共有12组中断(INT1-INT12)。每组中断有相应的中断标志(PIEIFR)和使能寄存器(PIEIER),这些寄存器控制PIE向CPU申请中断。同时CPU还根据PIEIFR和PIEIER寄存器确定执行哪个中断服务程序。在清除PIEIFR和PIEIER的位时,要遵循以下3个规则。
①不要用软件编程清除PIEIFR的位
②软件设置中断优先级
③使用PIEIER禁止中断
使能/禁止复用外设中断的处理
应用外设中断的使能/禁止标志位使能/禁止外设中断,PIEIER和CPU IER寄存器主要是在同一组中断内设置中断优先级。如果要修改PIEIER寄存器的设置,有两种方法。第一种方法是保护相应的PIE标志寄存器标志位,防止中断丢失。第二种方法是清除相应的PIE寄存器的标志位。
外设复用中断向CPU申请中断的流程
1:任何一个PIE中断组的外设或外部中断产生中断。如果外设模块内的中 断被使能,中断请求将被送到PIE模块。
2:PIE 模块将识别出PIE中断组x内的y中断(INTx.y)申请,然后相应的 PIE 中断标志位被锁存:PIEIFRx.y=1。
3:PIE 的中断如要送到CPU需满足下面两个条件:
①相应的使能位必须被设置(PIEIFRx.y=1)。
②相应的PIEACKx位必须被清除。
4:如果满足步骤3中的两个条件,中断请求将被送到CPU并且相应的相应 寄存器位被设置1(PIEACKx=1)。PIEACKx位将保持不变,除非为了使本组中的 其他中断向CPU发出申请而清除该位。
5:CPU 中断标志位被置位(CPUIFRx=1),表明产生一个CPU级的挂起中断。
6:如果CPU中断被使能(CPU IERx=1或者DBGIERx=1),并且全局中断使 能(INTM=0),CPU将处理中断INTx。
7:CPU 识别到中断并自动保存相关的中断信息,清除使能寄存器(IER)位, 设置INTM,清除EALLOW。CPU完成这些任务准备执行中断服务程序。
8:CPU 从PIE中获取响应的中断向量。
2.中断的配置
(1)使能外设对应的PIE中断,由于外设中断较多,它们是由PIE统一管理,所以要根据你所使用的外设中断选择对应的组,比如外部中断1,它是由PIE组1的第4线连接,这个在前面中断介绍时讲解过。因此可由PIE控制寄存器中相应中断使能位来控制。
PieCtrlRegs.PIEIER1.bit.INTx4 = 1; // 使能PIE组1的INT4
(2)使能外设中断,这个具体是由外设相关中断使能位来控制,比如外部中断1,这个可由外部中断1的控制寄存器中相应中断使能位来控制。
XIntruptRegs.XINT1CR.bit.ENABLE= 1; // 使能XINT1
(3)指定中断向量表中断服务函数地址,这个通过对PIE中断向量表寄存器的相应位进行设置,中断服务函数名可自定义,但是要符合C语言标识符命名规则,在中断函数名前需加上地址符"&"。在对PIE中断向量表寄存器设置时要先声明EALLOW,修改完成后还要声明EDIS。比如外部中断1,其设置如下:
EALLOW; // 修改被保护的寄存器,修改前应添加EALLOW语句
PieVectTable.XINT1 = &EXTI1_IRQn;
EDIS; // EDIS的意思是不允许修改被保护的寄存器
(4)使能CPU中断及全局中断,这个通过对IER和EINT寄存器相应位设置进行使能或者失能。比如外部中断1,其代码如下:
IER |= M_INT1; // 使能CPU中断1(INT1)
EINT; // 开全局中断
(5)编写中断服务函数 配置好中断后如果有触发,即会进入中断服务函数,中断服务函数名在前面已定义好,所以要保证一致,否则将不会进入中断服务函数内执行。在DSP28335软件开发中,要在中断服务函数名前加上关键字interrupt。例如外部中断1的中断服务函数:
interrupt void EXTI1_IRQn(void)
{
...功能程序
}
这些都是理论知识,看起来枯燥无味,下面通过例程进行更加深刻的理解
3.外部中断介绍
F28335共支持7个外部中断XINT1-XINT7(前面12个是CPU的中断线,),其中XINT1-XINT2只能对GPIO0-GPIO31配置;XINT3-XINT7只对GPIO32-GPIO63配置。XINT13还有一个不可屏蔽的外部中断XNMI共用中断源。每一个外部中断可以被选择为正边沿或负边沿触发,也可以被使能或者禁止(包括XNMI)。可屏蔽中断单元包括一个16位增计数器,该计数器在检测到有效中断边沿时复位为0,同时用来准确记录中断发生的时间。
3.1外部中断相关寄存器
3.2.外部中断配置步骤
外部中断相关函数及寄存器在DSP2833x_PieCtrl.c、DSP2833x_PieVect.c、DSP2833x_GlobalVariableDefs.c文件及其对应的头文件内查找到
1)失能CPU级中断,并初始化PIE控制器寄存器和PIE中断向量表
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
(2)使能IO口时钟,配置IO口为输入
(3)设置 IO 口与中断线的映射关系 比如我们K1它是连接在GPIO12上,要使用外部中断功能可以是外部中断1或者外部中断2,假如使用外部中断1,
代码如下:
EALLOW;
GpioIntRegs.GPIOXINT1SEL.bit.GPIOSEL = 12; // XINT1是GPIO12
EDIS;
(4)指定中断向量表中断服务函数地址 比如外部中断1,其设置如下:
EALLOW; // 修改被保护的寄存器,修改前应添加EALLOW语句
PieVectTable.XINT1 = &EXTI1_IRQn;
EDIS;
// EDIS的意思是不允许修改被保护的寄存器
(5)使能外设对应的PIE中断
比如外部中断1,它是由PIE组1的第4通道连接
PieCtrlRegs.PIEIER1.bit.INTx4 = 1; // 使能PIE组1的INT4
EDIS;
(6)设置外部中断触发方式并使能中断 外部中断的触发方式及中断使能是由XIntruptRegs寄存器控制,具体实现代码如下:
XIntruptRegs.XINT1CR.bit.POLARITY = 0; // 下降沿触发中断
XIntruptRegs.XINT1CR.bit.ENABLE= 1; // 使能XINT1
(7)使能CPU级中断及全局中断
IER |= M_INT1; // 使能CPU中断1(INT1)
EINT; // 开全局中断 ERTM;
8)编写外部中断服务函数 例如外部中断1的中断服务函数:
interrupt void EXTI1_IRQn(void)
{ ...功能程序 }
实验
本章所要实现的功能是:使用外部中断1功能通过按键K1控制D2亮灭,K4按键控制D3亮灭,D1指示灯闪烁提示系统运行状态。程序框架如下:
(1)初始化对应端口的外部中断功能 (
(2)编写外部中断函数
(3)编写主函数
建立工程:参考之前博客
采用模块化编程
LED.h
cpp
#ifndef _LEDS_H_
#define _LEDS_H_
#include "DSP2833x_Device.h" // DSP2833x 头文件
#include "DSP2833x_Examples.h" // DSP2833x 例子相关头文件
#define LED1_OFF (GpioDataRegs.GPCSET.bit.GPIO68=1)
#define LED1_ON (GpioDataRegs.GPCCLEAR.bit.GPIO68=1)
#define LED1_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO68=1)
#define LED2_OFF (GpioDataRegs.GPCSET.bit.GPIO67=1)
#define LED2_ON (GpioDataRegs.GPCCLEAR.bit.GPIO67=1)
#define LED2_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO67=1)
#define LED3_OFF (GpioDataRegs.GPCSET.bit.GPIO66=1)
#define LED3_ON (GpioDataRegs.GPCCLEAR.bit.GPIO66=1)
#define LED3_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO66=1)
#define LED4_OFF (GpioDataRegs.GPCSET.bit.GPIO65=1)
#define LED4_ON (GpioDataRegs.GPCCLEAR.bit.GPIO65=1)
#define LED4_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO65=1)
#define LED5_OFF (GpioDataRegs.GPCSET.bit.GPIO64=1)
#define LED5_ON (GpioDataRegs.GPCCLEAR.bit.GPIO64=1)
#define LED5_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO64=1)
#define LED6_OFF (GpioDataRegs.GPASET.bit.GPIO10=1)
#define LED6_ON (GpioDataRegs.GPACLEAR.bit.GPIO10=1)
#define LED6_TOGGLE (GpioDataRegs.GPATOGGLE.bit.GPIO10=1)
#define LED7_OFF (GpioDataRegs.GPASET.bit.GPIO11=1)
#define LED7_ON (GpioDataRegs.GPACLEAR.bit.GPIO11=1)
#define LED7_TOGGLE (GpioDataRegs.GPATOGGLE.bit.GPIO11=1)
void LED_Init(void);
#endif /* LED_H_ */
LED.c
cpp
#include "leds.h"
/*******************************************************************************
* 函 数 名 : LED_Init
* 函数功能 : LED初始化函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void LED_Init(void)
{
EALLOW;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;// 开启GPIO时钟
//LED1端口配置
GpioCtrlRegs.GPCMUX1.bit.GPIO68=0;//设置为通用GPIO功能
GpioCtrlRegs.GPCDIR.bit.GPIO68=1;//设置GPIO方向为输出
GpioCtrlRegs.GPCPUD.bit.GPIO68=0;//使能GPIO上拉电阻
//LED2端口配置
GpioCtrlRegs.GPCMUX1.bit.GPIO67=0;
GpioCtrlRegs.GPCDIR.bit.GPIO67=1;
GpioCtrlRegs.GPCPUD.bit.GPIO67=0;
//LED3端口配置
GpioCtrlRegs.GPCMUX1.bit.GPIO66=0;
GpioCtrlRegs.GPCDIR.bit.GPIO66=1;
GpioCtrlRegs.GPCPUD.bit.GPIO66=0;
//LED4端口配置
GpioCtrlRegs.GPCMUX1.bit.GPIO65=0;
GpioCtrlRegs.GPCDIR.bit.GPIO65=1;
GpioCtrlRegs.GPCPUD.bit.GPIO65=0;
//LED5端口配置
GpioCtrlRegs.GPCMUX1.bit.GPIO64=0;
GpioCtrlRegs.GPCDIR.bit.GPIO64=1;
GpioCtrlRegs.GPCPUD.bit.GPIO64=0;
//LED6端口配置
GpioCtrlRegs.GPAMUX1.bit.GPIO10=0;
GpioCtrlRegs.GPADIR.bit.GPIO10=1;
GpioCtrlRegs.GPAPUD.bit.GPIO10=0;
//LED7端口配置
GpioCtrlRegs.GPAMUX1.bit.GPIO11=0;
GpioCtrlRegs.GPADIR.bit.GPIO11=1;
GpioCtrlRegs.GPAPUD.bit.GPIO11=0;
GpioDataRegs.GPCSET.bit.GPIO68=1;
GpioDataRegs.GPCSET.bit.GPIO67=1;
GpioDataRegs.GPCSET.bit.GPIO66=1;
GpioDataRegs.GPCSET.bit.GPIO65=1;
GpioDataRegs.GPCSET.bit.GPIO64=1;
GpioDataRegs.GPASET.bit.GPIO10=1;
GpioDataRegs.GPASET.bit.GPIO11=1;
EDIS;
}
EXTI.h
cpp
#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_ */
EXTI.c
cpp
#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;
}
main.c
cpp
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);
}
}