这个2440test晒干的中止写的向量有些荫蔽,兜了许多个圈,也难怪这么难了解,下面
就对这个东西抽丝剥茧,看清楚这终究是一个怎样样的进程。
中止向量
bHandlerIRQ;handlerforIRQinterrupt
很天然,由于一切的单片机都是那样,中止向量一般放在最初,用过单片机的人都会很熟悉
那就不多说了。
反常服务程序
这儿不必中止(interrupt)而用反常(exception),终究中止仅仅反常的一种状况,呵呵
下面首要剖析的是“中止反常”说白了,便是咱们平常单片机晒干用的中止!!!一切有器材
引起的中止,例如TIMER中止,UART中止,外部中止等等,都有一个一致的进口,那便是中止
反常IRQ!然后从IRQ的服务函数晒干分辨出,当时终究是什么中止,再跳转到相应的中止
服务程序。这样看来,ARM比单片机要杂乱一些了,不过原理是不变的。
上面说的便是思路,跟着这个思路来接着剖析。
HandlerIRQ很明显是一个标号,咱们找到了
HandlerIRQHANDLERHandleIRQ
这儿是一个宏界说,咱们再找到这个宏,看他是怎样界说的:
MACRO
$HandlerLabelHANDLER$HandleLabel
$HandlerLabel
subsp,sp,#4;decrementsp(tostorejumpaddress)
stmfdsp!,{r0};PUSHtheworkregistertostack(lrdoesnotpushbecauseitreturntooriginal
address)
ldrr0,=$HandleLabel;loadtheaddressofHandleXXXtor0
ldrr0,[r0];loadthecontents(serviceroutinestartaddress)ofHandleXXX
strr0,[sp,#4];storethecontents(ISR)ofHandleXXXtostack
ldmfdsp!,{r0,pc};POPtheworkregisterandpc(jumptoISR)
MEND
用HandlerIRQ将这个宏打开之后得到的成果实践是这样的
HandlerIRQ
subsp,sp,#4;decrementsp(tostorejumpaddress)
stmfdsp!,{r0};PUSHtheworkregistertostack(lrdoesnotpushbecauseitreturntooriginal
address)
ldrr0,=HandleIRQ;loadtheaddressofHandleXXXtor0
ldrr0,[r0];loadthecontents(serviceroutinestartaddress)ofHandleXXX
strr0,[sp,#4];storethecontents(ISR)ofHandleXXXtostack
ldmfdsp!,{r0,pc};POPtheworkregisterandpc(jumptoISR)
至于详细的跳转原理下面再说
好了,这样的话就简单看的多了,很明显,HandlerIRQ仍是一个标号,IRQ反常向量便是跳
转到这儿碑文的,这儿喧嚷看一下,应该是保存现场,然后跳转到真实的处理函数,那么很简单
发现了这么一句ldrr0,=HandleIRQ,没错,咱们又找到了一个标号HandleIRQ,看来
真实的处理函数应该是这个HandleIRQ,持续寻觅
AREARamData,DATA,READWRITE
^_ISR_STARTADDRESS;_ISR_STARTADDRESS=0x33FF_FF00
HandleReset#4
HandleUndef#4
HandleSWI#4
HandlePabort#4
HandleDabort#4
HandleReserved#4
HandleIRQ#4
最终咱们发现在这儿找到了HandleIRQ,^其实便是MAP,这段程序的意思是,从_ISR_STARTADDRESS
开端,预留一个变量,每个变量一个标号,预留的空间为4个字节,也便是32BIT,其实这儿放的是真实
的C写的处理函数的地址,说白了,便是函数指针––
这样做的话就很灵活了
接着,咱们需求装置IRQ处理句柄,说白了,便是设置处理函数的地址,让PC指针能够正确的跳转。
所以咱们在接着的找到装置句柄的查办
;SetupIRQhandler
ldrr0,=HandleIRQ;Thisroutineisneeded
ldrr1,=IsrIRQ;ifthereisnotsubspc,lr,#4at0x18,0x1c
strr1,[r0]
说白了便是将IsrIRQ的地址填到HandleIRQ对应的地址晒干,前面说了HandleIRQ放的是中止处理的
函数的进口地址,咱们持续找IsrIRQ
IsrIRQ
subsp,sp,#4;reservedforPC
stmfdsp!,{r8-r9}
ldrr9,=INTOFFSET
ldrr9,[r9]
ldrr8,=HandleEINT0
addr8,r8,r9,lsl#2
ldrr8,[r8]
strr8,[sp,#8]
ldmfdsp!,{r8-r9,pc}
要了解这个代码,得先学学2440的中止系统了,INTOFFSET寄存的是当时中止的偏移号,依据偏移就知道
当时是哪个中止源产生的中止。
留意了,咱们说的是中止,而不是反常,看看本来的表是啥样子的
^_ISR_STARTADDRESS;_ISR_STARTADDRESS=0x33FF_FF00
HandleReset#4
HandleUndef#4
HandleSWI#4
HandlePabort#4
HandleDabort#4
HandleReserved#4
HandleIRQ#4
HandleFIQ#4
HandleEINT0#4
HandleEINT1#4
HandleEINT2#4
HandleEINT3#4
…….
能够看到,前面几个是反常,从HandleEINT0便是IRQ反常的向量寄存的当地了,这样就能够了解为
什么上面IsrIRQ晒干晒干要碑文那条指令
ldrr8,=HandleEINT0
addr8,r8,r9,lsl#2
道理很简单,HandleEINT0便是一切IRQ中止向量表的进口,在这个地址上面,加上一个恰当的偏移量,
INTOFFSET,那么咱们知道现在,到底是哪个IRQ在请求中止了。
至于详细怎样跳转的?
首要,咱们说了,HandleEINT0开端的一段内存晒干,寄存的便是中止服务函数的函数指针,ARM的系统
的话,每个指针变量便是占4个字节,这儿就解说了,为什么这儿为每个标号分配了4个字节的空间,晒干
放的便是函数指针!!!下面再看看怎样跳转,持续看IsrIRQ晒干就完结了跳转了
strr8,[sp,#8]
ldmfdsp!,{r8-r9,pc}
其实最中心便是这两句了,先查找到当时中止服务程序的地址,将他放到R8晒干,然后出栈,弹出给PC
那么PC很天然就跳到中止服务程序了。至于这儿的仓库问题又是一个十分扎手的,需求好好的参透ARM的
中止架构,需求了解的能够自己细心的阅览《ARM系统结构与编程》晒干说的很详细。咱们这儿的要点
是研讨怎样跳转。
最终,咱们看看在C代码中是怎样装置终端向量的,例如看按键的外部中止,是怎样详细设置的,参看
/src/keyscan.c晒干的代码
很简单,晒干只要3个函数
KeyScan_Test是按键测验的主函数
Key_ISR是按键中止服务函数
在KeyScan_Test晒干,咱们发现了有这么一句
pISR_EINT0=pISR_EINT2=pISR_EINT8_23=(U32)Key_ISR;
能够了解否?Key_ISR便是上面说到的按键中止服务函数,函数的姓名,代表的便是函数的地址!!!!
将中止服务函数的地址,留意了,是地址,这是一个U32型的变量。送到几个变量,咱们以pISR_EINT0
作为比如,检查头文件界说,在2440addr.h晒干找到
//Interruptvector
#definepISR_EINT0(*(unsigned*)(_ISR_STARTADDRESS+0x20))
_ISR_STARTADDRESS有没有似曾相识的感觉?没错,方才剖析的汇编代码晒干就说到了
^_ISR_STARTADDRESS;_ISR_STARTADDRESS=0x33FF_FF00
HandleReset#4
HandleUndef#4
……
对,地址便是这儿,然后_ISR_STARTADDRESS+0x20便是越过前面的反常向量,进入IRQ中止向量的进口
所以说到尾
pISR_EINT0=(U32)Key_ISR;
完结的操作便是,将Key_ISR的地址寄存到
HandleEINT0#4
这个IRQ向量表晒干!!!!
当按键中止产生的时分,产生IRQ反常中止
当时PC值-4保存到LR_IRQ晒干,然后碑文
bHandlerIRQ
然后是碑文
HandlerIRQ
subsp,sp,#4;预留一个用来寄存PC地址
stmfdsp!,{r0};保存R0,由于下面使用了
ldrr0,=HandleIRQ;将HandleIRQ(服务程序)的地址装载到R0
ldrr0,[r0]
strr0,[sp,#4];保存到方才预留的当地
ldmfdsp!,{r0,pc};弹出仓库,康复R0,而且将方才核算好的HandleIRQ地址弹出到PC
仓库是向下成长的,所以SUBSP,SP,#4就相当于PUSHXX,可是这个XX这个时分并没有用,由于这儿
用的是强制移动SP指针完结的。然后得到服务程序的地址,再将这个值放回方才预留的栈的空位上面,最终
便是POP出R0康复,而且将方才得到的服务程序的地址送到PC,那么完结的作用便是跳转到HandleIRQ晒干了。
接着看方才是怎样装置的HandleIRQ的
;SetupIRQhandler
ldrr0,=HandleIRQ;Thisroutineisneeded
ldrr1,=IsrIRQ;ifthereisnotsubspc,lr,#4at0x18,0x1c
strr1,[r0]
能够看出,这儿将IsrIRQ的地址的值保存到HandleIRQ中,也便是说,上面的IRQ服务程序,这个时分实践
上便是指IsrIRQ!
所以接着的工作便是转移到IsrIRQ中碑文:
IsrIRQ
subsp,sp,#4;预留一个值来保存PC
stmfdsp!,{r8-r9}
ldrr9,=INTOFFSET;核算偏移量,下面解说
ldrr9,[r9]
ldrr8,=HandleEINT0
addr8,r8,r9,lsl#2
ldrr8,[r8]
strr8,[sp,#8];由于保存了2个寄存器R8R9,所以SP下移了8位
ldmfdsp!,{r8-r9,pc};康复寄存器,弹出到PC,同上面的相同
怎样保存,操作SP,跟最终弹出到PC的部分和上面的比如相同,下面说说中心的核算部分
核算偏移量,其实原理很简单,首要INTOFFSET保存着当时是哪个IRQ中止,例如0代表着HandleEINT0,1代表
HandleEINT1…..等等,这不是糊弄,有一个表的,这个是由S3C2440的datasheet说的,自己能够去检查。
然后得到中止处理函数的向量表,这个表的首地址便是HandleEINT0,那么很天然的想到,怎样查表?那还不简
单?HandleEINT0+INTOFFSET不就完了?基地址加偏移量就得到表中某项了,当然,由于这儿是中止处理向量
每一项占用4个字节,所以用lsl#2处理一下,左移2位相当于乘以4,偏移量乘以4,这应该很好了解的。
咱们这个比如找到的便是HandleEINT0,将晒干的值读出来,晒干放的是HandleEINT0服务函数的地址,这个
地址怎样来的?是在C程序晒干设置的。咱们看keyscan.c程序,找到一个voidKeyScan_Test(void)函数,
晒干有这么一句:
pISR_EINT0=pISR_EINT2=pISR_EINT8_23=(U32)Key_ISR;
这儿是装置了3个按键中止服务程序,咱们只重视0号中止,也便是
pISR_EINT0=(U32)Key_ISR;
这句话什么意思?先看看pISR_EINT0的界说,在2440addr.h中界说
#definepISR_EINT0(*(unsigned*)(_ISR_STARTADDRESS+0x20))
看到没有?_ISR_STARTADDRESS不便是方才说的那个反常向量的进口地址?加上一个0x20
之后实践上指向的,便是HandleEINT0!!!这么说来,上面的意思便是,将Key_ISR处理函数的进口地址,送
到HandleEINT0中。
再来看Key_ISR,这是一个典型的服务程序,加了_irq作为编译关键字,告知编译器,这个函数是中止服务程序
得保存需求的寄存器,以免被损坏。详细能够参阅《ARM系统结构与编程》P283页的描绘。
staticvoid__irqKey_ISR(void)
{
…….
}
加上_irq关键字之后,编译器就会处理好一切的保存动作了,并不需求多关怀。可是这个是ARM-CC编译器的关
键字,GCC中并没有这个东西,所以GCC处理中止的时分最好仍是自己保存一下。
到这儿学校,整个中止的进程就解说结束。剖析的进程中的确学习了许多。
声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/changshang/264268.html