完整的基于UCOSII的程序应该是这样的: MACRO $IRQ_Label OS_HANDLER $IRQ_Exception_Function
EXPORT $IRQ_Label ; 输出的标号 IMPORT $IRQ_Exception_Function ; 引用的外部标号
$IRQ_Label SUB LR, LR, #4 ; 计算返回地址 STMFD SP!, {R0-R3, R12, LR} ; 保存任务环境 MRS R3, SPSR ; 保存状态 STMFD SP, {R3, SP, LR}^ ; 保存用户状态的R3,SP,LR,注意不能回写 ; 如果回写的是用户的SP,所以后面要调整SP LDR R2, =OSIntNesting ; OSIntNesting++ LDRB R1, [R2] ADD R1, R1, #1 STRB R1, [R2]
SUB SP, SP, #4*3 MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式 CMP R1, #1 LDREQ R0, =StackUsr LDREQ SP, [R0] BL $IRQ_Exception_Function ; 调用c语言的中断处理程序
MSR CPSR_c, #(NoInt | SYS32Mode) ; 切换到系统模式 LDR R2, =OsEnterSum ; OsEnterSum,使OSIntExit退出时中断关闭 MOV R1, #1 STR R1, [R2]
BL OSIntExit
LDR R2, =OsEnterSum ; 因为中断服务程序要退出,所以OsEnterSum=0 MOV R1, #0 STR R1, [R2]
MSR CPSR_c, #(NoInt | IRQ32Mode) ; 切换回irq模式 LDMFD SP, {R3, SP, LR}^ ; 恢复用户状态的R3,SP,LR,注意不能回写 ; 如果回写的是用户的SP,所以后面要调整SP LDR R0, =OSTCBHighRdy LDR R0, [R0] LDR R1, =OSTCBCur LDR R1, [R1] CMP R0, R1
ADD SP, SP, #4*3 ; MSR SPSR_cxsf, R3 LDMEQFD SP!, {R0-R3, R12, PC}^ ; 不进行任务切换 LDR PC, =OSIntCtxSw ; 进行任务切换 MEND
对于这句:STMFD SP, {R3, SP, LR}^ 因为寄存器列表中包涵有SP,且第一个操作数寄存器也是SP,虽然意义上不是同一个SP,但是此时如果使用:STMFD SP!, {R3, SP, LR}^ 回写SP是不行的,编译就不会通过,违反了ARM汇编的规则设置。所以要写成:STMFD SP, {R3, SP, LR}^ 然后后面(注意:好像还不能立刻减回来,要隔个一两句再减,不然可能有警告!)再把SP减回来:SUB SP, SP, #4*3
而 STMFD SP!, {R3,LR}^ 用ADS编译可能会有一个警告,但是功能是对的。我用H-JTAG + S3C2410 仿真看过
对于出栈的情况也是一样。
保存用户模式下的一些寄存器到IRQ堆栈中去,是为了后面在系统模式下运行中断处理程序,总的来说也就是为了实现IRQ中断嵌套!
以上是本人的理解仅供参考,如有不对,还请指出,共同学习啊! |