freeRTOS(1)任务及有关API

任务状态

在这里插入图片描述

  • 处于运行态的任务就是当前正在使用处理器的任务。
  • 就绪态的任务是指已经准备就绪的任务(没有被阻塞或挂起),处于就绪态的任务还没有被运行,是因为有一个同优先级更高的任务在运行中
  • 如果一任务正在等待某个外部事件就处于阻塞态,处于阻塞态的任务有一个超时时间,超出任务时间就会退出阻塞态,即使等待的时间没有到来。

任务控制块

freeRTOS 的每个任务都有一些属性需要存储,把这些属性集合到一起用一个结构体来表示的就成为任务控制块。

任务堆栈

freeRTOS 的任务堆栈是在进行任务切换的时候,将当前任务现场(寄存器值等)保存在此任务的任务堆栈里,等到此任务下次运行的时候会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着上次中断的地方开始运行。
创建任务的时候要给任务指定堆栈,如果使用函数的动态方法创建任务,那么任务堆栈就会由程序自动创建;如果使用静态方法创建任务,那么需要程序员自定义任务堆栈。

任务创建与删除

xTaxkCreate()

使用动态的方法创建一个任务
任务需要 RAM 来保存与任务有关的状态信息(任务控制块),任务也需要一定的 RAM 来作为任务堆栈。如果使用函数 xTaskCreate()来创建任务的话那么这些所需的 RAM 就会自动的从 FreeRTOS 的堆中分配, 因此必须提供内存管理文件。

  • pxTaskCode: 任务函数。
  • pcName: 任务名字,一般用于追踪和调试,任务名字长度不能超过configMAX_TASK_NAME_LEN。
  • usStackDepth: 任务堆栈大小,注意实际申请到的堆栈是 usStackDepth 的 4 倍。其中空闲任务的任务堆栈大小为 configMINIMAL_STACK_SIZE。
  • pvParameters:传递给任务函数的参数。
  • uxPriotiry: 任务优先级,范围 0~ configMAX_PRIORITIES-1。
  • pxCreatedTask:任务句柄,任务创建成功以后会返回此任务的任务句柄, 这个句柄其实就是任务的任务堆栈。 此参数就用来保存这个任务句柄。其他 API 函数可能会使用到这个句柄。
  • 返回值:
    pdPASS: 任务创建成功。
    errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: 任务创建失败,因为堆内存不足!
BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char * const pcName,
							const uint16_t usStackDepth,
							void * const pvParameters,
							UBaseType_t uxPriority,
							TaskHandle_t * const pxCreatedTask ) 
{
	TCB_t *pxNewTCB;
	BaseType_t xReturn;
	#if( portSTACK_GROWTH > 0 )
	{}
	#else 
	{
		StackType_t *pxStack;
		/* 给堆栈申请内存 */	
		pxStack = (StackType_t *) pvPortMalloc((((size_t)usStackDepth)*sizeof(StackType_t))); 
		if( pxStack != NULL ){
			/* 堆栈内存申请成功,给任务控制块申请内存 */
			pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); 
			if( pxNewTCB != NULL ){
				/* 任务块内存申请成功就初始化任务堆栈字段为前面申请到的堆栈内存,失败的话就释放前面申请成功的内存 */
				pxNewTCB->pxStack = pxStack;
			} else { vPortFree( pxStack ); }
		}else{ pxNewTCB = NULL;}
		}
	#endif
	if( pxNewTCB != NULL ){
		/* 初始化任务 */
		prvInitialiseNewTask( pxTaskCode, pcName, (uint32_t) usStackDepth, pvParameters, uxPriority, 	
								pxCreatedTask, pxNewTCB, NULL );
		/* 将新建任务添加到就绪列表中 */
		prvAddNewTaskToReadyList( pxNewTCB );
		xReturn = pdPASS;
	} else {
			xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
	}
	return xReturn;
}

接着分析一下任务初始化函数

