【ESP32+freeRTOS学习笔记-(三)任务】

news2024/12/26 23:42:04

目录

  • 1、任务相关基本概念
    • 1.1 任务函数原型
    • 1.2 任务句柄TaskHandle_t 及任务控制块TCB_t
    • 1.3 任务状态
    • 1.4 优先级(Task Priorities)
  • 2 创建任务
    • 2.1 xTaskCreate
    • 2.2 xTaskCreateStatic()
  • 3 延迟函数使任务进入阻塞状态
    • vTaskDelay()函数
    • vTaskDelayUntil()函数
  • 4 优先级操作函数
    • 4.1 vTaskPrioritySet() 函数
    • 4.2 uxTaskPriorityGet() 函数
  • 5 任务删除函数
  • 6 总结

1、任务相关基本概念

1.1 任务函数原型

在上一章里(原文链接),已经把任务的定义说明了

FreeRTOS驱动的执行一定功能的对象叫任务”(task),这些任务就是由C语言编写的一段代码或叫函数。这段函数一般具有一个无限循环,不能带有return语句。因为如果return了,那么任务其实就返回了,结束了。该任务也消亡了。任务有很多相关的属性,其中比较重要的是任务的状态(就绪态ready , 运行态running , 阻塞态blocked,挂起态suspend)

这只是理论的描述,具体到操作上,任务是一个函数。该函数的原型为

void ATaskFunction( void * vParameters );

FreeRTOS里给这个原理定义了一个类型TaskFunction_t ,定义如下:
在这里插入图片描述

也就是所有做为任务的函数必须定义成如上格式。其无返回值,函数名自取,参数为void*一个无类型指针。

实例:任务函数的实例:

void  myTask(void * p){
	char * 	str  =  "myTask is runnint.";
	while(1){
		printf(str);
	}
	vTaskDelete(NULL);
}

1.2 任务句柄TaskHandle_t 及任务控制块TCB_t

该句柄的定义链如下:
首先在task.h中定义了TaskHandle_t
在这里插入图片描述
这里可以看出实际TaskHandle_t是“任务控制块”这个结构体类型指针的别名。原文对任务控制块的的描述如下:

任务控制块。为每个任务分配任务控制块(TCB),并存储任务状态信息,包括指向任务上下文的指针(任务的运行时环境,包括寄存器值)

任务控制块的原始定义在task.h头文件中:

typedef struct tskTaskControlBlock
{
    /* 栈顶<元素>指针,注意与 pxEndOfStack 的区别。 必须是结构体的第一个成员. */
    volatile StackType_t * pxTopOfStack; 

    /* MPU 模块设置,必须是结构体的第二个成员 */
    #if ( portUSING_MPU_WRAPPERS == 1 )
        xMPU_SETTINGS xMPUSettings;
    #endif

    ListItem_t xStateListItem;                  /* 列表项指示任务当前的状态 (Ready, Blocked, Suspended ). */
    ListItem_t xEventListItem;                  /* 用于在事件列表中. */
    UBaseType_t uxPriority;                     /* 任务优先级(0为优先级最低). */
    StackType_t * pxStack;                      /* 栈底指针,指向栈开始处. */
    char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名 */

    /* 如果栈向上生长,则定义栈顶指针 */
    #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
        StackType_t * pxEndOfStack; 
    #endif

    #if ( portCRITICAL_NESTING_IN_TCB == 1 )
        UBaseType_t uxCriticalNesting; /* 保存临界区的栈深度. 在函数vTaskEnterCritical() 和 vTaskExitCritical()中会维护这个值*/
    #endif

    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxTCBNumber;  /*TCB的编号,每次增加一个task,TCB的编号就会增加. */
        UBaseType_t uxTaskNumber; /*task的编号,其值和uxTCBNumber相等,可用于调试追踪. */
    #endif

    #if ( configUSE_MUTEXES == 1 )
        UBaseType_t uxBasePriority; /*上一次分配给该任务的优先级,用于优先级继承机制. */
        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

    /* task 运行的总时间 */
    #if ( configGENERATE_RUN_TIME_STATS == 1 )
        uint32_t ulRunTimeCounter; 
    #endif

    /*  See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html for additional information. */
    #if ( configUSE_NEWLIB_REENTRANT == 1 )
        struct  _reent xNewLib_reent;
    #endif

    /* 任务通知相关变量 */
    #if ( configUSE_TASK_NOTIFICATIONS == 1 )
        volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    #endif

    /* FreeRTOS.h 中有关于 tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE 的详细注释。主要的作用是决定task创建的资源是动态还是静态分配内存. */
    #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) 
        uint8_t ucStaticallyAllocated; /* 标记stack 和 TCB分配方式,在删除任务时,根据此标志来对应释放内存 */
    #endif

    /* delay 终止标志 */
    #if ( INCLUDE_xTaskAbortDelay == 1 )
        uint8_t ucDelayAborted;
    #endif

    /* error number*/
    #if ( configUSE_POSIX_ERRNO == 1 )
        int iTaskErrno;
    #endif
} tskTCB;     

