API函数
任务创建 xTaskCreate()
函数原型(tasks.c中):
1 2 3 4 5 6
| 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:任务的名字,随意起,字符串型
- usStackDepth:任务堆栈大小(实际上申请到的是这里的4倍),设的太小任务可能无法运行!
- pvParameters:任务函数的参数,不需要传参设为NULL即可
- uxPriority:任务优先级,0~(configMAX_PRIORITIES-1)
- pxCreatedTask:任务句柄,实际是一个指针,也是任务的任务堆栈
返回值:
- pdPASS:数值1,任务创建成功,且添加到就绪列表
- 错误代码:负数,任务创建识别
这里的返回值是BaseType_t,实际它是long类型,可以在portmacro.h文件中看到其定义:
1
| typedef long BaseType_t;
|
另外,任务句柄的类型为TaskHandle_t,实际它是void *类型,可以在task.h文件中看到其定义:
1
| typedef void * TaskHandle_t;
|
注:xTaskCreate()是一种动态创建任务的方式,系统通过heap_4.c的配置为任务自动分配相关内存,还有一种静态创建任务的方式xTaskCreateStatic(),这里先不介绍。
任务删除 vTaskDelete()
函数原型(tasks.c中):
1
| void vTaskDelete( TaskHandle_t xTaskToDelete )
|
参数:
- xTaskToDelete:要删除的任务的任务句柄
注:通过 xTaskCreate()动态创建的任务,在使用vTaskDelete()删除后,该任务创建时申请的堆栈和内存会在系统的空闲任务中被释放掉。
任务调度 vTaskStartScheduler()
函数原型(tasks.c中):
1
| void vTaskStartScheduler( void )
|
不需要参数,开启后就由FreeRTOS开始任务调度工作。
程序设计
主函数
主函数还是我们熟悉的main函数,但FreeRTOS里的main函数不需要自己设计成死循环,只需要创建任务并开启任务调度,即可使系统持续运行。
任务的创建一般都是先创建一个开始任务,然后开始任务再负责创建其它子任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); LED_Init();
xTaskCreate((TaskFunction_t )start_task, (const char* )"start_task", (uint16_t )START_STK_SIZE, (void* )NULL, (UBaseType_t )START_TASK_PRIO, (TaskHandle_t* )&StartTask_Handler); vTaskStartScheduler(); }
|
开始任务函数
开始任务函数的功能就是用来创建其它的子任务,创建完之后会把自己删除掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void start_task(void *pvParameters) { taskENTER_CRITICAL(); xTaskCreate((TaskFunction_t )task1_task, (const char* )"task1_task", (uint16_t )TASK1_STK_SIZE, (void* )NULL, (UBaseType_t )TASK1_TASK_PRIO, (TaskHandle_t* )&Task1Task_Handler); xTaskCreate((TaskFunction_t )task2_task, (const char* )"task2_task", (uint16_t )TASK2_STK_SIZE, (void* )NULL, (UBaseType_t )TASK2_TASK_PRIO, (TaskHandle_t* )&Task2Task_Handler); vTaskDelete(StartTask_Handler); taskEXIT_CRITICAL(); }
|
两个任务函数
每个任务函数都是一个死循环,注意循环中必须添加vTaskDelay()延时函数,用于任务的切换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void task1_task(void *pvParameters) { while(1) { LEDa_Toggle; vTaskDelay(500); } }
void task2_task(void *pvParameters) { while(1) { LEDb_ON; vTaskDelay(200); LEDb_OFF; vTaskDelay(800); } }
|
main.c所有程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| #include "stm32f4xx.h" #include "led.h"
#include "FreeRTOS.h" #include "task.h"
#define START_TASK_PRIO 1 #define START_STK_SIZE 128 TaskHandle_t StartTask_Handler; void start_task(void *pvParameters);
#define TASK1_TASK_PRIO 2 #define TASK1_STK_SIZE 128 TaskHandle_t Task1Task_Handler; void task1_task(void *pvParameters);
#define TASK2_TASK_PRIO 3 #define TASK2_STK_SIZE 128 TaskHandle_t Task2Task_Handler; void task2_task(void *pvParameters);
int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); LED_Init();
xTaskCreate((TaskFunction_t )start_task, (const char* )"start_task", (uint16_t )START_STK_SIZE, (void* )NULL, (UBaseType_t )START_TASK_PRIO, (TaskHandle_t* )&StartTask_Handler); vTaskStartScheduler(); }
void start_task(void *pvParameters) { taskENTER_CRITICAL(); xTaskCreate((TaskFunction_t )task1_task, (const char* )"task1_task", (uint16_t )TASK1_STK_SIZE, (void* )NULL, (UBaseType_t )TASK1_TASK_PRIO, (TaskHandle_t* )&Task1Task_Handler); xTaskCreate((TaskFunction_t )task2_task, (const char* )"task2_task", (uint16_t )TASK2_STK_SIZE, (void* )NULL, (UBaseType_t )TASK2_TASK_PRIO, (TaskHandle_t* )&Task2Task_Handler); vTaskDelete(StartTask_Handler); taskEXIT_CRITICAL(); }
void task1_task(void *pvParameters) { while(1) { LEDa_Toggle; vTaskDelay(500); } }
void task2_task(void *pvParameters) { while(1) { LEDb_ON; vTaskDelay(200); LEDb_OFF; vTaskDelay(800); } }
|
运行结果
运行效果是板子上的两个LED按照各自任务函数中设定的亮灭时间不断闪烁。
使用系统的原因就是可以让两个任务看起来像是同时运行,试想,如果是裸机系统,虽然也可以实现同样功能(这两个LED任务的闪烁规律比较简单),但需要将两个任务结合起来管理亮灭时间,两个任务就纠缠在一起了,如果是两个更复杂的任务,裸机系统可能就无法实现了。