IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> FreeRTOS学习第三篇——FreeRTOS任务创建(下) -> 正文阅读

[嵌入式]FreeRTOS学习第三篇——FreeRTOS任务创建(下)

声明:本文为博主的学习篇章,欢迎大家指错,共同学习
在解决一下上篇遗留下来的问题之前,还得提前做些功课,了解一些FreeRTOS的全局变量。

PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/* 按照优先级排序的就绪任务列表 */
PRIVILEGED_DATA static List_t xDelayedTaskList1;						/* 延时任务列表 */
PRIVILEGED_DATA static List_t xDelayedTaskList2;						/* 延迟任务列表(使用两个列表—这个用于已经溢出当前滴答计数的延迟。 */
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;				/* 指向当前正在使用的延迟任务列表。*/
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;		/* 指向当前用于保存已溢出当前滴答计数的任务的延迟任务列表。 */
PRIVILEGED_DATA static List_t xPendingReadyList;						/* 调度器挂起时已就绪的任务。当调度器恢复时,它们将被移到就绪列表中 */

#if( INCLUDE_vTaskDelete == 1 )

	PRIVILEGED_DATA static List_t xTasksWaitingTermination;				/* 已被删除的任务-但它们的内存尚未释放。 */
	PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U;

#endif

#if ( INCLUDE_vTaskSuspend == 1 )

	PRIVILEGED_DATA static List_t xSuspendedTaskList;					/* 当前挂起的任务。 */

#endif

在FreeRTOS中,每个任务都有一个状态(就绪、挂起、延时等),所以FreeRTOS就用任务状态列表项来表述该任务的状态。具体是这样的:FreeRTOS会创建一些状态列表,如就绪任务列表,挂起任务列表等,任务处于什么状态就将任务状态列表项挂在哪个列表项。就绪任务列表是一个列表数组,每个不同的优先级享有独立的就绪任务列表,也就是说pxReadyTasksLists[0]是专门给优先级为0的任务挂载的。configMAX_PRIORITIES在文件FreeRTOSConfig.h中定义,描述了FreeRTOS最高的优先级。
当任务因为vTask_Delay等阻塞性堵塞函数暂时无法继续运行下去时,任务状态列表项会被挂在延时任务列表下,表示当前任务在等待阻塞时间结束,并且任务状态列表项中的值会被赋值为阻塞时间。之前有说过,列表项插入列表是根据列表项的值进行升序排序的,也就是说延时列表下挂的第一个列表项永远都是阻塞时间最短的任务。
pxDelayedTaskList用于指向当前的延时任务列表。这里有两个延时任务列表和两个指向延时任务列表的指针。对于一个计数器来说,一直加上去的话总有溢出的时候,系统节拍计数器也不例外,当计数器溢出后计数值会从重装载值重新开始计数。其实当一个任务被阻塞,设定了一个阻塞时间时,其任务状态列表项的值并不是一个需要等待多少时间的值,而是等待时间加上系统计数器当前的计数值。

if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) )
		{
			vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) );
		}
		else
		{
			xTimeToWake = xConstTickCount + xTicksToWait;

			listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake );

所以当系统节拍计数器溢出时,延迟任务列表下挂的所有任务项的值都需要进行特殊处理,所以就会有两个延时任务列表和两个指针。
xPendingReadyList用于保存已经就绪,但是任务调度器未开启,暂时无法运行的任务状态列表项。根据经验来说这里的任务状态列表项的值应该是任务的优先级,再根据列表项插入列表的API实现来看,此列表的第一项永远都是优先级最高任务状态列表项,对于相同优先级的任务状态列表项,最先进来的列表项排在前面,这也比较符合正常思维。

对于上面所说的全局变量在第一次创建任务的时候需要对这些变量进行初始化。

static void prvInitialiseTaskLists( void )
{
UBaseType_t uxPriority;

	for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
	{
		vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
	}

	vListInitialise( &xDelayedTaskList1 );
	vListInitialise( &xDelayedTaskList2 );
	vListInitialise( &xPendingReadyList );

	#if ( INCLUDE_vTaskDelete == 1 )
	{
		vListInitialise( &xTasksWaitingTermination );
	}
	#endif

	#if ( INCLUDE_vTaskSuspend == 1 )
	{
		vListInitialise( &xSuspendedTaskList );
	}
	#endif 

	pxDelayedTaskList = &xDelayedTaskList1;
	pxOverflowDelayedTaskList = &xDelayedTaskList2;
}