旧的tskTCB名称保持在上面,然后再次定义为下面的新TCB_t名称,以启用旧的内核感知调试器。
在这里插入图片描述

以上可以清楚的知道,任务控制块也好,任务句柄也好,其实就是这么一个数据结构,内部存储了与该任务运行相关的所有必要的各种环境数据,状态数据,栈数据,资源数据,调度相关数据等。与任务一一对应起来。一个任务控制块对应一个具体的任务。FreeRTOS系统中所有对任务的操作,调度,控制,管理等行为都必须通过任务控制块来完成。


1.3 任务状态

CPU对FreeRTOS来说就是一个资源,一个用于执行任务的资源。因此当一个CPU上要执行多个任务时,必然就要解决任务的切换。这个时间段(比如1个毫秒)里要执行任务A,下一个时间段要执行任务B,执行任务B时任务A就不能工作了。因此对FreeRTOS从控制的角度而言,每个时刻,任务A与任务B就会处理不同的状态。比如运行态与非运行态。当然,FreeRTOS面对的任务运行的状态还会受到任务本身实际情况的影响,比如,有的任务因为某些资源没就绪,就会停在那里等待,某些任务可能在一个特定条件下不需要运行了,会处理持起状态而不参与到系统的调度中从而节约CPU时间。为能全面的反应当下任务的状态,同时也要精确控制,FreeRTOS对任务定义了以下四种状态:

运行态runnint :正在占用CPU时间的任务。
就绪态ready:处于未运行状态但未被阻止或挂起的任务称为处于就绪状态。它们能够运行,因此“准备”运行,但当前未处于运行状态。
阻塞态blocked :等待事件的任务被称为处于“阻塞”状态。任务进入阻塞状态等待两种不同类型的事件:一个是时间(时间相关)事件,例如,一个任务可能会用xTaskDelay()函数使自已进入 Blocked 状态以等待 10 毫秒通过。另一个是同步事件——事件源自另一个任务或中断。例如,任务可能进入阻塞状态以等待数据到达队列。同步事件涵盖了广泛的事件类型。
暂停态suspended:处于暂停(或叫挂 起)状态的任务对调度程序不可用,节约了系统时间。进入Suspended状态的唯一方法是调用vTaskSuspend()API函数,离开的唯一的方法是调用vTaskResume()或xTaskResumeFromISR()API功能。

任务状态在实际调度时,是以链表的形态存在的。FreeRTOS会为每个状态维护一个对应状态链表,当某个任务处于什么态时,他的TCB就会被调入对应的状态链表中。如,一个任务现在处在阻塞态,那它的TCB就会被写入阻塞状态链表。因此,一个任务在整个生命周期中所处的状态会随着程序的运行而不断被挂入不同的状态链表,下面是状态循环图:
在这里插入图片描述

1.4 优先级(Task Priorities)

任务优先级用于确定任务被调度时的优先顺序。因此在任务创建时,开发者必须以参数的形式指定这个任务的优先级,优先级也可以在任务执行过程中被动态的改变。
FreeRTOS用宏configMAX_PRIORITIES 来控制可以设置的最大优先级。这个宏在 FreeRTOSConfig.h中进行设置。

FreeRTOS调度器确保处于就绪或正在运行状态的任务总是优先于同样处于就绪状态的较低优先级的任务而获得处理器(CPU)时间。换句话说,处于运行状态的任务始终是能够运行的最高优先级任务。