static void prvInitialiseNewTask( 	TaskFunction_t pxTaskCode,
									const char * const pcName,
									const uint32_t ulStackDepth,
									void * const pvParameters,
									UBaseType_t uxPriority,
									TaskHandle_t * const pxCreatedTask,
									TCB_t *pxNewTCB,
									const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
	StackType_t *pxTopOfStack;
	UBaseType_t x;

	/* 如果使能了堆栈溢出检测功能或者追踪功能的话就使用一个定值tskSTACK_FILL_BYTE(0xa5U) 来填充任务堆栈 */
	#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( 	\
			INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) 
	{
		( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
	}
	#endif

	/* 计算堆栈栈顶 pxTopOfStack,后面初始化堆栈的时候需要用到 */
	#if( portSTACK_GROWTH < 0 )
	{
		pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
		pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) &  \
					   ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); 
		configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) \ 
							portBYTE_ALIGNMENT_MASK ) == 0UL ) );
	}
	#else 
	{
		pxTopOfStack = pxNewTCB->pxStack;
		configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
		pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
	}
	#endif 

	/* 保存任务的任务名 */
	for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ){
		pxNewTCB->pcTaskName[ x ] = pcName[ x ];
		if( pcName[ x ] == 0x00 ){
			break;
		} else { mtCOVERAGE_TEST_MARKER(); }
	}

	/* 任务名数组添加字符串结束符’\0’ */
	pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

	/* 判断任务优先级是否合法,如果设置的任务优先级大于 configMAX_PRIORITIES,则将优先级修改为 configMAX_PRIORITIES-1 */
	if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ){
		uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
	} else { mtCOVERAGE_TEST_MARKER(); }

	pxNewTCB->uxPriority = uxPriority; /* 初始化任务控制块的优先级字段 uxPriority */
	#if ( configUSE_MUTEXES == 1 )
	{   /* 使能了互斥信号量功能,需要初始化相应的字段 */
		pxNewTCB->uxBasePriority = uxPriority;
		pxNewTCB->uxMutexesHeld = 0;
	}
	#endif
	
	/* 初始化列表项 xStateListItem 和 xEventListItem */
	vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
	vListInitialiseItem( &( pxNewTCB->xEventListItem ) );

	/* 设置列表项 xStateListItem 和 xEventListItem 属于当前任务的任务控制块 */
	/* 设置列表项xEventListItem 的字段 xItemValue 为configMAX_PRIORITIES- uxPriority */
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
	listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
	listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB );

	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
	{
		pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
	}
	#endif /* portCRITICAL_NESTING_IN_TCB */

	#if ( configUSE_APPLICATION_TASK_TAG == 1 )
	{
		pxNewTCB->pxTaskTag = NULL;
	}
	#endif /* configUSE_APPLICATION_TASK_TAG */

	#if ( configGENERATE_RUN_TIME_STATS == 1 )
	{
		pxNewTCB->ulRunTimeCounter = 0UL;
	}
	#endif /* configGENERATE_RUN_TIME_STATS */

	#if ( portUSING_MPU_WRAPPERS == 1 )
	{
		vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth );
	}
	#else
	{
		/* Avoid compiler warning about unreferenced parameter. */
		( void ) xRegions;
	}
	#endif

	#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
	{
		for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
		{
			pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL;
		}
	}
	#endif

	#if ( configUSE_TASK_NOTIFICATIONS == 1 )
	{
		pxNewTCB->ulNotifiedValue = 0;
		pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
	}
	#endif

	#if ( configUSE_NEWLIB_REENTRANT == 1 )
	{
		/* Initialise this task's Newlib reent structure. */
		_REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) );
	}
	#endif

	#if( INCLUDE_xTaskAbortDelay == 1 )
	{
		pxNewTCB->ucDelayAborted = pdFALSE;
	}
	#endif

	/* 调用函数 pxPortInitialiseStack()初始化任务堆栈 */
	#if( portUSING_MPU_WRAPPERS == 1 )
	{
		pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );
	}
	#else /* portUSING_MPU_WRAPPERS */
	{
		pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
	}
	#endif /* portUSING_MPU_WRAPPERS */

	if( ( void * ) pxCreatedTask != NULL ){
		/* 生成任务句柄,返回给参数 pxCreatedTask,从这里可以看出任务句柄其实就是任务控制块 */
		*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
	}
	else{ mtCOVERAGE_TEST_MARKER();}
}

接着看一下任务堆栈初始化函数

