FreeRTOS记录(二、FreeRTOS任务API认识和源码简析)_freertos多任务api

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

FreeRTOS 任务API

来认识一下FreeRTOS的常用任务API:

API名称CMSIS封装的APIAPI说明
xTaskCreateosThreadCreate动态创建任务
xTaskCreateStaticosThreadCreate静态创建任务
vTaskDeleteosThreadTerminate删除任务
vTaskSuspendosThreadSuspend挂起任务
vTaskResumeosThreadResume恢复任务
xTaskResumeFromISRosThreadResume在中断中恢复任务
uxTaskPriorityGetosThreadGetPriority获取任务优先级
vTaskPrioritySetosThreadSetPriority设置任务优先级
vTaskDelayosDelay相对延时任务
vTaskDelayuntilosDelayUntil绝对延时任务

创建任务

任务创建分为动态xTaskCreate和静态xTaskCreateStatic,但是在CubeMX中通过封装后统一使用的是osThreadCreate,可以查看一下osThreadCreate实现:

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* Thread Management \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
/\*\*
\* @brief Create a thread and add it to Active Threads and set it to state READY.
\* @param thread\_def thread definition referenced with \ref osThread.
\* @param argument pointer that is passed to the thread function as start argument.
\* @retval thread ID for reference by other functions or NULL in case of error.
\* @note MUST REMAIN UNCHANGED: \b osThreadCreate shall be consistent in every CMSIS-RTOS.
\*/
osThreadId osThreadCreate (const osThreadDef_t \*thread_def, void \*argument)
{
  TaskHandle_t handle;
  
#if( configSUPPORT\_STATIC\_ALLOCATION == 1 ) && ( configSUPPORT\_DYNAMIC\_ALLOCATION == 1 )
  if((thread_def->buffer != NULL) && (thread_def->controlblock != NULL)) {
    handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR \*)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              thread_def->buffer, thread_def->controlblock);
  }
  else {
    if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR \*)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              &handle) != pdPASS)  {
      return NULL;
    } 
  }
#elif( configSUPPORT\_STATIC\_ALLOCATION == 1 )

    handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR \*)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              thread_def->buffer, thread_def->controlblock);
#else
  if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR \*)thread_def->name,
                   thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
                   &handle) != pdPASS)  {
    return NULL;
  }     
#endif
  
  return handle;
}

由上可以得知,CubeMX生成的osThreadCreate会根据我们在Config Parameters中的Memory Allocation内存分配中选择的配置结果自动的选择xTaskCreate还是xTaskCreateStatic

xTaskCreate
在这里插入图片描述xTaskCreateStatic
在这里插入图片描述

删除任务

vTaskDelete封装后是osThreadTerminate,如下:

osStatus osThreadTerminate (osThreadId thread_id)
{
#if (INCLUDE\_vTaskDelete == 1)
  vTaskDelete(thread_id);
  return osOK;
#else
  return osErrorOS;
#endif
}

参数为NULL vTaskDelete(NULL)是删除任务自己

挂起任务

void vTaskSuspend( TaskHandle_t xTaskToSuspend )传入的参数是 任务句柄

vTaskSuspend封装后是osThreadSuspend,如下:

//
osStatus osThreadSuspend (osThreadId thread_id)
{
#if (INCLUDE\_vTaskSuspend == 1)
    vTaskSuspend(thread_id);
  
  return osOK;
#else
  return osErrorResource;
#endif
}

参数为NULL vTaskSuspend(NULL)是挂起任务自己

osThreadSuspendAll挂起所有任务

osStatus osThreadSuspendAll (void)
{
  vTaskSuspendAll();
  
  return osOK;
}

恢复任务

vTaskResume在任务中恢复任务(将任务从挂起状态恢复到就绪态)
xTaskResumeFromISR在中断中恢复任务封装后是osThreadResume,如下:

osStatus osThreadResume (osThreadId thread_id)
{
#if (INCLUDE\_vTaskSuspend == 1) 
  if(inHandlerMode())
  {
    if (xTaskResumeFromISR(thread_id) == pdTRUE)
    {
      portYIELD\_FROM\_ISR(pdTRUE);
    }
  }
  else
  {
    vTaskResume(thread_id);
  }
  return osOK;
#else
  return osErrorResource;
#endif
}

osThreadResumeAll恢复所有任务

