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