StackType_t *pxPortInitialiseStack( StackType_t * pxTopOfStack,
									TaskFunction_t pxCode,
									void * pvParameters )
{
	pxTopOfStack--;
	*pxTopOfStack = portINITIAL_XPSR; 
	pxTopOfStack--;
	*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; 
	pxTopOfStack--;
	*pxTopOfStack = ( StackType_t ) prvTaskExitError; 
	pxTopOfStack -= 5; 
	*pxTopOfStack = ( StackType_t ) pvParameters;
	pxTopOfStack -= 8;
	return pxTopOfStack;
}

(1)、寄存器 xPSR 值为 portINITIAL_XPSR,其值为 0x01000000。 xPSR 是 Cortex-M 的一个内核寄存器,叫做程序状态寄存器, 0x01000000 表示这个寄存器的 bit24 为 1, 表示处于 Thumb 状态,即使用的 Thumb 指令。
(2)、 寄存器 PC 初始化为任务函数 pxCode。
(3)、 寄存器 LR 初始化为函数 prvTaskExitError。
(4)、 跳过 4 个寄存器, R12, R3, R2, R1,这四个寄存器不初始化。
(5)、寄存器 R0 初始化为 pvParameters, 一般情况下,函数调用会将 R0~R3 作为输入参数,R0 也可用作返回结果,如果返回
值为 64 位,则 R1 也会用于返回结果,这里的 pvParameters 是作为任务函数的参数, 保存在寄存器 R0 中。
(6)、跳过 8 个寄存器, R11、 R10、 R8、 R7、 R6、 R5、 R4

xTaskCreateStatic()

此函数和 xTaskCreate()的功能相同, 也是用来创建任务的,但是使用此函数创建的任务所需的RAM需要用用户来提供。

TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
								const char * const pcName,
								const uint32_t ulStackDepth,
								void * const pvParameters,
								UBaseType_t uxPriority,
								StackType_t * const puxStackBuffer,
								StaticTask_t * const pxTaskBuffer )

  • pxTaskCode: 任务函数。
  • pcName: 任务名字,一般用于追踪和调试,任务名字长度不能超过configMAX_TASK_NAME_LEN。
  • usStackDepth: 任务堆栈大小,由于本函数是静态方法创建任务,所以任务堆栈由用户给出,一般是个数组,此参数就是这个数组的大小。
  • pvParameters: 传递给任务函数的参数。
  • uxPriotiry: 任务优先级,范围 0~ configMAX_PRIORITIES-1。
  • puxStackBuffer: 任务堆栈,一般为数组,数组类型要为 StackType_t 类型。
  • pxTaskBuffer: 任务控制块。
  • 返回值:
    NULL: 任务创建失败, puxStackBuffer 或 pxTaskBuffer 为 NULL 的时候会导致这个错误的发生。
    其他值:任务创建成功,返回任务的任务句柄。

xTaskCreateRestricted()

此函数也是用来创建任务的, 只不过此函数要求所使用的 MCU 有 MPU(内存保护单元),用此函数创建的任务会受到 MPU 的保护。 其他的功能和函数 xTaxkCreate()一样。

BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition,
								  TaskHandle_t * pxCreatedTask )
  • pxTaskDefinition: 指向一个结构体 TaskParameters_t,这个结构体描述了任务的任务函数、堆栈大小、优先级等。
  • pxCreatedTask: 任务句柄。
  • 返回值:
    pdPASS: 任务创建成功。
    其他值: 任务未创建成功, 很有可能是因为 FreeRTOS 的堆太小了。

vTaskDelete()

删除一个用函数 xTaskCreate()或者 xTaskCreateStatic()创建的任务,被删除了的任务不再存在,也就是说再也不会进入运行态。 任务被删除以后就不能再使用此任务的句柄!如果此任务是使用动态方法创建的,也就是使用函数 xTaskCreate()创建的,那么在此任务被删除以后此任务之前申请的堆栈和控制块内存会在空闲任务中被释放掉,因此当调用函数 vTaskDelete()删除任务以后必须给空闲任务一定的运行时间。只有那些由内核分配给任务的内存才会在任务被删除以后自动的释放掉,用户分配给任务的内存需要用户自行释放掉,比如某个任务中用户调用函数 pvPortMalloc()分配了 500 字节的内存,那么在此任务被删除以后用户也必须调用函数 vPortFree()将这 500 字节的内存释放掉,否则会导致内存泄露。

  • xTaskToDelete:要删除的任务的任务句柄。
  • 返回值:无
