carloscn / blog

My blog
Apache License 2.0
87 stars 20 forks source link

DSP-F2812的中断系统 #161

Open carloscn opened 1 year ago

carloscn commented 1 year ago

DSP-F2812的中断系统

2014年5月1日,于沈阳化工大学,5号楼

第一次接触中断概念还是在C51单片机中,那时候讲的中断就是很简单的一个功能,LED的点亮,后来在图书馆借书发现真的好多程序在中断中可以执行的。那时候发现啊中断真是个重要的概念。后来我C51就放弃了,很久一段时间我开始接触2812这个概念又一次的出现,或者吧,中断是在MCU中的一个重要的思想。X2812的中断系统,我们再一次的学习,现在我就要进行一个整理,算是我对X2812的一个理解。

1. 什么是中断

中断是个通用的概念,任何MCU中都会有中断系统。给举个生活中的简单的例子,我正在打字,然后电话突然响了,然后我停下手中的工作去接电话,然后处理一些事情,电话结束,然后我继续打字。我在举一个软件中断的例子,我写了一段C语言的代码,里面当然有必要的main函数,还有我定义的add()函数,我开始在main函数中赋值,然后就执行了add()函数,以下例子:

#include <stdio.h>

int add(int,int);

void main(void){
    int a,b,c;
    a = 1;
    b = 2;
    c = add(a,b);
    a = 2;
    b = 3;
}
int add(int a,int b){
    int c;
    c = a + b;
    return(c);
}   

这是一个非常简单的C语言程序,我们看到我们看到在c = add(a,b);这条语句的时候,程序脱离了main函数转而进行了add函数。这就是个中断。或许我们可以这么理解。本例中中断的触发方式是通过add(a,b)这种方式使程序进入了中断。当然生活中的例子打电话,电话铃响了就是一个触发方式。那么同样的道理,我们的DSP中也有特定的方式触发中断,我们要告诉DSP这里有个中断要执行了。

DSP的中断系统我们需要一部分一部分的理解。首先要明确,DSP的中断系统分为三级CPU级,PIE级,外设级别。这个就像是皇帝办公,CPU当然就是皇帝了,PIE是个太监吧,外设级就像是大臣。大臣一般都是写奏折,奏折上面写了一大堆什么哪里发大水了,有个地方要兴建什么东西啊之类的。写奏折就是为了上表皇帝,让皇帝去处理。但是大臣不可以直接交给皇帝,万一夹了什么暗器之类的伤到皇帝怎么办,而且各种各样的大臣,有什么礼部,文部,各种各样的官员,皇帝是很闹心的。一会儿礼部,一会儿翰林的,一会儿搞礼仪,一会儿搞文化的,是非常非常闹心的。因为各种各样的理由,产生了一个中间级的中断,这个中断就是PIE中断。Peripheral Interrupt Expansion全称就是外围中断扩展,其实就是外设中断的管理器。也就是那个太监。它负责的就是把外设中断传递给CPU中断,也就是大臣们提出请求,然后交给太监,太监们的工作就是分类,排序,然后等皇帝处理完一批,然后再交上去一批。用正常的话翻译过来就是外设中断提出请求交给PIE进行处理,然后再给CPU等待响应。那么我们就来具体研究一下这个中断的过程,每个过程都是怎么进行的!

2. X2812的CPU中断

书中的几句话,写的非常的好。DSP中,申请信号通常是由软件和硬件产生的信号,他们可以使得CPU暂停正在执行的主程序,转而去执行一个中断服务子程序。通常中断申请信号是由外围设备提出的,表示一个特殊的事情已经发生,请求CPU暂停正在执行的程序,去处理响应的紧急的事件。例如,CPU定时器0完成一个周期的计数,就会发出一个周期中断的请求信号,这个信号通知CPU定时器已经完成了一段时间的计时,这个时候可能有些禁忌的情况需要CPU过来处理。

CPU定时器0的中断是个很重要的中断,因为它可以通过计时的方式准确的进入我们的中断子程序,我们在中断子程序中建立一些为我们服务的程序,就能达到相应的要求,我么要会使用定时器中断进行计时操作!

下面我们分别来说说三级中断,从皇帝开始了!

2.1 CPU中断的概述