任何数量的任务都可以共享相同的优先级。如果未定义configUSE_TIME_SLICING,或者如果configUSE_TIME_SLICING设置为1,则同等优先级的就绪状态任务将使用时间分段循环调度方案共享可用的处理时间。

2 创建任务

FreeRTOS提供的任务创建函数通常有两个,一个是xTaskCreate(),另一个是xTaskCreateStatic()。这两个函数是相对通用的函数,但对于esp32这种双核cpu的平台。ESP-IDF提供了两个替代函数。下面一一解释。

2.1 xTaskCreate

函数的声明

#include “FreeRTOS.h”
#include “task.h”
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, 
						 const char * const pcName, 
 						 uint16_t usStackDepth, 
 						 void *pvParameters, 
 						 UBaseType_t uxPriority, 
 						 TaskHandle_t *pxCreatedTask );

参数

参数注释
TaskFunction_t pvTaskCode这个参数就是你要创建的任务的原型函数指针。在上面1.1节里有详细描述。
const char * pcName一个任务的描述名称。这是主要主用是使调试更方便,但也可被用于调用xTaskGetHandle()函数来获得任务句柄。应用程序预定义常量configMAX_TASK_NAME_LEN定义了名称中包括空中止符的字母的最大长度
uint16_t usStackDepth每个任务都有自已的唯一的栈,当任务被创建时由内核分配给任务。usStackDepth的值告诉内核分配多大的栈。这个值指定的栈能容下的字的数量,不是字节数。例如,在一个4字节的栈宽度的平台,如果usStackDepth被设为100,那么栈的空间将被分配为100*4字节。栈的深度乘以栈的宽度不能大于变量size_t类型的最大取值。用于空闲任务的栈的尺寸由程序预定义常量configMINIMAL_STACK_SIZE来定义。在这个由所选择的MCU所提供的demo中,被分配给这个常量的值,是在这个平台中被推荐给任务的最小值。如果你的任务使用很大的栈空间,那么你必须指定更大的值。
void * pvParameters任务函数接受一个指向void的指针参数。被分配给pvParameters的值将传递给任务。这个参数有一个指向void的类型,允许任务参数更有效率,并间接的通过casting,接收任意类型参数。举例,通过在任务创建这个点时把整形casting到void指针,则整形能被传递进任务函数
UBaseType_t uxPriority定义任务执行的优先级。优先级能被分配为0(最低优先级)至configMAX_PRIORITIES-1(最高优先级)。configMAX_PRIORITIES是一个用户定义的常量。如果configUSE_PORT_OPTIMISED_TASK_SELECTION被设为0那么没有可用的优先级上限(以外的所使用的数据类型和微控制器中可用的RAM的限制)但是建议使用最低所需要的优先级,以避免浪费内存。传递一个uxPriority值大于(configMAX_PRIORITIES - 1)将导致分配给任务的优先级被默默地设置为最大的合法的值。值越小,优先级也越小。
TaskHandle_t * pxCreatedTaskpxCreatedTask能被和于传递出一个被创建的任务的句柄。然后,该句柄可用于在API调用中引用该任务例如,修改任务优先级或删除任务。如果你的应用程序没有使用任务句柄,那么pxCreatedTask可被设置为NULL

有两种可能的返回值

返回值解释
pdPASS这表示任务已成功创建。
pdFAIL这表明任务尚未创建,因为FreeRTOS没有足够的堆内存来分配足够的RAM来保存任务数据结构和堆栈。

注意:configSUPPORT_DYNAMIC_ALLOCATION必须设置为1,才能使用这个函数

实列

typedef struct A_STRUCT
{
 	char cStructMember1;
 	char cStructMember2;
} xStruct;         //定义了这个结构体类型,传递胡数给任务。 
 
xStruct xParameter = { 1, 2 }; //定义具体的结构体变量,作为给任务传递的参数
 
void vTaskCode( void * pvParameters )    //定义任务的原型函数,这个函数名将做为xTaskCreate()的第一个参数,详见下面代码
{
 	xStruct *pxParameters;   //定义的这个结构体指针,用于接收参数传入的指针
 	pxParameters = ( xStruct * ) pvParameters; //把参数的void 类型指针强转成xStruct类型指针
 
 	if( pxParameters->cStructMember1 != 1 )
 	{
 		/* 这里进行一些处理,比如打印传入的参数(略). */
 	} 
	for( ;; )  //进入无限循环,进行任务的具体处理
 	{
 		/* Task code goes here. */
 	}
} 