void vTaskDelete( TaskHandle_t xTaskToDelete )
	{
	TCB_t *pxTCB;
	taskENTER_CRITICAL();
	{
		/* 调用函数 prvGetTCBFromHandle() 获取要删除任务的任务控制块,参数为任务句柄。如果参数为当前正在执行的任务句柄那么返回值就为 NULL */
		pxTCB = prvGetTCBFromHandle( xTaskToDelete );
		
		/* 将任务从任务就绪列表中删除 */
		if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) {
			taskRESET_READY_PRIORITY( pxTCB->uxPriority );
		} else { mtCOVERAGE_TEST_MARKER(); }
		
		/* 查看任务是否正在等待某个事件(如信号量、队列等),因为如果任务等待某个事件的话这个任务会被放到相应的列表中,这里需要将其从相应的列表中删除掉 */
		if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) {
			( void ) uxListRemove( &( pxTCB->xEventListItem ) );
		} else { mtCOVERAGE_TEST_MARKER(); }
		
		uxTaskNumber++;
		
		/* 要删除的任务是正在运行的任务 */
		if( pxTCB == pxCurrentTCB )
		{
		/* 需要等任务运行结束进行内存释放,先将任务放到 xTasksWaitingTermination 列表中 */
			vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );
			/* uxDeletedTasksWaitingCleanUp 是一个全局变量,用来记录有多少个任务需要释放内存 */
			++uxDeletedTasksWaitingCleanUp;
			portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
		} else {
		/* 删除的是别的任务,变量 uxCurrentNumberOfTasks 减一,也就是当前任务数减一 */
			--uxCurrentNumberOfTasks;
			prvDeleteTCB( pxTCB ); /* 因为是删除别的任务,所以可以直接调用函数 prvDeleteTCB()删除任务控制块 */
			/* 重新计算一下还要多长时间执行下一个任务,也就是下一个任务的解锁时间,防止有任务的解锁时间参考了刚刚被删除的那个任务 */
			prvResetNextTaskUnblockTime(); 
		}
		traceTASK_DELETE( pxTCB );
		}
		taskEXIT_CRITICAL();

		if( xSchedulerRunning != pdFALSE ) {
			if( pxTCB == pxCurrentTCB ) {
				configASSERT( uxSchedulerSuspended == 0 );
				portYIELD_WITHIN_API(); /* 如果删除的是正在运行的任务那么删除完以后肯定需要强制进行一次任务切换 */
			}else{ mtCOVERAGE_TEST_MARKER(); }
		}
	}

任务挂起与恢复

vTaskSuspend()

此函数用于将某个任务设置为挂起态, 进入挂起态的任务永远都不会进入运行态。退出挂起态的唯一方法就是调用任务恢复函数 vTaskResume()或 xTaskResumeFromISR()。

