引言
在嵌入式实时系统中,任务管理是操作系统的核心功能之一。FreeRTOS作为一款轻量级的实时操作系统,提供了灵活的任务创建、删除、挂起、恢复以及优先级调度机制。本文将以通俗易懂的方式讲解FreeRTOS任务管理的基本概念,并通过一个基于STM32标准外设库的完整示例,帮助读者快速上手。
一、什么是任务?
在FreeRTOS中,**任务(Task)**可以理解为一个独立执行的函数,它拥有自己的栈空间和优先级。每个任务在系统中无限循环执行,由调度器根据优先级和状态决定何时运行。
打个比方:任务就像工厂里的工人,每个工人负责不同的工作(函数),而调度器就是车间主任,负责安排工人的工作和休息。
二、任务的状态
FreeRTOS中的任务有四种主要状态:
- 运行态(Running):任务正在使用CPU执行。
- 就绪态(Ready):任务准备就绪,等待调度器分配CPU。
- 阻塞态(Blocked):任务等待某个事件(如延时、信号量),暂时不参与调度。
- 挂起态(Suspended):任务被主动挂起,不会被执行,除非被恢复。
三、任务管理API
FreeRTOS提供了丰富的任务管理函数,常用如下:
| 函数 | 功能 |
|---|---|
xTaskCreate() |
创建任务 |
vTaskDelete() |
删除任务 |
vTaskDelay() |
延时(阻塞) |
vTaskDelayUntil() |
精确延时 |
vTaskSuspend() |
挂起任务 |
vTaskResume() |
恢复挂起的任务 |
uxTaskPriorityGet() |
获取任务优先级 |
vTaskPrioritySet() |
设置任务优先级 |
四、创建第一个任务
在FreeRTOS中,创建任务使用xTaskCreate()函数,其原型如下:
c
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
configSTACK_DEPTH_TYPE usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pxCreatedTask );
pvTaskCode:任务函数指针。pcName:任务名称(仅用于调试)。usStackDepth:任务栈大小(单位:字,不是字节)。pvParameters:传递给任务的参数。uxPriority:任务优先级,数值越大优先级越高。pxCreatedTask:任务句柄,用于后续操作。
示例:创建一个LED闪烁任务
c
void LED_Task(void *pvParameters)
{
while(1)
{
GPIO_SetBits(GPIOC, GPIO_Pin_13); // LED熄灭
vTaskDelay(500 / portTICK_PERIOD_MS);
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // LED点亮
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
int main(void)
{
// 初始化硬件
LED_Init();
// 创建任务
xTaskCreate(LED_Task, "LED", 128, NULL, 2, NULL);
// 启动调度器
vTaskStartScheduler();
while(1);
}
五、完整工程示例(基于STM32F103标准外设库)
下面给出一个完整的FreeRTOS任务管理示例,包含两个任务:LED闪烁任务和按键扫描任务,演示任务的创建、优先级、挂起与恢复。
5.1 硬件配置
- 开发板:STM32F103C8T6
- LED:PC13
- 按键:PA0(按下为低电平)
5.2 工程文件结构
- main.c
- FreeRTOSConfig.h
- stm32f10x_it.c (包含FreeRTOS中断处理)
- 其他标准外设库文件
5.3 代码实现
main.c
c
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
// 任务句柄
TaskHandle_t LED_Task_Handle = NULL;
TaskHandle_t KEY_Task_Handle = NULL;
// LED初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC, GPIO_Pin_13); // 初始熄灭
}
// 按键初始化
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
// LED任务:每500ms翻转一次
void LED_Task(void *pvParameters)
{
while(1)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_13,
(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13)));
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
// 按键任务:检测按键,按下时挂起/恢复LED任务
void KEY_Task(void *pvParameters)
{
static uint8_t key_state = 0;
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
vTaskDelay(20 / portTICK_PERIOD_MS); // 消抖
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
key_state = !key_state;
if(key_state)
{
vTaskSuspend(LED_Task_Handle); // 挂起LED任务
}
else
{
vTaskResume(LED_Task_Handle); // 恢复LED任务
}
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET); // 等待释放
}
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
int main(void)
{
// 初始化硬件
LED_Init();
KEY_Init();
// 创建LED任务,优先级2
xTaskCreate(LED_Task, "LED_Task", 128, NULL, 2, &LED_Task_Handle);
// 创建按键任务,优先级3(更高)
xTaskCreate(KEY_Task, "KEY_Task", 256, NULL, 3, &KEY_Task_Handle);
// 启动调度器
vTaskStartScheduler();
// 正常情况下不会执行到这里
while(1);
}
FreeRTOSConfig.h 关键配置
c
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 )
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES ( 5 )
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 0
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
5.4 中断服务函数修改(stm32f10x_it.c)
需要将SysTick、PendSV、SVC中断处理函数替换为FreeRTOS版本:
c
#include "FreeRTOS.h"
#include "task.h"
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
void PendSV_Handler(void)
{
xPortPendSVHandler();
}
void SVC_Handler(void)
{
vPortSVCHandler();
}
六、任务管理进阶
6.1 删除任务
任务可以删除自己或其他任务(需谨慎):
c
vTaskDelete(NULL); // 删除自己
vTaskDelete(TaskHandle); // 删除指定任务
6.2 修改优先级
c
vTaskPrioritySet(TaskHandle, newPriority);
UBaseType_t prio = uxTaskPriorityGet(TaskHandle);
6.3 获取任务状态
c
eTaskState state = eTaskGetState(TaskHandle);
七、总结
通过本文,我们了解了FreeRTOS任务的基本概念、状态和常用API,并通过一个完整的STM32标准外设库示例演示了任务的创建、挂起与恢复。任务管理是FreeRTOS的基础,掌握后可以进一步学习队列、信号量等内核对象。
实际开发中,合理划分任务、分配优先级和栈大小是保证系统稳定运行的关键。建议初学者多动手实践,观察任务切换现象,加深理解。
参考资料:
- FreeRTOS官方文档:https://www.freertos.org/
- 《FreeRTOS实时内核实用指南》
希望这篇博客对你有帮助!如果有疑问,欢迎评论区交流。