中断请求被分为可屏蔽中断和不可屏蔽中断两大类。可屏蔽中断根据目前处理任务的优先级别考虑是否优先处理,或者是立即处理,而不可屏蔽中断,只要接到中断请求,就要做出中断处理。 当同时多个任务到来,究竟先处理哪个中断请求,就需要对各个中断进行优先级排序。
1.1概述
28335中断源分为片内外设中断(PWM,CAP,QEP,定时器)及片外中断源(即外部中断输入引脚XINT1,XINT2引入的外部中断源)。F28335内部有16个中断线,其中包括2个不可屏蔽中断(RESET和NMI)与14个可屏蔽中断。可屏蔽中断通过相应的中断使能寄存器使用或者禁止产生的中断,在这14个可屏蔽中断中,其中TIM1和TIM2产生的中断请求通过INT13、INT14中断线到达CPU,这两个中断已经预留给了实时操作系统,因此剩下的12个可屏蔽中断可供外部中断和处理器内部单元使用。F28335的外设中断源远远不止12个,共用58个。那么如何将这58个外设中断源分配给这12个中断线呢,这就需要F28335的PIE外设中断扩展模块来完成。F28335的中断源及连接如下图所示:
图1 中断源以及连接关系
图2 F28335 中断源及IO复用结构图
1.2三级中断机制
28335采用三级中断机制,分别为外设级中断、PIE级中断和CPU级中断。CPU只能响应从CPU中断线上过来的中断请求。但F28335中断源很多,CPU没有那么多中断线,在有限中断线的情况下,只能安排中断线进行复用,其复用管理就有了中间层的PIE级中断,外设要能够成功产生中断响应,就要首先经外设级中断允许,然后经PIE允许,最终CPU做出响应。
图3 处理器中断扩展模块图
中断响应过程分为两块,下半部分为PIE小组响应外设中断的过程,上半部分为CPU响应12组PIE中断的过程。
1. 外设级中断
CPU正常处理程序过程中,外设产生了中断事件(比如定时器定时时间到,串口接收数据完成),那么该外设对应中断标志寄存器(IF)响应的位将被自动置位,如果该外设对应中断使能寄存器(IE)中响应的使能位正好置位(需要软件控制),则外设产生的中断将向PIE控制器发出中断申请。若对应外设级没有被使能,相当于该中断被屏蔽,不会向PIE提出中断申请,但此时中断标志寄存器的标志位将保持不变,一直处于中断置位状态,若清除标志位需要使用软件编程。
2. PIE级中断
因为中断源很多但是28335并没有那么多中断线,因此将CPU的12个中断线交给PIE进行复用管理。如图4所示。
图4 PIE中断扩展原理
由图可知,PIE将外设中断分为了12组,分别对应CPU的12个可屏蔽中断线,并且每一组由8个外设级中断组成,这8外设级中断分别对应外设接口的中断引脚,PIE通过一个8选1的多路选择器将这8个外设中断组成一组。具体连接关系如图5所列。实际有效的外设中断为58个其余为保留。
与外设中断类似,在PIE模块内每组中断有相应的中断标志位(PIEIFRx)和使能位(PIE-IERx.y)。除此之外,每组PIE中断(INT1~INT12)有一个响应标志位(PIEACK)。
一但PIE有中断产生,响应的中断标志位PIEIFR置1.如果响应的PIE中断使能位也置1,则PIE将检查响应的PIEACK以确定CPU是否准备响应该中断。如果相应位清零,PIE向CPU申请中断。置1则需要等待相应的位置0。PIE通过对PIEACKx
的位控制来控制每1组中只有1个中断能被响应,一旦响应后,就需要PIEACKX
相应位清零,以让它能够响应该组中后边过来的中断。
图5 PIE中断分组
3. CPU级中断
一旦CPU申请中断,CPU级中断标志位(IFR)将置1。中断标志位锁存到标志寄存器后,只有CPU中断使能寄存器(IER)或中断调试使能寄存器(DBGIER)相应的使能位和全局中断屏蔽位(INTM)被使能时才会响应中断申请。CPU 级使能可屏蔽中断采用CPU中断使能寄存器(IER)还是中断调试使能寄存器(DBGIER)与中断处理方式有关。标准处理模式下,不使用中断调试使能寄存器(DBGIER)。只有当F28335使用实时调试(Real-time Debug)且CPU被停止(Halt)时,才使用中断调试使能寄存器(DBGIER),此时INTM不起作用。如果F28335使用实时调试而CPU仍然工作运行,则采用标准的中断处理。
图6 中断处理流程
1.3中断向量
CPU响应中断,就是CPU要去执行相应的中断服务程序,其响应过程是CPU将现执行程序的指令地址压入堆栈,跳转到中断服务程序入口地址,中断服务程序的入口地址就是中断向量,这个中断向量用2个16位寄存器存放。入口地址是22位的,地址的低16位保存在该向量的低16位;地址的高16位则保存在它的高6位,更高的10位保留。
1. 中断向量分配
每条中断线对应的不是唯一中断,每组PIE对应的也不是唯一中断,中断服务程序要处理所有输入的中断请求,这就要求编程人员在服务程序的入口处采用软件方法将这些中断线复用的中断分开,以便能够正确响应中断。但是软件分离的方法会影响中断的响应速度,在实时性要求高的应用中不能使用。这就涉及如何加快中断服务程序的问题。
2. PIE向量表
在F28335中采用PIE中断向量表来解决上述问题,通过PIE中断向量表使得96个可能产生的中断都有各自独立的32位入口地址。CPU 的中断优先级有高到低依次是从INT1-INT12。每组PIE控制的8个中断优先级依次是从INTx.1-INTx.8。
3. 中断向量的映射方式
在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寄存器的值进行修改。
图7 映射向量配置表
①在F28335器件中,复位后VMAP和M0M1MAP模式均被置1,而ENPIE模式强制为0。
②复位向量始终boot ROM 获取。复位程序引导(BOOT)完成后,用户需要重新初始化PIE中断向量表,应用程序使能PIE中断向量表,并从PIE向量表中获取中断向量。
图8 复位后的映射向量配置表
1.4中断操作
1.复用中断操作过程
PIE模块8个中断分成一组与外部中断一起共用一个CPU中断,总共有12组中断(INT1-INT12)。每组中断有相应的中断标志(PIEIFR)和使能寄存器(PIEIER),这些寄存器控制PIE向CPU申请中断。同时CPU还根据PIEIFR和PIEIER 寄存器确定执行哪个中断服务程序。在清除PIEIFR和PIEIER的位时,要遵循以下3个规则。
①不要用软件编程清除PIEIFR的位:清除PIEIFR寄存器的位时,有可能会使产生的中断丢失。要清除PIEIFR位时,还未被执行的中断必须被执行,如果用户希望在执行正常的服务程序之前就要清除PIEIFR位时,需要遵循以下步骤:
1,设置 EALLOW 位为 1,允许修改 PIE 向量表。
2,修改 PIE 向量表,使外设服务程序指针向量指向一个临时的 ISR,这个临
时的 ISR 只执行一个中断返回(IRET)操作。
3,使能中断,使中断执行临时中断服务程序。
4,在执行完中断服务程序之后,PIEIFR 位将被清除。
5,修改 PIE 向量表,重新映射外设服务程序到正确的中断服务程序。
6,清除 EALLOW 位。
CPU中断标志寄存器IFR在CPU内部,这样操作将不会影响任何向CPU申请
的中断。
②软件设置中断优先级。使用CPUIER寄存器控制全部中断的优先级,PIEIER
寄存器控制每组中断的优先级,只有与被服务的中断在同一组时,修改PIEIFR
寄存器的值才有意义,当PIEACK位保持来自CPU中断时,修改操作才被最终执
行。当来自无关本组的中断被执行时,禁止本组的PIEIER位没有意义。
③使用 PIEIER 禁止中断。如果PIEIFR寄存器用来使能一个中断,同样可以
禁止该中断。
定时器中断
1. 定时器操作原理
28335有3个32位的通用定时器,分别为TIMER0.TIMER1.TIMER2。定时器2预留给DSP的实时操作系统BIOS。如果没有使用实时操作系统,3个定时器都可以被用户使用。定时器功能如下图所示:
图9 定时器流程
定时器有预分频模块和一个定时、计数模块,预分频模块包括16位(TDDRH:TDDR)定时器分频与16位预定标计数器(PSCH:PSC);定时计数模块包括32位周期寄存器(PRDH:PRD)和一个32位计数寄存器(TIMH:TIM)。当系统时钟(SYSCLKOUT)来一个脉冲,PSCH:PSC 预定标计数器减 1,当 PSCH:
PSC 预定标计数器减到 0 的时候,预定标计数器产生下溢后向定时器的 32 位计
数器 TIMH:TIM 借位,即 TIMH:TIM 计数器减 1,同时 PSCH:PSC 可以重载定时器分频寄存器(TDDRH:TDDR)的值;当计数寄存器 TIMH:TIM 减到 0 产生下溢的时候,计数寄存器会重载周期寄存器(PRDH:PRD)的值,同时定时器会产生一个中断信号给 CPU。定时器的中断结构如下图所示:
图10 定时器中断结构
2. 定时器相关寄存器
3.程序清单与注释
① 定时器0初始化程序
void Init_CpuTimers(floatfrep, floatperiod)
{
EALLOW;
SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK= 1 ;
EDIS;
//定时器0的初始化
// 指向定时器0的寄存器地址
CpuTimer0.RegsAddr= &CpuTimer0Regs;
// 设置定时器0的周期寄存器值
CpuTimer0Regs.PRD.all= 0xFFFF;
CpuTimer0Regs.TPR.all= 0;
CpuTimer0Regs.TPRH.all= 0;
//确保定时器为停止状态
CpuTimer0Regs.TCR.bit.TSS=1;
//重载使能
CpuTimer0Regs.TCR.bit.TRB= 1;
CpuTimer0.InterruptCount= 0;
//PIE设置
EALLOW;
PieVectTable.TINT0= &TIM0_IRQn;
EDIS;
PieCtrlRegs.PIEIER1.bit.INTx7= 1 ;
//定义响应时间
ConfigCpuTimer(&CpuTimer0,frep, period);
//cpu内核设置
CpuTimer0Regs.TCR.bit.TSS= 0; // 开启定时器功能
IER |= M_INT1;
EINT;
ERTM;
}
interrupt void TIM0_IRQn(void)
{
EALLOW;
CpuTimer0.InterruptCount ++;
if(CpuTimer0.InterruptCount == 1){
LED1 = ~LED1;}
if(CpuTimer0.InterruptCount == 2){
LED2 = ~LED2;}
LED5 = ~LED5;
LED6 = ~LED6;
PieCtrlRegs.PIEACK.bit.ACK1 =1;
EDIS;
}
外部中断初始化程序
void Init_InterrputKEY1(void)
{
EALLOW;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1; // GPIO时钟
//外设中断设置
GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO12 = 0;
GpioCtrlRegs.GPAPUD.bit.GPIO12 = 0;
GpioCtrlRegs.GPBMUX2.bit.GPIO48 = 0;
GpioCtrlRegs.GPBDIR.bit.GPIO48 = 1;
GpioCtrlRegs.GPBPUD.bit.GPIO48 = 0;
GpioCtrlRegs.GPAQSEL1.bit.GPIO12 = 0; // 系统时钟不分频
GpioDataRegs.GPBCLEAR.bit.GPIO48 = 1;
EDIS;
EALLOW;
GpioIntRegs.GPIOXINT1SEL.bit.GPIOSEL = 12; // 定义外部中断接口
EDIS;
//PIE中端设置
EALLOW;
PieVectTable.XINT1 = &Key_IRQn_1; // 定义外部中断事件
EDIS;
PieCtrlRegs.PIEIER1.bit.INTx4 = 1;
XIntruptRegs.XINT1CR.bit.POLARITY = 0; // c触发方式
XIntruptRegs.XINT1CR.bit.ENABLE = 1; // 外部中断使能
//CPU中断设置
IER |= M_INT1;
EINT; // 开全局中断
ERTM; } // 可以在线仿真
interrupt void Key_IRQn_1(void)
{
Uint32 i;
for(i=0;i<10000;i++);
while(!KEY_H1);
LED3 = ~LED3;
PieCtrlRegs.PIEACK.bit.ACK1 = 1;
}
void Init_InterrputKEY2(void)
{
EALLOW;
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;
//外设中断设置
GpioCtrlRegs.GPAMUX1.bit.GPIO13 = 0;
GpioCtrlRegs.GPADIR.bit.GPIO13 = 0;
GpioCtrlRegs.GPAPUD.bit.GPIO13 = 0;
GpioCtrlRegs.GPBMUX2.bit.GPIO48 = 0;
GpioCtrlRegs.GPBDIR.bit.GPIO48 = 1;
GpioCtrlRegs.GPBPUD.bit.GPIO48 = 0;
GpioCtrlRegs.GPAQSEL1.bit.GPIO13 = 2; // 系统时钟不分频
GpioCtrlRegs.GPACTRL.bit.QUALPRD1 = 0xff; // 采样周期为 510 * SYSCLK
GpioDataRegs.GPBCLEAR.bit.GPIO48 = 1;
EDIS;
EALLOW;
GpioIntRegs.GPIOXINT2SEL.bit.GPIOSEL = 13;
EDIS;
//PIE中端设置
EALLOW;
PieVectTable.XINT2 = &Key_IRQn_2;
EDIS;
PieCtrlRegs.PIEIER1.bit.INTx5 = 1;
XIntruptRegs.XINT2CR.bit.POLARITY = 0;
XIntruptRegs.XINT2CR.bit.ENABLE = 1;
//CPU中断设置
IER |= M_INT1;
EINT; // 开全局中断
ERTM; } // 可以在线仿真
interrupt void Key_IRQn_2(void)
{
Uint32 i;
for(i=0;i<10000;i++);
while(!KEY_H2);
LED4 = ~LED4;
PieCtrlRegs.PIEACK.bit.ACK1 = 1;
}
时钟中断 与 外部GPIO中断 初始化的不同点
1. 时钟初始化不需要定义触发方式 即XIntruptRegs.XINT2CR.bit.POLARITY = 0;
2. 时钟初始化不需要使能相应中断口 XIntruptRegs.XINT2CR.bit.ENABLE = 1;
3. 时钟初始化需要定义周期值与计数器的起点值
4. 时钟初始化需要定义中断时间与重载使能(TRB),开启(TSS)