osStatus osThreadResumeAll (void)
{
  if (xTaskResumeAll() == pdTRUE)
    return osOK;
  else
    return osErrorOS;
  
}

任务优先级

uxTaskPriorityGet获取任务优先级 封装后是osThreadGetPriority,还有一个从中断中获取没有写出来,但是通过封装后的函数可以看出来:

osPriority osThreadGetPriority (osThreadId thread_id)
{
#if (INCLUDE\_uxTaskPriorityGet == 1)
  if (inHandlerMode())
  {
    return makeCmsisPriority(uxTaskPriorityGetFromISR(thread_id));  
  }
  else
  {  
    return makeCmsisPriority(uxTaskPriorityGet(thread_id));
  }
#else
  return osPriorityError;
#endif
}

vTaskPrioritySet设置任务优先级 封装后是osThreadSetPriority,如下:

osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority)
{
#if (INCLUDE\_vTaskPrioritySet == 1)
  vTaskPrioritySet(thread_id, makeFreeRtosPriority(priority));
  return osOK;
#else
  return osErrorOS;
#endif
}

延时任务

vTaskDelay相对延时封装后osDelay,如下:

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* Generic Wait Functions \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/
/\*\*
\* @brief Wait for Timeout (Time Delay)
\* @param millisec time delay value
\* @retval status code that indicates the execution status of the function.
\*/
osStatus osDelay (uint32_t millisec)
{
#if INCLUDE\_vTaskDelay
  TickType_t ticks = millisec / portTICK_PERIOD_MS;
  
  vTaskDelay(ticks ? ticks : 1);          /\* Minimum delay = 1 tick \*/
  
  return osOK;
#else
  (void) millisec;
  
  return osErrorResource;
#endif
}

vTaskDelayuntil绝对延时封装后osDelayUntil,如下:

/\*\*
\* @brief Delay a task until a specified time
\* @param PreviousWakeTime Pointer to a variable that holds the time at which the 
\* task was last unblocked. PreviousWakeTime must be initialised with the current time
\* prior to its first use (PreviousWakeTime = osKernelSysTick() )
\* @param millisec time delay value
\* @retval status code that indicates the execution status of the function.
\*/
osStatus osDelayUntil (uint32_t \*PreviousWakeTime, uint32_t millisec)
{
#if INCLUDE\_vTaskDelayUntil
  TickType_t ticks = (millisec / portTICK_PERIOD_MS);
  vTaskDelayUntil((TickType_t \*) PreviousWakeTime, ticks ? ticks : 1);
  
  return osOK;
#else
  (void) millisec;
  (void) PreviousWakeTime;
  
  return osErrorResource;
#endif
}

FreeRTOS 任务源码简析

任务状态

在分析任务源码之前,先得了解一下FreeRTOS中任务的四种基本状态:
运行态,挂起态,阻塞态,就绪态。

在官方文档中:
在这里插入图片描述

任务控制块

FreeRTOS 的每个任务都有一些属性需要存储,把这些属性集合在一起的结构体叫做任务控制块TCB_t (Task control block),源码如下:

