中断
F28335中断概述
- F28335内部有16个中断线,其中包括2个不可屏蔽中断(RESET和NMI)与14个可屏蔽中断(可通过相应的中断使能寄存器使用或者禁止产生的中断)。其中TIM1 和 TIM2 产生的中断请求通过 INT13、 INT14 中断线直接到达 CPU,这两个中断已经预留给了实时操作系统,因此剩下的 12 个可屏蔽中断可供外部中断和处理器内部单元使用。
- F28335 的外设中断源共有58个,而可用的中断线只剩12个了。因此就需要 F28335 的 PIE 外设中断扩展模块来完成将中断源分配给中断线,即12个中断线分别设置为一组,每组有12个中断源。因此当其中一个中断源使用完成后,需要在中断回调函数中手动将PIEACK清零,如果这里不清零下一次就不会进入中断了(写1就是清零,PieCtrlRegs.PIEACK.bit.ACK1=1;)
F28335 中断源及 IO 复用结构图如下图所示:
执行过程
F28335 的中断采用的是 3 级中断机制,分别是外设级中断、PIE 级中断和 CPU 级中断。外设要能够成功产生中断响应,就要首先经外设级中断允许,然后经 PIE 允许,最终 CPU 做出响应
- 外设部分:首先将外设中断(IE)使能,当中断事件发生时,中断标志位就会置起(不管外设中断有没有使能,中断发生时,中断标志位都会置起。要想中断标志寄存器复位,就需要软件编程清除。当没有对他进行清除,此时一旦使能了,就立马会向PIE发出中断申请。因此在进入中断服务函数以后需要手动复位外设的中断标志寄存器,有些硬件外设会自动复位中断标志寄存器),向PIE控制器发出中断申请(当没有使能,则不会向PIE发出中断请求)
- PIE部分:共有12个PIE中断管理模块,每个模块内部分为8个组。当外设向PIE发出中断请求时,相应的中断标志位(PIEIFRx.y)将置 1(执行完中断服务函数后,会自动硬件清除,别手动清除,因为CPU要根据这个选择相应的中断服务函数)。如果 相应的 PIE 中断使能位也置 1,则 PIE 将检查相应的 PIEACKx 以确定 CPU 是否准备响应该中断。如果相应的 PIEACKx 位清零,PIE 向 CPU 申请中断;如果 PIEACKx 置 1,PIE 将等待到相应的 PIEACKx 清零才向 CPU 申请中断。PIE 通过对 PIEACKx 的位控制来控制每 1 组中只有 1 个中断能被响应,一旦响应后,就需要将 PIEACKX 相应位清零,以让它能够响应该组中后边过来的中断。
- CPU级中断:PIE向CPU发送中断请求后,相应的中断线对应的中断标志位(IFR)将置1,此时如果使能了对应了的中断线使能请求(IER)和及全局中断屏蔽位(INTM)使能(这个如果关闭了,那所有的中断就都进不来了),就会产生中断,此时就会去执行中断服务函数。
- 中断服务函数:CPU 响应中断,就是 CPU 要去执行相应的中断服务程序,其响应过程是 CPU 将现执行程序的指令地址压入堆栈,跳转到中断服务程序入口地址,中断服务程 序的入口地址就是中断向量。每个外设都对应有自己的中断服务函数(CPU根据 PIEIFR 和 PIEIER 寄存器确定执行哪个中断服务程序),需要自己手动设置。CPU 的中断优先级由高到低依次是 INT1-INT12,每组 PIE 控制的 8 个中断优先级依次是从 INTx.1-INTx.8
外部中断
简介
F28335 共 支 持 7 个 外 部中 断 XINT1-XINT7 (就是某个中断线组中对应的某一个), 其 中 XINT1-XINT2 只 能 对 GPIO0-GPIO31 配置;XINT3-XINT7 只对 GPIO32-GPIO63 配置。XINT13 还有一个 不可屏蔽的外部中断 XNMI 共用中断源。每一个外部中断可以被选择为正边沿或 负边沿触发,也可以被使能或者禁止(包括 XNMI)。
配置步骤
void main()
{
int i=0;
InitSysCtrl();
//以下四步是都要做的,初始化最初的中断
InitPieCtrl();//初始化PIE控制寄存器为已知状态,也就是使能位和中断标志位全部失能 DSP2833x_PieCtrl.c里自带的函数
IER = 0x0000;//失能所有CPU级的中断
IFR = 0x0000;//清除所有CPU级的中断标志位
InitPieVectTable();//初始化向量表 DSP2833x_PieVect.c中自带的函数
EXTI1_Init();
while(1)
{
DELAY_US(100);
}
}
void EXTI1_Init(void)
{
EALLOW;//开启写保护
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1; // GPIO input clock,输出也用这个
EDIS;
EALLOW;
//KEY端口配置
GpioCtrlRegs.GPAMUX1.bit.GPIO12=0; //GPIO功能
GpioCtrlRegs.GPADIR.bit.GPIO12=0;//方向为输入
GpioCtrlRegs.GPAPUD.bit.GPIO12=0;//默认上拉
GpioCtrlRegs.GPAQSEL1.bit.GPIO12 = 0; // 配置时钟采用,0就是与时钟同步。外部中断1(XINT1)与系统时钟SYSCLKOUT同步
EDIS;
//设置 IO 口与中断线的映射关系
EALLOW;
GpioIntRegs.GPIOXINT1SEL.bit.GPIOSEL = 12; // XINT1是GPIO12
EDIS;
//指定中断向量表中断服务函数地址
EALLOW; // 修改被保护的寄存器,修改前应添加EALLOW语句
PieVectTable.XINT1 = &EXTI1_IRQn; // .后面就是选择需要被定义中断向量的是哪个外设,比如DMA什么的 DSP2833x_PieVect.h
EDIS; // EDIS的意思是不允许修改被保护的寄存器
//使能外设对应的 PIE 中断
PieCtrlRegs.PIEIER1.bit.INTx4 = 1; //使能第一中断线中的第四个 先是PIE1组(PIEIER1), 然后.其中的第四个(INTx4) DSP2833x_PieCtrl.h
//设置外部中断触发方式并使能中断 DSP2833x_XIntrupt.h
XIntruptRegs.XINT1CR.bit.POLARITY = 0; // 下降沿触发中断
XIntruptRegs.XINT1CR.bit.ENABLE= 1; // 使能XINT1
IER |= M_INT1; // 使能CPU中断1(INT1)
EINT; // 开全局中断
ERTM;//在debug调试的时候可以使用这个
}
interrupt void EXTI1_IRQn(void)
{
Uint32 i;
for(i=0;i<10000;i++); //键盘消抖动
while(!GpioDataRegs.GPADAT.bit.GPIO12);//判断是否按下,按下就变为低电平
LED2_TOGGLE;//#define LED2_TOGGLE (GpioDataRegs.GPCTOGGLE.bit.GPIO67=1)
PieCtrlRegs.PIEACK.bit.ACK1=1;//对PIEACK清零,如果这里不清零下一次就不会进入中断了(写1就是清零)
}
定时器中断
简介
- 定时器1,2一般是给操作系统预留的,其不需经过PIE,直接进入CPU(所以下面的图表中只有TIMER0)。当使用操作系统时,这时程序可用的就只有0了。
- 这三个定时器的中断信号分别为TINT0, TINT1, TINT2,分别对应于中断向量INT1,INT13,INT14。
- 图中分为两个模块,预分频模块(左边)和一个定时/计数模块(右边那两个)。当系统时钟(SYSCLKOUT)来一个脉冲,PSCH:PSC 预定标计数器减 1,当 PSCH: PSC 预定标计数器减到 0 的时候,预定标计数器产生下溢后向定时器的 32 位计 数器 TIMH:TIM 借位,即 TIMH:TIM 计数器减 1,同时 PSCH:PSC 可以重载定时器 分频寄存器(TDDRH:TDDR)的值;当计数寄存器 TIMH:TIM 减到 0 产生下溢的时候,计数寄存器会重载周期寄存器(PRDH:PRD)的值,同时定时器会产生一 个中断信号给 CPU。
其实就是设定TDDR,PRD的值。他们会将值分别装载到PSC和TIM,然后不断递减,当PSC递减完后,会向TIM借1 ,TIM递减完后会产生一个中断,然后PSC和TIM分别重新装载值。
寄存器
寄存器 | 名称 | 作用 |
---|---|---|
TIM | 计数器 | 减小到0时会产生一个中断,然后重载周期寄存器(PRDH:PRD)的值 |
TIMH | 计数器高 | |
PRD | 周期寄存器 | 其值会装载到TIM中,开启新的一轮轮回 |
PRDH | 周期寄存器高 | |
TCR | 控制寄存器 | 各位见下面表格 |
TPR | 预定标寄存器 | 就是用来设置TDDR(PSC装载的数 低8位)和PSC(随时钟周期的到来不断减1 高8位)的值 |
TPRH | 预定标寄存器高 |
- TCR控制寄存器各位
具体使用
main
void main()
{
int i=0;
InitSysCtrl();
//以下四步是都要做的,初始化最初的中断
InitPieCtrl();//初始化PIE控制寄存器为已知状态,也就是使能位和中断标志位全部失能 DSP2833x_PieCtrl.c里自带的函数
IER = 0x0000;//失能所有CPU级的中断
IFR = 0x0000;//清除所有CPU级的中断标志位
InitPieVectTable();//初始化向量表 DSP2833x_PieVect.c中自带的函数
LED_Init();
TIM0_Init(150,500000);
while(1)
{
i++;
if(i%2000==0)
{
LED1_TOGGLE;
}
DELAY_US(100);
}
}
time.c
//定时器0初始化函数
//Freq:CPU时钟频率(150MHz)
//Period:定时周期值,单位us
void TIM0_Init(float Freq, float Period)
{
EALLOW;//关闭写保护
SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // CPU Timer 0
EDIS;//开启写保护
//设置定时器0的中断入口地址为中断向量表的INT0
EALLOW;
PieVectTable.TINT0 = &TIM0_IRQn;//TIM0就用这个表示TINT0
EDIS;
//DSP2833x_CpuTimers.c
//使用InitCpuTimers(void)进行定时器的初始化,首先定义一个结构体struct CPUTIMER_VARS CpuTimer0;
//配置寄存器CpuTimer0Regs
//指向定时器0的寄存器地址
CpuTimer0.RegsAddr = &CpuTimer0Regs;
//设置定时器0的周期寄存器值
CpuTimer0Regs.PRD.all = 0xFFFFFFFF;
//我觉得下面这几步都是用不到的,因为在ConfigCpuTimer函数中,做了相同的设置了
//设置定时器预定标计数器值为0,我觉得这步不需要,因为在函数中有相同的设置了
CpuTimer0Regs.TPR.all = 0;
CpuTimer0Regs.TPRH.all = 0;
//确保定时器0为停止状态,我觉得这步不需要,因为在函数中有相同的设置了
CpuTimer0Regs.TCR.bit.TSS = 1;//停止:1 启动:0
//重载使能,我觉得这步不需要,因为在函数中有相同的设置了
CpuTimer0Regs.TCR.bit.TRB = 1;
// Reset interrupt counters:
CpuTimer0.InterruptCount = 0;
CpuTimer0Regs
//当想要定时500ms时,Freq设定为150(150MHz/150)Period设定为500000
ConfigCpuTimer(&CpuTimer0, Freq, Period);
//开始定时器功能
CpuTimer0Regs.TCR.bit.TSS=0;
//开启CPU第一组中断并使能第一组中断的第7个小中断,即定时器0
IER |= M_INT1;//第一个中断线
PieCtrlRegs.PIEIER1.bit.INTx7 = 1;//第7个
//使能总中断
EINT;
ERTM;
}
interrupt void TIM0_IRQn(void)
{
EALLOW;
LED2_TOGGLE;
PieCtrlRegs.PIEACK.bit.ACK1=1;//对PIEACK清零,如果这里不清零下一次就不会进入中断了(写1就是清零)
EDIS;//开启写保护
}