void main( void )
{
	TaskHandle_t xHandle;
 
 	/* 接下来创建任务. */
 	xTaskCreate( 
 		vTaskCode,     /* 指向刚才定义的函数的指针,其实就是函数名,做为这个任务的具体实现。 */
 		"Demo task", 	/* 给任务起的文本名*/
 		STACK_SIZE, 	/* 定义的栈的大小,以字为单位(4字节) 比如1024个字*/
 		(void*) &xParameter,    /* 给任务的参数的引用,这里用void*强转是为了防止编译器产生烦人的告警 */
 		TASK_PRIORITY, 			/* 指定创建的任务的优先级. */
 		&xHandle 	/* 创建的任务的句柄会存入xHandle*/
 	) 
 	xTaskStartSchadule();  //启动调度器
}

2.2 xTaskCreateStatic()

每个任务都需要RAM,用于保存任务状态(任务控制块或TCB),并由任务用作其堆栈。如果使用xTaskCreate()创建任务,则所需的RAM将自动从FreeRTOS堆中分配。如果使用xTaskCreateStatic()创建任务,则RAM由应用程序编写器提供,这将导致两个额外的函数参数,但允许在编译时静态分配RAM。
新创建的任务最初处于就绪状态,但如果没有更高优先级的任务可以运行,则会立即变为运行状态任务。
可以在调度程序启动之前和之后创建任务。

#include “FreeRTOS.h”
#include “task.h”
TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode, 
 								const char * const pcName, 
 								uint32_t ulStackDepth, 
 								void *pvParameters, 
 								UBaseType_t uxPriority, 
 								StackType_t * const puxStackBuffer,
 								StaticTask_t * const pxTaskBuffer );
参数注释
StackType_t * const puxStackBuffer必须指向至少具有ulStackDepth索引的StackType_t变量数组。数组将用作创建的任务的堆栈,因此必须是持久的(不在函数创建的堆栈框架中声明,也不在应用程序执行时可以合法覆盖的任何其他内存中声明)。
StaticTask_t * const pxTaskBuffer必须指向StaticTask_t类型的变量。该变量将用于保存创建的任务的数据结构(TCB),因此它必须是持久的(不在函数创建的堆栈帧中声明,也不在应用程序执行时可以合法覆盖的任何其他内存中声明)。

返回值

返回值解释
NULL无法创建任务,因为puxStackBuffer或pxTaskBuffer为NULL。
任何其它值如果返回非NULL值,则创建了任务,返回的值是创建的任务的句柄。

注意:在FreeRTOSConfig中,configSUPPORT_STATIC_ALLOCATION必须设置为1。h表示此功能可用。

 
#define STACK_SIZE 200  //标注做为堆栈的缓冲区大小。注意:这里单位是StackType_t大小,如果是32位,设置为100,那么将分配400字节(100*32位)。
StaticTask_t xTaskBuffer;  //这个结构体变量用于存储创建的任务的TCB
/* Buffer that the task being created will use as its stack. Note this is an array of 
StackType_t variables. The size of StackType_t is dependent on the RTOS port. */
StackType_t xStack[ STACK_SIZE ]; //具体定义栈数组,StackType_t的大小根RTOS port相关。即不同平台或架构可能不同。

void vTaskCode( void * pvParameters )   //任务的具体实现
{
 	configASSERT( ( uint32_t ) pvParameters == 1UL );  //就是比较一下,传入的参数是否为1
 	for( ;; )
 	{
 		/* 具体的处理代码 */
 	} 
}


void main( void )
{
 TaskHandle_t xHandle = NULL;
 /* 创建任务,并以静态方式分配任务栈 */
 	xHandle = xTaskCreateStatic(
 		vTaskCode, 		/* 任务的原型函数名 */
 		"NAME", 	/* 任务的文本名 */
 		STACK_SIZE, 	/* xStack数组的元素大小. */
 		( void * ) 1, 	/* 传送1给任务 */
 		tskIDLE_PRIORITY,	/* 任务的优先级 */
 		xStack, 	/* 做为任务栈的数组大小 */
 		&xTaskBuffer ); 	/* 存储任务的TCB,这个数据和xTaskCreate()不一样,不是存在栈里的,必须提前静态分配 */
 		
 // puxStackBuffer和pxTaskBuffer不为NULL,因此任务将被创建,xHandle将是任务的句柄。 
 xTaskStartSchadule();
 vTaskSuspend( xHandle );   //使用句柄挂起任务。
}