void vTaskSuspend( TaskHandle_t xTaskToSuspend )
	{
	TCB_t *pxTCB;

	taskENTER_CRITICAL();
	{
		/* 通过函数 prvGetTCBFromHandle()获取要删除任务的任务控制块 */
		pxTCB = prvGetTCBFromHandle( xTaskToSuspend );
		traceTASK_SUSPEND( pxTCB );
		
		// 将任务从就绪或者延时列表中删除,并且将任务放到挂起列表中
		if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){
			taskRESET_READY_PRIORITY( pxTCB->uxPriority );
		}else{ mtCOVERAGE_TEST_MARKER(); }
		
		/* 查看任务是否正在等待某个事件(如信号量、队列等),如果任务还在等待某个事件的话就将其从相应的事件列表中删除 */
		if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) {
			( void ) uxListRemove( &( pxTCB->xEventListItem ) );
		} else { mtCOVERAGE_TEST_MARKER(); }
		
		/* 将任务添加到挂起任务列表尾,挂起任务列表为 xSuspendedTaskList,所有被挂起的任务都会被放到这个列表中 */
		vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
	}
	taskEXIT_CRITICAL();

	if( xSchedulerRunning != pdFALSE ){	
		taskENTER_CRITICAL();
		{
	/* 重新计算一下还要多长时间执行下一个任务,也就是下一个任务的解锁时间。防止有任务的解锁时间参考了刚刚被挂起的那个任务。 */
			prvResetNextTaskUnblockTime();
		}
		taskEXIT_CRITICAL();
	} else { mtCOVERAGE_TEST_MARKER(); }

	if( pxTCB == pxCurrentTCB )
	{
		if( xSchedulerRunning != pdFALSE )
		{
      /* 如果刚刚挂起的任务是正在运行的任务,并且任务调度器运行正常,那么这里就需要调用函数 portYIELD_WITHIN_API()
         强制进行一次任务切换。 */
			configASSERT( uxSchedulerSuspended == 0 );
			portYIELD_WITHIN_API();
		} else {
		/* 调用函数 listCURRENT_LIST_LENGTH()判断一下系统中所有的任务是不是都被挂起了,也就是查看列表 xSuspendedTaskList
		 的长度是不是等于 uxCurrentNumberOfTasks。如果等于的话就说明系统中所有的任务都被挂起了(实际上不存在这种情况,因为最
		 少都有一个空闲任务是可以运行的 */
			if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) {
			/* 如果所有任务都被挂起的话 pxCurrentTCB 就只能等于 NULL 了,这样当有新任务被创建的时候 pxCurrentTCB 就可以指向这个新任务 */
				pxCurrentTCB = NULL;
			} else { vTaskSwitchContext(); }/* 有其他的没有被挂起的任务,调用 vTaskSwitchContext()获取下一个要运行的任务 */
		}
	} else { mtCOVERAGE_TEST_MARKER(); }
}

  • xTaskToSuspend: 要挂起的任务的任务句柄,创建任务的时候会为每个任务分配一个任务句柄。如果使用函数 xTaskCreate()创建任务的话那么函数的参数pxCreatedTask 就是此任务的任务句柄,如果使用函数 xTaskCreateStatic()创建任务的话那么函数的返回值就是此任务的任务句柄。也可以通过函数 xTaskGetHandle()来根据任务名字来获取某个任务的任务句柄。如果参数为 NULL 的话表示挂起任务自己。
  • 返回值:无

vTaskResume()

将一个任务从挂起态恢复到就绪态, 只有通过函数 vTaskSuspend()设置为挂起态的任务才可以使用 vTaskRexume()恢复!

void vTaskResume( TaskHandle_t xTaskToResume )
{
	TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;
	configASSERT( xTaskToResume );

	if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ){
		taskENTER_CRITICAL();
		{
			if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ){
			/* 调用函数 prvTaskIsTaskSuspended()判断要恢复的任务之前是否已经被挂起了,恢复的肯定是被挂起的任务,没有挂起就不用恢复。 */
				traceTASK_RESUME( pxTCB );
				/* 首先将要恢复的任务从原来的列表中删除,任务被挂起以后都会放到任务挂起列表 xSuspendedTaskList 中 */
				( void ) uxListRemove(  &( pxTCB->xStateListItem ) );
				/* 将要恢复的任务添加到就绪任务列表中 */
				prvAddTaskToReadyList( pxTCB );

				if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) {
				/* 要恢复的任务优先级高于当前正在运行的任务优先级 */
				/* 调用 taskYIELD_IF_USING_PREEMPTION() 来完成一次任务切换 */
					taskYIELD_IF_USING_PREEMPTION();
				} else { mtCOVERAGE_TEST_MARKER(); }
			} else { mtCOVERAGE_TEST_MARKER(); }
		}
		taskEXIT_CRITICAL();
	} else { mtCOVERAGE_TEST_MARKER(); }
}
  • xTaskToResume: 要恢复的任务的任务句柄。
  • 返回值:无

xTaskResumeFromISR()

此函数是 vTaskResume()的中断版本,用于在中断服务函数中恢复一个任务。

BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume)
  • xTaskToResume: 要恢复的任务的任务句柄。
  • 返回值:
    pdTRUE: 恢复运行的任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务), 这意味着在退出中断服务函数以后必须进行一次上下文切换。
    pdFALSE: 恢复运行的任务的任务优先级低于当前正在运行的任务(被中断打断的
    任务),这意味着在退出中断服务函数的以后不需要进行上下文切换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值