任务基础知识
FreeRTOS任务特性
- 简单
- 没有使用限制
- 支持抢占
- 支持优先级
- 每个任务都拥有堆栈导致RAM内存使用量加大
- 使用抢占必须考虑重入的问题(可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的)
FreeRTOS任务状态
- 运行态
- 就绪态
- 阻塞态
- 挂起态
FreeRTOS任务优先级
在FreeRTOSconfig.h中定义了可使用的最大优先级
#define configMAX_PRIORITIES (32) //可使用的最大优先级
数字越大,优先级越高
FreeRTOS任务实现
任务实现的具体功能代码
void vATaskFunction(void *pvParameters)
{
--任务应用程序--
for( ; ; )
{
vTaskDelay();
vTaskDelete(NULL);
}
}
FreeRTOS任务控制块
- 描述任务属性的数据结构成为任务控制块,为TCB_t
- FreeRTOS 的每个任务都有一些属性需要存储,FreeRTOS 把这些属性集合到一起用一个结构体来表示,这个结构体叫做任务控制块
FreeRTOS任务堆栈
- FreeRTOS 之所以能正确的恢复一个任务的运行就是因为有任务堆栈在保驾护航,任务调度器在进行任务切换的时候会将当前任务的现场(CPU
寄存器值等)保存在此任务的任务堆栈中,等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着从上次中断的地方开始运行,这里看不懂没关系 - 还有一个地方特别要注意的就是分配任务的时候,你给的堆栈内存大小一定要大于任务所占内存大小,不然会无法运行
- 在程序中任务的实际堆栈大小就应该是我们所定义的 4 倍
基本了解了我们任务的基础知识后就开始创建一个任务来验证FreeRTOS是否移植成功
任务创建与删除
任务创建与删除有动态方法与静态方法之分,他们之间的区别是:
- 动态方法:任务控制块与内存堆栈所使用的内存由FreeRTOS自动分配
- 静态方法:需要自己手动去分配
在这里我主要记录动态方法
相关API函数
xTaskCreate()函数(动态方法创建):
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 倍。其中空闲任务的任务堆栈大小为
configMINIMAL_STACK_SIZE - pvParameters: 传递给任务函数的参数
- uxPriotiry: 任务优先级,范围 0~ configMAX_PRIORITIES-1
- pxCreatedTask: 任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是任务的任务堆栈。此参数就用来保存这个任务句柄。其他 API函数可能会使用到这个句柄
vTaskDelete( TaskHandle_t xTaskToDelete )函数:
xTaskToDelete //要删除的任务句柄,删除自己就是NULL
具体实例:
任务一是第一个LED灯500ms闪烁一次,任务二是1s内第二个LED灯亮200ms,灭800ms。任务一运行5次之后删除任务二。
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
//开始任务的参数
#define START_TASK_SIZE 120
#define START_TASK_PRIO 3
TaskHandle_t start_handle;
void start_task( void * pvParameters );
//任务一的参数
#define TASK1_SIZE 120
#define TASK1_PRIO 4
TaskHandle_t task1_handle;
void task1( void * pvParameters );
//任务二的参数
#define TASK2_SIZE 120
#define TASK2_PRIO 5
TaskHandle_t task2_handle;
void task2( void * pvParameters );
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
delay_init();
uart_init(115200);
LED_Init();
xTaskCreate((TaskFunction_t ) start_task,
(char * ) "start_task",
(uint16_t ) START_TASK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t * ) &start_handle );
//开启任务调度
vTaskStartScheduler();
}
void start_task( void * pvParameters )
{
xTaskCreate((TaskFunction_t ) task1,
(char * ) "task1",
(uint16_t ) TASK1_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_PRIO,
(TaskHandle_t * ) &task1_handle );
xTaskCreate((TaskFunction_t ) task2,
(char * ) "task2",
(uint16_t ) TASK2_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_PRIO,
(TaskHandle_t * ) &task2_handle );
vTaskDelete(start_handle);
}
void task1( void * pvParameters )
{
unsigned char i = 0;
while(1)
{
i++;
if(i == 5)
{
vTaskDelete(task2_handle);
printf("task1 is be delete!");
}
LED0 = ~LED0;
vTaskDelay(500);
printf("task1 is runing\r\n");
}
}
void task2( void * pvParameters )
{
while(1)
{
LED1 = 0;
vTaskDelay(200);
LED1 = 1;
vTaskDelay(800);
printf("task2 is runing\r\n");
}
}