3 延迟函数使任务进入阻塞状态

为什么不用空循环:在一般的开发中,为了延迟一段时间,都是使用空循环非常粗略地去地轮询一个递增的循环计数器,直到它达到一个固定值后循环结束。这种方法的缺点是很明显的,就是无效地占用了CPU的运行时间。对多任务操作系统而言,这种浪费是不受控以及不允许的。因此在FreeRTOS中,使用vTaskDelay()来替代这个空循环,并使任务进入一个阻塞状态,这样这个时间段里的CPU时间可以被FreeRTOS用来调度给其它任务使用。这样就极大地解决了之前说的这个浪费行为。

从这里也要以看出阻塞的任务是不占用CPU的。

vTaskDelay()函数

声明

void vTaskDelay( TickType_t xTicksToDelay );

参数

参数解释
TickType_t xTicksToDelayxTicksToDelay指的是任务在转换回就绪状态之前,任务将保持在“阻止”状态的tick中断数。例如,如果一个任务在当下的tick count为10000时调用了vTaskDelay(100),那么它将立即进入阻止状态,并保持在阻止状态,直到tick count 达到10100。宏pdMS_TO_TICKS()可用于将以毫秒为单位指定的时间转换为以刻度为单位的时间。例如,调用vTaskDelay(pdMS_TO_TICKS(100))将导致调用任务在100毫秒内保持“阻止”状态。

实例

void vTaskFunction( void *pvParameters )
{
char *pcTaskName;
const TickType_t xDelay250ms = pdMS_TO_TICKS( 250 );
 /* 下面语句把一个传入的void指针强转为一个char指针 */
 pcTaskName = ( char * ) pvParameters;
 /* 具体任务的处理内容放在一个无限循环中 */
 for( ;; )
 {
 /* 这个函数的意思就是输出一个变量的内容 */
 vPrintString( pcTaskName );
 
 vTaskDelay( xDelay250ms ); //调用这个函数,使任务进入阻塞时间一段时间。参数是以tick中断次数做为计时单位,常用pdMS_TO_TICKS()宏将毫秒转换成tick中断次数。
 } }

函数的图示
在这里插入图片描述

vTaskDelayUntil()函数

vTaskDelayUntil() 类似于 vTaskDelay()。vTaskDelay() 参数指定了在调用 vTaskDelay()时开始的延迟时间。任务保持阻塞状态的时间长度由 vTaskDelay() 参数指定,状态的时间与调用 vTaskDelay() 的时间相关。该延迟时间是不计算任务的执行时间的。
相反, vTaskDelayUntil() 是在需要固定执行周期(您希望任务时间加上延迟时间的总时间是以固定频率定期执行)时应使用的 API 函数。

在这里插入图片描述
声明

void vTaskDelayUntil( TickType_t * pxPreviousWakeTime, TickType_t xTimeIncrement );

参数

参数解释
TickType_t * pxPreviousWakeTime这个参数的命名是假设 vTaskDelayUntil() 被用来实现一个定期和固定频率执行的任务。在这种情况下,pxPreviousWakeTime 保存任务上次离开阻塞状态(被“唤醒”)的时间。该时间用作参考点来计算任务下一次应该离开阻塞状态的时间。pxPreviousWakeTime 指向的变量在vTaskDelayUntil() 函数中自动更新;它通常不会被应用程序代码修改,但必须在第一次使用之前将其初始化为当前滴答计数。
TickType_t xTimeIncrement此参数确定任务时间周期。该周期包含了任务执行时间及阻塞时间。xTimeIncrement 的单位是“ticks”。宏 pdMS_TO_TICKS() 可用于将指定的毫秒时间转换为刻度指定的时间。

实例

