谈谈rtos中多任务之间是如何进行转换(Cortex-M3)

本文详细解析了如何通过异常中断实现任务间的转换,涉及保存现场、堆栈指针的使用、中断服务程序的工作原理,以及在STM32中的PendSV_Handler示例。重点介绍了堆栈指针(如MSP和PSP)在单任务和多任务环境中的作用和切换过程。
摘要由CSDN通过智能技术生成

其实,是采用异常中断的方式,运行中断服务函数,然后再中断服务函数完成任务之间的转换。

 从上图中可以看出,在使用异常中断来进行任务转换时,首先先要保存现场,即:将原本在执行的任务进行压栈保存,用户按照下面的顺序进行压栈保存,之后各寄存器将会按照上图(1.)的顺序被找到正确的位置。

 然后接下来就是在中断服务程序中究竟如何完成任务转换?

在完成保存原本正在执行的任务之后,那么接下来要实现的效果当然就是在中断服务函数结束时自动跳转到另一个任务去执行。这里我们可以自己猜想一下,肯定是有某一个寄存器中存放着一个指针,使得程序在执行完中断之后,到这个寄存器去看一下接下来要去哪里执行,就算我们原本只有一个任务(经典的main程序),在中断触发时,去执行中断服务函数,执行完之后接着在main程序中继续执行接下来的指令,那么系统怎么知道中断前执行到哪个位置,其实,就是在运行中断服务函数之前我们会保存现场,其中就把后续程序的地址保存在某个寄存器中。

按照上面的推理,我们可以推断出“某个寄存器”中存放的地址决定了中断结束后该去哪里运行程序。既然如此,那么我们是不是就可以擅自修改“某个寄存器”的值,让他不是指向中断前的位置,而是指向另一个任务的位置呢?

其实上面说到的“某个寄存器” 就是指这里的堆栈指针R13这个特殊寄存器,然后其实在只有单任务时,使用的是主堆栈指针(MSP),这个就不能随意更改,其实意思就是不适用于多任务跳转,那么进程堆栈指针(PSP)就是可以拿来更改的寄存器。

系统怎么判断此时正在使用的是哪一个(MSP还是PSP)呢?

从上图可以看出,系统是根据控制寄存器(CONTROL)来判断使用的是哪一个寄存器,也可以通过修改LR的位2来实现,而这里我们可以发现LR其实就是保存现场时需要压栈保存的八个寄存器中的LR。

所以在中断中实现任务跳转的代码如下:

;********************************************************************************************************
;                                          PendSVHandler异常
;********************************************************************************************************
PendSV_Handler
; 任务的保存,即把CPU寄存器的值存储到任务的堆栈中	
	CPSID   I                                 ; 关中断,NMI和HardFault除外,防止上下文切换被中断	
	MRS     R0, PSP                           ; 将psp的值加载到R0
	CBZ     R0, OS_CPU_PendSVHandler_nosave   ; 判断R0,如果值为0则跳转到OS_CPU_PendSVHandler_nosave
	                                          ; 进行第一次任务切换的时候,R0肯定为0
	
	; 在进入PendSV异常的时候,当前CPU的xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0会自动存储到当前任务堆栈,同时递减PSP的值
	STMDB   R0!, {R4-R11}                     ; 手动存储CPU寄存器R4-R11的值到当前任务的堆栈
	
	LDR     R1, = OSTCBCurPtr                 ; 加载 OSTCBCurPtr 指针的地址到R1,这里LDR属于伪指令
	LDR     R1, [R1]                          ; 加载 OSTCBCurPtr 指针到R1,这里LDR属于ARM指令
	STR     R0, [R1]                          ; 存储R0的值到	OSTCBCurPtr->OSTCBStkPtr,这个时候R0存的是任务空闲栈的栈顶

; 任务的切换,即把下一个要运行的任务的堆栈内容加载到CPU寄存器中
OS_CPU_PendSVHandler_nosave  
	; OSTCBCurPtr = OSTCBHighRdyPtr;
	LDR     R0, = OSTCBCurPtr                 ; 加载 OSTCBCurPtr 指针的地址到R0,这里LDR属于伪指令
	LDR     R1, = OSTCBHighRdyPtr             ; 加载 OSTCBHighRdyPtr 指针的地址到R1,这里LDR属于伪指令
	LDR     R2, [R1]                          ; 加载 OSTCBHighRdyPtr 指针到R2,这里LDR属于ARM指令
	STR     R2, [R0]                          ; 存储 OSTCBHighRdyPtr 到 OSTCBCurPtr
	
	LDR     R0, [R2]                          ; 加载 OSTCBHighRdyPtr 到 R0
	LDMIA   R0!, {R4-R11}                     ; 加载需要手动保存的信息到CPU寄存器R4-R11
	
	MSR     PSP, R0                           ; 更新PSP的值,这个时候PSP指向下一个要执行的任务的堆栈的栈底(这个栈底已经加上刚刚手动加载到CPU寄存器R4-R11的偏移)
	ORR     LR, LR, #0x04                     ; 确保异常返回使用的堆栈指针是PSP,即LR寄存器的位2要为1
	CPSIE   I                                 ; 开中断
	BX      LR                                ; 异常返回,这个时候任务堆栈中的剩下内容将会自动加载到xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
	                                          ; 同时PSP的值也将更新,即指向任务堆栈的栈顶。在STM32中,堆栈是由高地址向低地址生长的。
	
	NOP                                       ; 为了汇编指令对齐,不然会有警告
	
	
	END                                       ; 汇编文件结束

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小丑爱表现

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值