邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛
分类: 嵌入式
2017-09-04 13:59:04
任务切换函数:根据不同的CPU,这个函数一般都是用汇编语言写,一般思想都是:
1、将CPU的通用寄存器压入堆栈(每一个任务都有自己的堆
栈);
2、将堆栈指针SP赋值给任务控制块中的OSTCBStkPtr中;
3、获得最高优先级的任务块;
4、将最高任务快中的OSTCBStkPtr赋值
给堆栈指针SP;
5、将新的任务块中的值出栈给CPU通用寄存器;
6、中断返回IRET(RETI),使PC指向待运行任务。
普通任务切换函数OSCtxSw()示意性代码:
OSCtxSw(void)
{
压入堆栈;
OSTCBCur->OSTCBStkPtr=SP;
OSTCBCur=OSTCBHighRdy;
OSPrioCur=OSPrioHighRdy;
SP=OSTCBHighRdy->OSTCBStkPtr;
出栈;
IRET;
}
中断级任务切换函数OSIntCtxSw()示意代码:
OSIntCtxSw(void)
{
OSTCBCur=OSTCBHighRdy;
OSPrioCur=OSPrioHighRdy;
SP=OSTCBHighRdy->OSTCBStkPtr;
出栈;
RETI;
}
OSIntCtxSw(void)在调用之前,在进入中断的时候已经将任务的通用寄存器压入堆栈里面了,所以任务切换函数后面是相同的;
IRET、RETI指令都是将在将堆栈中保存的地址取出,送给PC;唯一的区别就是RETI指令除了中断返回的作用之外还有将“优先级生效”触发器清零的功能;
;********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level)
; void OSCtxSw(void)
;
; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch. This function
; triggers the PendSV exception which is where the real work is done.
;********************************************************************************************************
OSCtxSw ; 任务切换
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
;********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From interrupt level)
; void OSIntCtxSw(void)
;
; Notes: 1) OSIntCtxSw() is called by OSIntExit() when it determines a context switch is needed as
; the result of an interrupt. This function simply triggers a PendSV exception which will
; be handled when there are no more interrupts active and interrupts are enabled.
;********************************************************************************************************
OSIntCtxSw ; 中断类型任务切换
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
这两个切换的代码都是一样的,主要都是用pendsv中断来实现。具体的pendsv代码如下:
;********************************************************************************************************
; HANDLE PendSV EXCEPTION
; void OS_CPU_PendSVHandler(void)
;
; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing
; context switches with Cortex-M3. This is because the Cortex-M3 auto-saves half of the
; processor context on any exception, and restores same on return from exception. So only
; saving of R4-R11 is required and fixing up the stack pointers. Using the PendSV exception
; this way means that context saving and restoring is identical whether it is initiated from
; a thread or occurs due to an interrupt or exception.
;
; 2) Pseudo-code is:
; a) Get the process SP, if 0 then skip (goto d) the saving part (first context switch);
; b) Save remaining regs r4-r11 on process stack;
; c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP;
; d) Call OSTaskSwHook();
; e) Get current high priority, OSPrioCur = OSPrioHighRdy;
; f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy;
; g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr;
; h) Restore R4-R11 from new process stack;
; i) Perform exception return which will restore remaining context.
;
; 3) On entry into PendSV handler:
; a) The following have been saved on the process stack (by processor):
; xPSR, PC, LR, R12, R0-R3
; b) Processor mode is switched to Handler mode (from Thread mode)
; c) Stack is Main stack (switched from Process stack)
; d) OSTCBCur points to the OS_TCB of the task to suspend
; OSTCBHighRdy points to the OS_TCB of the task to resume
;
; 4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we
; know that it will only be run when no other exception or interrupt is active, and
; therefore safe to assume that context being switched out was using the process stack (PSP).
;********************************************************************************************************
PendSV_Handler
CPSID I ; Prevent interruption during context switch
MRS R0, PSP ; PSP is process stack pointer
CBZ R0, PendSV_Handler_nosave ; Skip register save the first time (第一次切换的时候,不用保存中断现场,因为之前并没有任务执行过)
// 保存当前任务的中断现场
SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
STM R0, {R4-R11} ;将cpu的寄存器R4-R11保存到任务堆栈中
STM:S的含义仍然是STORE,与LDM是配对使用的,其指令格式上也相似,即区别于STR,是将堆栈指针写在左边,而把寄存器组写在右边。
STMFD SP!, {R0} 同样的,该指令也可理解为: STMFD [SP]!, {R0} 意思是:把R0保存到堆栈(sp指向的地址)中。 |
B: 跳转。
B label 满足条件立即跳转到Lable指定的地址执行
BL: 带链接的跳转。 首先将当前指令的下一条指令地址保存在LR寄存器,然后跳转的lable。通常用于调用子程序,可通过在子程序的尾部添加mov pc, lr 返回。
BX: 带状态切换的跳转。最低位为1时,切换到Thumb指令执行,为0时,解释为ARM指令执行。
BLX: 带链接和状态切换的跳转。结合了BX与BL功能,
操作:
BL 和 BLX 指令可将下一个指令的地址复制到 lr(r14,链接寄存器)中。
BX 和 BLX 指令可将处理器的状态从 ARM 更改为 Thumb,或从 Thumb 更改为 ARM。
BLX label 无论何种情况,始终会更改处理器的状态。
BX Rm 和 BLX Rm 可从 Rm 的位 [0] 推算出目标状态:
如果 Rm 的位 [0] 为 0,则处理器的状态会更改为(或保持在)ARM 状态
如果 Rm 的位 [0] 为 1,则处理器的状态会更改为(或保持在)Thumb 状态。
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy;
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
STR指令的格式为: STR{条件} 源寄存器,<存储器地址> STR指令用亍从源寄存器中将一个32位的字数据传送到存储器中 |