CPU的中断同F2812一样,有两种方式,一种是软件触发,一种是硬件触发(片内外设或者外围中断信号)。还有种说法,中断分为两种,一种是可屏蔽中断(屌丝级),一种是不可屏蔽中断(大官儿级的),可屏蔽中断就是可以被打断,(CPU如果很忙的话该中断发出信号,CPU可以很不友好的说,你给我等会儿。)可是不可屏蔽中断就不一样了(CPU就说,是的领导,以最快的速度执行。啥都不管了。)不可屏蔽中断就是不可以被屏蔽的中断,一旦中断信号发出了申请,CPU必须无条件的立即执行相应的服务子程序。我只介绍一个不可屏蔽中断:

image-20221203100256288

左面的引脚看到了吧,XNMI_XINT13引脚,通过该引脚可以产生叫做NMI的不可屏蔽中断,该引脚是低电平的时候,CPU就能检验到一个有效的中断请求,然后就进行他的中断服务程序。(领导级别的中断就是不一样,不顾一切的执行,优先级最高了。我们记住这个领导就好了。皇帝都要服从他们的命令,我们就封它为皇太后怎么样,以哀家自称。这个厉害!)

CPU就像是个皇宫,里面有做处理的皇帝,有不可屏蔽的皇太后,还有一群可以被屏蔽的妃子。这群妃子怎么工作呢。妃子有的要求皇帝过来的时候皇帝就会放下手中的事儿去找妃子了。假如皇帝在书房处理大臣们的奏折,这个时候定时器0妃子吵吵了,说非得要皇上过去看她,她病的很严重。皇帝放下手中的笔,把没处理完的奏折小心的放一个特殊的位置,方便下一次的处理。然后就去找定时器0妃子了。皇帝保存好奏折就是保护现场的行为。DSP的CPU会奖一些工作例如ST0,T,AH,AL,PC等寄存器的内容放在堆栈里面。然后才去执行中断。

当然了,后宫的等级制度是很严格的,比如这32个妃子,有32个妃子呢,谁得宠,谁不得宠,谁家背景深厚,都有说道。比如定时器0妃子刚才不叫皇上去她那里吗?这时候皇后说头疼,也发出头疼也发出同样的请求叫皇上去。皇上肯定先去比较重要的那里啊。一般啊,我们认为皇后是比较重要的,皇后是RESET中断。优先级最高了。定时器0妃子就闹心了。只能等皇帝看完皇后再去看妃子。如果皇帝看完皇后了,定时器0妃子还在呼唤皇帝,那么皇帝就去定时器0妃子那里,如果这时候皇后又吵吵腿疼,继续叫皇上。皇上还要留在皇后这里,定时器0妃子只能等着了。。。。(可怜的定时器0妃子,如果我是皇后,我就一直叫着皇帝….你妹的就别想了。不过我们不用担心定时器0妃子,定时器0妃子的等级还是比较高的。)

DSP可屏蔽中断的优先级迎刃而解,CPU的中断是有优先级的。优先级高的先处理。其实皇太后(NMI)也是有优先级的。只不过真的很特殊,她的地位非常尊贵,不可以和其他妃子相提并论,所以我们暂且把皇天后奉在他的寄存器里面,不去管她,让她安心礼佛。如果有一天她出来了, 那么CPU必然无条件的执行皇太后的命令!

2.1.2 CPU中断使能寄存器

使能就是一个按钮呗,按下它就开始运转了。我们通过程序来控制,使能就是个很重要的事儿了。简而言之,使能寄存器,就是个开关寄存器。IER里面包含的开关有很多啊,从INT1到INT14,还有DLOGINT,RTOSINT(仿真有关)相应的位如果设置到它的头上,那么久开始了中断操作。

例子(主函数通常写的) IER = 0x0000; 意思就是全部禁止

2.1.3 CPU中断标记寄存器

如果举手,上面举的电话铃声响起。来提示CPU要来进行这个中断。再举一个比较详细的例子,学校上课时老师提出问题,然后学生积极举手发言,没有老师的允许同学们是不可以站起来说话的,只有老师点名你你你,才可以进行发言。说的就是这个意思,IFR就是上面使能寄存器对应的标记寄存器,写0就是该中断被屏蔽,写1就是该中断已经举手,准备就绪了。如果这中断举手了,还被CPU命中,那么一拍即合。这个位置就表示CPU是否提出了请求。与上面同样包含INT1----INT14,还有两个仿真。

IFR = 0x0000;

