IMPORT SWI_Exception ;软中断异常处理程序 表示将c程序中的该函数挂接到此段汇编代码中,同样的道理 EXPORT __OSStartHighRdy EXPORT OSIntCtxSw ;中断退出时的入口 参见startup.s中的IRQ_Handler EXPORT SoftwareInterrupt ;软中断入口上面的申明是将该段汇编代码挂接到外面, 因此在外部可以直接调用函数名 继续看OS_CPU_A.S的其他部分代码,就是两个软件异常中断处理函数OSIntCtxSw和OSStarHighRdyOSIntCtxSw代码是中断服务子程序使得更高优先级的任务进入就绪状态后,中断返回后需要切换到该任务时调用的,这是被切换的任务的CPU寄存器的值已经在响应中断后存入了堆栈中,因此,这里不需要重复保存了直接切换任务即可,具体过程看代码OSIntCtxSw ;下面为保存任务环境 ;当响应软件异常中断后进入了系统模式,在上面的代码中我们可以看到,进入系统模式时保存的堆栈结构从顶到底依次是:R0,R1,R2,R3,R12,LR,而在用户模式中任务的堆栈结构应该是:OsEnterSum,CPSR,RO-12,LR,PC,所以在进行软件中断任务切换之前先要保存原来任务的堆栈结构。 LDR R2, [SP, #20] ;获取PC LDR R12, [SP, #16] ;获取R12 MRS R0, CPSR MSR CPSR_c, #(NoInt | SYS32Mode) MOV R1, LR STMFD SP!, {R1-R2} ;保存LR,PC STMFD SP!, {R4-R12} ;保存R4-R12 MSR CPSR_c, R0 LDMFD SP!, {R4-R7} ;获取R0-R3 ADD SP, SP, #8 ;出栈R12,PC MSR CPSR_c, #(NoInt | SYS32Mode) STMFD SP!, {R4-R7} ;保存R0-R3 LDR R1, =OsEnterSum ;获取OsEnterSum LDR R2, [R1] STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum ;保存当前任务堆栈指针到当前任务的TCB LDR R1, =OSTCBCur LDR R1, [R1] STR SP, [R1] BL OSTaskSwHook ;调用钩子函数 ;OSPrioCur <= OSPrioHighRdy LDR R4, =OSPrioCur LDR R5, =OSPrioHighRdy LDRB R6, [R5] STRB R6, [R4] ;OSTCBCur <= OSTCBHighRdy LDR R6, =OSTCBHighRdy LDR R6, [R6] LDR R4, =OSTCBCur STR R6, [R4] OSIntCtxSw_1 ;获取新任务堆栈指针 LDR R4, [R6] ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP LDR LR, [SP, #-8] MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式 MOV SP, R4 ;设置堆栈指针 LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum ;恢复新任务的OsEnterSum LDR R3, =OsEnterSum STR R4, [R3] MSR SPSR_cxsf, R5 ;恢复CPSR LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务
__OSStartHighRdy MSR CPSR_c, #(NoInt | SYS32Mode) ;调整到管理模式 ;告诉uC/OS-II自身已经运行 LDR R4, =OSRunning MOV R5, #1 STRB R5, [R4] ;标记多任务运行标记为真 BL OSTaskSwHook ;调用钩子函数,可以运行用户自定义的函数 LDR R6, =OSTCBHighRdy ;R6存有最高优先级的就绪任务的控制块地址 LDR R6, [R6] B OSIntCtxSw_1 ;转到前面编写的中断返回函数块的任务跳转部分的代码,因为这两个函数都要用到这部分代码,进入这段代码之前高优先级的就绪任务的任务控制快地址存在R6中。 AREA SWIStacks, DATA, NOINIT,ALIGN=2 SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆栈空间 OSIntCtxSw_1的代码:OSIntCtxSw_1 ;获取新任务堆栈指针 LDR R4, [R6] ;任务控制块的堆栈指针放在R6中,现在放在R4中 ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP LDR LR, [SP, #-8] MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式 MOV SP, R4 ;设置堆栈指针,R4存有没有改动过的堆栈指针 LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum ;恢复新任务的OsEnterSum LDR R3, =OsEnterSum STR R4, [R3] MSR SPSR_cxsf, R5 ;恢复CPSR LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务,恢复现场,异常处理返回;中断返回指令的寄存器列表其中必须包括PC后的^符号,表示这是一条特殊形式的指令。这条指令在从存储器中装载PC的同时,CPSR也得到恢复。这里使用的堆栈指针SP是属于异常模式的寄存器,每个异常模式有自己的堆栈指针。SoftwareInterrupt LDR SP, StackSvc ; 重新设置堆栈指针 STMFD SP!, {R0-R3, R12, LR} ;保存寄存器 MOV R1, SP ; R1指向参数存储位置 MRS R3, SPSR TST R3, #T_bit ; 中断前是否是Thumb状态 LDRNEH R0, [LR,#-2] ; 是: 取得Thumb状态SWI号 BICNE R0, R0, #0xff00 LDREQ R0, [LR,#-4] ; 否: 取得arm状态SWI号 BICEQ R0, R0, #0xFF000000 ; r0 = SWI号,R1指向参数存储位置 CMP R0, #1 LDRLO PC, =OSIntCtxSw LDREQ PC, =__OSStartHighRdy ; SWI 0x01为第一次任务切换 BL SWI_Exception LDMFD SP!, {R0-R3, R12, PC}^ StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)OSIntCtxSw ;下面为保存任务环境 LDR R2, [SP, #20] ;获取PC(LR) LDR R12, [SP, #16] ;获取R12 MRS R0, CPSR MSR CPSR_c, #(NoInt | SYS32Mode) MOV R1, LR STMFD SP!, {R1-R2} ;保存LR,PC STMFD SP!, {R4-R12} ;保存R4-R12 MSR CPSR_c, R0 LDMFD SP!, {R4-R7} ;获取R0-R3 ADD SP, SP, #8 ;出栈R12,PC MSR CPSR_c, #(NoInt | SYS32Mode) STMFD SP!, {R4-R7} ;保存R0-R3 LDR R1, =OsEnterSum ;获取OsEnterSum LDR R2, [R1] STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum ;保存当前任务堆栈指针到当前任务的TCB LDR R1, =OSTCBCur LDR R1, [R1] STR SP, [R1] BL OSTaskSwHook ;调用钩子函数 ;OSPrioCur <= OSPrioHighRdy LDR R4, =OSPrioCur LDR R5, =OSPrioHighRdy LDRB R6, [R5] STRB R6, [R4] ;把OSPrioHighRdy最高优先级的就绪任务传给OSPrioCur ;OSTCBCur <= OSTCBHighRdy LDR R6, =OSTCBHighRdy LDR R6, [R6] LDR R4, =OSTCBCur STR R6, [R4] ;将最高优先级的任务控制块指针传给当前任务控制块指针
关于中断和时钟节拍,UCOS-II对于ARM7通用的中断服务程序的汇编与c函数接口如下:MACRO和MEND伪指令用于宏定义,MACRO标识宏定义的开始,MEND标识宏定义的结束。定义之后在程序中就可以通过宏指令多次调用该段代码MACRO $IRQ_Label HANDLER $IRQ_Exception_ EXPORT $IRQ_Label ; 输出的标号 IMPORT $IRQ_Exception_ ; 引用的外部标号$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 SP, =StackUsr BL $IRQ_Exception_ ; 调用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二:OS_CPU_C.C 个文件中要求用户编写10个简单的C函数,但是只有1个函数是必要的,其余的函数必须声明,但不一定要包含任何代码,大致看了一下作用好像是用来调试之类的。唯一要编写的是OSTaskStkInit() OSTaskStkInit()函数的功能是初始化任务的栈结构,任务的堆栈结构与CPU的体系结构、编译器有密切的关联。从ARM的结构可以写出如下的栈结构:程序计数器PC,程序链接器LR,R12-R1,R0用于传递第一个参数pdata,CPSR/SPSR,关中断计数器(用于计算关中断的次数,这样就实现了中断的嵌套),返回的地址指针是指向的最后一个存入的数据,而不是一个空地址。软件中断异常SWI服务程序C语言部分 void SWI_Exception(int SWI_Num, int *Regs):参数SWI_Num对应前面文件中定义的功能号,其中0、1号的功能在后面的文件中定义,这里只定义了其他10个功能。 2、3分别对应关中断和开中断 关中断:MRS R0, SPSR //在软件中断的时候直接对程序状态保存寄存器SPSR操作也就是对CPSR的操作 ORR R0, R0, #NoInt //在汇编语言中对寄存器的对应位置位用ORR,清零用BIC MSR SPSR_c, R0 //SPSR_c表示的是只改变SPSR的控制段的8位代码,其他三段_f,_s,_x中标志位在_f段,其他为保留位 开中断:MRS R0, SPSR //在开中断中基本与上面相同,只是ORR改成BIC清零 BIC R0, R0, #NoInt MSR SPSR_c, R 由于需要实现中断嵌套,所以只有当关中断的计数器减为0的时候才能够开中断,而且每次关中断的时候该计数器都应该加1。另外,插入汇编语言时用_asm指令。 80、81、82、83分别对应系统模式、用户模式、ARM指令集、THUMB指令集 系统模式:MRS R0, SPSR BIC R0, R0, #0x1f //先将控制模式的低5位清零 ORR R0, R0, #SYS32Mode //设置成系统模式的1F MSR SPSR_c, R0 用户模式:MRS R0, SPSR BIC R0, R0, #0x1f ORR R0, R0, #USR32Mode //设置成用户模式的10 MSR SPSR_c, R0 ARM指令集与THUMB指令集的代码如下: ptcb = OSTCBPrioTbl[Regs[0]]; if (ptcb != NULL) { ptcb -> OSTCBStkPtr[1] &= ~(1 << 5); } ptcb = OSTCBPrioTbl[Regs[0]]; if (ptcb != NULL) { ptcb -> OSTCBStkPtr[1] |= (1 << 5); } 昨天就是看到这里,出现了一个意识到是不能忽悠的地方就是UCOS里面的任务控制块OS_TCB的概念,因此今天的任务就是把这部分看看。。。 大概回忆了一下昨天晚上的工作,开始今天的工作吧
一点一点来,什么不会就学什么,都不会就都学。。。没有问题只要你肯努力。。。。。。__OSStartHighRdy MSR CPSR_c, #(NoInt | SYS32Mode) ;MSR:在ARM中只有MSR能够直接设置状态寄存器CPSR或SPSR,可以是立即数或者源寄存器,NoInt是禁止中断,SYS32Mode是系统模式 ;告诉uC/OS-II自身已经运行 LDR R4, =OSRunning ;OSRunning正在运行多任务的标志,=OSRunning是把OSRunning的地址加载到R4,R4里存的是一个地址。。。 MOV R5, #1 STRB R5, [R4] ;将R5存储到R4存的地址的变量即OSRunning中,也就是将OSRunning置1 BL OSTaskSwHook ;调用钩子函数,OSTaskSwHook 是用于扩展的,在任务切换的时候执行用户自己定义的功能。 LDR R6, =OSTCBHighRdy ;OSTCBHighRdy指向最高优先级任务的控制块TCB的指针!!!将放指针的地址放到R6中。 LDR R6, [R6] ;将R6地址处的数据读出即OSTCBHighRdy的地址放到R6中 B OSIntCtxSw_1 ;跳转到OSIntCtxSw_1 AREA SWIStacks, DATA, NOINIT,ALIGN=2 SvcStackSpace SPACE SVC_STACK_LEGTH * 4 ;管理模式堆栈空间继续昨天没有看完的代码OSIntCtxSw ;下面为保存任务环境 LDR R2, [SP, #20] ;获取PC,放入R2 LDR R12, [SP, #16] ;获取R12,//R12存的什么东西啊??? MRS R0, CPSR MSR CPSR_c, #(NoInt | SYS32Mode) ;进入系统模式并禁止中断 MOV R1, LR ;R1放LR值 STMFD SP!, {R1-R2} ;保存LR,PC,将R1,R2存入SP STMFD SP!, {R4-R12} ;保存R4-R12,将R4-12存入SP MSR CPSR_c, R0 ;再回到之前的模式 LDMFD SP!, {R4-R7} ;获取R0-R3 ADD SP, SP, #8 ;出栈R12,PC MSR CPSR_c, #(NoInt | SYS32Mode) STMFD SP!, {R4-R7} ;保存R0-R3 LDR R1, =OsEnterSum ;获取OsEnterSum LDR R2, [R1] STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum ;保存当前任务堆栈指针到当前任务的TCB LDR R1, =OSTCBCur LDR R1, [R1] STR SP, [R1] BL OSTaskSwHook ;调用钩子函数 ;OSPrioCur <= OSPrioHighRdy LDR R4, =OSPrioCur LDR R5, =OSPrioHighRdy LDRB R6, [R5] STRB R6, [R4] ;OSTCBCur <= OSTCBHighRdy LDR R6, =OSTCBHighRdy LDR R6, [R6] LDR R4, =OSTCBCur STR R6, [R4] OSIntCtxSw_1 ;获取新任务堆栈指针 LDR R4, [R6] ;把OSTCBHighRdy指向最高优先级任务的控制块TCB的指针给R4 ADD SP, R4, #68 ;17寄存器:CPSR,OsEnterSum,R0-R12,LR,SP LDR LR, [SP, #-8] ;取出LR放到LR MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式并且保持禁止中断 MOV SP, R4 ;设置堆栈指针 LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum。LDMFD数据出栈,放入R4,R5 ;恢复新任务的OsEnterSum LDR R3, =OsEnterSum ;OsEnterSum的地址存入R3 STR R4, [R3] ;把R4的值赋给OsEnterSum MSR SPSR_cxsf, R5 ;恢复CPSR;在管理模式里是修改SPSR LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务 ,恢复现场,异常处理返回 |