我们知道F28335的中断采用的是3级中断机制,分别是外设级中断、PIE级中断和CPU级中断,如图1所示:
我们知道F28335内部有16个中断线,2个不可屏蔽中断和14个可屏蔽中断INT1-IN14。其中12个可屏蔽中断INT1-INT12通过PIE外部中断扩展。
PIE将外设中断分成了12组,分别对应着CPU的12个可屏蔽中断线INT1-INT12,每一组由8个外设级中断组成。8个外设中断分别对应相应外设接口的中断引脚,PIE通过一个8选1的多路选择器,将8个外设中断组成一组,EPWM模块是F28335的一个外设,EPWM模块对应的中断所在PIE组为第三组,即连接的CPU的中断线为INT3,其中对应的8个外设中断,只用到了六个,INT3.1-INT3.6分别对应的是EPWM1-6模块的外设中断,剩余两个外设中断保留。
如果外设要能够成功产生中断响应,就要首先经外设级中断允许,然后经PIE允许,然后经过CPU允许,最终CPU做出响应。
具体的流程就是,首先外设级的中断需要打开,我们需要在EPWM模块的初始化文件中进行设置,比如,如果我们需要使用EPwm1模块的外设中断,那么就需要将EPwm1模块的中断进行使能,并设置什么时候进入中断,几个事件进一次中断等:
EPwm1Regs.ETSEL.bit.INTSEL=ET_CTR_ZERO;//当计数值CTR到0时候,ePWM1模块中断触发(更新比较寄存器CMPA和CMPB的值)EPwm1Regs.ETSEL.bit.INTEN=1;// ePWM1模块中断产生位,使能中断EPwm1Regs.ETPS.bit.INTPRD=1;//每次事件发生都触发中断,在第一个事件时,产生SOC信号,即SOCBCNT=01,产生中断这样我们初始化EPWM模块之后,就相当于开启了外设级中断。EPWM模块的中断对应的是PIE中断的第三组,EPWM1模块的中断对应INT3.1,当EPWM1中断信号,即时基计数器CTR为0时,外设EPWM1模块的中断信号进入PIE模块,PIE模块接收到外设请求的中断之后,PIEIFR即PIE模块的中断标志位会自动置1(不需要人为设置),之后经过PIEIER,即PIE模块的中断使能位,如果相应的PIEIER中断使能位也置1(需要在主程序中设置),PIE模块将检查PIEACK。
PIEACK共有12个位,PIEACK1至PIEACK12分别对应PIE模块的12组向量,PIEACK3对应的是PIE中断的第3组,即EPWM模块的中断,PIE模块将检查PIEACK3确定CPU是否准备中断,由图1知道,PIEACK3信号是低有效,当PIEACK3位清零时,通过一个与门,与之后是1的话,PIE模块就可以将外设级中断送入CPU的中断线,比如EPWM的中断对应INT3,从而进入CPU级中断,此时经过CPU的中断标志位IFR会自动置1(不需要人为设置),之后同样经过CPU的中断使能位IER,如果IER也置1(需要在主程序中设置),那么之后会经过CPU的总中断INTM,当INTM位被置0时,外设中断就可以进入CPU被执行。
当PIE送入一个中断进入CPU之后,PIEACK3对应的位会自动置1,CPU读到PIEACK3位为1的时候,就会断开CPU的中断标志位IFR,防止下一个中断进入CPU中断。
当CPU执行当前的中断的程序之后,在中断程序结束之后,需要将ePWM中断的中断标志位写1清除ePWM的中断标志位,并将ePWM对应的组的PIEACK3位置1,即是CPU给PIEACK3位写1(就是中断程序中将PIEAC3K位置1),PIEACK3位写1之后,PIE模块对应的3组的所有中断标志位PIEIFR3.1到PIEIFR3.8会自动清零全部断开,之后PIEACK3位会自动清零,等待下一个中断。
以下面的中断为例:
interruptvoidepwm1_isr(void){k2++;EPwm1Regs.ETCLR.bit.INT=1;//清除标志位PieCtrlRegs.PIEACK.all=PIEACK_GROUP3;//清除应答位,中断结束后准备下一次中断响应。EINT;}这里定义了一个ePWM1模块的中断,中断函数的名字为epwm1_isr,那么我们需要在主程序的最开始声明这个中断函数:
interruptvoidepwm1_isr(void);//声明EPWM1中断服务程序并在主函数里面需要将epwm1_isr中断函数的入口地址映射到ePWM1的中断的入口地址:
PieVectTable.EPWM1_INT=&epwm1_isr;//映射使用的中断向量由于我们中断使用的ePWM1的中断,ePWM1的中断是属于外设级中断,所以首先需要在EPWM的初始化程序中,开启ePWM1模块的外设中断:
EPwm1Regs.ETSEL.bit.INTSEL=ET_CTR_ZERO;//当计数值CTR到0时候,ePWM1模块中断触发EPwm1Regs.ETSEL.bit.INTEN=1;// ePWM1模块中断产生位,使能中断EPwm1Regs.ETPS.bit.INTPRD=1;//每次事件发生都触发中断,在第一个事件时,产生SOC信号,即SOCBCNT=01,产生中断根据图1,我们知道当外设中断进入PIE模块时,PIE模块的中断标志位PIEIFR会自动置1,但是PIE的中断使能位PIEIER需要在主程序中设置:
PieCtrlRegs.PIEIER3.bit.INTx1 = 1;//允许PIE级中断INT3.1,即EPWM1中断由于PIEACK3的位初始状态为0,所以ePWM1中断会顺利进入CPU级中断INT3,CPU的中断标志位IFR会自动置1,但是CPU的中断使能位IER需要在主程序中设置:
IER |= M_INT3;//允许CPU的INT3(对应的是EPWM1~6)之后还需要在主程序设置CPU级的总中断INTM,让INTM置0使能CPU的全局中断:
EINT; // Enable Global interrupt INTM这样外设ePWM1的中断就能顺利进入CPU进行执行了,我们前面分析了,PIEACK3位初始状态为0,进入中断服务子程序之后,PIEACK3会自动置1,CPU读取到PIEACK3为1时,就会关闭CPU的所有中断线。
在中断服务子程序epwm1_isr函数中打一个Breakpoint断点 ● :
interruptvoidepwm1_isr(void){● k2++;// ● 表示断点位置EPwm1Regs.ETCLR.bit.INT=1;//清除标志位PieCtrlRegs.PIEACK.all=PIEACK_GROUP3;//清除应答位,中断结束后准备下一次中断响应。EINT;}当程序刚载入到目标板的时候,在CCS的Expression中观察PIEACK寄存器的所有的值,如图2所示:
可以发现当程序还没运行的时候PIEACK的所有位初始状态为0,之后点击运行程序,当程序运行到Breakpoint断点的时候,再次查看PIEACK寄存器的所有位的值,如图3所示:
之后按住F6进行单步运行,首先是清除ePWM1模块的中断标志位:
➤ EPwm1Regs.ETCLR.bit.INT=1;//清除标志位PieCtrlRegs.PIEACK.all=PIEACK_GROUP3;//清除应答位,中断结束后准备下一次中断响应。再次按F6进行单步运行:
EPwm1Regs.ETCLR.bit.INT=1;//清除标志位➤ PieCtrlRegs.PIEACK.all=PIEACK_GROUP3;//清除应答位,中断结束后准备下一次中断响应。运行最后一句之后,查看PIEACK寄存器的值的变化,当再次按F6,运行完最后一条语句之后,会发现PIEACK3位会清零,如图4所示:
这也就是CPU给PIEACK的所有位写PIEACK_GROUP3之后,PIEACK会将PIE模块的所有中断标志位全部清零断开,同时PIEACK3寄存器位也被自动清零,准备下一次的中断。
最后再次观看PIEACK寄存器的英文描述,如图5所示:
如果CPU读到PIEACK的某一位为0,表明PIE能够将对应组的中断送入CPU中断;
如果CPU写0到PIEACK,没有效果;
如果CPU读到PIEACK的某一位为1,表明已经有某一组的中断进入了CPU中断,其他组的中断被阻断。
如果CPU写1到PIEACK的某一位,表明PIEACK对应位会自动清零,允许PIE模块将正在等待进入CPU中断的中断函数送入CPU执行。
以上就是对中断执行的全部过程的理解。