void vTaskFunction( void *pvParameters )
{
	char *pcTaskName;
  	TickType_t xLastWakeTime;
  	pcTaskName = ( char * ) pvParameters;
 /* xLastWakeTime变量必须被初始化为当前的tick count值。在之后,这个变量会被vTaskDelayUntil()自动更新为下一次离开阻塞的时间值*/
 xLastWakeTime = xTaskGetTickCount();
 for( ;; )
 {
 vPrintString( pcTaskName );    //这个任务的实际工作是打印出这个变量内容
 /*  此任务准确的每 250 毫秒执行一次。  xLastWakeTime 在 vTaskDelayUntil() 中自动更新,因此任务不会显式更新。 */
 vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS( 250 ) );
 } }

4 优先级操作函数

4.1 vTaskPrioritySet() 函数

说明
该函数用于在调度器scheduler启动后,必变任何任务的优先级。
注意:在FreeRTOSConfig.h中 INCLUDE_vTaskPrioritySet 宏设置为1时,vTaskPrioritySet()函数才有效。

声明

void vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority)

参数

参数说明
TaskHandle_t pxTask需要被修改优先级的任务的句柄。在任务创建时分配的。
UBaseType_t uxNewPriority需要设定的优先级。

4.2 uxTaskPriorityGet() 函数

说明
该函数用于查询指定任务的优先级。在FreeRTOSConfig.h中 INCLUDE_uxTaskPriorityGet宏设置为1时,uxTaskPriorityGet()才有效

声明

UBaseType_t uxTaskPriorityGet( TaskHandle_t pxTask );

参数

参数说明
TaskHandle_t pxTask要查询的任务句柄。

返回值

返回查询任务的优先级。

实例
说明:

  1. task1是以最高优先级创建的,因此保证首先运行。在将task2的优先级提高到高于其自身优先级之前,任务1打印出几个字符串。
  2. task2一具有最高的相对优先级就开始运行(进入运行状态)。任何时候只能有一个任务处于运行状态,因此当task2处于运行状态时,task1处于就绪状态。
  3. task2在将自己的优先级设置回低于task1之前打印出一条消息。
  4. task2将其优先级降低意味着task1再次成为最高优先级的任务,因此task1重新进入运行状态,迫使task2返回就绪状态。
void vTask1( void *pvParameters )
{
	UBaseType_t uxPriority;

	uxPriority = uxTaskPriorityGet( NULL );  //参数为NULL意为返回调用本函数的任务的优先级
 	for( ;; )
 	{
 		vPrintString( "Task 1 is running\r\n" );

 		vPrintString( "About to raise the Task 2 priority\r\n" );
 		vTaskPrioritySet( xTask2Handle, ( uxPriority + 1 ) );
 		/* 到这一步,调度器会切换task2任务运行。 */
 } }

void vTask2( void *pvParameters )
{
	UBaseType_t uxPriority;
 
 	uxPriority = uxTaskPriorityGet( NULL );
 
 	for( ;; )
 	{
 /* For this task to reach this point Task 1 must have already run and
 set the priority of this task higher than its own.
 Print out the name of this task. */
 		vPrintString( "Task 2 is running\r\n" );
 
 		vPrintString( "About to lower the Task 2 priority\r\n" );
 		vTaskPrioritySet( NULL, ( uxPriority - 2 ) );  //把task2的任务优先级又改回低于task1,切换到task1运行。
 } }

以下是main函数的定义:

TaskHandle_t xTask2Handle = NULL;
int main( void )
{
 	xTaskCreate( vTask1, "Task 1", 1000, NULL, 2, NULL );
  	xTaskCreate( vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle );
 
 	vTaskStartScheduler();  
 	for( ;; );
}

5 任务删除函数

说明
本函数用于删除任务自已或其它任务。删除的任务不存在了,所以不能再进入Running状态。
不要试图使用任务句柄去引用一个已被删除的任务。
当一个任务被删除时,空闲任务负责释放用于保存被删除任务的堆栈和数据结构(任务控制块)的内存。因此,如果应用程序使用 vTaskDelete() API 函数,那么应用程序还必须确保空闲任务不会被处理时间不足(空闲任务必须在运行状态下分配时间)。
只有由内核自己分配的内存会在一个任务被删除时被自动释放的。内存,或任何其它资源,这些由应用程序(而不是内核)分配给任务的,必须被应用程序在任务删除时显式的释放。

声明

