目录
说明:
一、任务调度器
1.1、开启任务调度器函数
1.2、任务调度器实现步骤
1.3、xPortStartScheduler()函数
二、启动一个任务
2.1、prvStartFirstTask()函数
2.2、vPortSVCHandler()函数
三、任务切换
3.1、任务切换触发途径
3.2、PendSV中断触发任务切换步骤
说明:
关于内容:
1)以下内容多为概念了解与步骤分析
2)暂无个人示例代码,使用的是FreeRTOS的官方示例代码
3)若想移植代码测试的,请移步其它地方寻找,下文内容暂无个人示例代码供测试
关于其它:
1)操作系统:win 10
2)平台:keil 5 mdk
3)语言:c语言
4)板子:STM32系列移植FreeRTOS
一、任务调度器
1.1、开启任务调度器函数
名称:vTaskStartScheduler(),作用:启动任务调度器,当任务调度器启动后,FreeRTOS会开始进行任务的调度。
1.2、任务调度器实现步骤
1)创建空闲任务:prvldelTask
如下图1:
图1
2)创建软件定时器任务:xTimerCreateTimerTask(可选,不配置宏定义则不创建)
如下图2:
图2
3)关中断(会在启动第一个任务时开启)
如下图3:
图3
4)初始化全局变量
如下图4,因为刚启动任务调度器,此时没有任务在运行,给等待时间为最大值8个F,如下图5:
图4
图5
5)初始化任务运行时间统计功能的时基定时器
如下图6,实际上并没有实现功能,只是定义了一个接口:
图 6
6)调用函数xPortStartScheduler完成任务调度器
如下图7:
图7
1.3、xPortStartScheduler()函数
作用:完成启动任务调度器中与硬件框架相关的配置部分,以及启动第一个任务。
1)检测用户在FreeRTOSConfig.h文件中对中断的相关配置是否有误
如下图8,内容较多,截取部分:
图8
2)配置PendSV和SysTick的中断优先级为最低优先级
如下图9,通过给寄存器赋值给优先级:
图9
3)调用vPortSetupTimerlnterrupt()函数配置SysTick
图下图10:
图10
4)初始化临界区嵌套计数器为 0
如下图11:
图11
5)调用函数prvStartFirstTask()函数启动第一个任务
如下图12:
图12
二、启动一个任务
如何启动一个任务?
找到优先级最高的任务,将最高优先级任务的寄存器值放到CPU的寄存器中,就相当于启动了最高优先级的任务。
例如:要启动的第一个任务是A,任务A的寄存器值,在创建时就保存在申请的任务堆栈中,将任务A堆栈的寄存器值取出,放到CPU寄存器上,即可。
注意:
1)中断产生时,硬件自动将PSR,PC(R15,程序计算器PC),LR(R14,连接寄存器),R12,R3-R0保存和恢复,而R4-R11需要手动保存和恢复。R12(MSP主栈堆指针-->中断、PSP进程栈堆指针)
2)进入中断后,硬件会强制使用MSP指针,此时LR(R14,连接寄存器)的值将会被自动更新为特殊的EXC_RETURN
2.1、prvStartFirstTask()函数
作用:初始化启动第一个任务前的环境,主要是重新设置MSP指针,并使能全局中断(开启PendSV)
什么是MSP指针?
程序在运行过程需要一定的栈空间来保存局部变量等信息。当有信息保持到栈里面时,MCU会自动更新SP指针,ARM Cortex-M内核提供了两个栈空间。
MSP(主堆栈指针):由OS内核、异常服务例程以及需要所有特权访问的应用程序代码来使用;
PSP(进程堆栈指针):用于常规的应用程序代码(不处于异常服务例程中时);
在FreeRTOS中,中断使用MSP(主堆栈),中断以外使用PSP(进程堆栈)。
1)复位MSP初始值
如下图13:
图13
2)使能中断(开启中断)
如下图14:
图14
3)触发SVC中断(获取当前优先级最高的任务控制块pxCurrentTCP;将该任务的寄存器值出栈至CPU寄存器中;设置PSP;返回r14,执行第一个任务)
如下图15:
图15
2.2、vPortSVCHandler()函数
1)通过pxCurrentTCP获取优先级最高的就绪态任务的任务栈地址,优先级最高的就绪态任务是系统将要运行的任务
如下图16:
图16
2)通过任务栈顶指针,将任务栈中的内容出栈到CPU寄存器中,任务栈中的内容在调用任务函数时已初始化,设置PSP值
图下图17:
图17
3)设置控制中断寄存器值写0,允许中断
如下图18:
图18
4)R14在ISR中,记录了异常返回值EXC_RETURN
异常返回值EXC_RETURN合法值如下图19:
图19
三、任务切换
任务切换的本质是?
CPU寄存器的切换(切换到那个任务的寄存器值,就是在运行那个任务)。
3.1、任务切换触发途径
相当于触发PnedSV中断的途径,如下:
1)滴答定时器触发
2)调用FreeRTOS的API函数触发,例如:portYIELD()
本质上是通过向中断控制和状态寄存器ICSR的bit28写入1挂起PendSV来启动PendSV中断。
3.2、PendSV中断触发任务切换步骤
1)当前PSP是正在运行的任务的栈指针,读取当前PSP进程指针,存入r0
2)压栈(也叫保存现场,从r0开始往下)
如下图20:
图20
3)获取当前最高优先级任务控制块
4)出栈(也叫恢复现场,把寄存器的值给CPU寄存器)
如下图21:
图21
5)更新切换后的任务栈指针给PSP(也就是把r0给PSP)
6)bx r14执行更新任务函数
3.3、任务切换实现函数
名称:__asm void xPortPendSVHandler( void ),作用:实现任务切换
实现步骤如下:
1)手动8字节对齐,给PSP,r0的值
如下图22:
图22
2)获取当前最高优先级任务
如下图23:
图23
3)获取当前任务最高优先级
如下图24:
图24
4)开中断
如下图25:
5)压栈
如下图26:
图26
6)把栈顶给PSP
如下图27:
文章来源:https://www.toymoban.com/news/detail-414053.html
图27文章来源地址https://www.toymoban.com/news/detail-414053.html
到了这里,关于FreeRTOS任务调度与任务切换 | FreeRTOS八的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!