《FreeRTOS一》
一、系统栈与任务栈
FreeRTOS的系统栈在MCU的启动文件当中修改大小,任务栈在FreeRTOSConfig.h当中定义:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
由于 Cortex-M3 和 M4 内核具有双堆栈指针,MSP 主堆栈指针和 PSP 进程堆栈指针,或者叫 PSP任务堆栈指针也是可以的。在 FreeRTOS 操作系统中,主堆栈指针 MSP 是给系统栈空间使用的, 进程堆栈指针 PSP 是给任务栈使用的。 也就是说,在 FreeRTOS 任务中,所有栈空间的使用都是通过PSP 指针进行指向的。 一旦进入了中断函数以及可能发生的中断嵌套都是用的 MSP 指针。实际应用中系统栈空间分配多大,主要是看可能发生的中断嵌套层数, 下面我们就按照最坏执行情况进行考虑,所有的寄存器都需要入栈, 此时分为两种情况:
64字节:对于 Cortex-M3 内核和未使用 FPU(浮点运算单元) 功能的 Cortex-M4 内核在发生中断时需要将 16 个通用寄存器全部入栈, 每个寄存器占用 4 个字节,也就是 16*4 = 64 字节的空间。可能发生几次中断嵌套就是要 64 乘以几即可。 当然,这种是最坏执行情况,也就是所有的寄存器都入栈。
( 注:任务执行的过程中发生中断的话,有 8 个寄存器是自动入栈的,这个栈是任务栈, 进入中断以后其余寄存器入栈以及发生中断嵌套都是用的系统栈)200字节:对于具有 FPU(浮点运算单元) 功能的 Cortex-M4 内核, 如果在任务中进行了浮点运算,那么在发生中断的时候除了 16 个通用寄存器需要入栈,还有 34 个浮点寄存器也是要入栈的, 也就是(16+34)*4 = 200 字节的空间。当然,这种是最坏执行情况,也就是所有的寄存器都入栈。
( 注:任务执行的过程中发送中断的话,有 8 个通用寄存器和 18 个浮点寄存器是自动入栈的,这个栈是任务栈, 进入中断以后其余通用寄存器和浮点寄存器入栈以及发生中断嵌套都是用的系统栈)
详见宋岩翻译的《CORTEX M3权威指南》 第九章 的中断的具体行为的讲解
二、任务状态
FreeRTOS 的运行支持以下四种状态:
1 Running—运行态
当任务处于实际运行状态被称之为运行态, 即 CPU 的使用权被这个任务占用。
2 Ready—就绪态
处于就绪态的任务是指那些能够运行( 没有被阻塞和挂起), 但是当前没有运行的任务, 因为同优先
级或更高优先级的任务正在运行。
3 Blocked—阻塞态
由于等待信号量,消息队列,事件标志组等而处于的状态被称之为阻塞态,另外任务调用延迟
函数也会处于阻塞态。
4 Suspended—挂起态
类似阻塞态,通过调用函数 vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行,只有调用函数 xTaskResume()才可以将这个任务从挂起态恢复。
三、任务相关操作函数
1、创建任务函数:
1 | |
2、删除任务函数
1
2
3void vTaskDelete(
TaskHandle_t xTask
); /* 任务句柄 */
关于使用该函数:
使用此函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1
#define INCLUDE_vTaskDelete 1
如果用往此函数里面填的任务 ID 是 NULL,即数值 0 的话,那么删除的就是当前正
执行的任务,此任务被删除后, FreeRTOS 会切换到任务就绪列表里面下一个要执行的最高优先级任务。
在 FreeRTOS 中,被该函数删除的任务的内存需要在空闲任务中释放, 如果用户在
FreeRTOS 中调用了这个函数的话,一定要让空闲任务有执行的机会, 否则这块内存是无法释放的。另外, 创建的这个任务在使用中申请了动态内存,这个内存不会因为此任务被删除而删除,这一点要注意, 一定要在删除前手动将此内存释放。
3、挂起任务与恢复任务函数
1 | |
关于函数的使用:
使用这两个函数需要在 FreeRTOSConfig.h 配置文件中配置如下宏定义为 1
#define INCLUDE_vTaskSuspend 1
如果挂起函数里面填的任务 ID 是 NULL,即数值 0 的话,那么挂起的就是当前正在执 行的任务,此任务被挂起后, FreeRTOS 会切换到任务就绪列表里面下一个要执行的高优先级任务。
多次调用挂起函数的话,只需调用一次vTaskResume 即可将任务从挂起态恢复。
恢复函数是用于任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的xTaskResumeFromISR(), 以后缀 FromISR 结尾。
1 | |
如果用户打算采用这个函数实现中断与任务的同步,要注意一种情况,如果此函数的调用优先于函数`vTaskSuspend()`被调用,那么此次同步会丢失,这种情况下建议使用信号量实现同步。