void vTaskDelete( TaskHandle_t pxTaskToDelete );

参数

参数说明
TaskHandle_t pxTaskToDelete要被删除的任务句柄

如果参数等于NULL则是删除调用该函数的任务自已。

6 总结

任务是FreeRTOS最重要的概念。因此回绕任务的概念比较多。从使用的角度出发,需要明白任务是一段带无限循环的永不退出的函数。任务句柄是系统操控任务的重要指针。任务之间的调度重要依据是优先级。优先级数值越高就越优先被调度。任务会有四种不同的工作状态。开发者对任务的使用主要是任务的创建,任务的优先级修改,任务的删除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/108516.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++初阶】vector的模拟实现

文章目录vector的介绍vector的模拟实现成员变量Member functionsconstructor&#xff08;构造函数&#xff09;destructor&#xff08;析构函数&#xff09;operatorIteratorsbeginendCapacitysizecapacityreserveresizeElement accessoperator[]Modifierspush_backpop_backins…

手办商城系统|Springboot+vue+ElementUI手办商城系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

C语言重点解剖预处理要点速记

1.宏定义字符串的时候一定要带上双引号。 2.程序的翻译&#xff0c;就是把文本代码翻译成二进制代码。分为4个阶段&#xff0c;即预处理&#xff0c;编译&#xff0c;汇编&#xff0c;链接。 3.预处理&#xff1a;头文件展开&#xff0c;去注释&#xff0c;宏替换&#xff0c…

基于Spring cloud + vue 的前后端分离的社团管理系统,数据库采用MYSQL,前端使用 VUE + element

背景 因学校没有成熟的社团管理系统&#xff0c;一为满足学校需求&#xff0c; 其次开源的社团管理项目&#xff0c;多为mvc ssm框架 很少有前后端分离的微服务框架&#xff0c;为了科技的发展&#xff0c; 更好学习解决高并发问题&#xff0c;也为了更好地掌握锤炼新技术&…

Web前端:广州蓝景实训

0基础也能学&#xff0c;5个月的课程时间&#xff0c;从小白到就业课程内容紧贴市场就业要求。广州蓝景前端实训课程&#xff0c;6年时间培养出过千名工程师&#xff0c;成功就业。今天就跟大家分享&#xff0c;蓝景学员的学习体验流程&#xff1a; 一、前端技术学习 1、授课…

3.Kafka

1.定义 1.1传统定义 Kafka是一个分布式的基于发布订阅模式的消息队列&#xff0c;主要应用于大数据的实时处理领域发布订阅&#xff1a;消息发布者不会直接将消息发送给订阅者&#xff0c;而是将发布的消息分为不同的类别&#xff0c;订阅者只接收感兴趣的消息 1.2最新定义 …

计算机网络期末重点考点总复习

第三章数据链路层 CRC检验问题 知识点 例题 要发送的数据为1101011011。采用CRC的生成多项式是 P(X)X^4 X 1试求应添加在数据后面的余数。数据在传输过程中最后一个1变成了0&#xff0c;问接收端能否发现&#xff1f; 若数据在传输过程中最后两个1都变成了0&#xff0c;问接…

MacOS系统安装Qt教程

我以为搜索QT下载&#xff0c;安装好QT Creator就能开开心心HelloWorld&#xff0c;然而还是太年轻了&#xff0c;一个 No suitable kit found 报错告诉我&#xff0c;可能没那么简单。于是&#xff0c;我去查了查&#xff0c;大概原因就是苹果处理器和x86不太一样&#xff0c;…

借助这几个工具,轻松实现在手机压缩视频

我有一个习惯&#xff0c;就是会将生活中发生的趣事&#xff0c;或是沿途好看的风景&#xff0c;用手机录制下来&#xff0c;这样的话&#xff0c;后续再回看这些视频的时候&#xff0c;就能勾起相对应的美好回忆。 但是久而久之&#xff0c;这些视频积累太多的话&#xff0c;就…

Springboot 2.7.5 HikariCP 连接池多数据源配置

一. 引言 当前项目遇到需要连接多个数据库的场景&#xff0c;此时需要引入多数据源了. 还有一些诸如以下的场景: 与第三方对接时&#xff0c;有些合作方并不会为了你的某些需求而给你开发一个功能&#xff0c;他们可以提供给你一个可以访问数据源的只读账号&#xff0c;你需要…