寄存器就算是结束了,然后我们进行下一个话题。就是CPU可屏蔽中断的相应过程。现在还要注意一个问题啊,我们研究的是CPU中断内部的情况,从外边看,PIE和外设中断是不管CPU是怎么处理的,就像是黑盒一样,自己内部做着自己的事儿。PIE只管分配外设的中断给CPU,剩下CPU家事儿自己管理。

image-20221203100434978

这是从PDF上截取的图片很简单吧,开关有两个IFR,IER,然后还有个INTM。INTM算是总闸。不开它啥都白扯。INTM是全局中断,是要开启的。我们要开IFR,IER然后要开的就是全局中断INTM。

CPU开始看到谁举手,也就是IFR检查,然后在在举手里面检查IER是否使能,如果使能就检查全局中断INTM开了没有,如果都开了,那么久开始进行处理。然后IFR就会被自动清0等待其他的响应。这个遵循优先级的原则。我们要注意了。还有个问题,IER,IFR设定范围是INT1----INT12那么INT13,INT14去哪里了?一会儿再说啦!

好了,CPU中断这块就完成了,我们将它封装起来,不管他里面做什么,我们看看需要设置什么。对应了设置,不就能利用好CPU了吗?我们还管他做什么,剩下的就是他自己的事儿了。我们需要设置那个IFR,IER,还有INTM全局中断,可以了。然后注意了,CPU开始执行谁先清它的标志位,如果我们想让这个中断继续执行,那么我们在手动把标志位置回来。就这些了。

2.2 PIE级中断

image-20221203100513428

看到了吗?CPU-TIMER0发出TINT0信号给PIE,PIE控制的是INT1---INT12,所以对于刚才的CPU里面的,INT1---INT12位的设定是因为这个,然而那个多出INT13,INT14分别是TIMER1和TIMER2的位置。看来TIMER1和TIMER2这个应该就是亲王,不需要太监来管理他们的奏折,直接访问CPU。都是自家人嘛。除了TIMER0了还有一些片内外设中断服从PIE管制,什么EV,ADC,SCI,SPI,CAN。他们要时钟,同样也在PIE管辖范围内。那些皇亲国戚咱们就不管了,他们很简单,及时走后门呗,什么亲王,皇太后,我们就不去理会了。咱们研究的是INT1-----INT12的。我们只消记住,CPU-TIMER0是正常的途径,需要PIE的认证

2.2.1 PIE中断的概述

我们刚从皇宫里面出来,了解了皇帝,皇太后,亲王,还有32个妃子。现在我们就要进入太监们的世界,看看太监是怎么工作的。

DSP的世界一共设立了96个大臣,对应的,PIE中有挑出了拔尖儿的12个太监,来管理96个大臣的请求,12个太监就把大臣们的请求放在对应的INT1-----INT12中。太监们把大臣分为8组,每一个太监负责8个大臣。

image-20221203100550076

上面就是名单了,我们可以看到哪个太监负责哪组的哪个大臣。现在定时器0特别想知道负责他的提案的是哪个公公。希望他办事儿快一些。可以从上面的名单看到,TINT0是在INT1太监的第7组放着。TIMER0就开始去找INT1公公,希望他能快一点儿提交自己的奏折。当然了,INT1公公还要负责ADCINT,XINT2,XINT1的,面子蛮大的。所以大臣们还得畏惧他们三分哦。

公公也是有等级的,谁贪上了有大全的公公谁就合适呗。当然最有面子的公公就是INT1,而且公公们在自己的世界执行事例也要按照自己的规矩走,那么就是从大的组往小的组走,INT1公公最厉害,所以啥都得INT1公公先来,它的名单第一个是WAKEINT也就是这个大臣是最先走的,然后是TINT0,INT1公公最后一个PDPINTA,执行完了,INT2公公的T1OFINT才能执行。所以也要注意公公们的三六九等。

公公们也仿造皇宫,实行举手,使能制度,每个公公都有个PIEIERx,PIEIFRx,初次之外还有皇宫控制他们的PIECTRL和他们之间的PIEACK。比如INT9公公持有PIEIER9,PIEIFR9的权利。谁举手就去传递谁给CPU。

1. PIE中断使能寄存器

INTx8-----INTx1,8位。其他的保留,分别对那些大臣使能的。不用多说了吧

2. PIE中断标志寄存器

INTx8-----INTx1,同理。以上是每个公公都有的,下面是共有的。

