一、“任务”的特点
1.简而言之 :
使用 RTOS 的实时应用程序可以被构建为一组独立的任务。每个任务在自己的上下文中执行,不依赖于系统内的其他任务或 RTOS 调度器本身。在任何时间点,应用程序中只能执行一个任务,实时 RTOS 调度器负责决定所要执行的任务。因此, RTOS 调度器可以在应用程序执行时重复启停每个任务(将任务调入或调出)。由于任务不了解 RTOS 调度器活动,因此实时 RTOS 调度器负责确保任务调入时的处理器上下文(寄存器值、堆栈内容等)与任务调出时的处理器上下文完全相同。为实现这一点,每个任务都分配有自己的堆栈。当任务调出时,执行上下文被保存到该任务的堆栈中,以便以后再调入相同的任务时可以准确地恢复其执行上下文。
2.任务状态:·运行 ·就绪 ·阻塞 ·挂起
3.任务优先级:低优先级数字表示低优先级任务
每个任务均被分配了从 0 到 ( configMAX_PRIORITIES - 1 ) 的优先级,其中的 configMAX_PRIORITIES 在 FreeRTOSConfig.h 中定义。
低优先级数字表示低优先级任务。 空闲任务的优先级为零 (tskIDLE_PRIORITY)。
任意数量的任务可共用相同的优先级。 如果 configUSE_TIME_SLICING 未经定义, 或者如果 configUSE_TIME_SLICING 设置为 1,则相同优先级的就绪状态任务 将使用时间切片轮询调度方案共享可用的处理时间 。
4.任务调度:默认使用固定优先级的抢占式调度策略执行时间片轮询调度
configUSE_PREEMPTION 设置为 0,则关闭“抢占”,1 表示开启“抢占”
- “固定优先级” 是指调度器不会永久更改任务的优先级, 尽管它可能会因 优先级继承而暂时提高任务的优先级。
- “抢占式调度” 是指调度器始终运行优先级最高且可运行的 RTOS 任务, 无论任务何时能够运行。例如, 如果中断服务程序 (ISR) 更改了优先级最高且可运行的任务, 调度器会停止当前正在运行的低优先级任务 并启动高优先级任务——即使这发生在同一个时间片内 。此时,据说高优先级任务 “抢占”了低优先级任务。
- “轮询调度” 是指具有相同优先级的任务轮流进入运行状态。
- “时间片” 是指调度器会在每个 tick 中断上在同等优先级任务之间进行切换, tick 中断之间的时间构成一个时间片。tick 中断是 RTOS 用来衡量时间的周期性中断。
5.任务实现
TaskFunction_t 类型是指返回 void 并将 void 指针作为其唯一参数的函数。所有实现任务的函数 都应该是这种类型的函数。该参数可用于将任何类型的信息传递到任务中
二、任务创建
TaskHandle_t :任务引用的类型,调用 xTaskCreate(通过指针参数)返回 TaskHandle_t 变量,可以将该变量用作 vTaskDelete 的参数来删除任务。
struct tskTaskControlBlock;
typedef struct tskTaskControlBlock * TaskHandle_t;
1、 动态任务创建:
xTaskCreate 默认 configSUPPORT_DYNAMIC_ALLOCATION 为1 时生效
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const configSTACK_DEPTH_TYPE usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
返回值:任务创建成功,返回pdPASS,xTaskCreate()
创建任务,则所需的 RAM 将自动 从 FreeRTOS堆中分配。
参数:
pvTaskCode 指向任务入口函数的指针
pcName 任务的描述性名称。
任务名称的最大长度由 FreeRTOSConfig.h 中的 configMAX_TASK_NAME_LEN 定义。
uxStackDepth 要分配用于任务堆栈的堆栈。
如果堆栈的宽度为 16 位,uxStackDepth为100则将分配200字节用作该任务的堆栈。
pvParameters 作为参数传递给创建的任务的一个值。
uxPriority 创建任务执行的优先级 。
pxCreatedTask 用于将句柄传递至由xTaskCreate()函数创建的任务,pxCreatedTask 是可选的,
可设置为NULL。
2、静态任务创建:
xTaskCreateStatic 默认configSUPPORT_STATIC_ALLOCATION 为0,函数不可用。使用 xTaskCreateStatic()
创建任务,则 RAM 由应用程序编写者提供,这会产生更多的参数但允许在编译时静态分配 RAM。
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,//注意是32位
void * const pvParameters,
UBaseType_t uxPriority,
StackType_t * const puxStackBuffer,
StaticTask_t * const pxTaskBuffer )
静态创建任务个别参数定义与动态创建任务不同:
参数:
ulStackDepth puxStackBuffer 参数用于将 StackType_t 变量数组传递给 xTaskCreateStatic()。、
必须将 ulStackDepth 设置为数组中的索引数。
puxStackBuffer 必须指向至少具有 ulStackDepth 索引的 StackType_t 数组(请参阅上面的
ulStackDepth 参数),该数组用作任务的堆栈,因此必须是永久性的(而不是在函数的堆
栈上声明)。
pxTaskBuffer 必须指向 StaticTask_t 类型的变量。该变量用于保存新任务的数据结构体 (TCB) ,因此
必须是持久的(而不是在函数的堆栈中声明)。
返回:
如果 puxStackBuffer 和 pxTaskBuffer 均不为 NULL,则创建任务,并返回任务的句柄。如果 puxStackBuffer 或 pxTaskBuffer 为 NULL,则不会创建任务,并返回 NULL。
静态创建任务需要实现两个函数:空闲任务与定时器任务
vApplicationGetIdleTaskMemory();
vApplicationGetTimerTaskMemory();
3、移除任务:
void vTaskDelete( TaskHandle_t xTaskToDelete ),配置为1才可以使用
参数:
xTask | 待删除的任务的句柄。传递 NULL 将导致调用任务被删除。 |
4、 任务恢复:
void vTaskResume( TaskHandle_t xTaskToResume );
参数:
xTaskToResume | 要恢复的任务句柄。 |
从 ISR 内调用的恢复挂起任务的函数:
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume );
需要在FreeRTOSConfig 定义以下,默认是开启状态
5、任务优先级
IRQ 任务: IRQ 任务是指通过中断服务程序进行触发的任务, 此类任务应该设置为所有任务里面优先级最高的。
高优先级后台任务: 比如按键检测,串口消息处理等,都可以归为这一类任务。
低优先级的时间片调度任务: 比如 emWin 的界面显示, LED 数码管的显示等不需要实时执行的都可以归为这一类任务。 实际应用中用户不必拘泥于将这些任务都设置为优先级 1 的同优先级任务,可以设置多个优先级, 只需注意这类任务不需要高实时性。
空闲任务: 空闲任务是系统任务。
特别注意: IRQ 任务和高优先级任务必须设置为阻塞式(调用消息等待或者延迟等函数即可) , 只有这样, 高优先级任务才会释放 CPU 的使用权, ,从而低优先级任务才有机会得到执行。
注意:
对于 STM32F103, F407 和 F429 来说, 中断优先级的数值越小, 优先级越高。 而 FreeRTOS的任务优先级是, 任务优先级数值越小, 任务优先级越低。
(1)任务优先级修改
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ); | /* 任务句柄 */ /* 给任务配置的新优先级 */ |
使用注意:1.第二个参数里面填的是 NULL,即数值 0 的话,那么配置的就是当前正在执行的任务
2. 如果被修改的任务的优先级,修改后高于正在执行的任务, 将执行任务切换,切换到修改好的高优先级任务。需要uxNewPriority < configMAX_PRIORITIES !!!
(2)任务优先级获取
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ); /* 任务句柄 */
如果传递 NULL 句柄会导致返回的是调用任务的优先级。
三、测试例程
https://gitee.com/hutaooooooo/git_keil/tree/master/freetos_CreateTask