Web漏洞扫描篇-Nessus使用

软件介绍 Nessus是一种漏洞扫描器&#xff0c;个人和组织广泛使用它来识别和修复计算机系统中的漏洞。Nessus可以扫描广泛的漏洞&#xff0c;包括缺少补丁、弱密码和配置错误的系统&#xff0c;它可以扫描单个系统或整个网络上的漏洞。Nessus可以在各种平台上运行&#xff0c;…

【云原生进阶之容器】第一章Docker核心技术1.10节——Docker网络模型设计

1 容器网络背景概述 1.1 Linux的namespace+cgroup 先来简要回顾一下前面的内容,namespace和cgroup是Linux 内核的两大特性,namespace的诞生据说就是为了支持容器技术,那么这俩特性到底干了啥呢? - namespace:linux支持多种类型的namespace,包括Network,IPC,PID, Mount…

基于HOG、LBP完成特征工程,基于SVM/RF/XGBOOST/GBDT/CNN/DNN完成人脸识别+表情识别

在我之前的文章中写过很多关于人脸识别和表情识别的文章&#xff0c;今天有一个项目的需求就是需要做两种或者是多种任务&#xff0c;我在开发完对应的模型之后就突然想到了之前做过的人脸识别和表情识别的项目&#xff0c;就想着是否可以基于机器学习/深度学习等方式来同时实现…

算法训练第五十七天 | LeetCode 647、516动态规划结尾

LeetCode 647回文子串 题目简析&#xff1a; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成&#xff0c;也会被视作不同的子串。 思路分析&#xff1a; 本题的难点 主要…

【Aptos与Sui智能合约】(Move长话短说) #03 - 智能合约编写与发布

前言:本篇学习如何在Aptos上发布两个自定义的智能合约 0x1 前置条件 安装好Aptos工具,MacOS、Ubuntu、Windows系统的预编译的安装包下载地址 https://github.com/aptos-labs/aptos-core/releases?q=cli&expanded=true 想自己从源码开始编译,你就下载Source Code 安装…

千万不要把Request传递到异步线程里面,有坑

前几天在网上冲浪的时候看到一篇技术文章&#xff0c;讲的是他把一个 request 请求传递到了线程池里面&#xff0c;然后遇到了一个匪夷所思的情况。 他写了这篇文章&#xff0c;把自己针对这个问题的探索过程分享了出来&#xff1a; 《springboot 中如何正确的在异步线程中使用…

艾美捷脂质过氧化检测试剂盒参数说明和文献参考

脂质过氧化的定量对于评估氧化损伤在病理生理学疾病中的作用至关重要。脂质过氧化导致饱和和不饱和脂质的高反应性和不稳定的氢过氧化物的形成。 艾美捷脂质过氧化检测试剂盒直接利用与亚铁离子的氧化还原反应测量过氧化氢&#xff0c;将脂质过氧化氢提取到氯仿中&#xff0c;提…

【python绘制地图——folium的方法和类的介绍(思维导图)】

Python使用folium制作地图并生成png图片 第一章 folium的方法和类的介绍&#xff08;思维导图&#xff09; 第二章 使用folium制作地图 第三章 folium实用功能进阶 第三章 使用Html2Image生成png图片 第四章 使用reportlab制作pdf报告 文章目录Python使用folium制作地图并生成…

轮廓图编程-自定义QChartView

目录 一、功能需求 二、实现效果 三、实现方法 一、功能需求 3D测量软件中&#xff0c;需要在轮廓上进行二次编程&#xff0c;需要显示轮廓线&#xff0c;然后可以调节矩形框的范围的获取参数&#xff0c;如华汉的HyperShape3D软件&#xff0c;对轮廓的编程界面如下。 二、实…

【Java基础知识复盘】String、StringBuffer、StringBuilder篇——持续更新中

本人知识复盘系列的博客并非全部原创&#xff0c;大部分摘自网络&#xff0c;只是为了记录在自己的博客方便查阅&#xff0c;往后也会陆续在本篇博客更新本人查阅到的新的知识点&#xff0c;望悉知&#xff01; String类 在 Java 中字符串属于对象&#xff0c;Java 提供了 Str…