3. PIE应答寄存器(自己协调同组中断)

这个的作用是响应同组中断,一共是12位,也就是12个公公中有哪个公公要上报皇帝,就要把这个开了,比如:INT1公公要开始上报自己负责的大臣,那么就要应答CPU,告诉CPU我要上报,等CPU说呈上来,那么INT1公公就要按照名单顺序一一上报给皇帝。(INT1那位就被写0)。哪个公公想开下个中断就要等它那位置清0才行。这就是应答寄存器。PIEACK,11~0位,分别为0 ---- 11的公公。

4. PIE控制寄存器

PIEVECT位:15~1位,表示从PIE向量表中取回的向量地址。 ENPIE位:位0,从PIE块取回向量使能。

3. X2812的三级中断系统分析

image-20221203100801532

看这个流程,大概就是从片内外设或者是外部的中断申请一些中断然后传递给PIE级,PIE进行相应的PIEIER和PIEIFR的开关设置,之后,必须要进行ACK应答,才能进入CPU级,CPU根据相应的中断,开始判断IFR和IER,最后INTM开始了CPU的执行。

下面我们来重点讲解一下:

I. 外设级中断

片内外设级中断,也就是包含EV,SCI,SPI,ADC,ECAN等模块,每个模块都有相应的动作来触发中断的产生,可能是周期中断,也可能是比较中断,千变万化。但是每个外设的中断都遵守着固定的准则,每个外设级中断的原理都是相同的,如同PIE,和CPU级的中断,标志位,和使能位的方式来控制。在外设的某个寄存器与该中断的相关标志位被置位1,该外设就向PIE控制器发出一个中断请求。相反,虽然中断事件已经发生了,相应的中断标志位还没有被置位,那么这个中断就不会发生。如果学生想要回答老师的问题,但是没有举手,老师怎么会知道你要回答问题呢。那么上个道理也如同,你举手了,但是不会作答,这个问题还是没有解决。只有你举手了还会做出正确的答案,老师才能认为这个问题结束。所以,我们不但要置位标志位,还要使能使能位。 我们举个例子,TIMER0这个计数寄存器,TIMH:TIM到0的时候,就会发送一个定时器0的中断T0INT事件,CPU定时器0的周期中断。这时候,如果TIMER0TCR的第15位定时器中断标志位TIF被置位1。这个时候如果TIMER0CTLR的第14位,也就是定时器TIE的值是 1的话,则CPU定时器0就会向PIE控制器发出中断请求。当然如果TIE的值是0的话,那么该中断就没有被使能,就不会向PIE发送请求,而且中断标志位一直保持是1,除非通过程序清除。(SCI,SPI不用)

CpuTimer0Regs.TCR.bit.TIF = 1 ;// 清除定时器中断标志位。  写1来清除标志位,写0无效。

中断的标志位是由外设特定的行为产生才被置位的。比如TIMH:TIM到0标志位被置1。比较单元的比较到达那个数,标志位被置1。然后我们能做的就是使能这个寄存器的中断,IE。所以想要进行中断,一半在于这个模块的顺利进行,另一半的权利在于我们手里的IE。但我们的权限要胜于IF,因为我们能将其IF置0;

外设级的中断我们只需要注意的就是IE了。配置那个模块的控制寄存器之类的,还要在初始化的时候将标志位置位。剩下的在于PIE级的。

II. PIE级

当外设产生中断的时候响应的中断标志位级被只为了,中断使能位使能之后,外设就会把中断请求提交给PIE中断,由PIE裁决哪个是重要的那个是不重要的。同样滴, PIE级的中断也有标志位和使能位,但是PIE多了一个应答寄存器,这个ACK相当于一个关卡,因为一个PIE组要管理8个中断,要是同时请求,那么除了优先级可以控制,一起向CPU发送请求,也是会混乱的。所以ACK的存在使得一次放行一个中断,按照优先级来。是个这么原理,加入TIMER0的PIE级中断INT1进行中,ACK的INT1的位(0位)就被置位,并且一直保持着1状态。如果PIE1组内发生了其他的中断请求,则暂时不会被PIE控制器响应并送给CPU,必须等到PIEACK的INT1的位被清0之后才能响应,而且它还得保持请求状态,所以一定要手动复位PIEACK的相关位,是的PIE控制器能够响应同组内的其他中断。对PieCtrl.PIEACK.bit.ACK1 = 1; // 响应PIE组1内的其他中断。