/\*
 \* Task control block. A task control block (TCB) is allocated for each task,
 \* and stores task state information, including a pointer to the task's context
 \* (the task's run time environment, including register values)
 \*/
typedef struct tskTaskControlBlock
{
	volatile StackType_t	\*pxTopOfStack;	/\*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. \*/

	#if ( portUSING\_MPU\_WRAPPERS == 1 )
		xMPU_SETTINGS	xMPUSettings;		/\*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. \*/
	#endif

	ListItem_t			xStateListItem;	/\*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). \*/
	ListItem_t			xEventListItem;		/\*< Used to reference a task from an event list. \*/
	UBaseType_t			uxPriority;			/\*< The priority of the task. 0 is the lowest priority. \*/
	StackType_t			\*pxStack;			/\*< Points to the start of the stack. \*/
	char				pcTaskName[ configMAX_TASK_NAME_LEN ];/\*< Descriptive name given to the task when created. Facilitates debugging only. \*/ /\*lint !e971 Unqualified char types are allowed for strings and single characters only. \*/

	#if ( ( portSTACK\_GROWTH > 0 ) || ( configRECORD\_STACK\_HIGH\_ADDRESS == 1 ) )
		StackType_t		\*pxEndOfStack;		/\*< Points to the highest valid address for the stack. \*/
	#endif

	#if ( portCRITICAL\_NESTING\_IN\_TCB == 1 )
		UBaseType_t		uxCriticalNesting;	/\*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. \*/
	#endif

	#if ( configUSE\_TRACE\_FACILITY == 1 )
		UBaseType_t		uxTCBNumber;		/\*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. \*/
		UBaseType_t		uxTaskNumber;		/\*< Stores a number specifically for use by third party trace code. \*/
	#endif

	#if ( configUSE\_MUTEXES == 1 )
		UBaseType_t		uxBasePriority;		/\*< The priority last assigned to the task - used by the priority inheritance mechanism. \*/
		UBaseType_t		uxMutexesHeld;
	#endif

	#if ( configUSE\_APPLICATION\_TASK\_TAG == 1 )
		TaskHookFunction_t pxTaskTag;
	#endif

	#if( configNUM\_THREAD\_LOCAL\_STORAGE\_POINTERS > 0 )
		void			\*pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
	#endif

	#if( configGENERATE\_RUN\_TIME\_STATS == 1 )
		uint32_t		ulRunTimeCounter;	/\*< Stores the amount of time the task has spent in the Running state. \*/
	#endif

	#if ( configUSE\_NEWLIB\_REENTRANT == 1 )
		/\* Allocate a Newlib reent structure that is specific to this task.
 Note Newlib support has been included by popular demand, but is not
 used by the FreeRTOS maintainers themselves. FreeRTOS is not
 responsible for resulting newlib operation. User must be familiar with
 newlib and must provide system-wide implementations of the necessary
 stubs. Be warned that (at the time of writing) the current newlib design
 implements a system-wide malloc() that must be provided with locks. \*/
		struct	_reent xNewLib_reent;
	#endif

	#if( configUSE\_TASK\_NOTIFICATIONS == 1 )
		volatile uint32_t ulNotifiedValue;
		volatile uint8_t ucNotifyState;
	#endif

	/\* See the comments above the definition of
 tskSTATIC\_AND\_DYNAMIC\_ALLOCATION\_POSSIBLE. \*/
	#if( tskSTATIC\_AND\_DYNAMIC\_ALLOCATION\_POSSIBLE != 0 ) /\*lint !e731 Macro has been consolidated for readability reasons. \*/
		uint8_t	ucStaticallyAllocated; 		/\*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. \*/
	#endif

	#if( INCLUDE\_xTaskAbortDelay == 1 )
		uint8_t ucDelayAborted;
	#endif

} tskTCB;

/\* The old tskTCB name is maintained above then typedefed to the new TCB\_t name
below to enable the use of older kernel aware debuggers. \*/
typedef tskTCB TCB_t;

pxTopOfStack任务堆栈栈顶
xStateListItem状态列表项
xEventListItem事件列表项
uxPriority任务优先级
*pxStack任务栈起始地址
pcTaskName[16]任务名称
*pxEndOfStack任务堆栈栈底

状态列表项 :
每个任务都有4种状态,FreeRTOS 使用一种高效的数据结构双向链表保存任务的状态,Linux中也是。

事件列表项 :
信号量,消息队列,软件定时器等事件的管理

任务创建流程分析

任务创建流程如下图所示:
在这里插入图片描述
在分析FreeRTOS 任务创建源码之前,我们得先了解一下栈的不同类型。

栈的四种类型

不同的硬件平台stack的增长方式不同,总的来说是有4种增长方式:

满减栈、满增栈、空减栈、空增栈。

满减栈:栈指针指向最后压入栈的数据,数据入栈时,sp先减一再入栈。

满增栈:栈指针指向最后压入栈的数据,数据入栈时,sp先加一再入栈。

空减栈:栈指针指向下一个将要放入数据的位置,数据入栈时,先入栈sp再减一。

空增栈:栈指针指向下一个将要放入数据的位置,数据入栈时,先入栈sp再加一。

满栈进栈是先移动指针再存;满栈出栈是先出数据再移动指针;

空栈进栈先存再移动指针;空栈出栈先移动指针再取数据。

ARM M3,M4内核使用的是 满减栈。

任务创建源码分析

我们在源码中直接使用注释分析:

