FreeRTOS学习:三.任务调度器

FreeRTOS学习之路三:任务调度器
前面两节讲了任务栈、任务内容、任务列表的联系,当我们创建好多个任务并把它添加到任务列表时,系统是如何来进行任务间的切换与调度。就靠freertos系统里面的调度器进行这项工作。
先看看freertos一般创建任务开始调度的代码流程。
在这里插入图片描述

流程就是创建任务,任何启动调度器。xTaskCreateStatic是一个静态内存任务创建函数,在前面已经说过,来看看这个函数的参数。
1.任务入口:也就是函数名,函数名也就代表任务起始地址。
2.任务名称:一段字符串。
3.任务栈大小:也就是自己定义的一个静态数组的大小。
4.任务形参:传进函数里面的参数
5.任务优先级:数字越大优先级越高。
6.任务栈起始地址:数组名。
7.任务控制块:指向任务TCB的指针。
返回值:pdPass代表创建任务成功,也就是为1.
然后将任务添加的任务列表上,直接调用vListInsertEnd函数。
最后启动调度器。
调度器
vTaskStartScheduler();
这个函数被称为调度器,可以说是freertos的核心。
函数原型:void vTaskStartScheduler( void ),无返回值,无参数。
函数实现:

为了方便学习,这里可以用野火简化的调度器函数,原函数有许多宏,这里列出一些必要的实现过程。
void vTaskStartScheduler( void )
{
/*======================================创建空闲任务start==============================================*/   
/*空闲任务用于不造成CPU资源的浪费,当没有比空闲任务优先级高的任务时,系统就会一直运行此函数,此函数可以用来做一些资源清理等功能。*/  
    TCB_t *pxIdleTaskTCBBuffer = NULL;
    StackType_t *pxIdleTaskStackBuffer = NULL;
    uint32_t ulIdleTaskStackSize;
    
    /* 获取空闲任务的内存:任务栈和任务TCB */
    vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, 
                                   &pxIdleTaskStackBuffer, 
                                   &ulIdleTaskStackSize );    
    
    xIdleTaskHandle = xTaskCreateStatic( (TaskFunction_t)prvIdleTask,              /* 任务入口 */
					                     (char *)"IDLE",                           /* 任务名称,字符串形式 */
					                     (uint32_t)ulIdleTaskStackSize ,           /* 任务栈大小,单位为字 */
					                     (void *) NULL,                            /* 任务形参 */
                                         (UBaseType_t) tskIDLE_PRIORITY,           /* 任务优先级,数值越大,优先级越高 */
					                     (StackType_t *)pxIdleTaskStackBuffer,     /* 任务栈起始地址 */
					                     (TCB_t *)pxIdleTaskTCBBuffer );           /* 任务控制块 */
/*======================================创建空闲任务end================================================*/ 
/*下面两句主要是关于系统心跳时钟,暂时不管*/
    xNextTaskUnblockTime = portMAX_DELAY;
    xTickCount = ( TickType_t ) 0U;
    //xTickCount = ( TickType_t ) 0xfffffffcUL;
    /* 启动调度器 ,重要的是此函数*/
    if( xPortStartScheduler() != pdFALSE )
    {
        /* 调度器启动成功,则不会返回,即不会来到这里 */
    }
}

xPortStartScheduler函数,初始化时钟和系统悬挂的优先级为最低,为了任务在调度和被挂起时不被影响。

seType_t xPortStartScheduler( void )
{
    /* 配置PendSV 和 SysTick 的中断优先级为最低 */
	portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
	portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
    
    /* 初始化SysTick */
    vPortSetupTimerInterrupt();

	/* 启动第一个任务,不再返回 */
	prvStartFirstTask();

	/* 不应该运行到这里 */
	return 0;
}

prvStartFirstTask(),此函数开始真正的调度,这是一段汇编代码,需要了解ARM架构,总而言之,开始会从中断向量表中找到中断服务子程序,

__asm void prvStartFirstTask( void )
{
	PRESERVE8

	/* 在Cortex-M中,0xE000ED08是SCB_VTOR这个寄存器的地址,
       里面存放的是向量表的起始地址,即MSP的地址 */
	ldr r0, =0xE000ED08
	ldr r0, [r0]
	ldr r0, [r0]

	/* 设置主堆栈指针msp的值 */
	msr msp, r0
    
	/* 使能全局中断 */
	cpsie i
	cpsie f
	dsb
	isb
	
    /* 调用SVC去启动第一个任务 */
	svc 0  
	nop
	nop
}

当代码执行到svc这一行时,在单步执行的时候会去指向svc中断服务子程序。在这一段中任务会找到第一个优先级高的TCB任务指针,将里面任务栈里面的内容分加载到寄存器中,其中有一个就是任务的入口地址,最后bx r14是中断返回,然后将任务栈里面的内容出栈进行任务执行。

__asm void vPortSVCHandler( void )
{
    extern pxCurrentTCB;
    
    PRESERVE8

	ldr	r3, =pxCurrentTCB	/* 加载pxCurrentTCB的地址到r3 */
	ldr r1, [r3]			/* 加载pxCurrentTCB到r1 */
	ldr r0, [r1]			/* 加载pxCurrentTCB指向的值到r0,目前r0的值等于第一个任务堆栈的栈顶 */
	ldmia r0!, {r4-r11}		/* 以r0为基地址,将栈里面的内容加载到r4~r11寄存器,同时r0会递增 */
	msr psp, r0				/* 将r0的值,即任务的栈指针更新到psp */
	isb
	mov r0, #0              /* 设置r0的值为0 */
	msr	basepri, r0         /* 设置basepri寄存器的值为0,即所有的中断都没有被屏蔽 */
	orr r14, #0xd           /* 当从SVC中断服务退出前,通过向r14寄存器最后4位按位或上0x0D,
                               使得硬件在退出时使用进程堆栈指针PSP完成出栈操作并返回后进入线程模式、返回Thumb状态 */
    
	bx r14                  /* 异常返回,这个时候栈中的剩下内容将会自动加载到CPU寄存器:
                               xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
                               同时PSP的值也将更新,即指向任务栈的栈顶 */
}

[1]: 参考《FreeRTOS 内核实现与应用开发实战指南

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值