III. CPU级

同样的道理CPU级的也存在着IER和IFR,中断标志位和中断使能位。当一个外设中断通过PIE发送到CPU时,Cpu中断的标志位IFR就会被INTx就会被置位。例如,当CPU中断标志中断T0INT发送CPU时,IFR的第0位INT1就会被置位,然后该状态就会被锁存到IFR中。这是CPU不会马上去执行这个中断,而是检查IER寄存器相关的位使能情况和CPU寄存器ST1中全局中断控制开关INTM的使能情况。如果IER中的相关位被置位,并且INTM的值为0(注意啦,为0),CPU就会响应。

CPU接收到中断请求,并发现可以去响应时,就得暂时停止正在进行的程序,转而执行它的中断服务子程序,但在此时就如同上面所说的,要保护现场,皇上要去找妃子,就要保存好刚刚正在批阅的奏折。执行时CPU就会将相应的IFR位进行清除(清除的是自己的IFR不是外设和PIE的标志位,自己管自己的事儿。)EALLOW被清除,INTM被置位,就是全局中断自动关闭。就是拒绝其他的中断,然后专心进行中断服务子程序,CPU然后会在向量表里面提取出对应的中断向量ISR,转而执行中断服务子程序。CPU的中断标志位的置位和清除是自己完成的。

5. 实现中断的必要步骤

  1. 系统时钟控制方面:
    
    #include <DSP28_Device.h>

void InitSysCtrl(void){ Uint16 i; EALLOW; SysCtrlRegs.WDCR = 0x0068; SysCtrlRegs.PLLCR = 0xA; for(i = 1; i < 5000; i++){ } SysCtrlRegs.HISPCP.all = 0x0001; SysCtrlRegs.LOSPCP.all = 0x0002; EDIS; }


2.  GPIO控制方面:
```C
#include <DSP28_Device.h>

void InitGpio(void){
    EALLOW;
    GpioMuxRegs.GPFMUX.bit.XF_GPIOF14 = 0;  // 设置为I/O口
    GpioMuxRegs.GPFDIR.bit.GPIOF14 = 1;  // 设置为输出方向
    GpioDataRegs.GPFSET.bit.GPIOF14 = 1; // 初始化电平
    EDIS;
}
  1. 主函数阶段:

    #include <DSP28_Device.h>
    void main(void){
    InitSysCtrl();
    DINT;               // CPU级中断 禁止CPU中断也就是关闭INTM
    IER = 0x0000;        // CPU级中断 关闭所有使能和中断标志位
    IFR = 0x0000;
    InitPieCtrl();
    InitPieVectTable();
    InitGpio();
    InitCpuTimers();
    PieCtrl.PIEIER1.bit.INTx7 = 1;  // PIE级中断 :使能PIE模块中的CPU定时器0的中断
    IER |= M_INT1;
    EINT;
    ERTM;
    ConfigCpuTimer(&CpuTimer0,150,1000000);
    CpuTimer0Regs.TCR.bit.TSS = 1;
    for( ; ;){}
    }
  2. 中断阶段

    interrupt void TINT0_ISR(void){
    CpuTimer0.InterruptCount++;
    GpioDataRegs.GPFCLEAR.bit.GPIOF14 = 1;
    // ....
    CpuTimer0Regs.TCR.bit.TIF  = 1; // 清除外设中断的标志位
    PieCtrl.PIEACK.bit.ACK1 = 1;
    EINT;
    }

    补充:

实际上,在主函数中PIE的使能还有一个就是指定ISR服务程序。

EALLOW;
PieVectTable.SCIRXINTA = & SCIRXINTA_ISR;
PieVextTable.SCITXINTA = & SCITXINTA_ISR;
EDIS;

在初始化向量表之后,这样的话我们就可以在主函数中写入中断程序,而不是进入到defultISR文件中写入中断函数,只要我们指定对后面的向量表就一切没有问题。CPU是要从向量表中调回程序的!

中断函数的原理总结完毕了,书本上的知识也就是这么多,PDF对于英文的考验可想而知,毕竟英文读的少些,平时要注重自己英语水平的提升。我们要在实践中保证自己的技能,我们要对自己负责。或许吧,如同这DSP,我们要考究他的根基,才能更深入的明白。再见,X2812的中断!