#if( configSUPPORT\_DYNAMIC\_ALLOCATION == 1 )

	BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char \* const pcName,		/\*lint !e971 Unqualified char types are allowed for strings and single characters only. \*/
							const configSTACK_DEPTH_TYPE usStackDepth,
							void \* const pvParameters,
							UBaseType_t uxPriority,
							TaskHandle_t \* const pxCreatedTask )
	{
	TCB_t \*pxNewTCB;
	BaseType_t xReturn;

		/\* If the stack grows down then allocate the stack then the TCB so the stack
 does not grow into the TCB. Likewise if the stack grows up then allocate
 the TCB then the stack. 
 根据自己的硬件平台堆栈增长方式来判断使用那一部分编译,
 堆栈增长方式:
 1、满减栈
 2、满增栈
 3、空减栈
 4、空增栈
 #define portSTACK\_GROWTH ( -1 )
 表示满减栈
 \*/
		#if( portSTACK\_GROWTH > 0 )
		{
			/\* Allocate space for the TCB. Where the memory comes from depends on
 the implementation of the port malloc function and whether or not static
 allocation is being used.
 任务栈内存分配
 \*/
			pxNewTCB = ( TCB_t \* ) pvPortMalloc( sizeof( TCB_t ) );

			if( pxNewTCB != NULL )
			{
				/\* Allocate space for the stack used by the task being created.
 The base of the stack memory stored in the TCB so the task can
 be deleted later if required. 
 任务控制块内存分配
 \*/
				pxNewTCB->pxStack = ( StackType_t \* ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) \* sizeof( StackType_t ) ) ); /\*lint !e961 MISRA exception as the casts are only redundant for some ports. \*/

				if( pxNewTCB->pxStack == NULL )
				{
					/\* Could not allocate the stack. Delete the allocated TCB. \*/
					vPortFree( pxNewTCB );
					pxNewTCB = NULL;
				}
			}
		}
		#else 
		/\* portSTACK\_GROWTH \*/
		/\*
 M3,M4中使用的下面这种
 
 #define portSTACK\_TYPE uint32\_t
 typedef portSTACK\_TYPE StackType\_t;
 
 每个内存是4个字节(StackType\_t),所以创建堆栈是按照字来分配的
 默认的是128 x 4 = 512字节;
 
 \*/
		{
		StackType_t \*pxStack;

			/\* Allocate space for the stack used by the task being created. \*/
			pxStack = ( StackType_t \* ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) \* sizeof( StackType_t ) ) ); /\*lint !e961 MISRA exception as the casts are only redundant for some ports. \*/

			if( pxStack != NULL )
			{
				/\* 
 Allocate space for the TCB.
 根据TCB的大小还得分配空间 
 \*/
				pxNewTCB = ( TCB_t \* ) pvPortMalloc( sizeof( TCB_t ) ); /\*lint !e961 MISRA exception as the casts are only redundant for some paths. \*/

				if( pxNewTCB != NULL )
				{
					/\* Store the stack location in the TCB.
 赋值栈地址
 \*/
					pxNewTCB->pxStack = pxStack;
				}
				else
				{
					/\* The stack cannot be used as the TCB was not created. Free
 it again. 
 释放栈空间
 \*/
					vPortFree( pxStack );
				}
			}
			else
			{
				pxNewTCB = NULL;
			}
		}
		#endif /\* portSTACK\_GROWTH \*/

		if( pxNewTCB != NULL )
		{
			#if( tskSTATIC\_AND\_DYNAMIC\_ALLOCATION\_POSSIBLE != 0 ) /\*lint !e731 Macro has been consolidated for readability reasons. \*/
			{
				/\* Tasks can be created statically or dynamically, so note this
 task was created dynamically in case it is later deleted. \*/
				pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
			}
			#endif /\* configSUPPORT\_STATIC\_ALLOCATION \*/
			/\*
 新建任务初始化
 初始化任务控制块
 初始化任务堆栈
 \*/
			prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
			/\*
 把任务添加到就绪列表中
 \*/
			prvAddNewTaskToReadyList( pxNewTCB );
			xReturn = pdPASS;
		}
		else
		{
			xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
		}

		return xReturn;
	}

#endif /\* configSUPPORT\_DYNAMIC\_ALLOCATION \*/

任务删除流程分析

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ULL );
/*
把任务添加到就绪列表中
*/
prvAddNewTaskToReadyList( pxNewTCB );
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}

	return xReturn;
}

#endif /* configSUPPORT_DYNAMIC_ALLOCATION */


### 任务删除流程分析




**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
[外链图片转存中...(img-DcdmqgO2-1715874477878)]
[外链图片转存中...(img-qQTYBtab-1715874477878)]

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值