最近导师的项目中要应用到DSP F28335,由于之前一直对f28335的中断不是很理解,所以花了一点时间仔细看了一下《手把手教你学DSP:基于TMS28335》这本书讲解中断的章节,感觉有一定的收获,在此记录下来。
1 什么是中断
CPU进行正常程序处理时,有时会被要求接收更高级别的指令或实时性要求更高的任务,不得不中断当前的程序处理,而去响应后者,即进入中断服务程序,这就是中断。
当处理完这些任务之后,要继续刚才的处理,因此在执行中断服务程序的时候,必须保存执行现场以确保在完成更高级别任务或指令时能够再接着做刚才被打断的任务。
中断请求的分类:
- 可屏蔽中断:根据目前处理任务的优先级别来考虑其是否优先处理
- 不可屏蔽中断:只要接到中断请求,就要做出中断处理,如RESET和NMI。
2 F28335中断概述及中断源
中断源类型:
- 片内外设中断源:如PWM、CAP、QEP、定时器等;
- 片外中断源:外部中断输入引脚XINT1、XINT2引入的外部中断源。
F28335内部有16个中断线,其中包括2个不可屏蔽中断(RESET和NMI)与14个可屏蔽中断。
下图所示为F28335中断结构:
从上图可以看出:
- 定时器1和定时器2产生的中断请求通过INT13、INT14中断线到达CPU,实际上这两个中断已经预留给了实时操作系统;
- INT1~INT12可屏蔽中断可供外部中断和处理器内部的单元使用;
- F28335外设中断源有58个,PIE外部中断扩展将这58个外设中断源分配给这12个中断线(由后面要讲的中断向量表进行管理)。
- 外部中断源均通过PIE模块进行判断处理。
3 F28335的3级中断机制
在F28335中,产生一个完整的中断需要经过3个阶段,这就是3级中断机制,如下图所示,3级中断机制分别为外设级中断、PIE级中断和CPU级中断。
从图中可以看出,外设要能够成功产生中断响应,就要首先经外设级中断允许,然后经PIE允许,然后经CPU允许,最终CPU做出响应。
3.1 外设级中断
- 当外设产生了中断事件,那么该外设对应中断标志寄存器(IF) 相应的位将被 自动置位(不需要人为去对寄存器操作);
- 如果该外设对应**中断使能寄存器(IE)**中相应的使能位正好置位,则外设产生的中断将向PIE控制器发出中断申请,从而进入到PIE级中断;
- 如果IE一直没有被使能,则需要软件编程将IF复位;
- 进入中断服务后,有部分硬件外设会自动复位中断标志寄存器,多数外设需要编程复位。
3.2 PIE级中断
PIE模块作用是在有限中断线的情况下用来管理多个中断源触发。
结构如下图所示:
从上图可知:
- PIE将外设中断分成了12个组,分别对应CPU的12个可屏蔽中断线,每组由8各外设级中断组成,这8个外设中断分别对应相应外设接口的中断引脚;
- CPU外设中断源为58个,而PIE能够支持的中断源为96个,能完美的解决CPU外设中断源数目多的问题。
PIE中断流程:
- 一旦PIE控制器有中断产生,相应的中断标志位(PIEIFRx.y)将置1;
- 如果相应的PIE中断使能位也置1,则PIE将检查相应的PIEACKx以确定CPU是否准备响应该中断;
- 如果相应的PIEACKx位清零,PIE向CPU申请中断;
- 如果PIEACKx置1,PIE将等待到相应的PIEACKx清零才向CPU申请中断;
中断向量表:
中断向量表记录着各个中断源对应的地址,当我们需要将某个外设设置中断时,就需要使能对应的中断源,参考中断向量表。
3.3 CPU级中断
一旦CPU申请中断,CPU级中断标志位(IFR)将置1。中断标志位锁存到标志寄存器后,只有CPU中断使能寄存器(IER)或中断调试使能寄存器(DBGIER)相应的使能位和全局中断屏蔽位(INTM)被使能时才会响应中断申请。
4 中断应用示例(定时器中断为例)
中断的配置与PIE模块的响应机制完全对应。在此应用一个简单的示例,利用定时器0(timer0)产生中断,点亮led灯(使led灯闪烁)。
首先可以从上面的中断向量表中查找到,timer0对应的中断编号为INT1.7,并且我们还可以在TI提供的DSP2833x_PieVect.h文件中找到,名称为TINT0,如下所示:
// Group 1 PIE Peripheral Vectors:
PINT SEQ1INT;
PINT SEQ2INT;
PINT rsvd1_3;
PINT XINT1;
PINT XINT2;
PINT ADCINT; // ADC
PINT TINT0; // Timer 0
PINT WAKEINT; // WD
// Group 2 PIE Peripheral Vectors:
PINT EPWM1_TZINT; // EPWM-1
PINT EPWM2_TZINT; // EPWM-2
PINT EPWM3_TZINT; // EPWM-3
PINT EPWM4_TZINT; // EPWM-4
PINT EPWM5_TZINT; // EPWM-5
PINT EPWM6_TZINT; // EPWM-6
PINT rsvd2_7;
PINT rsvd2_8;
...
中断配置步骤:
第一步:
基础的初始化之后,对PIE模块进行指定,一般在InitPieVectTable();PIE模块初始化之后进行指定。
void main(void)
{
InitSysCtrl(); // 初始化系统控制
DINT; // disable interrupt(屏蔽CPU中断)
InitPieCtrl(); // 初始化PIE控制寄存器
IER = 0x0000; // 清除中断使能寄存器和中断标志寄存器
IFR = 0x0000;
InitPieVectTable();// 初始化中断向量表
EALLOW; // 开启寄存器写操作
PieVectTable.TINT0 = &cpu_timer0_isr; // 将中断函数的地址传入
EDIS; // 关闭寄存器写操作
InitCputTimers(); // 初始化CPU寄存器定时器
ConfigCpuTimer(&CpuTimer0, 150, 500000); // 配置timer0,时钟cpu频率为150MHz,定时500ms
}
第二步:
打开PIE模块的时钟,打开定时器timer0中断需要使能INT1.7,如下所示:
void main(void)
{
// 第1步
InitSysCtrl(); // 初始化系统控制
DINT; // disable interrupt(屏蔽CPU中断)
InitPieCtrl(); // 初始化PIE控制寄存器
IER = 0x0000; // 清除中断使能寄存器和中断标志寄存器
IFR = 0x0000;
InitPieVectTable();// 初始化中断向量表
EALLOW; // 开启寄存器写操作
PieVectTable.TINT0 = &cpu_timer0_isr; // 将中断函数的地址传入
EDIS; // 关闭寄存器写操作
InitCputTimers(); // 初始化CPU寄存器定时器
ConfigCpuTimer(&CpuTimer0, 150, 500000); // 配置timer0,时钟cpu频率为150MHz,定时500ms
//第2步
IER |= M_INT1; //全局使能INT1
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; //使能INT1.7
EINT; //使能中断INTM(全局中断屏蔽位),响应中断申请
ERTM; //使能实时中断DBGM(实时调试)
for(;;); //一直循环下去
}
需要注意的是,咱们并没有设置PIEIFR标志位为1,那怎么能工作呢,其实中断响应的时候自动置1了。
第三步:
在设置的中断中写函数,并在中断里面清除响应标志位PIEACK。如下所示:
interrupt void cpu_timer0_isr(void)
{
Xint1Count++;
GpioDataRegs.GPATOGGLE.bit.GPIO47 = 1; //每隔0.5s翻转一次高低电平
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; //由于timer0在group1中,所以需要清除group1的响应标志位PIEACK,以便接收group1中更多的中断
}