FreeRtos源码分析之启动任务调度vTaskStartScheduler(二)

更新时间:2023-05-20 15:07:39 阅读: 评论:0

FreeRtos源码分析之启动任务调度vTaskStartScheduler(⼆)
⼀、概述
FreeRtos在创建任务之后,需要启动任务调度器才能使任务正常有序的运⾏。任务调度器的开启依赖于底层硬件,对于CortexM3内核⽽⾔,任务调度器需要⽤到中断和滴答定时器。FreeRtos在对中断优先级、空闲任务等进⾏初始化之后,会开启滴答定时器的中断,这样每隔1毫秒系统就会进⼊滴答定时器中断,FreeRtos会在这个中断中进⾏诸如记录系统运⾏时间、查找下⼀个就绪态任务等操作。换句话说,滴答定时器就是FreeRtos的⼼脏。
FreeRtos任务调度需要⽤到CortexM3和汇编知识,可以结合CorteM3权威指南进⾏学习。
⼆、FreeRtos任务调度开启流程
FreeRtos调⽤vTaskStartScheduler函数开启任务调度,流程如下:
[1]初始化空闲任务
[2]创建定时器管理任务
[3]中断初始化
[4]滴答定时器初始化
[5]调⽤vPortStartFirstTask函数出发svc中断
[6]在SVC中断函数vPortSVCHandler中启动第⼀个任务
2.1、空闲任务
1. 当没有其它任务运⾏时,FreeRtos会⾃动运⾏空闲任务。空闲任务的优先级默认为0,在所有的任务中优先级最低。即使有和空闲任
务相同优先级的任务被创建,系统依然会优先运⾏⾮空闲任务的其它任务。
空闲任务会回收调⽤vTaskDelete(NULL)删除⾃⼰任务的内存,避免内存泄漏。
2. 空闲任务会不停的监测是否有新的任务处于就绪状态,如果有则进⾏任务切换
gre考试费用3. 空闲任务可以⽤于实现系统的低功耗,其原理是在系统空闲时间进⼊硬件低功耗模式。
空闲任务的功能总结如下:
回收已删除任务的内存;
检查是否需要进⾏任务切换
调⽤vApplicationIdleHook函数,执⾏⽤户⾃定义代码
可以⽤于实现系统的低功耗功能
空闲任务源码:
static portTASK_FUNCTION( prvIdleTask, pvParameters )
{
/* 防⽌编译警告. */
(void) pvParameters;
/** 空闲任务,在任务调度器启动时⾃动创建. **/
/*如果具有安全上下⽂的任务删除了⾃⼰,在这种情况下,空闲任务负责删除任务的安全上下⽂(如果有). */
portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE );
for(;;)
{
/* 如果有任务删除了⾃⼰,空闲任务负责释放被删除任务的TCB和内存。 */
prvCheckTasksWaitingTermination();
#if ( configUSE_PREEMPTION == 0 )
counterattack{
/*如果没有使⽤抢占式调度器,只要有可⽤的其它任务就进⾏任务切换*/
taskYIELD();
}
#endif/* configUSE_PREEMPTION */
#if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
{北欧神话和希腊神话
/* 如果使⽤抢占式调度的任务具有相同的优先级,那么它们将会按照时间⽚进⾏调度。
* 如果有任务和空闲任务具有相同的优先级,空闲任务应该在时间⽚耗尽之前进⾏调度。
* 由于我们只是从列表中读取内容,因此此处不需要关键区域,偶尔出现的错误值也⽆关紧要。
* 如果处于空闲优先级的就绪列表包含多个任务,则准备执⾏⾮空闲任务以外的任务。*/
if(listCURRENT_LIST_LENGTH(&( pxReadyTasksLists[ tskIDLE_PRIORITY ]))>( UBaType_t )1)//如果就绪列表中的任务数量⼤于1则执⾏任务切换
{
taskYIELD();
}
el
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif/* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */
#if ( configUSE_IDLE_HOOK == 1 )
sacmi{
extern void vApplicationIdleHook(void);
/* 调⽤⽤户⾃定义的钩⼦函数。这个函数禁⽌调⽤可能阻塞的函数。 */
vApplicationIdleHook();
}
#endif/* configUSE_IDLE_HOOK */朗文3h少儿英语教材
/
分子式* configUSE_TICKLESS_IDLE表⽰是否开启低功耗模式,为0表⽰关闭,⾮0表⽰打开. */
#if ( configUSE_TICKLESS_IDLE != 0 )
{
//此处省略低功耗部分代码
}
#endif/* configUSE_TICKLESS_IDLE */
muzak}
}
2.2、定时器任务
定时器任务也在任务调度开始前创建,其优先级为2,略⾼于空闲任务,主要⽤来对FreeRtos软件定时器进⾏管理。
2.4、中断初始化中断
CortexM3和CortexM4的每⼀个外部中断都有⼀个对应的优先级寄存器,每个寄存器占⽤8位,但是最少允许使⽤最⾼3位。 4 个相临的优先级寄存器拼成⼀个 32 位寄存器,分别是抢占优先级和响应(亚)优先级。这些寄存器可以按照字节、字或者半字访问。 如下图:
stm32f4xx系列芯⽚使⽤⾼4位⽤来配置优先级,优先级取值范围为0-15。低4位始终为0,⽤户⽆法修改,FreeRtos使⽤这个特性来检验优先级中断寄存器的可⽤位数。
FreeRtos中断初始化流程:
[1]读取地址为0xE000E400优先级寄存器的值;
[2]将0xFF写⼊该优先级寄存器;
[3]读取该优先级寄存器地址的内容;
[4]判断读取到的值有多少位为1从⽽确定实际可⽤的优先级位数;
[5]使⽤读出来的值和stm32f4芯⽚实际的可⽤优先级进⾏⽐较,避免出错;
[6]恢复0xE000E400优先级寄存器访问之前的值;
[7]将PendSV和SysTick的优先级设为最低;
[8]初始化SysTick定时器
源码:
#if ( configASSERT_DEFINED == 1 )
{
volatile uint32_t ulOriginalPriority;
//计算后得到的值为0xE000E400,表⽰外部中断优先级寄存器的地址
volatile uint8_t *const pucFirstUrPriorityRegister =(volatile uint8_t *const)( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTE RRUPT_NUMBER );
volatile uint8_t ucMaxPriorityValue;
/*  保存即将被破坏的优先级值,后⾯会修改寄存器的值. */
ulOriginalPriority =*pucFirstUrPriorityRegister;
/* 确定有多少优先级位可⽤,不可⽤的位始终为0,通过将寄存器所有位置1,再读取寄存器的值来确定. */
*pucFirstUrPriorityRegister = portMAX_8_BIT_VALUE;
/* Read the value back to e how many bits stuck. */
ucMaxPriorityValue =*pucFirstUrPriorityRegister;
/* U the same mask on the maximum system call priority. */
ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;
/* 根据读回来的值确定最⼤优先级组的值,⽐如读出来的值是0xf0,那么表⽰最⾼4位⽤来表⽰优先级*/
英语四级成绩什么时候公布
ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;
while(( ucMaxPriorityValue & portTOP_BIT_OF_BYTE )== portTOP_BIT_OF_BYTE )
{
ulMaxPRIGROUPValue--;
ucMaxPriorityValue <<=( uint8_t )0x01;
}
#ifdef __NVIC_PRIO_BITS
{
/*检查CMIS配置的优先级位数是否和实际得到的硬件优先级位数是否相同*/
/*stm32f4使⽤了⾼4位作为可配置的优先级位*/
configASSERT(( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue )== __NVIC_PRIO_BITS );
}
#endif
#ifdef configPRIO_BITS
{
/* Check the FreeRTOS configuration that defines the number of
* priority bits matches the number of priority bits actually queried
* from the hardware. */
configASSERT(( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue )== configPRIO_BITS );
}
#endif
/* Shift the priority group value back to its position within the AIRCR
* register. */
ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;
ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;growingpains
/* 将中断寄存器的值复原*/
*pucFirstUrPriorityRegister = ulOriginalPriority;
}
#endif/* conifgASSERT_DEFINED */
/* 使 PendSV and SysTick 的优先级最低. */
portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;
portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;
/* 初始化SysTick定时器. 此时中断已经被关闭. */
vPortSetupTimerInterrupt();
天津翻译
2.4、初始化SysTick定时器
CortexM3和CortexM4都具有SysTick定时器,它被捆绑在 NVIC中,⽤于产⽣SYSTICK异常(异常号: 15)。
SysTick定时器有四个寄存器,相关定义如下:
SysTick定时器重装载数值寄存器的初始值计算公式:
t=reload*(1/clock)
计数值为:
reload = clock*t-1
减⼀是因为计数值从0开始。⽐如stm32f407的系统时钟是168MHz,如果我们希望FreeRtos的时钟节拍设置为1ms,那么得到reload 的值为168000000*(1/1000)-1
FreeRtos对SysTick初始化的源码如下:
__weak void vPortSetupTimerInterrupt(void)
{
/* Stop and clear the SysTick. */
portNVIC_SYSTICK_CTRL_REG =0UL;//复位SysTick控制寄存器
portNVIC_SYSTICK_CURRENT_VALUE_REG =0UL;//清零当前数值寄存器
/* Configure SysTick to interrupt at the requested rate. */
portNVIC_SYSTICK_LOAD_REG =( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ )-1UL;//设置重装载数值寄存器值
//使⽤内部时钟、SysTick倒数⾄0时产⽣中断、使能SysTick
portNVIC_SYSTICK_CTRL_REG =( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}
FreeRtos将SysTick的中断函数重新定义:

本文发布于:2023-05-20 15:07:39,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/115935.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:任务   寄存器   空闲   中断
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图