这个函数非常简单,就是调用了之前说过的列表初始化函数。

接着就该继续上一篇遗漏的知识了

将任务加入到就绪任务列表中

static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )
{
	taskENTER_CRITICAL();						/* 进入临界区 */	
	{
		uxCurrentNumberOfTasks++;				/* 任务统计数量加一 */
		if( pxCurrentTCB == NULL )
		{
			pxCurrentTCB = pxNewTCB;			/* 如果当前没有任务在运行,就将此任务赋给pxCurrentTCB */

			if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
			{
				prvInitialiseTaskLists();		/* 如果当前任务的数量为一,即第一次创建任务就初始化一些列表 */
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			if( xSchedulerRunning == pdFALSE )	/* 如果没有打开调度器 */
			{
				/* 如果新创建的任务优先级大于变量pxCurrentTCB指向的任务优先级,则设置pxCurrentTCB指向当前新创建的任务TCB(确保pxCurrentTCB指向优先级最高的就绪任务) */ 
				if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
				{
					pxCurrentTCB = pxNewTCB;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
			
		uxTaskNumber++;						/* 任务控制块的编号加一 */

		#if ( configUSE_TRACE_FACILITY == 1 )	/* 可视化追踪功能 */
		{
			pxNewTCB->uxTCBNumber = uxTaskNumber;
		}
		#endif
		traceTASK_CREATE( pxNewTCB );

		prvAddTaskToReadyList( pxNewTCB );		/* 添加任务到就绪列表中 */

		portSETUP_TCB( pxNewTCB );				/* 将pxNewTCB设置为空 */
	}
	taskEXIT_CRITICAL();						/* 退出临界区 */

	if( xSchedulerRunning != pdFALSE )
	{	/* 如果创建的任务的优先级高于当前任务,那么现在应该运行它 */
		if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority )
		{
			taskYIELD_IF_USING_PREEMPTION();	/* 进行任务切换 */
		}
		else 
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}
}

首先是进入临界区,什么是临界区呢,这里又是怎么进入的?
临界区指的是那些必须完整运行,中途不能被打断的代码段。也就是说,在运行这段代码的时候不能切换任务,也不能让中断打断。所以在运行这段代码之前要先进入临界区,运行完后退出临界区就能够确保这段代码运行中途不被打断。如何进入临界区呢?答案就是关中断。这里补充一个知识点,在FreeRTOS中,所有任务的切换都是在PendSV中断函数里进行的,也就是说,要进行任务切换就必须发起一次PendSV中断。在FreeRTOS中,所有中断的优先级都高于最高优先级的任务,所以所有中断都可以打断任务的运行。所以关闭中断也就关闭了任务之间的切换。退出临界区就是开中断啦。
uxCurrentNumberOfTasks是一个全局变量,用于记录当前的任务数量(创建了未删除的所有任务)。
pxCurrentTCB也是一个全局变量,用于记录当前正在执行的任务。
如果调度器还没打开(程序刚开始运行时,可能会先创建几个任务,之后才会启动调度器),并且新创建的任务优先级大于变量pxCurrentTCB指向的任务优先级,则设置pxCurrentTCB指向当前新创建的任务TCB(确保pxCurrentTCB指向优先级最高的就绪任务)。
第一次创建任务需要对一些列表进行初始化。
uxTaskNumber用于记录任务数量变化的次数,每次任务数量发生了变化,这个值就会加一(任务创建和任务删除)。
prvAddTaskToReadyList是在task.c中的一个宏定义

#define prvAddTaskToReadyList( pxTCB )																\
	traceMOVED_TASK_TO_READY_STATE( pxTCB );														\
	taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );												\
	vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
	tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )

taskRECORD_READY_PRIORITY用于将此任务的优先级保存到就绪任务优先级的变量中。vListInsertEnd就是列表项的尾部插入操作了。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 16:31:24  更:2022-03-03 16:33:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:55:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码