2812移植ucos2经验-中断中完成任务切换,消息发送
转自 http://www.hellodsp.com/bbs/forum.php?mod=viewthread&tid=17754&fromuid=365745
最近做了ucos2在2812的移植,之前在网上查资料时发现很多朋友说在2812上跑ucos时中断中无法进行任务切换, 或者是中断中无法发信号或消息。经过一段时间的调试,我解决了这个问题,发个经验贴,希望能对大家有帮助。
说到底这个问题是2812堆栈结构的问题,先看看2812CPU介绍中对中断堆栈保护的介绍吧。Table 3−3. Register Pairs Saved and SP Positions for Context Saves
1 --SP position before step8
1st ST0 0 0 --SP position before step 8
T 1 1
2nd AL 0 0
AH 1 1
3rd PL 0 0
PH 1 1
4th AR 0 0
AR 1 1
5th ST1 0 0
DP 1 1
6th IER 0 0
DBGSTAT 1 1
7th Return address
(low half)
0 0
Return address
( high half)
1 1
0 ← SP position after save 0
1 1 ← SP position after save
这是2812进入中断处理时cpu自动完成工作的一部分,从上面可以看出,在进入中断时,CPU自动按顺序保存ST0—---Return address等寄存器的值。要特别注意的是2812在保存寄存器时是按照32位寻址的,看这段话
These registers are saved in pairs; each pair is saved in a single 32-bit operation.
At the end of each 32-bit save operation, the SP is incremented by 2.
The CPU expects all 32-bit saves to be even-word aligned by the memory wrapper.
这三句话告诉我们在CPU存这些寄存器的时候,是成对存储的;每存一对寄存器,SP+2;最重要的是2812要求所有成对存储的寄存器从偶地址开始。于是有了上面两种情况。
SP position before step8 这里的step8指得是SP加1的操作,0和1分别表示偶和奇地址。
第一种情况sp最初指向奇地址(表中左边一列):
1)sp+1 ,现在sp指向偶地址
2)开始存储寄存器,因为sp指向偶地址,于是寄存器存在sp和sp+1的地方
3) sp<-sp+2 现在sp还是指向偶地址,继续重复1)
结束后sp指向偶地址。
第二种情况sp最初指向偶地址(表中右边一列)
1) sp<-sp+1,现在sp指向奇地址。
2) 开始存储寄存器,由于sp指向奇地址,所以储存在sp和sp-1(不是sp+1!)的位置。从这里也可以看出为什么2812要在加上sp<-sp+1的操作。否则的话,如果sp最初指向奇地址,那么寄存器就存在sp和sp-1的位置了,这时sp-1里面是原来存的内容,这个内容就会被覆盖了。如下图:
原来的内容 0 ST0 0(ST0已经把原内容覆盖!!)
空的 1 --SP before save T 1
空的 0 (而后sp+2) 空的 0
空的 1 空的 1—sp after save
费了一大堆话解释这个,就是要说明,在32位存储时寄存器对是从偶地址开始的。为了在中断中发送信号或消息就要在中断中调用函数OSMboxPost (MBOX, (void *)&event),这时要注意SP的指向问题。中断程序不要用interrupt关键字来写,最好用汇编,在汇编中完成出栈入栈的工作,而后在汇编中调用C函数完成中断服务,结构如下
.page
.text
_SAMPLE:
.ref _SAMPLE_service
.ref _OSIntNesting
.ref _OSIntExit
.global _SAMPLE
PUSH RPC
MOVW DP,#_OSIntNesting ;提示uc/OS-II 进入中断
INC @_OSIntNesting ; |168|
LCR _SAMPLE_service
LCR _OSIntExit
POP RPC
IRET
.end
SAMPLE是用汇编写的中断函数,实际上不用担心汇编很难写,我们要做的大部分事情都是在那个汇编中调用的C函数SAMPLE_service来完成的,汇编只用来完成进入和退出中断的工作。
在SAMPLE_service中我们要发送消息,于是要调用OSMboxPost (MBOX, (void *)&event)
Void SAMPLE_service(void)
{
…..
OSMboxPost (MBOX, (void *)&event);
…..
}
问题就在汇编中调用C函数的时候,如果进入C函数之前SP不是指向偶地址的,那么在进入和退出C函数的时候就有可能发生前面所说的堆栈内容被覆盖的问题。最保险的办法,就是在进入C函数之前将SP调整到偶地址,而后在C函数中不再定义变量(在定义局部变量的话又会使用堆栈了。。)。为了解决这个问题,2812指令集中提供了ASP和NASP两个指令。ASP能够将sp调整到偶地址
if(SP = odd)
SP = SP + 1;
SPA = 1;else
SPA = 0;
NASP能够将SP退回到原来的位置。所以在上面的汇编中断处理程序中每调用C函数之前就先执行ASP指令,之后在执行NASP指令,同时在C函数中不定义局部变量,就能保证中断中任务切换或者信号,消息发送的成功。如下所示:
.page
.text
_SAMPLE:
.ref _SAMPLE_service
.ref _OSIntNesting
.ref _OSIntExit
.global _SAMPLE
PUSH RPC
MOVW DP,#_OSIntNesting ;提示uc/OS-II 进入中断
INC @_OSIntNesting ; |168|
ASP ;调用C函数之前 先执行ASP指令将SP调整到偶地址
LCR _SAMPLE_service
NASP ;返回之后在调用NASP指令将SP调整回去
LCR _OSIntExit ;这个函数是用汇编写的,不用先执行ASP指令。
POP RPC
IRET
.end