FreeRTOS中Pendsv任务切换过程的分析
一、Pendsv中断任务解析
(1)uxCriticalNesting 是进入临界区的次数
(2)pxCurrentTCB是FreeRTOS运行时当前的任务块
(3)vTaskSwitchContext是用来选择下一个要运行任务的函数
(4)mrs r0, psp 是将进程栈指针送到r0
(5)isb是指令同步代码,由于CortexM3,4为哈弗结构,因此保证执行完之前指令之后再继续执行后续指令
(6)ldr r3, =pxCurrentTCB 将pxCurrentTCB的地址放入r3
(7)ldr r2, [r3] 获取当前的任务块地址(此处注意任务块首地也是指向任务栈的栈顶指针)
(8)stmdb r0!, {r4-r11} 保存剩下的8个寄存器,为什么是剩下的8个寄存器,在没有浮点运算的cortexM3,M4中,cpu中的普通寄存器有15个,分别为r0-r12,r13(SP:栈指针),r14(LR,链接寄存器,主要用来中断等返回),r15(PC:程序计数器,指向当前指令的之后的第4个地址,因为为3流水线结构),而在进入中断时,cpu会自动将r0-r3,r12,r14,返回地址,xPSR(程序状态寄存器)共8个寄存器压栈,而普通寄存器还剩r4-r11(SP是不用压栈的)
(9)str r0, [r2] 将r0的内容(即新的栈顶指针)写到任务块中(任务栈的栈顶指针),这样下次调任务的时候就可以执行出栈操作。
(10)stmdb sp!, {r3, r14},将r3,r14入栈,r3保存了pxCurrentTCB的地址,而r14用来中断返回的,由于下面调用了vTaskSwitchContext,防止这两个寄存器内容丢失。(注意此处压的栈是msp,并非psp,因为处理模式下只能使用msp)
(11)
mov r0,#configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
dsb
isb
是用来屏蔽中断,即进入临界区,并进行数据指令同步
(12)bl vTaskSwitchContext选择下一个要运行的任务,注意此函数中将pxCurrentTCB的值改成了下一个要运行的任务,而r3指向的是pxCurrentTCB的地址,故取出来后的是下一个要运行任务的任务块的地址!!!(pxCurrentTCB是一个房间,此房间内容是要运行的任务块地址,而r3保存的是pxCurrentTCB的房间号,故取到的虽是同一个pxCurrentTCB的房间,但pxCurrentTCB的房间里面实际内容已经改变了,这是理解后面的关键)
(13)mov r0, #0
msr basepri, r0
打开中断,即退出临界区
(14)ldmia sp!, {r3, r14} 恢复r3,r14
(15)
ldr r1, [r3]
ldr r0, [r1]
ldmia r0!, {r4-r11}
此时r0指向下一个要运行任务的首地址,即任务栈顶(详细分析见(12)),恢复r4-r11的内容,故此处恢复的是即将运行任务的“环境”
(13)
msr psp, r0
isb
更新psp进程栈指针,即指向了下一个要运行任务的任务栈的地址(那些要运行的“环境”已经出栈了)
(14)
bx r14
nop
执行此代码后上述的8个寄存器将自动出栈,注意此处恢复的也是下个运行任务的8组寄存器(因为从该中断返回是进入线程模式,用的是psp,(13)中已将psp更新为下一任务的栈地址),故PC将指向下个任务,至此任务切换完成
【注】r14内容一直的是用来返回pendsv中断的
void vTaskSwitchContext( void )函数
(1)如果任务调度器挂起则置位标志位
(2)taskSELECT_HIGHEST_PRIORITY_TASK();选则下一个要运行的函数
taskSELECT_HIGHEST_PRIORITY_TASK()
(1)portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities );
其定义为 uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )
__clz是前导零计算的汇编指令,即是检测uxTopReadyPriorities从高位到第一个不为0的0个数,用31减去则得到该第一个1对应的位数,即为最高优先级的任务。(获取最高优先级任务有软件通用方法和硬件方法两种:1硬件方法就是设置优先级标志组,即该字中每个位代表一个优先级,则可用前导零来获取最高优先级任务(但并不是所有cpu都提供该指令)但优先级数量只有32位 2利用软件方法获取,优先级数不受限制)
(2)listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );因为同等最高优先级任务放在同一个uxTopReadyPriorities下标的链表中,则调用获取链表中第一个任务,并且更新到pxCurrentTCB中
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)