uCOS移植代码主要位于工程路径下的/uCOS-II/Ports/ARM/Generic/IAR/os_cpu_a.asm(在uC-CPU下有个cpu_a.s,里面定义了OS_CPU_SR_Save和OS_CPU_SR_Restore,但实际上未用到,用到的是os_cpu_a.asmOS_CPU_SR_Save和OS_CPU_SR_Restore来进行开关中断)
这里就os_cpu_a.asm里的主要几个函数进行简单分析.更加详细的内容可参考邵贝贝译的《嵌入式实时操作系统uCOS-II》(第二版)
1.开关中断
;********************************************************************************************************
; CRITICAL SECTION METHOD 3 FUNCTIONS
;
; Description: Disable/Enable interrupts by preserving the state of interrupts. Generally speaking you
; would store the state of the interrupt disable flag in the local variable 'cpu_sr' and then
; disable interrupts. 'cpu_sr' is allocated in all of uC/OS-II's functions that need to
; disable interrupts. You would restore the interrupt disable state by copying back 'cpu_sr'
; into the CPU's status register.
;
;这是开关中断方式3的实现方式,先是保存CPSR值(最终保存到变量cpu_sr),然后禁止中断,最后返回
OS_CPU_SR_Save
MRS R0, CPSR
ORR R1, R0, #OS_CPU_ARM_CONTROL_INT_DIS ; Set IRQ and FIQ bits in CPSR to disable all interrupts.
MSR CPSR_c, R1
BX LR ; Disabled, return the original CPSR contents in R0.
OS_CPU_SR_Restore
MSR CPSR_c, R0
BX LR
//在cpu.h中定义了开关中断的宏
#define OS_CRITICAL_METHOD 3
#if OS_CRITICAL_METHOD == 3
//未定义OS_CPU_INT_DIS_MEAS_EN,OS_CPU_IntDisMeasStart()是用来中断计时的,未用到
#if OS_CPU_INT_DIS_MEAS_EN > 0
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save(); /
OS_CPU_IntDisMeasStart();}
#define OS_EXIT_CRITICAL() {OS_CPU_IntDisMeasStop(); /
OS_CPU_SR_Restore(cpu_sr);}
#else
//实际用到的定义
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#endif
#endif
2.然后就是任务切换,运行最高优先级任务的例程,如OSStartHighRdy,OSCtxSw,OSIntCtxSw,以OSStartHighRdy为例,主要都是进行push/pop stack,PC指针的切换.
;********************************************************************************************************
; START MULTITASKING
; void OSStartHighRdy(void)
;
; Note(s) : 1) OSStartHighRdy() MUST:
; a) Call OSTaskSwHook() then,
; b) Set OSRunning to TRUE,
; c) Switch to the highest priority task.
;********************************************************************************************************
OSStartHighRdy
; Change to SVC mode.
MSR CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SVC)
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
MOV LR, PC
BX R0
LDR R0, =OSRunning ; OSRunning = TRUE;
MOV R1, #1
STRB R1, [R0]
; SWITCH TO HIGHEST PRIORITY TASK:
LDR R0, =OSTCBHighRdy ; Get highest priority task TCB address,
LDR R0, [R0] ; Get stack pointer,
LDR SP, [R0] ; Switch to the new stack,
LDR R0, [SP], #4 ; Pop new task's CPSR,
MSR SPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC}^ ; Pop new task's context.
3.异常/中断处理
接下来实现了在cstartp.s引用的几个异常处理函数
OS_CPU_ARM_ExceptUndefInstrHndlr
OS_CPU_ARM_ExceptSwiHndlr
OS_CPU_ARM_ExceptPrefetchAbortHndlr
OS_CPU_ARM_ExceptDataAbortHndlr
OS_CPU_ARM_ExceptAddrAbortHndlr
OS_CPU_ARM_ExceptFiqHndlr
;以OS_CPU_ARM_ExceptFiqHndlr为例:
OS_CPU_ARM_ExceptFiqHndlr
SUB LR, LR, #4 ; LR offset to return from this exception: -4.
STMFD SP!, {R0-R12, LR} ; Push working registers.
MOV R2, LR ; Save link register.
MOV R0, #OS_CPU_ARM_EXCEPT_FIQ ; Set exception ID to OS_CPU_ARM_EXCEPT_FIQ.
B OS_CPU_ARM_ExceptHndlr ; Branch to global exception handler.
;然后跳转到OS_CPU_ARM_ExceptHndlr
OS_CPU_ARM_ExceptHndlr
MRS R1, SPSR ; Save CPSR (i.e. exception's SPSR).
; DETERMINE IF WE INTERRUPTED A TASK/IRQ OR ANOTHER LOWER PRIORITY EXCEPTION:
; SPSR.Mode = SVC : task or IRQ handled in SVC mode,
; SPSR.Mode = FIQ, IRQ, ABT, UND : other exceptions,
; SPSR.Mode = USR : *unsupported state*.
AND R3, R1, #OS_CPU_ARM_MODE_MASK
CMP R3, #OS_CPU_ARM_MODE_SVC
BNE OS_CPU_ARM_ExceptHndlr_BrkExcept
;如果不是SVC模式进入OS_CPU_ARM_ExceptHndlr_BrkExcept否则进入OS_CPU_ARM_ExceptHndlr_BrkTask,最终都会进入bsp.c中的OS_CPU_ExceptHndlr进行实际的处理
;另一个异常处理就是OS_CPU_ARM_ExceptIrqHndlr,进行完堆栈保存等处理后也会进入OS_CPU_ExceptHndlr,该函数位于bsp.c,这里实际只进行了对IRQ的处理,实际的中断函数都是通过这里实现的,自带的注释已经很详细就不细述了.
void OS_CPU_ExceptHndlr (CPU_INT32U except_id)
{
CPU_FNCT_VOID pfnct;
if (except_id == OS_CPU_ARM_EXCEPT_IRQ) {
pfnct = (CPU_FNCT_VOID)VICVectAddr; /* Read the interrupt vector from the VIC */
while (pfnct != (CPU_FNCT_VOID)0) { /* Make sure we don't have a NULL pointer */
(*pfnct)(); /* Execute the ISR for the interrupting device */
VICVectAddr = 1; /* Acknowlege the VIC interrupt */
pfnct = (CPU_FNCT_VOID)VICVectAddr; /* Read the interrupt vector from the VIC */
}
} else {
/* Infinite loop on other exceptions. */
/* Should be replaced by other behavior (reboot, etc.) */
while (DEF_TRUE) {
;
}
}
}