🌟 关注「嵌入式软件客栈」公众号 🌟,解锁实战技巧!💻🚀
1. FreeRTOS多任务基础架构
1.1 任务模型概述
FreeRTOS作为一款轻量级实时操作系统,其核心是多任务处理能力。在FreeRTOS中,应用程序被组织为一组自主任务,每个任务在自己的上下文(如独立的栈空间)中执行,彼此间没有依赖关系。
每个任务可以被视为一个独立的程序,拥有自己的入口函数、栈空间和局部变量。调度器负责任务的启动、停止、切入和切出。
1.2 任务状态与转换
FreeRTOS中的任务可以处于以下几种状态:
- 运行态(Running):当前正在CPU上执行
- 就绪态(Ready):可以运行,但当前没有获得CPU
- 阻塞态(Blocked):等待某个事件或延时到期
- 挂起态(Suspended):通过API手动挂起,不参与调度
- 删除态(Deleted):任务被删除,资源释放
任务状态转换图:
创建 调度器选择
-----> [就绪态] ----------> [运行态]
^ ^ | |
| | 时间片到期 | |
| +<----------------+ |
| |
| 等待事件/延时 |
+<----------------------+
^ |
| v
解除挂起| |手动挂起
| |
[挂起态] <---------------+
2. 任务创建与生命周期管理
2.1 创建任务
在FreeRTOS中,任务通常使用xTaskCreate()函数创建:
/* 创建数据采集任务 */
void vSensorTask(void *pvParameters)
{
// 任务初始化代码
float sensorData = 0.0f;
// 任务主循环
while(1)
{
// 读取传感器数据
sensorData = readSensor();
// 发送数据到处理队列
xQueueSend(dataQueue, &sensorData, portMAX_DELAY);
// 延时100ms
vTaskDelay(pdMS_TO_TICKS(100));
}
// 任务永远不应该到达这里,如果到达,应该删除自己
vTaskDelete(NULL);
}
// 主函数中创建任务
BaseType_t xReturned;
TaskHandle_t xSensorTaskHandle = NULL;
xReturned = xTaskCreate(
vSensorTask, /* 任务函数 */
"Sensor", /* 任务名称 */
configMINIMAL_STACK_SIZE*2, /* 栈大小(字)*/
NULL, /* 任务参数 */
tskIDLE_PRIORITY + 1, /* 优先级 */
&xSensorTaskHandle /* 任务句柄 */
);
if(xReturned != pdPASS)
{
/* 任务创建失败,通常是内存不足 */
configASSERT(0);
}
2.2 任务控制
2.2.1 挂起和恢复任务
// 在特定条件下挂起传感器任务
if(systemStatus == SYSTEM_ERROR)
{
vTaskSuspend(xSensorTaskHandle);
printf("传感器任务已挂起\r\n");
}
// 系统恢复后重新启动任务
if(systemStatus == SYSTEM_NORMAL)
{
vTaskResume(xSensorTaskHandle);
printf("传感器任务已恢复\r\n");
}
2.2.2 删除任务
// 删除不再需要的任务
vTaskDelete(xSensorTaskHandle);
xSensorTaskHandle = NULL;
2.2.3 延时任务
// 简单延时(相对延时)
vTaskDelay(pdMS_TO_TICKS(1000)); // 延时1秒
// 精确周期延时(绝对延时)
TickType_t xLastWakeTime = xTaskGetTickCount();
for(;;)
{
// 执行周期性工作
doPeriodicWork();
// 固定周期500ms
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(500));
}
3. 高级任务调度策略
3.1 优先级调度
FreeRTOS使用优先级调度算法,高优先级任务会抢占低优先级任务:
// 定义不同优先级
#define PRIORITY_HIGH (configMAX_PRIORITIES - 1)
#define PRIORITY_MEDIUM (configMAX_PRIORITIES / 2)
#define PRIORITY_LOW (tskIDLE_PRIORITY + 1)
// 创建不同优先级任务
xTaskCreate(vEmergencyTask, "Emergency", 1024, NULL, PRIORITY_HIGH, NULL);
xTaskCreate(vNormalTask, "Normal", 1024, NULL, PRIORITY_MEDIUM, NULL);
xTaskCreate(vBackgroundTask,"Background",1024, NULL, PRIORITY_LOW, NULL);
3.2 时间片轮转
对于相同优先级任务,FreeRTOS采用时间片轮转调度:
// 创建两个相同优先级的任务
xTaskCreate(vProcessTask1, "Process1", 1024, NULL, PRIORITY_MEDIUM, NULL);
xTaskCreate(vProcessTask2, "Process2", 1024, NULL, PRIORITY_MEDIUM, NULL);
// 这两个任务会轮流执行,每个任务每次执行configTICK_RATE_HZ个时钟周期
3.3 动态优先级调整
// 根据任务重要性临时提高优先级
void vHandleImportantEvent(void)
{
// 保存原始优先级
UBaseType_t uxOriginalPriority;
uxOriginalPriority = uxTaskPriorityGet(NULL);
// 临时提高当前任务优先级
vTaskPrioritySet(NULL, PRIORITY_HIGH);
// 处理重要事件
processImportantEvent();
// 恢复原始优先级
vTaskPrioritySet(NULL, uxOriginalPriority);
}
4. 任务间通信机制
4.1 队列通信
// 创建队列
QueueHandle_t xDataQueue;
xDataQueue = xQueueCreate(10, sizeof(SensorData_t));
// 发送方任务
void vSensorTask(void *pvParameters)
{
SensorData_t xData;
while(1)
{
// 采集数据
xData.value = readSensor();
xData.timestamp = xTaskGetTickCount();
// 发送到队列
if(xQueueSend(xDataQueue, &xData, pdMS_TO_TICKS(100)) != pdPASS)
{
// 发送失败处理
errorHandler(ERROR_QUEUE_FULL);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
// 接收方任务
void vProcessTask(void *pvParameters)
{
SensorData_t xReceivedData;
while(1)
{
// 从队列接收数据
if(xQueueReceive(xDataQueue, &xReceivedData, portMAX_DELAY) == pdPASS)
{
// 处理数据
processData(&xReceivedData);
}
}
}
4.2 信号量
// 创建二值信号量(互斥锁)
SemaphoreHandle_t xMutex;
xMutex = xSemaphoreCreateMutex();
// 使用互斥锁保护共享资源
void vTask(void *pvParameters)
{
while(1)
{
// 尝试获取互斥锁
if(xSemaphoreTake(xMutex, pdMS_TO_TICKS(500)) == pdTRUE)
{
// 访问共享资源
accessSharedResource();
// 释放互斥锁
xSemaphoreGive(xMutex);
}
else
{
// 无法获取互斥锁的处理
handleMutexTimeout();
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
4.3 事件组
// 创建事件组
EventGroupHandle_t xEventGroup;
xEventGroup = xEventGroupCreate();
// 定义事件位
#define EVENT_TEMPERATURE_BIT (1 << 0)
#define EVENT_HUMIDITY_BIT (1 << 1)
#define EVENT_PRESSURE_BIT (1 << 2)
#define ALL_SENSOR_BITS (EVENT_TEMPERATURE_BIT | EVENT_HUMIDITY_BIT | EVENT_PRESSURE_BIT)
// 生产者任务
void vTemperatureTask(void *pvParameters)
{
while(1)
{
// 读取温度
readTemperature();
// 设置对应事件位
xEventGroupSetBits(xEventGroup, EVENT_TEMPERATURE_BIT);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// 消费者任务
void vProcessingTask(void *pvParameters)
{
EventBits_t uxBits;
while(1)
{
// 等待所有传感器数据就绪
uxBits = xEventGroupWaitBits(
xEventGroup, // 事件组
ALL_SENSOR_BITS, // 等待的位
pdTRUE, // 清除位
pdTRUE, // 等待所有位
portMAX_DELAY); // 无限等待
// 所有数据就绪,进行处理
processSensorData();
}
}
5. 多任务异常处理与排查
5.1 常见异常类型
5.1.1 栈溢出
// 配置栈溢出钩子函数
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
{
// 记录栈溢出信息
printf("栈溢出: 任务[%s]发生栈溢出\r\n", pcTaskName);
// 系统复位或其他处理
NVIC_SystemReset();
}
5.1.2 优先级反转
// 使用优先级继承互斥锁防止优先级反转
SemaphoreHandle_t xMutex;
xMutex = xSemaphoreCreateMutex();
// 高优先级任务
void vHighPriorityTask(void *pvParameters)
{
while(1)
{
// 尝试获取互斥锁
if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE)
{
// 访问共享资源
accessSharedResource();
// 释放互斥锁
xSemaphoreGive(xMutex);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
5.1.3 死锁检测
// 使用超时机制防止死锁
void vTask(void *pvParameters)
{
while(1)
{
// 尝试获取互斥锁,设置超时时间
if(xSemaphoreTake(xMutex, pdMS_TO_TICKS(1000)) == pdTRUE)
{
// 访问共享资源
accessSharedResource();
// 释放互斥锁
xSemaphoreGive(xMutex);
}
else
{
// 超时,可能存在死锁
printf("警告:可能存在死锁\r\n");
// 记录系统状态或重启
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
5.2 任务监控与调试
5.2.1 看门狗任务
// 定义任务状态检查结构
typedef struct {
TaskHandle_t handle;
uint32_t lastCheckpoint;
bool healthy;
} TaskMonitor_t;
TaskMonitor_t taskMonitors[MAX_MONITORED_TASKS];
// 监控任务实现
void vWatchdogTask(void *pvParameters)
{
while(1)
{
// 检查各个任务的状态
for(int i = 0; i < MAX_MONITORED_TASKS; i++)
{
if(taskMonitors[i].handle != NULL)
{
// 获取任务状态
eTaskState state = eTaskGetState(taskMonitors[i].handle);
// 检查任务是否更新了检查点
if(taskMonitors[i].lastCheckpoint == 0 ||
(state == eBlocked && taskMonitors[i].healthy == false))
{
// 任务可能已经卡住
handleTaskFailure(i);
}
}
}
// 喂硬件看门狗
HAL_IWDG_Refresh(&hiwdg);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// 任务检查点更新
#define UPDATE_TASK_CHECKPOINT() do { \
for(int i = 0; i < MAX_MONITORED_TASKS; i++) { \
if(taskMonitors[i].handle == xTaskGetCurrentTaskHandle()) { \
taskMonitors[i].lastCheckpoint = xTaskGetTickCount(); \
taskMonitors[i].healthy = true; \
break; \
} \
} \
} while(0)
5.2.2 任务统计信息
// 定义任务统计信息结构
typedef struct {
char taskName[configMAX_TASK_NAME_LEN];
UBaseType_t priority;
uint32_t stackRemaining;
eTaskState state;
uint32_t runTime; // 百分比 * 100
} TaskStats_t;
// 统计任务实现
void vStatisticsTask(void *pvParameters)
{
TaskStats_t stats[MAX_TASKS];
UBaseType_t uxArraySize, x;
while(1)
{
// 获取任务数量
uxArraySize = uxTaskGetNumberOfTasks();
// 遍历所有任务
for(x = 0; x < uxArraySize; x++)
{
TaskHandle_t xHandle;
TaskStatus_t xTaskDetails;
// 获取下一个任务句柄
xHandle = xTaskGetIdleTaskHandle(); // 简化版,实际需要遍历所有任务
// 获取任务详细信息
vTaskGetInfo(xHandle, &xTaskDetails, pdTRUE, eInvalid);
// 填充统计结构
strncpy(stats[x].taskName, xTaskDetails.pcTaskName, configMAX_TASK_NAME_LEN);
stats[x].priority = xTaskDetails.uxCurrentPriority;
stats[x].stackRemaining = xTaskDetails.usStackHighWaterMark;
stats[x].state = xTaskDetails.eCurrentState;
}
// 打印统计信息
printTaskStats(stats, uxArraySize);
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
6. 工业级应用案例分析
6.1 智能传感器网络系统
以下是一个智能传感器网络系统的简化实现,包含数据采集、处理、存储和通信多个任务:
// 系统架构图
/*
+---------------+ +---------------+ +---------------+
| 传感器采集任务 | -> | 数据处理任务 | -> | 数据存储任务 |
+---------------+ +---------------+ +---------------+
| | |
v v v
+-----------------------------------------------+
| 监控任务 |
+-----------------------------------------------+
^ ^ ^
| | |
+---------------+ +---------------+ +---------------+
| 网络通信任务 | <- | 命令处理任务 | <- | 告警处理任务 |
+---------------+ +---------------+ +---------------+
*/
// 主要队列和信号量定义
QueueHandle_t xRawDataQueue; // 原始数据队列
QueueHandle_t xProcessedQueue; // 处理后数据队列
QueueHandle_t xStorageQueue; // 存储任务队列
QueueHandle_t xAlarmQueue; // 告警队列
QueueHandle_t xCommandQueue; // 命令队列
SemaphoreHandle_t xSpiMutex; // SPI总线互斥锁
SemaphoreHandle_t xI2CMutex; // I2C总线互斥锁
// 传感器采集任务
void vSensorTask(void *pvParameters)
{
SensorData_t xData;
// 任务初始化
sensorInit();
while(1)
{
// 获取SPI总线访问权
if(xSemaphoreTake(xSpiMutex, pdMS_TO_TICKS(100)) == pdPASS)
{
// 读取传感器数据
xData.temperature = readTemperature();
xData.humidity = readHumidity();
xData.pressure = readPressure();
xData.timestamp = xTaskGetTickCount();
// 释放SPI总线
xSemaphoreGive(xSpiMutex);
// 将数据发送到处理队列
xQueueSend(xRawDataQueue, &xData, pdMS_TO_TICKS(10));
// 超出阈值检查
if(xData.temperature > TEMP_HIGH_THRESHOLD)
{
AlarmData_t xAlarm = {
.type = ALARM_HIGH_TEMPERATURE,
.value = xData.temperature,
.timestamp = xData.timestamp
};
xQueueSend(xAlarmQueue, &xAlarm, pdMS_TO_TICKS(10));
}
}
// 周期性延时
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// 数据处理任务
void vProcessTask(void *pvParameters)
{
SensorData_t xRawData;
ProcessedData_t xProcessedData;
while(1)
{
// 等待原始数据
if(xQueueReceive(xRawDataQueue, &xRawData, portMAX_DELAY) == pdPASS)
{
// 数据过滤和处理
xProcessedData.temperature = applyFilter(xRawData.temperature);
xProcessedData.humidity = applyFilter(xRawData.humidity);
xProcessedData.pressure = applyFilter(xRawData.pressure);
xProcessedData.timestamp = xRawData.timestamp;
// 计算统计数据
calculateStatistics(&xProcessedData);
// 发送到存储队列
xQueueSend(xProcessedQueue, &xProcessedData, pdMS_TO_TICKS(100));
}
// 检查点更新
UPDATE_TASK_CHECKPOINT();
}
}
// 网络通信任务
void vNetworkTask(void *pvParameters)
{
ProcessedData_t xData;
CommandData_t xCommand;
// 初始化网络
networkInit();
while(1)
{
// 检查是否有数据需要发送
if(xQueueReceive(xProcessedQueue, &xData, pdMS_TO_TICKS(10)) == pdPASS)
{
// 发送数据到云平台
sendDataToCloud(&xData);
}
// 检查是否有新命令
if(checkForRemoteCommands(&xCommand))
{
// 发送到命令队列
xQueueSend(xCommandQueue, &xCommand, pdMS_TO_TICKS(10));
}
// 检查点更新
UPDATE_TASK_CHECKPOINT();
vTaskDelay(pdMS_TO_TICKS(50));
}
}
6.2 实时控制系统案例
// 定义控制系统参数
typedef struct {
float setPoint; // 设定值
float currentValue; // 当前值
float kp, ki, kd; // PID参数
float integral; // 积分项
float lastError; // 上一次误差
float outputLimit; // 输出限制
} PIDController_t;
// 控制任务
void vControlTask(void *pvParameters)
{
PIDController_t xPID = {
.setPoint = 25.0f,
.kp = 1.2f,
.ki = 0.1f,
.kd = 0.05f,
.integral = 0.0f,
.lastError = 0.0f,
.outputLimit = 100.0f
};
float sensorValue = 0.0f;
float controlOutput = 0.0f;
// 初始化硬件
initActuators();
// 获取采样时间
TickType_t xLastWakeTime = xTaskGetTickCount();
const float dt = 0.01f; // 10ms控制周期
while(1)
{
// 读取传感器值
if(xQueueReceive(xSensorQueue, &sensorValue, 0) != pdPASS)
{
// 如果队列为空,直接读取
xSemaphoreTake(xSensorMutex, portMAX_DELAY);
sensorValue = readSensorDirect();
xSemaphoreGive(xSensorMutex);
}
xPID.currentValue = sensorValue;
// 计算误差
float error = xPID.setPoint - xPID.currentValue;
// 计算PID各项
float proportional = xPID.kp * error;
xPID.integral += xPID.ki * error * dt;
// 积分限幅
if(xPID.integral > xPID.outputLimit) xPID.integral = xPID.outputLimit;
if(xPID.integral < -xPID.outputLimit) xPID.integral = -xPID.outputLimit;
float derivative = xPID.kd * (error - xPID.lastError) / dt;
xPID.lastError = error;
// 计算输出
controlOutput = proportional + xPID.integral + derivative;
// 输出限幅
if(controlOutput > xPID.outputLimit) controlOutput = xPID.outputLimit;
if(controlOutput < -xPID.outputLimit) controlOutput = -xPID.outputLimit;
// 设置执行器输出
setActuator(controlOutput);
// 精确周期控制
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(10));
}
}
7. 系统优化与性能调优
7.1 内存优化
// 使用静态分配替代动态内存
// 定义任务所需的栈和控制块
static StaticTask_t xTaskBuffer;
static StackType_t xStack[configMINIMAL_STACK_SIZE];
// 使用静态方式创建任务
TaskHandle_t xHandle = xTaskCreateStatic(
vTaskCode, // 任务函数
"NAME", // 任务名称
configMINIMAL_STACK_SIZE, // 栈深度
NULL, // 参数
tskIDLE_PRIORITY + 1, // 优先级
xStack, // 栈缓冲区
&xTaskBuffer // 任务控制块
);
7.2 栈大小优化
// 检查栈使用情况
void vStackUsageTask(void *pvParameters)
{
UBaseType_t uxHighWaterMark;
while(1)
{
// 获取各任务栈的高水位标记
uxHighWaterMark = uxTaskGetStackHighWaterMark(xTask1Handle);
printf("Task1 stack remaining: %lu\r\n", uxHighWaterMark);
uxHighWaterMark = uxTaskGetStackHighWaterMark(xTask2Handle);
printf("Task2 stack remaining: %lu\r\n", uxHighWaterMark);
// 根据高水位标记调整栈大小
vTaskDelay(pdMS_TO_TICKS(10000));
}
}
7.3 CPU利用率监控
// 定义运行时间统计变量
volatile uint32_t ulIdleCycleCount = 0;
volatile uint32_t ulTotalCycleCount = 0;
// 空闲任务钩子函数
void vApplicationIdleHook(void)
{
ulIdleCycleCount++;
}
// CPU利用率监控任务
void vCPUMonitorTask(void *pvParameters)
{
uint32_t ulLastTotalCycles, ulLastIdleCycles;
uint32_t ulCurrentTotalCycles, ulCurrentIdleCycles;
while(1)
{
// 保存当前计数
ulLastTotalCycles = ulTotalCycleCount;
ulLastIdleCycles = ulIdleCycleCount;
// 等待采样周期
vTaskDelay(pdMS_TO_TICKS(1000));
// 读取新计数
ulCurrentTotalCycles = ulTotalCycleCount;
ulCurrentIdleCycles = ulIdleCycleCount;
// 计算CPU利用率
uint32_t ulTotalCyclesDiff = ulCurrentTotalCycles - ulLastTotalCycles;
uint32_t ulIdleCyclesDiff = ulCurrentIdleCycles - ulLastIdleCycles;
float cpuUsage = 100.0f - ((float)ulIdleCyclesDiff / (float)ulTotalCyclesDiff * 100.0f);
printf("CPU使用率: %.2f%%\r\n", cpuUsage);
}
}
7.4 实时性能优化技巧
-
合理设置任务优先级
- 实时任务 > 通信任务 > 后台任务
- 避免过多相同优先级任务
-
最小化关中断时间
- 关中断代码应尽可能短
- 使用临界区保护资源
-
避免任务过度阻塞
- 任务阻塞应有超时机制
- 避免长时间持有互斥量
-
优化任务切换开销
- 减少不必要的任务挂起/恢复操作
- 合并频繁切换的小任务
-
使用任务通知替代轻量级同步
- 任务通知比信号量更轻量级
- 速度快,内存占用小
// 使用任务通知代替信号量
void vSenderTask(void *pvParameters)
{
while(1)
{
// 处理数据
processData();
// 通知接收任务
xTaskNotifyGive(xReceiverTaskHandle);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void vReceiverTask(void *pvParameters)
{
while(1)
{
// 等待通知
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// 处理数据
handleNotification();
}
}
总结
FreeRTOS多任务开发是一项需要综合考虑系统架构、资源分配、通信协调和异常处理的复杂工作。通过合理设计任务结构,使用适当的通信机制,实施有效的异常处理策略,可以构建出高效、可靠的嵌入式系统。
在实际应用中,开发者需要根据特定的硬件资源和应用需求,灵活运用这些技术,找到效率、资源占用和实时性之间的最佳平衡点。
关注 嵌入式软件客栈 公众号,获取更多内容

FreeRTOS多任务开发打造嵌入式系统
2174

被折叠的 条评论
为什么被折叠?



