ESP32学习之freeRTOS(第一篇)

freeRTOS任务状态及切换

ESP32学习之freeRTOS(第一篇)_第1张图片

运行态(Running)

当一个任务实际执行时,它就处于运行态。如果RTOS运行的处理器只有一个内核,那么在任何给定的时间内只能有一个任务处于运行态。

就绪态(Ready)

就绪任务是指能够执行,但是还没有执行的任务(不是阻塞态或挂起态),因为有一个优先级相同或更高的任务正在运行。

阻塞态(blocked)

如果任务正在等待一个临时或外部事件,则任务处于阻塞态。等待的事件一般有以下几种情况:

1、等待时间事件:任务调用vTaskDelay()函数后,任务进入阻塞态,直到延时周期结束(Note that the vTaskDelay() API function is available only when INCLUDE_vTaskDelay is set to 1 in FreeRTOSConfig.h.)

2、等待同步事件:事件源自另一个任务或中断。例如:任务可以阻塞等待一个队列、信号量、事件组、通知或信号量事件。处于阻塞状态的任务通常有一个“超时”周期,在此之后任务将超时并被解除阻塞,即使任务等待的事件尚未发生。

处于阻塞态的任务不占用任何处理器的时间,且不会被选择进入运行态。

挂起态(Suspended)

与处于阻塞态的任务一样,处于挂起态的任务不能被调度器调度,处于挂起态的任务没有超时时间。进入Suspended状态的唯一方法是调用 vTaskSuspend() API 函数,退出Suspended状态唯一的方法是调用 vTaskResume() 或 TaskResumeFromISR() API 函数。

ESP32学习之freeRTOS(第一篇)_第2张图片

从上面图中可以看出,调度器只能调度处在ready状态的任务去执行。

FreeRTOS任务的创建和删除

任务的创建和删除

1、函数xTaskCreate
此函数用来创建一个任务,任务需要RAM来保存与任务有关的状态信息(任务控制块),任务也需要一定的RAM 来作为任务堆栈。
如果使用函数xTaskCreate()来创建任务的话那么这些所需的RAM就会自动的从FreeRTOS的堆中分配,因此必须提供内存管理文件,默认我们使用heap_4.c这个内存管理文件,而且宏configSUPPORT_DYNAMIC_ALLOCATION必须为1。
如果使用函数xTaskCreateStatic()创建的话这些RAM就需要用户来提供了。新创建的任务默认就是就绪态的,如果当前没有比它更高优先级的任务运行那么此任务就会立即进入运行态开始运行,不管在任务调度器启动前还是启动后,都可以创建任务。此函数也是我们以后经常用到的,本教程所有例程均用此函数来创建任务,函数原型如下:

 BaseType_t xTaskCreate( 
        TaskFunction_t pxTaskCode, 
        const char *const pcName, 
        const uint16_t usStackDepth, 
        void *const pvParameters, 
        UBaseType_t uxPriority, 
        TaskHandle_t *const pxCreatedTask);

参数:
pxTaskCode:任务函数。
pcName:任务名字,一般用于追踪和调试,任务名字长度不能超configMAX_TASK_NAME_LEN。
usStackDepth:任务堆栈大小,注意实际申请到的堆栈是usStackDepth的4倍,例如数值128,即128字。其中空闲任务的任务堆栈大小为 configMINIMAL_STACK_SIZE。
pvParameters:传递给任务函数的参数。
uxPriotiry:任务优先级,范围0~configMAX_PRIORITIES-1。若填写的数值超过configMAX_PRIORITIES-1,则静默地配置为 configMAX_PRIORITIES-1。有些版本增加了configASSERT( uxPriority pxCreatedTask:任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是任务的任务堆栈。此参数就用来保存这个任 务句柄。其他API函数可能会使用到这个句柄。
返回值:
pdPASS:任务创建成功。
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:任务创建失败,因为堆内存不足!

2、函数xTaskCreateStatic
此函数和xTaskCreate()的功能相同,也是用来创建任务的,但是使用此函数创建的任务所需的 RAM需要用用户来提供。如果要使用此函数的话需要将configSUPPORT_STATIC_ALLOCATION定义为1。函数原型如下:

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,数值越大,优先级就越高,在FreeRTOSConfig.h可以配置configMAX_PRIORITIES.若填写的数值超过configMAX_PRIORITIES-1,则静默地配置为configMAX_PRIORITIES-1。有些版本增加了
configASSERT( uxPriority < configMAX_PRIORITIES )语句,且使能configASSERT_DEFINED,则停留在该处。
puxStackBuffer:任务堆栈,一般为数组,数组类型要为StackType_t类型。
pxTaskBuffer:任务控制块。
返回值:
NULL;任务创建失败,puxStackBuffer 或 pxTaskBuffer为 NULL的时候会导致这个错误的发生。
其他值:任务创建成功,返回任务的任务句柄
注意,若configSUPPORT_STATIC_ALLOCATION有效了,需要为系统的空闲任务和定时器任务提供独立的内存空间,详细添加的代码如下:

 static StaticTask_t xIdleTaskTCBBuffer; 
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];


/* 空闲任务所需内存 */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
 StackType_t **ppxIdleTaskStackBuffer,StaticTask_t **pulIdleTaskStackSize)
 {  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer; 
    *ppxIdleTaskStackBuffer = &xIdleStack[0];
    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
 /* place for user code */
 } 
 
 
static StackType_t TimerTaskStack[configMINIMAL_STACK_SIZE];
 static StaticTask_t TimerTaskTCB;
 
 /* 定时器任务所需内存 */
 void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,
 StackType_t**ppxTimerTaskStackBuffer,StackType_t**pulTimerTaskStackSize)
 {
  *ppxTimerTaskTCBBuffer=&TimerTaskTCB;
  *ppxTimerTaskStackBuffer=TimerTaskStack;
  *pulTimerTaskStackSize=configMINIMAL_STACK_SIZE;
 }

函数栈大小确定
函数的栈大小计算起来是比较麻烦的,那么有没有简单的办法来计算呢?有的,一般 IDE 开发环境都有这样的功能,比如 MDK 会生成一个 htm 文件,通过这个文件用户可以知道每个被调用函数的最大栈需求以及各个函数之间的调 用关系。但是 MDK 无法确定通过函数指针实现函数调用时的栈需求。另外,发生中断或中断嵌套时的现场保护需要的栈 空间也不会统计。

app_task_rtc (Thumb, 128 bytes, Stack size 48 bytes, main.o(i.app_task_rtc))
[Stack] 4 Max Depth = 200 + Unknown Stack Size
Call Chain = app_task_rtc ⇒ dgb_printf_safe ⇒ xQueueGenericSend ⇒ xTaskResumeAll ⇒ xTaskIncrementTick

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

BaseType_t xTaskCreateRestricted( 
    const TaskParameters_t * const pxTaskDefinition,
    TaskHandle_t *pxCreatedTask );

参数:
pxTaskDefinition:指向一个结构体TaskParameters_t,这个结构体描述了任务的任务函数、堆栈大小、优先级等。此结构体在文件 task.h中有定义。
pxCreatedTask:任务句柄。
返回值:
pdPASS:任务创建成功。
其他值:任务创建失败,因为堆栈内存不足!

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

 vTaskDelete( TaskHandle_t xTaskToDelete )

你可能感兴趣的:(学习)