别让你的ESP32任务打架!一文学会信号量和互斥量的交通规则--上

文章总结(帮你们节约时间)

  • 信号量和互斥量是ESP32多任务编程的核心同步机制,掌握它们就像学会了多线程世界的"交通规则"
  • 计数信号量适合资源池管理,二进制信号量用于任务同步,互斥量专门保护共享资源
  • 优先级反转和死锁是两大常见陷阱,但通过优先级继承和合理设计完全可以避免
  • Arduino IDE下的FreeRTOS API使用简单,但理解底层原理才能写出高效稳定的代码

你有没有遇到过这样的情况:ESP32项目中多个任务同时访问串口,结果打印出来的信息乱七八糟,像是外星人发来的密码?或者两个任务都在等待对方释放资源,最后谁也动不了,整个系统就这样"死"在那里?

这就是多任务编程中最经典的问题!就像十字路口没有红绿灯,车辆横冲直撞,不出事故才怪呢。而信号量(Semaphore)和互斥量(Mutex)就是我们的"交通信号灯",它们负责协调各个任务的执行顺序,确保系统井然有序。

ESP32作为一颗双核处理器,天生就具备多任务处理能力。当你在Arduino IDE中使用xTaskCreate()创建多个任务时,这些任务会并发执行,共享CPU时间片。但是,当多个任务需要访问同一个资源时,问题就来了!

想象一下,你有一个全局变量int counter = 0,两个任务都要对它进行递增操作。任务A读取counter的值(比如是100),正准备加1,这时任务B也读取了counter的值(还是100),然后任务A写入101,任务B也写入101。结果本来应该是102的counter,最终只变成了101!这就是典型的竞态条件(Race Condition)。

// 这是一个有问题的代码示例
int counter = 0;

void taskA(void *parameter) {
    for(;;) {
        counter++;  // 危险!非原子操作
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void taskB(void *parameter) {
    for(;;) {
        counter++;  // 危险!非原子操作
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

这就是为什么我们需要信号量和互斥量的原因!它们就像是代码世界的"门禁系统",确保在任何时刻只有被授权的任务才能访问关键资源。

基础概念:信号量和互斥量的本质差异

在深入学习之前,我们必须搞清楚信号量和互斥量的本质区别。很多初学者经常把它们搞混,就像把"借钱"和"排队"搞混一样。

**信号量(Semaphore)**就像是一个"令牌桶"。想象一下网吧的上机卡,网吧总共有20台电脑,就发放20张上机卡。顾客想上网就必须先拿到卡,用完后归还。如果卡都被拿光了,后来的顾客就得等待。信号量的值表示可用资源的数量,它可以是任意非负整数。

**互斥量(Mutex)**则更像是一把"独占锁"。想象一下公共厕所的门锁,同一时间只能有一个人使用,其他人必须在外面等待。互斥量只有两种状态:锁定或未锁定,专门用于保护临界区。

让我们用一个生动的比喻来理解:

  • 信号量像是停车场的车位。停车场有100个车位,就相当于计数为100的信号量。每来一辆车,可用车位减1;每走一辆车,可用车位加1。当车位满了,新来的车就得等待。

  • 互斥量像是单人卫生间的门锁。不管外面排了多少人,里面同时只能有一个人。这个人用完出来后,下一个人才能进去。

从技术角度来看,二者的区别在于:

  1. 语义不同:信号量用于资源计数和任务同步,互斥量专门用于互斥访问
  2. 所有权不同:信号量没有所有权概念,任何任务都可以释放;互斥量有所有权,只有获取者才能释放
  3. 优先级继承:互斥量支持优先级继承,信号量不支持
  4. 递归性:互斥量可以递归获取,信号量不行

在Arduino ESP32开发中,FreeRTOS提供了丰富的API来操作这些同步原语:

// 信号量相关API
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
SemaphoreHandle_t xSemaphoreCreateBinary(void);
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);

// 互斥量相关API
SemaphoreHandle_t xSemaphoreCreateMutex(void);
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);
BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait);
BaseType_t xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex);

信号量深度解析:从计数到二进制的奇妙世界

计数信号量:资源池的智能管理者

计数信号量就像是一个智能的资源分配器。它维护着一个内部计数器,表示当前可用资源的数量。每当一个任务需要资源时,计数器减1;当任务释放资源时,计数器加1。

让我们来看一个实际的例子:假设你的ESP32项目需要管理3个SPI设备,但只有一条SPI总线。这时候计数信号量就派上用场了!

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 创建一个计数信号量,最大计数为3,初始计数为3
SemaphoreHandle_t spiSemaphore;

void setup() {
    Serial.begin(115200);
    
    // 创建计数信号量:最多3个任务可以同时使用SPI资源
    spiSemaphore = xSemaphoreCreateCounting(3, 3);
    
    if (spiSemaphore == NULL) {
        Serial.println("信号量创建失败!");
        return;
    }
    
    // 创建多个任务来模拟SPI设备访问
    xTaskCreate(spiTask, "SPI_Task_1", 2048, (void*)1, 1, NULL);
    xTaskCreate(spiTask, "SPI_Task_2", 2048, (void*)2, 1, NULL);
    xTaskCreate(spiTask, "SPI_Task_3", 2048, (void*)3, 1, NULL);
    xTaskCreate(spiTask, "SPI_Task_4", 2048, (void*)4, 1, NULL);
    xTaskCreate(spiTask, "SPI_Task_5", 2048, (void*)5, 1, NULL);
    
    Serial.println("所有任务创建完成!");
}

void spiTask(void *parameter) {
    int taskId = (int)parameter;
    
    for(;;) {
        // 尝试获取SPI资源,最多等待1秒
        if (xSemaphoreTake(spiSemaphore, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
            Serial.printf("任务%d获得SPI资源,开始传输数据...\n", taskId);
            
            // 模拟SPI数据传输(耗时操作)
            vTaskDelay(2000 / portTICK_PERIOD_MS);
            
            Serial.printf("任务%d完成SPI传输,释放资源\n", taskId);
            
            // 释放SPI资源
            xSemaphoreGive(spiSemaphore);
        } else {
            Serial.printf("任务%d等待SPI资源超时!\n", taskId);
        }
        
        // 等待一段时间再次尝试
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    // 主循环可以做其他事情
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

运行这个程序,你会看到类似这样的输出:

所有任务创建完成!
任务1获得SPI资源,开始传输数据...
任务2获得SPI资源,开始传输数据...
任务3获得SPI资源,开始传输数据...
任务4等待SPI资源超时!
任务5等待SPI资源超时!
任务1完成SPI传输,释放资源
任务2完成SPI传输,释放资源
任务3完成SPI传输,释放资源
任务4获得SPI资源,开始传输数据...
任务5获得SPI资源,开始传输数据...

看到了吗?前3个任务同时获得了SPI资源,而任务4和5必须等待。这就是计数信号量的威力!它精确地控制了同时访问资源的任务数量。

但是,你可能会问:为什么不直接用一个全局变量来计数呢?答案是:原子性!信号量的获取和释放操作是原子的,不会被任务切换打断。而普通的变量操作可能会被中断,导致竞态条件。

二进制信号量:简单而强大的同步工具

二进制信号量就像是一个简化版的计数信号量,它的计数值只能是0或1。你可以把它想象成一个"开关"或者"标志位"。

二进制信号量最常用的场景是任务同步。比如,你希望任务A完成某个操作后,任务B才能开始执行。这时候二进制信号量就是最佳选择!

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t binarySemaphore;

void setup() {
    Serial.begin(115200);
    
    // 创建二进制信号量
    binarySemaphore = xSemaphoreCreateBinary();
    
    if (binarySemaphore == NULL) {
        Serial.println("二进制信号量创建失败!");
        return;
    }
    
    // 创建生产者和消费者任务
    xTaskCreate(producerTask, "Producer", 2048, NULL, 2, NULL);
    xTaskCreate(consumerTask, "Consumer", 2048, NULL, 1, NULL);
    
    Serial.println("生产者-消费者任务创建完成!");
}

void producerTask(void *parameter) {
    int dataCounter = 0;
    
    for(;;) {
        // 模拟数据生产过程
        dataCounter++;
        Serial.printf("生产者:正在生产数据 #%d...\n", dataCounter);
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        
        Serial.printf("生产者:数据 #%d 生产完成!通知消费者\n", dataCounter);
        
        // 通知消费者数据已准备好
        xSemaphoreGive(binarySemaphore);
        
        // 等待一段时间再生产下一个数据
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void consumerTask(void *parameter) {
    for(;;) {
        Serial.println("消费者:等待数据...");
        
        // 等待生产者的通知
        if (xSemaphoreTake(binarySemaphore, portMAX_DELAY) == pdTRUE) {
            Serial.println("消费者:收到通知,开始处理数据...");
            
            // 模拟数据处理过程
            vTaskDelay(1500 / portTICK_PERIOD_MS);
            
            Serial.println("消费者:数据处理完成!\n");
        }
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

这个例子展示了二进制信号量在任务同步中的应用。生产者任务生产数据后,通过xSemaphoreGive()通知消费者;消费者任务通过xSemaphoreTake()等待通知。

你会看到类似这样的输出:

生产者-消费者任务创建完成!
消费者:等待数据...
生产者:正在生产数据 #1...
生产者:数据 #1 生产完成!通知消费者
消费者:收到通知,开始处理数据...
消费者:数据处理完成!

消费者:等待数据...
生产者:正在生产数据 #2...
生产者:数据 #2 生产完成!通知消费者
消费者:收到通知,开始处理数据...
消费者:数据处理完成!

信号量的创建与配置策略

在ESP32的Arduino环境中,创建信号量有几种不同的方式,每种方式都有其特定的用途:

// 1. 创建计数信号量
SemaphoreHandle_t xSemaphoreCreateCounting(
    UBaseType_t uxMaxCount,     // 最大计数值
    UBaseType_t uxInitialCount  // 初始计数值
);

// 2. 创建二进制信号量
SemaphoreHandle_t xSemaphoreCreateBinary(void);

// 3. 使用静态内存创建信号量(高级用法)
SemaphoreHandle_t xSemaphoreCreateCountingStatic(
    UBaseType_t uxMaxCount,
    UBaseType_t uxInitialCount,
    StaticSemaphore_t *pxSemaphoreBuffer
);

创建策略的选择原则:

  1. 资源池管理:使用计数信号量,最大计数等于资源数量
  2. 任务同步:使用二进制信号量,初始状态通常为空(计数为0)
  3. 事件通知:使用二进制信号量,可以替代传统的标志位
  4. 内存受限环境:考虑使用静态创建方式,避免堆内存碎片

让我们看一个更复杂的例子,展示如何在一个实际的IoT项目中使用信号量:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "WiFi.h"

// 信号量句柄
SemaphoreHandle_t wifiSemaphore;        // WiFi连接状态信号量
SemaphoreHandle_t sensorSemaphore;      // 传感器数据信号量
SemaphoreHandle_t uartSemaphore;        // 串口访问信号量

// 全局变量
float temperature = 0.0;
float humidity = 0.0;
bool wifiConnected = false;

void setup() {
    Serial.begin(115200);
    
    // 创建各种信号量
    wifiSemaphore = xSemaphoreCreateBinary();
    sensorSemaphore = xSemaphoreCreateBinary();
    uartSemaphore = xSemaphoreCreateMutex();  // 串口需要互斥访问
    
    if (wifiSemaphore == NULL || sensorSemaphore == NULL || uartSemaphore == NULL) {
        Serial.println("信号量创建失败!");
        return;
    }
    
    // 创建各种任务
    xTaskCreate(wifiTask, "WiFi_Task", 4096, NULL, 3, NULL);
    xTaskCreate(sensorTask, "Sensor_Task", 2048, NULL, 2, NULL);
    xTaskCreate(dataUploadTask, "Upload_Task", 4096, NULL, 1, NULL);
    xTaskCreate(displayTask, "Display_Task", 2048, NULL, 1, NULL);
    
    Serial.println("IoT系统启动完成!");
}

void wifiTask(void *parameter) {
    WiFi.begin("YourWiFiSSID", "YourPassword");
    
    for(;;) {
        if (WiFi.status() == WL_CONNECTED && !wifiConnected) {
            wifiConnected = true;
            safePrint("WiFi连接成功!");
            xSemaphoreGive(wifiSemaphore);  // 通知其他任务WiFi已连接
        } else if (WiFi.status() != WL_CONNECTED && wifiConnected) {
            wifiConnected = false;
            safePrint("WiFi连接断开!");
        }
        
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

void sensorTask(void *parameter) {
    for(;;) {
        // 模拟读取传感器数据
        temperature = random(200, 350) / 10.0;  // 20.0 - 35.0°C
        humidity = random(300, 800) / 10.0;     // 30.0 - 80.0%
        
        safePrint("传感器数据更新:温度=" + String(temperature) + "°C, 湿度=" + String(humidity) + "%");
        
        // 通知其他任务数据已更新
        xSemaphoreGive(sensorSemaphore);
        
        vTaskDelay(10000 / portTICK_PERIOD_MS);  // 每10秒更新一次
    }
}

void dataUploadTask(void *parameter) {
    for(;;) {
        // 等待传感器数据更新
        if (xSemaphoreTake(sensorSemaphore, portMAX_DELAY) == pdTRUE) {
            // 等待WiFi连接
            if (xSemaphoreTake(wifiSemaphore, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
                safePrint("开始上传数据到云端...");
                
                // 模拟数据上传过程
                vTaskDelay(2000 / portTICK_PERIOD_MS);
                
                safePrint("数据上传完成!");
                
                // 重新释放WiFi信号量,让其他任务也能使用
                xSemaphoreGive(wifiSemaphore);
            } else {
                safePrint("WiFi未连接,跳过数据上传");
            }
        }
    }
}

void displayTask(void *parameter) {
    for(;;) {
        safePrint("=== 系统状态 ===");
        safePrint("WiFi状态: " + String(wifiConnected ? "已连接" : "未连接"));
        safePrint("当前温度: " + String(temperature) + "°C");
        safePrint("当前湿度: " + String(humidity) + "%");
        safePrint("================\n");
        
        vTaskDelay(15000 / portTICK_PERIOD_MS);  // 每15秒显示一次状态
    }
}

// 安全的串口打印函数
void safePrint(String message) {
    if (xSemaphoreTake(uartSemaphore, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
        Serial.println("[" + String(millis()) + "] " + message);
        xSemaphoreGive(uartSemaphore);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

这个例子展示了一个完整的IoT系统中信号量的使用:

  1. WiFi信号量:用于通知其他任务WiFi连接状态
  2. 传感器信号量:用于通知数据更新
  3. 串口互斥量:保护串口访问,避免输出混乱

信号量的获取与释放机制

信号量的获取和释放是多任务编程的核心操作。理解这些操作的内部机制,对于编写高效的并发程序至关重要。

获取信号量(Take)的过程:

BaseType_t xSemaphoreTake(
    SemaphoreHandle_t xSemaphore,  // 信号量句柄
    TickType_t xTicksToWait        // 等待时间(时钟节拍数)
);

当任务调用xSemaphoreTake()时,FreeRTOS会执行以下步骤:

  1. 检查信号量计数:如果计数大于0,立即减1并返回成功
  2. 计数为0时的处理:如果计数为0且等待时间为0,立即返回失败
  3. 进入等待状态:如果计数为0且等待时间大于0,任务进入阻塞状态
  4. 超时处理:如果在指定时间内没有获取到信号量,返回超时错误

释放信号量(Give)的过程:

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);

释放信号量的过程相对简单:

  1. 增加计数:将信号量计数加1(如果未达到最大值)
  2. 唤醒等待任务:如果有任务在等待此信号量,唤醒优先级最高的任务
  3. 任务切换:如果被唤醒的任务优先级更高,可能发生任务切换

让我们通过一个详细的例子来观察这个过程:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t testSemaphore;

void setup() {
    Serial.begin(115200);
    
    // 创建一个初始计数为0的二进制信号量
    testSemaphore = xSemaphoreCreateBinary();
    
    // 创建多个等待任务
    xTaskCreate(waitingTask, "Waiter_1", 2048, (void*)1, 1, NULL);
    xTaskCreate(waitingTask, "Waiter_2", 2048, (void*)2, 1, NULL);
    xTaskCreate(waitingTask, "Waiter_3", 2048, (void*)3, 1, NULL);
    
    // 创建信号量释放任务
    xTaskCreate(signalingTask, "Signaler", 2048, NULL, 2, NULL);
    
    Serial.println("信号量测试开始!");
}

void waitingTask(void *parameter) {
    int taskId = (int)parameter;
    
    for(;;) {
        Serial.printf("任务%d:开始等待信号量...\n", taskId);
        
        TickType_t startTime = xTaskGetTickCount();
        
        // 尝试获取信号量,最多等待5秒
        if (xSemaphoreTake(testSemaphore, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
            TickType_t endTime = xTaskGetTickCount();
            TickType_t waitTime = endTime - startTime;
            
            Serial.printf("任务%d:成功获取信号量!等待时间:%d ms\n", 
                         taskId, waitTime * portTICK_PERIOD_MS);
            
            // 模拟使用资源
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            
            Serial.printf("任务%d:使用完毕,准备下次等待\n", taskId);
        } else {
            Serial.printf("任务%d:等待信号量超时!\n", taskId);
        }
        
        // 等待一段时间再次尝试
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

void signalingTask(void *parameter) {
    for(;;) {
        Serial.println("信号发送者:等待3秒后释放信号量...");
        vTaskDelay(3000 / portTICK_PERIOD_MS);
        
        Serial.println("信号发送者:释放信号量!");
        xSemaphoreGive(testSemaphore);
        
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

运行这个程序,你会看到类似这样的输出:

信号量测试开始!
任务1:开始等待信号量...
任务2:开始等待信号量...
任务3:开始等待信号量...
信号发送者:等待3秒后释放信号量...
信号发送者:释放信号量!
任务1:成功获取信号量!等待时间:3000 ms
任务1:使用完毕,准备下次等待
任务1:开始等待信号量...
信号发送者:等待3秒后释放信号量...
信号发送者:释放信号量!
任务2:成功获取信号量!等待时间:8000 ms
任务2:使用完毕,准备下次等待

从输出可以看出,每次只有一个任务能够获取到二进制信号量,其他任务必须等待。这就是信号量的"排队"机制!

等待时间的艺术:

等待时间的设置是一门艺术,需要在响应性和资源利用率之间找到平衡:

// 不同的等待策略
xSemaphoreTake(semaphore, 0);                    // 立即返回,不等待
xSemaphoreTake(semaphore, 100 / portTICK_PERIOD_MS);  // 等待100ms
xSemaphoreTake(semaphore, portMAX_DELAY);        // 永久等待
  • 立即返回(0):适用于非关键操作,失败了可以稍后重试
  • 有限等待:适用于有实时性要求的场景,避免任务长时间阻塞
  • 永久等待:适用于必须获取资源才能继续的场景,但要小心死锁

优先级反转问题的深入剖析

优先级反转是多任务系统中一个非常经典的问题,它就像是"小鬼当家"——低优先级任务居然能够阻塞高优先级任务!

什么是优先级反转?

想象这样一个场景:

  • 任务H(高优先级)需要访问某个共享资源
  • 任务L(低优先级)正在使用这个资源
  • 任务M(中等优先级)开始运行,抢占了任务L的CPU时间

结果就是:任务H被任务L阻塞,而任务L又被任务M抢占,导致任务H实际上被中等优先级的任务M间接阻塞了!这就是优先级反转。

让我们用代码来重现这个经典问题:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t resourceSemaphore;

void setup() {
    Serial.begin(115200);
    
    // 创建一个互斥量来保护共享资源
    resourceSemaphore = xSemaphoreCreateMutex();
    
    // 创建三个不同优先级的任务
    xTaskCreate(lowPriorityTask, "Low_Priority", 2048, NULL, 1, NULL);
    xTaskCreate(mediumPriorityTask, "Medium_Priority", 2048, NULL, 2, NULL);
    xTaskCreate(highPriorityTask, "High_Priority", 2048, NULL, 3, NULL);
    
    Serial.println("优先级反转演示开始!");
}

void lowPriorityTask(void *parameter) {
    for(;;) {
        Serial.println("低优先级任务:尝试获取资源...");
        
        if (xSemaphoreTake(resourceSemaphore, portMAX_DELAY) == pdTRUE) {
            Serial.println("低优先级任务:获取资源成功,开始长时间操作...");
            
            // 模拟长时间的资源使用
            for (int i = 0; i < 10; i++) {
                Serial.printf("低优先级任务:正在使用资源... %d/10\n", i + 1);
                vTaskDelay(500 / portTICK_PERIOD_MS);
            }
            
            Serial.println("低优先级任务:释放资源");
            xSemaphoreGive(resourceSemaphore);
        }
        
        vTaskDelay(10000 / portTICK_PERIOD_MS);
    }
}

void mediumPriorityTask(void *parameter) {
    vTaskDelay(2000 / portTICK_PERIOD_MS);  // 延迟启动
    
    for(;;) {
        Serial.println("中等优先级任务:开始CPU密集型操作...");
        
        // 模拟CPU密集型操作(不使用共享资源)
        for (int i = 0; i < 20; i++) {
            Serial.printf("中等优先级任务:计算中... %d/20\n", i + 1);
            vTaskDelay(200 / portTICK_PERIOD_MS);
        }
        
        Serial.println("中等优先级任务:操作完成");
        vTaskDelay(8000 / portTICK_PERIOD_MS);
    }
}

void highPriorityTask(void *parameter) {
    vTaskDelay(3000 / portTICK_PERIOD_MS);  // 延迟启动,让低优先级任务先获取资源
    
    for(;;) {
        Serial.println("高优先级任务:急需访问共享资源!");
        
        TickType_t startTime = xTaskGetTickCount();
        
        if (xSemaphoreTake(resourceSemaphore, portMAX_DELAY) == pdTRUE) {
            TickType_t endTime = xTaskGetTickCount();
            TickType_t waitTime = endTime - startTime;
            
            Serial.printf("高优先级任务:终于获取到资源!等待时间:%d ms\n", 
                         waitTime * portTICK_PERIOD_MS);
            
            // 高优先级任务通常需要快速完成
            Serial.println("高优先级任务:快速使用资源");
            vTaskDelay(100 / portTICK_PERIOD_MS);
            
            Serial.println("高优先级任务:释放资源");
            xSemaphoreGive(resourceSemaphore);
        }
        
        vTaskDelay(15000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

运行这个程序,你会看到一个有趣的现象:高优先级任务明明应该优先执行,但却被迫等待低优先级任务释放资源。而在等待期间,中等优先级任务却能够抢占CPU,进一步延长了高优先级任务的等待时间!

优先级继承:解决方案的精髓

FreeRTOS提供了一个优雅的解决方案:优先级继承(Priority Inheritance)。当高优先级任务被低优先级任务持有的互斥量阻塞时,低优先级任务会临时继承高优先级任务的优先级,直到释放互斥量。

这就像是"临时提拔"——为了让高优先级任务能够尽快获得资源,系统临时提升了低优先级任务的优先级,让它能够抢占中等优先级任务的CPU时间,尽快完成工作并释放资源。

// 使用支持优先级继承的互斥量
SemaphoreHandle_t priorityInheritanceMutex;

void setup() {
    Serial.begin(115200);
    
    // 创建支持优先级继承的互斥量
    priorityInheritanceMutex = xSemaphoreCreateMutex();
    
    // 注意:FreeRTOS的互斥量默认支持优先级继承
    // 这与普通的二进制信号量不同!
    
    xTaskCreate(demonstratePriorityInheritance, "Demo_Task", 2048, NULL, 1, NULL);
    
    Serial.println("优先级继承演示");
}

void demonstratePriorityInheritance(void *parameter) {
    for(;;) {
        Serial.println("=== 优先级继承机制说明 ===");
        Serial.println("1. 互斥量自动支持优先级继承");
        Serial.println("2. 当高优先级任务被阻塞时,持有互斥量的低优先级任务会临时提升优先级");
        Serial.println("3. 这样可以防止中等优先级任务无限期地抢占CPU");
        Serial.println("4. 互斥量释放后,任务优先级恢复正常");
        Serial.println("=============================\n");
        
        vTaskDelay(10000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

信号量 vs 互斥量在优先级反转方面的区别:

这是一个很多人容易搞混的地方:

  • 二进制信号量:不支持优先级继承,可能发生优先级反转
  • 互斥量:自动支持优先级继承,有效防止优先级反转

所以,当你需要保护共享资源时,应该使用互斥量而不是二进制信号量!

互斥量完全指南:独占资源的守护神

互斥量的工作原理与内存模型

互斥量(Mutex)这个名字来源于"Mutual Exclusion"(互相排斥),它就像是一把智能锁,确保同一时间只有一个任务能够访问被保护的资源。

与信号量不同,互斥量有一个重要的概念:所有权(Ownership)。只有获取了互斥量的任务才能释放它,这就像是"谁借的钱谁还"一样简单明了。

互斥量的内存结构:

在ESP32的FreeRTOS实现中,互斥量实际上是一个特殊的信号量,但它包含了额外的信息:

typedef struct {
    UBaseType_t uxRecursiveCallCount;    // 递归调用计数
    void *pxMutexHolder;                 // 持有者任务句柄
    UBaseType_t uxBasePriority;          // 持有者的原始优先级
    // ... 其他内部字段
} MutexControlBlock_t;

这个结构让互斥量能够:

  1. 跟踪哪个任务持有了互斥量
  2. 实现优先级继承机制
  3. 支持递归获取(对于递归互斥量)
  4. 防止错误的释放操作

让我们通过一个详细的例子来理解互斥量的工作原理:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t sharedResourceMutex;
int sharedCounter = 0;

void setup() {
    Serial.begin(115200);
    
    // 创建互斥量
    sharedResourceMutex = xSemaphoreCreateMutex();
    
    if (sharedResourceMutex == NULL) {
        Serial.println("互斥量创建失败!");
        return;
    }
    
    // 创建多个任务来竞争共享资源
    xTaskCreate(incrementTask, "Increment_1", 2048, (void*)"任务A", 2, NULL);
    xTaskCreate(incrementTask, "Increment_2", 2048, (void*)"任务B", 2, NULL);
    xTaskCreate(incrementTask, "Increment_3", 2048, (void*)"任务C", 2, NULL);
    xTaskCreate(monitorTask, "Monitor", 2048, NULL, 1, NULL);
    
    Serial.println("互斥量演示开始!");
}

void incrementTask(void *parameter) {
    const char* taskName = (const char*)parameter;
    
    for(;;) {
        Serial.printf("%s:尝试获取互斥量...\n", taskName);
        
        // 获取互斥量,保护共享资源
        if (xSemaphoreTake(sharedResourceMutex, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
            Serial.printf("%s:成功获取互斥量,开始修改共享资源\n", taskName);
            
            // 临界区开始 - 只有一个任务能执行这里的代码
            int oldValue = sharedCounter;
            
            // 模拟复杂的操作过程
            Serial.printf("%s:读取当前值 = %d\n", taskName, oldValue);
            vTaskDelay(1000 / portTICK_PERIOD_MS);  // 模拟处理时间
            
            sharedCounter = oldValue + 1;
            Serial.printf("%s:更新后的值 = %d\n", taskName, sharedCounter);
            
            vTaskDelay(500 / portTICK_PERIOD_MS);   // 模拟额外处理
            // 临界区结束
            
            Serial.printf("%s:释放互斥量\n", taskName);
            xSemaphoreGive(sharedResourceMutex);
        } else {
            Serial.printf("%s:获取互斥量超时!\n", taskName);
        }
        
        // 等待一段时间再次尝试
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

void monitorTask(void *parameter) {
    for(;;) {
        Serial.printf("监控任务:当前共享计数器值 = %d\n", sharedCounter);
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

运行这个程序,你会看到类似这样的输出:

互斥量演示开始!
任务A:尝试获取互斥量...
任务B:尝试获取互斥量...
任务C:尝试获取互斥量...
任务A:成功获取互斥量,开始修改共享资源
任务A:读取当前值 = 0
监控任务:当前共享计数器值 = 0
任务A:更新后的值 = 1
任务A:释放互斥量
任务B:成功获取互斥量,开始修改共享资源
任务B:读取当前值 = 1
任务B:更新后的值 = 2
任务B:释放互斥量
监控任务:当前共享计数器值 = 2

从输出可以看出,即使有多个任务同时竞争,互斥量确保了每次只有一个任务能够修改共享资源,避免了数据竞争。

递归互斥量:解决重入问题的利器

有时候,我们会遇到这样的情况:一个任务已经获取了互斥量,但在执行过程中又需要再次获取同一个互斥量。如果使用普通的互斥量,这会导致死锁!

递归互斥量就是为了解决这个问题而设计的。它允许同一个任务多次获取同一个互斥量,只要获取和释放的次数匹配即可。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t recursiveMutex;

void setup() {
    Serial.begin(115200);
    
    // 创建递归互斥量
    recursiveMutex = xSemaphoreCreateRecursiveMutex();
    
    if (recursiveMutex == NULL) {
        Serial.println("递归互斥量创建失败!");
        return;
    }
    
    xTaskCreate(recursiveTask, "Recursive_Task", 2048, NULL, 1, NULL);
    
    Serial.println("递归互斥量演示开始!");
}
```cpp
void recursiveTask(void *parameter) {
    for(;;) {
        Serial.println("开始递归互斥量演示...");
        
        // 调用需要互斥量保护的函数
        processDataLevel1();
        
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

void processDataLevel1() {
    Serial.println("Level 1: 尝试获取递归互斥量...");
    
    if (xSemaphoreTakeRecursive(recursiveMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
        Serial.println("Level 1: 成功获取互斥量");
        
        // 模拟一些处理
        vTaskDelay(500 / portTICK_PERIOD_MS);
        
        // 调用另一个也需要同一个互斥量的函数
        processDataLevel2();
        
        Serial.println("Level 1: 释放互斥量");
        xSemaphoreGiveRecursive(recursiveMutex);
    } else {
        Serial.println("Level 1: 获取互斥量失败!");
    }
}

void processDataLevel2() {
    Serial.println("Level 2: 尝试获取递归互斥量...");
    
    if (xSemaphoreTakeRecursive(recursiveMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
        Serial.println("Level 2: 成功获取互斥量(递归)");
        
        // 模拟更多处理
        vTaskDelay(300 / portTICK_PERIOD_MS);
        
        // 甚至可以再次递归
        processDataLevel3();
        
        Serial.println("Level 2: 释放互斥量");
        xSemaphoreGiveRecursive(recursiveMutex);
    } else {
        Serial.println("Level 2: 获取互斥量失败!");
    }
}

void processDataLevel3() {
    Serial.println("Level 3: 尝试获取递归互斥量...");
    
    if (xSemaphoreTakeRecursive(recursiveMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
        Serial.println("Level 3: 成功获取互斥量(再次递归)");
        
        // 最终的处理逻辑
        Serial.println("Level 3: 执行核心业务逻辑");
        vTaskDelay(200 / portTICK_PERIOD_MS);
        
        Serial.println("Level 3: 释放互斥量");
        xSemaphoreGiveRecursive(recursiveMutex);
    } else {
        Serial.println("Level 3: 获取互斥量失败!");
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

这个例子展示了递归互斥量的强大之处:同一个任务可以多次获取同一个互斥量,只要确保获取和释放的次数匹配即可。

递归互斥量的内部计数机制:

递归互斥量内部维护一个计数器,记录被同一个任务获取的次数:

第1次获取:计数器 = 1
第2次获取:计数器 = 2
第3次获取:计数器 = 3
...
第1次释放:计数器 = 2
第2次释放:计数器 = 1
第3次释放:计数器 = 0(互斥量真正释放)

使用递归互斥量的经典场景:

  1. 函数调用链:函数A调用函数B,函数B调用函数C,它们都需要同一个互斥量
  2. 面向对象编程:类的公有方法调用私有方法,都需要保护同一个资源
  3. 状态机实现:不同状态处理函数可能相互调用,需要保护状态变量

互斥量的超时机制与错误处理

在实际项目中,合理的超时机制和错误处理是确保系统稳定性的关键。没有人愿意看到自己的ESP32因为一个任务无限期等待而整个系统"卡死"。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t criticalResourceMutex;
int criticalResource = 0;

void setup() {
    Serial.begin(115200);
    
    criticalResourceMutex = xSemaphoreCreateMutex();
    
    // 创建不同超时策略的任务
    xTaskCreate(impatientTask, "Impatient", 2048, NULL, 2, NULL);
    xTaskCreate(patientTask, "Patient", 2048, NULL, 1, NULL);
    xTaskCreate(resourceHogTask, "ResourceHog", 2048, NULL, 1, NULL);
    
    Serial.println("超时机制演示开始!");
}

void impatientTask(void *parameter) {
    for(;;) {
        Serial.println("急性子任务:我等不了太久!");
        
        // 只等待500ms
        if (xSemaphoreTake(criticalResourceMutex, 500 / portTICK_PERIOD_MS) == pdTRUE) {
            Serial.println("急性子任务:太好了,立即获取到资源!");
            
            criticalResource += 10;
            Serial.printf("急性子任务:快速处理,资源值 = %d\n", criticalResource);
            
            vTaskDelay(200 / portTICK_PERIOD_MS);  // 快速处理
            
            xSemaphoreGive(criticalResourceMutex);
            Serial.println("急性子任务:快速释放资源");
        } else {
            Serial.println("急性子任务:等不及了,去做别的事情!");
            
            // 执行备用方案
            Serial.println("急性子任务:执行备用处理逻辑");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void patientTask(void *parameter) {
    for(;;) {
        Serial.println("耐心任务:我可以等待较长时间");
        
        // 等待5秒
        TickType_t startTime = xTaskGetTickCount();
        
        if (xSemaphoreTake(criticalResourceMutex, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
            TickType_t waitTime = xTaskGetTickCount() - startTime;
            Serial.printf("耐心任务:等待了 %d ms 后获取到资源\n", 
                         waitTime * portTICK_PERIOD_MS);
            
            criticalResource += 50;
            Serial.printf("耐心任务:仔细处理,资源值 = %d\n", criticalResource);
            
            vTaskDelay(1000 / portTICK_PERIOD_MS);  // 仔细处理
            
            xSemaphoreGive(criticalResourceMutex);
            Serial.println("耐心任务:处理完成,释放资源");
        } else {
            Serial.println("耐心任务:连我都等超时了,肯定有问题!");
            
            // 记录错误或采取恢复措施
            logError("MUTEX_TIMEOUT", "耐心任务等待互斥量超时");
        }
        
        vTaskDelay(4000 / portTICK_PERIOD_MS);
    }
}

void resourceHogTask(void *parameter) {
    vTaskDelay(2000 / portTICK_PERIOD_MS);  // 延迟启动
    
    for(;;) {
        Serial.println("资源占用者:我要长时间使用资源!");
        
        if (xSemaphoreTake(criticalResourceMutex, portMAX_DELAY) == pdTRUE) {
            Serial.println("资源占用者:开始长时间占用资源...");
            
            // 模拟长时间的复杂操作
            for (int i = 0; i < 10; i++) {
                criticalResource += 1;
                Serial.printf("资源占用者:处理步骤 %d/10,资源值 = %d\n", 
                             i + 1, criticalResource);
                vTaskDelay(800 / portTICK_PERIOD_MS);
            }
            
            Serial.println("资源占用者:终于完成了,释放资源");
            xSemaphoreGive(criticalResourceMutex);
        }
        
        vTaskDelay(15000 / portTICK_PERIOD_MS);  // 长时间休息
    }
}

void logError(const char* errorCode, const char* description) {
    Serial.printf("错误日志 [%s]: %s (时间: %d ms)\n", 
                 errorCode, description, millis());
    
    // 在实际项目中,这里可以:
    // 1. 写入EEPROM或SD卡
    // 2. 发送到远程服务器
    // 3. 触发看门狗重启
    // 4. 激活备用系统
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

超时策略的选择指南:

  1. 立即返回(0):用于非关键操作,失败后有备用方案
  2. 短超时(100-1000ms):用于实时性要求高的操作
  3. 中等超时(1-5秒):用于重要但不紧急的操作
  4. 长超时(5-30秒):用于关键操作,但需要防止死锁
  5. 永久等待(portMAX_DELAY):只在确定不会死锁时使用

错误处理的最佳实践:

// 错误处理模板
BaseType_t mutexResult = xSemaphoreTake(myMutex, timeoutTicks);

switch (mutexResult) {
    case pdTRUE:
        // 成功获取互斥量
        // 执行临界区代码
        xSemaphoreGive(myMutex);
        break;
        
    case pdFALSE:
        // 获取失败(超时)
        Serial.println("警告:获取互斥量超时");
        
        // 选择处理策略:
        // 1. 执行备用方案
        // 2. 记录错误日志
        // 3. 通知用户
        // 4. 重试操作
        handleMutexTimeout();
        break;
        
    default:
        // 不应该到达这里
        Serial.println("错误:未知的互斥量返回值");
        break;
}

死锁问题的识别与预防

死锁是多任务编程中最令人头疼的问题之一。想象两个人在狭窄的桥上相遇,都不肯让路,结果谁也过不去。在程序中,死锁就是两个或多个任务相互等待对方释放资源,导致所有任务都无法继续执行。

经典的死锁场景:

// 这是一个会导致死锁的危险代码示例!
SemaphoreHandle_t mutexA;
SemaphoreHandle_t mutexB;

void task1(void *parameter) {
    for(;;) {
        // 任务1:先获取A,再获取B
        xSemaphoreTake(mutexA, portMAX_DELAY);
        Serial.println("任务1:获取到互斥量A");
        
        vTaskDelay(100 / portTICK_PERIOD_MS);  // 模拟处理时间
        
        xSemaphoreTake(mutexB, portMAX_DELAY);
        Serial.println("任务1:获取到互斥量B");
        
        // 执行需要两个资源的操作
        Serial.println("任务1:使用资源A和B");
        
        xSemaphoreGive(mutexB);
        xSemaphoreGive(mutexA);
        
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void task2(void *parameter) {
    for(;;) {
        // 任务2:先获取B,再获取A(顺序相反!)
        xSemaphoreTake(mutexB, portMAX_DELAY);
        Serial.println("任务2:获取到互斥量B");
        
        vTaskDelay(100 / portTICK_PERIOD_MS);  // 模拟处理时间
        
        xSemaphoreTake(mutexA, portMAX_DELAY);  // 死锁发生在这里!
        Serial.println("任务2:获取到互斥量A");
        
        // 这里的代码永远不会执行
        Serial.println("任务2:使用资源A和B");
        
        xSemaphoreGive(mutexA);
        xSemaphoreGive(mutexB);
        
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

死锁预防的黄金法则:

  1. 资源排序法:所有任务都按相同顺序获取多个互斥量
  2. 超时机制:避免使用portMAX_DELAY,总是设置合理的超时时间
  3. 资源分层:将资源分为不同层次,只能从低层向高层获取
  4. 银行家算法:在获取资源前检查是否会导致死锁

让我们看一个正确的实现:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t mutexA;
SemaphoreHandle_t mutexB;

void setup() {
    Serial.begin(115200);
    
    mutexA = xSemaphoreCreateMutex();
    mutexB = xSemaphoreCreateMutex();
    
    xTaskCreate(safeTask1, "Safe_Task_1", 2048, NULL, 1, NULL);
    xTaskCreate(safeTask2, "Safe_Task_2", 2048, NULL, 1, NULL);
    xTaskCreate(deadlockDetector, "Deadlock_Detector", 2048, NULL, 3, NULL);
    
    Serial.println("死锁预防演示开始!");
}

// 安全的任务实现:使用资源排序法
bool acquireMultipleMutexes(SemaphoreHandle_t first, SemaphoreHandle_t second, 
                           const char* taskName, TickType_t timeout) {
    Serial.printf("%s:尝试获取第一个互斥量...\n", taskName);
    
    if (xSemaphoreTake(first, timeout) == pdTRUE) {
        Serial.printf("%s:获取第一个互斥量成功\n", taskName);
        
        Serial.printf("%s:尝试获取第二个互斥量...\n", taskName);
        if (xSemaphoreTake(second, timeout) == pdTRUE) {
            Serial.printf("%s:获取第二个互斥量成功\n", taskName);
            return true;
        } else {
            Serial.printf("%s:获取第二个互斥量超时,释放第一个\n", taskName);
            xSemaphoreGive(first);
            return false;
        }
    } else {
        Serial.printf("%s:获取第一个互斥量超时\n", taskName);
        return false;
    }
}

void releaseMultipleMutexes(SemaphoreHandle_t first, SemaphoreHandle_t second, 
                           const char* taskName) {
    Serial.printf("%s:释放第二个互斥量\n", taskName);
    xSemaphoreGive(second);
    
    Serial.printf("%s:释放第一个互斥量\n", taskName);
    xSemaphoreGive(first);
}

void safeTask1(void *parameter) {
    for(;;) {
        Serial.println("安全任务1:开始工作");
        
        // 统一顺序:先A后B
        if (acquireMultipleMutexes(mutexA, mutexB, "安全任务1", 2000 / portTICK_PERIOD_MS)) {
            Serial.println("安全任务1:同时使用资源A和B");
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            
            releaseMultipleMutexes(mutexA, mutexB, "安全任务1");
        } else {
            Serial.println("安全任务1:无法获取所需资源,执行备用方案");
        }
        
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void safeTask2(void *parameter) {
    for(;;) {
        Serial.println("安全任务2:开始工作");
        
        // 同样的顺序:先A后B(避免死锁)
        if (acquireMultipleMutexes(mutexA, mutexB, "安全任务2", 2000 / portTICK_PERIOD_MS)) {
            Serial.println("安全任务2:同时使用资源A和B");
            vTaskDelay(800 / portTICK_PERIOD_MS);
            
            releaseMultipleMutexes(mutexA, mutexB, "安全任务2");
        } else {
            Serial.println("安全任务2:无法获取所需资源,执行备用方案");
        }
        
        vTaskDelay(4000 / portTICK_PERIOD_MS);
    }
}

void deadlockDetector(void *parameter) {
    TickType_t lastActivityTime = xTaskGetTickCount();
    
    for(;;) {
        TickType_t currentTime = xTaskGetTickCount();
        TickType_t elapsedTime = currentTime - lastActivityTime;
        
        if (elapsedTime > (10000 / portTICK_PERIOD_MS)) {
            Serial.println("死锁检测器:警告!系统可能发生死锁");
            Serial.printf("死锁检测器:已经 %d 秒没有活动\n", 
                         elapsedTime * portTICK_PERIOD_MS / 1000);
            
            // 在实际项目中,这里可以:
            // 1. 重启系统
            // 2. 记录错误日志
            // 3. 发送警报
            // 4. 尝试恢复
        }
        
        lastActivityTime = currentTime;
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

死锁检测的高级技巧:

// 死锁检测和恢复机制
class DeadlockDetector {
private:
    struct TaskInfo {
        TaskHandle_t handle;
        TickType_t lastActiveTime;
        const char* name;
    };
    
    TaskInfo monitoredTasks[10];
    int taskCount;
    
public:
    void addTask(TaskHandle_t task, const char* name) {
        if (taskCount < 10) {
            monitoredTasks[taskCount].handle = task;
            monitoredTasks[taskCount].name = name;
            monitoredTasks[taskCount].lastActiveTime = xTaskGetTickCount();
            taskCount++;
        }
    }
    
    void updateTaskActivity(TaskHandle_t task) {
        for (int i = 0; i < taskCount; i++) {
            if (monitoredTasks[i].handle == task) {
                monitoredTasks[i].lastActiveTime = xTaskGetTickCount();
                break;
            }
        }
    }
    
    bool checkForDeadlock(TickType_t timeoutMs) {
        TickType_t currentTime = xTaskGetTickCount();
        TickType_t timeoutTicks = timeoutMs / portTICK_PERIOD_MS;
        
        for (int i = 0; i < taskCount; i++) {
            if (currentTime - monitoredTasks[i].lastActiveTime > timeoutTicks) {
                Serial.printf("检测到可能的死锁:任务 %s 已经 %d ms 没有活动\n",
                             monitoredTasks[i].name,
                             (currentTime - monitoredTasks[i].lastActiveTime) * portTICK_PERIOD_MS);
                return true;
            }
        }
        
        return false;
    }
};

优先级继承算法的实现细节

优先级继承是FreeRTOS互斥量的一个重要特性,它能够有效防止优先级反转问题。让我们深入了解这个算法的工作原理。

优先级继承的工作流程:

  1. 高优先级任务被阻塞:当高优先级任务尝试获取被低优先级任务持有的互斥量时
  2. 临时提升优先级:低优先级任务临时继承高优先级任务的优先级
  3. 抢占中等优先级任务:提升后的低优先级任务能够抢占中等优先级任务的CPU时间
  4. 优先级恢复:当互斥量被释放时,任务优先级恢复到原始值
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t priorityInheritanceMutex;

void setup() {
    Serial.begin(115200);
    
    priorityInheritanceMutex = xSemaphoreCreateMutex();
    
    // 创建不同优先级的任务来演示优先级继承
    xTaskCreate(lowPriorityTask, "Low_Priority", 2048, NULL, 1, NULL);
    xTaskCreate(mediumPriorityTask, "Medium_Priority", 2048, NULL, 2, NULL);
    xTaskCreate(highPriorityTask, "High_Priority", 2048, NULL, 3, NULL);
    xTaskCreate(priorityMonitor, "Priority_Monitor", 2048, NULL, 4, NULL);
    
    Serial.println("优先级继承演示开始!");
}

void lowPriorityTask(void *parameter) {
    for(;;) {
        Serial.println("低优先级任务:开始执行");
        
        if (xSemaphoreTake(priorityInheritanceMutex, portMAX_DELAY) == pdTRUE) {
            Serial.println("低优先级任务:获取到互斥量");
            Serial.printf("低优先级任务:当前优先级 = %d\n", 
                         uxTaskPriorityGet(NULL));
            
            // 长时间持有互斥量
            for (int i = 0; i < 10; i++) {
                Serial.printf("低优先级任务:工作中... %d/10\n", i + 1);
                vTaskDelay(500 / portTICK_PERIOD_MS);
                
                // 检查当前优先级是否发生了变化
                UBaseType_t currentPriority = uxTaskPriorityGet(NULL);
                Serial.printf("低优先级任务:当前优先级 = %d\n", currentPriority);
            }
            
            Serial.println("低优先级任务:释放互斥量");
            xSemaphoreGive(priorityInheritanceMutex);
            
            Serial.printf("低优先级任务:释放后优先级 = %d\n", 
                         uxTaskPriorityGet(NULL));
        }
        
        vTaskDelay(15000 / portTICK_PERIOD_MS);
    }
}

void mediumPriorityTask(void *parameter) {
    vTaskDelay(1000 / portTICK_PERIOD_MS);  // 延迟启动
    
    for(;;) {
        Serial.println("中等优先级任务:开始CPU密集型工作");
        
        for (int i = 0; i < 15; i++) {
            Serial.printf("中等优先级任务:计算中... %d/15\n", i + 1);
            
            // 模拟CPU密集型操作
            volatile int dummy = 0;
            for (int j = 0; j < 100000; j++) {
                dummy++;
            }
            
            vTaskDelay(300 / portTICK_PERIOD_MS);
        }
        
        Serial.println("中等优先级任务:工作完成");
        vTaskDelay(10000 / portTICK_PERIOD_MS);
    }
}

void highPriorityTask(void *parameter) {
    vTaskDelay(2000 / portTICK_PERIOD_MS);  // 延迟启动,让低优先级任务先获取互斥量
    
    for(;;) {
        Serial.println("高优先级任务:紧急!需要立即访问共享资源");
        
        TickType_t startTime = xTaskGetTickCount();
        
        if (xSemaphoreTake(priorityInheritanceMutex, portMAX_DELAY) == pdTRUE) {
            TickType_t endTime = xTaskGetTickCount();
            TickType_t waitTime = endTime - startTime;
            
            Serial.printf("高优先级任务:等待了 %d ms 后获取到互斥量\n", 
                         waitTime * portTICK_PERIOD_MS);
            
            Serial.println("高优先级任务:快速处理紧急事务");
            vTaskDelay(200 / portTICK_PERIOD_MS);
            
            Serial.println("高优先级任务:释放互斥量");
            xSemaphoreGive(priorityInheritanceMutex);
        }
        
        vTaskDelay(20000 / portTICK_PERIOD_MS);
    }
}

void priorityMonitor(void *parameter) {
    TaskHandle_t lowTask = xTaskGetHandle("Low_Priority");
    TaskHandle_t mediumTask = xTaskGetHandle("Medium_Priority");
    TaskHandle_t highTask = xTaskGetHandle("High_Priority");
    
    for(;;) {
        Serial.println("=== 优先级监控 ===");
        
        if (lowTask != NULL) {
            Serial.printf("低优先级任务当前优先级: %d\n", 
                         uxTaskPriorityGet(lowTask));
        }
        
        if (mediumTask != NULL) {
            Serial.printf("中等优先级任务当前优先级: %d\n", 
                         uxTaskPriorityGet(mediumTask));
        }
        
        if (highTask != NULL) {
            Serial.printf("高优先级任务当前优先级: %d\n", 
                         uxTaskPriorityGet(highTask));
        }
        
        Serial.println("==================\n");
        
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

优先级继承的数学模型:

优先级继承可以用以下公式表示:

Peffective=max⁡(Poriginal,Pinherited)P_{effective} = \max(P_{original}, P_{inherited})Peffective=max(Poriginal,Pinherited)

其中:

  • PeffectiveP_{effective}Peffective 是任务的有效优先级
  • PoriginalP_{original}Poriginal 是任务的原始优先级
  • PinheritedP_{inherited}Pinherited 是继承的优先级

优先级继承链:

在复杂的系统中,优先级继承可能形成链式传递:

任务A (优先级1) 等待 互斥量X (被任务B持有)
任务B (优先级2) 等待 互斥量Y (被任务C持有)
任务C (优先级3)

结果:
任务C 继承优先级1
任务B 继承优先级1
任务A 保持优先级1

这种链式继承确保了高优先级任务的需求能够传播到整个依赖链中。

实战演练:经典应用场景的代码实现

理论知识再丰富,不如一个实际的项目来得实在!让我们通过几个经典的应用场景,看看信号量和互斥量在真实项目中是如何发挥作用的。

生产者-消费者模式的优雅实现

生产者-消费者模式是多任务编程中最经典的模式之一。想象一个智能工厂:生产线不断生产产品,仓库负责存储,销售部门负责销售。如果生产太快,仓库会爆满;如果生产太慢,销售部门就没货可卖。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"

// 缓冲区配置
#define BUFFER_SIZE 10
#define MAX_PRODUCERS 3
#define MAX_CONSUMERS 2

// 同步原语
SemaphoreHandle_t emptySlots;      // 空槽位信号量
SemaphoreHandle_t fullSlots;       // 满槽位信号量
SemaphoreHandle_t bufferMutex;     // 缓冲区互斥量

// 共享缓冲区
typedef struct {
    int data;
    int producerId;
    unsigned long timestamp;
} Product;

Product buffer[BUFFER_SIZE];
int bufferIn = 0;   // 生产者写入位置
int bufferOut = 0;  // 消费者读取位置

void setup() {
    Serial.begin(115200);
    
    // 创建同步原语
    emptySlots = xSemaphoreCreateCounting(BUFFER_SIZE, BUFFER_SIZE);  // 初始全空
    fullSlots = xSemaphoreCreateCounting(BUFFER_SIZE, 0);             // 初始无产品
    bufferMutex = xSemaphoreCreateMutex();
    
    if (emptySlots == NULL || fullSlots == NULL || bufferMutex == NULL) {
        Serial.println("信号量创建失败!");
        return;
    }
    
    // 创建生产者任务
    for (int i = 0; i < MAX_PRODUCERS; i++) {
        char taskName[20];
        sprintf(taskName, "Producer_%d", i + 1);
        xTaskCreate(producerTask, taskName, 2048, (void*)(i + 1), 2, NULL);
    }
    
    // 创建消费者任务
    for (int i = 0; i < MAX_CONSUMERS; i++) {
        char taskName[20];
        sprintf(taskName, "Consumer_%d", i + 1);
        xTaskCreate(consumerTask, taskName, 2048, (void*)(i + 1), 1, NULL);
    }
    
    // 创建监控任务
    xTaskCreate(monitorTask, "Monitor", 2048, NULL, 3, NULL);
    
    Serial.println("生产者-消费者系统启动!");
}

void producerTask(void *parameter) {
    int producerId = (int)parameter;
    int productCounter = 0;
    
    for(;;) {
        // 生产产品
        productCounter++;
        Product newProduct = {
            .data = productCounter,
            .producerId = producerId,
            .timestamp = millis()
        };
        
        Serial.printf("生产者%d:生产了产品#%d\n", producerId, productCounter);
        
        // 等待空槽位
        if (xSemaphoreTake(emptySlots, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
            // 获取缓冲区访问权限
            if (xSemaphoreTake(bufferMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
                // 临界区:写入缓冲区
                buffer[bufferIn] = newProduct;
                Serial.printf("生产者%d:将产品#%d放入缓冲区位置%d\n", 
                             producerId, productCounter, bufferIn);
                
                bufferIn = (bufferIn + 1) % BUFFER_SIZE;
                
                xSemaphoreGive(bufferMutex);
                
                // 通知消费者有新产品
                xSemaphoreGive(fullSlots);
            } else {
                Serial.printf("生产者%d:获取缓冲区访问权限超时\n", producerId);
                // 归还空槽位
                xSemaphoreGive(emptySlots);
            }
        } else {
            Serial.printf("生产者%d:缓冲区已满,等待超时\n", producerId);
        }
        
        // 模拟生产时间
        vTaskDelay((1000 + random(2000)) / portTICK_PERIOD_MS);
    }
}

void consumerTask(void *parameter) {
    int consumerId = (int)parameter;
    
    for(;;) {
        // 等待产品
        if (xSemaphoreTake(fullSlots, 3000 / portTICK_PERIOD_MS) == pdTRUE) {
            // 获取缓冲区访问权限
            if (xSemaphoreTake(bufferMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
                // 临界区:从缓冲区读取
                Product product = buffer[bufferOut];
                Serial.printf("消费者%d:从位置%d取出产品#%d (生产者%d生产)\n", 
                             consumerId, bufferOut, product.data, product.producerId);
                
                bufferOut = (bufferOut + 1) % BUFFER_SIZE;
                
                xSemaphoreGive(bufferMutex);
                
                // 通知生产者有空槽位
                xSemaphoreGive(emptySlots);
                
                // 模拟消费处理时间
                Serial.printf("消费者%d:正在处理产品#%d...\n", consumerId, product.data);
                vTaskDelay((500 + random(1000)) / portTICK_PERIOD_MS);
                
                Serial.printf("消费者%d:产品#%d处理完成\n", consumerId, product.data);
            } else {
                Serial.printf("消费者%d:获取缓冲区访问权限超时\n", consumerId);
                // 归还产品槽位
                xSemaphoreGive(fullSlots);
            }
        } else {
            Serial.printf("消费者%d:没有产品可消费\n", consumerId);
        }
        
        // 消费间隔
        vTaskDelay((800 + random(1200)) / portTICK_PERIOD_MS);
    }
}

void monitorTask(void *parameter) {
    for(;;) {
        // 获取当前缓冲区状态
        UBaseType_t emptyCount = uxSemaphoreGetCount(emptySlots);
        UBaseType_t fullCount = uxSemaphoreGetCount(fullSlots);
        
        Serial.println("=== 缓冲区状态监控 ===");
        Serial.printf("空槽位数量: %d/%d\n", emptyCount, BUFFER_SIZE);
        Serial.printf("产品数量: %d/%d\n", fullCount, BUFFER_SIZE);
        Serial.printf("缓冲区利用率: %.1f%%\n", (float)fullCount / BUFFER_SIZE * 100);
        
        if (fullCount == 0) {
            Serial.println("警告:缓冲区为空,消费者可能饥饿!");
        } else if (emptyCount == 0) {
            Serial.println("警告:缓冲区已满,生产者可能阻塞!");
        }
        
        Serial.println("========================\n");
        
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

这个实现展示了生产者-消费者模式的精髓:

  1. 计数信号量管理缓冲区容量
  2. 互斥量保护共享缓冲区
  3. 监控任务实时显示系统状态

多任务访问串口的同步控制

在ESP32项目中,多个任务都需要向串口输出信息是很常见的情况。如果没有同步控制,输出的信息会混乱不堪,就像多个人同时说话一样。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

SemaphoreHandle_t serialMutex;
SemaphoreHandle_t logSemaphore;

// 日志级别枚举
typedef enum {
    LOG_DEBUG = 0,
    LOG_INFO = 1,
    LOG_WARNING = 2,
    LOG_ERROR = 3
} LogLevel;

// 日志消息结构
typedef struct {
    LogLevel level;
    char message[128];
    char taskName[16];
    unsigned long timestamp;
} LogMessage;

// 日志缓冲区
#define LOG_BUFFER_SIZE 20
LogMessage logBuffer[LOG_BUFFER_SIZE];
int logBufferIn = 0;
int logBufferOut = 0;
int logBufferCount = 0;

void setup() {
    Serial.begin(115200);
    
    // 创建串口互斥量
    serialMutex = xSemaphoreCreateMutex();
    logSemaphore = xSemaphoreCreateBinary();
    
    if (serialMutex == NULL || logSemaphore == NULL) {
        Serial.println("互斥量创建失败!");
        return;
    }
    
    // 创建日志处理任务
    xTaskCreate(logProcessorTask, "Log_Processor", 2048, NULL, 3, NULL);
    
    // 创建多个业务任务
    xTaskCreate(sensorTask, "Sensor_Task", 2048, NULL, 2, NULL);
    xTaskCreate(networkTask, "Network_Task", 2048, NULL, 2, NULL);
    xTaskCreate(storageTask, "Storage_Task", 2048, NULL, 1, NULL);
    xTaskCreate(displayTask, "Display_Task", 2048, NULL, 1, NULL);
    
    safePrint("多任务串口同步系统启动!", LOG_INFO);
}

// 安全的串口打印函数
void safePrint(const char* message, LogLevel level) {
    const char* levelStrings[] = {"DEBUG", "INFO", "WARN", "ERROR"};
    const char* taskName = pcTaskGetTaskName(NULL);
    
    if (xSemaphoreTake(serialMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
        Serial.printf("[%8lu] [%s] [%s] %s\n", 
                     millis(), levelStrings[level], taskName, message);
        xSemaphoreGive(serialMutex);
    } else {
        // 如果无法获取互斥量,将消息加入缓冲区
        addLogToBuffer(message, level, taskName);
    }
}

// 添加日志到缓冲区
void addLogToBuffer(const char* message, LogLevel level, const char* taskName) {
    if (logBufferCount < LOG_BUFFER_SIZE) {
        LogMessage* logMsg = &logBuffer[logBufferIn];
        logMsg->level = level;
        logMsg->timestamp = millis();
        strncpy(logMsg->message, message, sizeof(logMsg->message) - 1);
        strncpy(logMsg->taskName, taskName, sizeof(logMsg->taskName) - 1);
        
        logBufferIn = (logBufferIn + 1) % LOG_BUFFER_SIZE;
        logBufferCount++;
        
        // 通知日志处理任务
        xSemaphoreGive(logSemaphore);
    }
}

// 日志处理任务
void logProcessorTask(void *parameter) {
    const char* levelStrings[] = {"DEBUG", "INFO", "WARN", "ERROR"};
    
    for(;;) {
        // 等待日志消息
        if (xSemaphoreTake(logSemaphore, portMAX_DELAY) == pdTRUE) {
            if (logBufferCount > 0) {
                LogMessage* logMsg = &logBuffer[logBufferOut];
                
                // 获取串口访问权限
                if (xSemaphoreTake(serialMutex, 2000 / portTICK_PERIOD_MS) == pdTRUE) {
                    Serial.printf("[%8lu] [%s] [%s] %s (从缓冲区)\n", 
                                 logMsg->timestamp, levelStrings[logMsg->level], 
                                 logMsg->taskName, logMsg->message);
                    
                    xSemaphoreGive(serialMutex);
                    
                    logBufferOut = (logBufferOut + 1) % LOG_BUFFER_SIZE;
                    logBufferCount--;
                }
            }
        }
    }
}

void sensorTask(void *parameter) {
    float temperature = 25.0;
    float humidity = 60.0;
    
    for(;;) {
        // 模拟传感器读取
        temperature += (random(-10, 11) / 10.0);
        humidity += (random(-5, 6) / 10.0);
        
        char message[64];
        sprintf(message, "温度: %.1f°C, 湿度: %.1f%%", temperature, humidity);
        safePrint(message, LOG_INFO);
        
        if (temperature > 35.0) {
            safePrint("温度过高警告!", LOG_WARNING);
        }
        
        if (humidity < 30.0 || humidity > 80.0) {
            safePrint("湿度异常!", LOG_WARNING);
        }
        
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void networkTask(void *parameter) {
    bool connected = false;
    int connectionAttempts = 0;
    
    for(;;) {
        if (!connected) {
            connectionAttempts++;
            char message[64];
            sprintf(message, "尝试连接网络... (第%d次)", connectionAttempts);
            safePrint(message, LOG_INFO);
            ```cpp
            // 模拟网络连接
            vTaskDelay(2000 / portTICK_PERIOD_MS);
            
            if (random(0, 100) < 70) {  // 70% 成功率
                connected = true;
                safePrint("网络连接成功!", LOG_INFO);
                connectionAttempts = 0;
            } else {
                safePrint("网络连接失败,稍后重试", LOG_ERROR);
                if (connectionAttempts >= 5) {
                    safePrint("网络连接多次失败,可能存在问题", LOG_ERROR);
                    connectionAttempts = 0;
                }
            }
        } else {
            // 已连接,模拟数据传输
            if (random(0, 100) < 10) {  // 10% 断开概率
                connected = false;
                safePrint("网络连接断开!", LOG_WARNING);
            } else {
                safePrint("数据上传成功", LOG_DEBUG);
            }
        }
        
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

void storageTask(void *parameter) {
    int fileCount = 0;
    
    for(;;) {
        // 模拟文件操作
        fileCount++;
        char message[64];
        sprintf(message, "保存文件 data_%d.txt", fileCount);
        safePrint(message, LOG_INFO);
        
        // 模拟写入时间
        vTaskDelay(1500 / portTICK_PERIOD_MS);
        
        if (random(0, 100) < 5) {  // 5% 失败率
            safePrint("文件写入失败,磁盘空间不足?", LOG_ERROR);
        } else {
            safePrint("文件保存成功", LOG_DEBUG);
        }
        
        vTaskDelay(8000 / portTICK_PERIOD_MS);
    }
}

void displayTask(void *parameter) {
    for(;;) {
        safePrint("更新显示内容", LOG_DEBUG);
        
        // 模拟显示更新
        vTaskDelay(500 / portTICK_PERIOD_MS);
        
        if (random(0, 100) < 15) {  // 15% 概率显示特殊信息
            safePrint("用户交互检测", LOG_INFO);
        }
        
        vTaskDelay(4000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

SPI总线的多设备管理

SPI总线是ESP32中常用的通信接口,但它是半双工的,同一时间只能与一个设备通信。这就需要用信号量来协调多个任务对SPI总线的访问。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "SPI.h"

// SPI设备信息结构
typedef struct {
    int csPin;
    int clockSpeed;
    int dataMode;
    const char* deviceName;
} SPIDevice;

// SPI总线管理
SemaphoreHandle_t spiBusMutex;
SemaphoreHandle_t spiResultSemaphore;

// 定义SPI设备
SPIDevice devices[] = {
    {5, 1000000, SPI_MODE0, "温度传感器"},
    {17, 2000000, SPI_MODE1, "加速度计"},
    {16, 500000, SPI_MODE0, "SD卡"},
    {4, 8000000, SPI_MODE2, "显示屏"}
};

#define DEVICE_COUNT (sizeof(devices) / sizeof(devices[0]))

// 共享结果缓冲区
typedef struct {
    int deviceId;
    uint8_t data[32];
    int dataLength;
    bool success;
    unsigned long timestamp;
} SPIResult;

SPIResult spiResults[10];
int resultIndex = 0;

void setup() {
    Serial.begin(115200);
    
    // 初始化SPI
    SPI.begin();
    
    // 创建SPI总线互斥量
    spiBusMutex = xSemaphoreCreateMutex();
    spiResultSemaphore = xSemaphoreCreateBinary();
    
    if (spiBusMutex == NULL || spiResultSemaphore == NULL) {
        Serial.println("SPI互斥量创建失败!");
        return;
    }
    
    // 初始化片选引脚
    for (int i = 0; i < DEVICE_COUNT; i++) {
        pinMode(devices[i].csPin, OUTPUT);
        digitalWrite(devices[i].csPin, HIGH);
    }
    
    // 创建设备访问任务
    for (int i = 0; i < DEVICE_COUNT; i++) {
        char taskName[20];
        sprintf(taskName, "SPI_Device_%d", i);
        xTaskCreate(spiDeviceTask, taskName, 2048, (void*)i, 2, NULL);
    }
    
    // 创建结果处理任务
    xTaskCreate(spiResultProcessor, "SPI_Result_Processor", 2048, NULL, 1, NULL);
    
    Serial.println("SPI多设备管理系统启动!");
}

void spiDeviceTask(void *parameter) {
    int deviceId = (int)parameter;
    SPIDevice* device = &devices[deviceId];
    
    for(;;) {
        Serial.printf("设备%d (%s):请求SPI总线访问\n", deviceId, device->deviceName);
        
        // 请求SPI总线访问权限
        if (xSemaphoreTake(spiBusMutex, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
            Serial.printf("设备%d (%s):获得SPI总线访问权限\n", deviceId, device->deviceName);
            
            // 配置SPI参数
            SPI.beginTransaction(SPISettings(device->clockSpeed, MSBFIRST, device->dataMode));
            
            // 选择设备
            digitalWrite(device->csPin, LOW);
            
            // 执行SPI通信
            bool success = performSPITransaction(deviceId);
            
            // 释放设备
            digitalWrite(device->csPin, HIGH);
            SPI.endTransaction();
            
            Serial.printf("设备%d (%s):SPI通信%s\n", 
                         deviceId, device->deviceName, success ? "成功" : "失败");
            
            // 释放SPI总线
            xSemaphoreGive(spiBusMutex);
        } else {
            Serial.printf("设备%d (%s):获取SPI总线超时!\n", deviceId, device->deviceName);
        }
        
        // 不同设备有不同的访问频率
        int delayTime = 2000 + (deviceId * 1000);
        vTaskDelay(delayTime / portTICK_PERIOD_MS);
    }
}

bool performSPITransaction(int deviceId) {
    SPIDevice* device = &devices[deviceId];
    uint8_t txData[4] = {0x00, 0x01, 0x02, 0x03};
    uint8_t rxData[4] = {0};
    
    Serial.printf("设备%d (%s):开始SPI数据传输...\n", deviceId, device->deviceName);
    
    // 模拟不同设备的通信协议
    switch (deviceId) {
        case 0:  // 温度传感器
            txData[0] = 0xD0;  // 读取温度寄存器
            break;
        case 1:  // 加速度计
            txData[0] = 0x80 | 0x32;  // 读取X轴数据
            break;
        case 2:  // SD卡
            txData[0] = 0x58;  // CMD24 (写块)
            break;
        case 3:  // 显示屏
            txData[0] = 0x2A;  // 设置列地址
            break;
    }
    
    // 执行SPI传输
    for (int i = 0; i < 4; i++) {
        rxData[i] = SPI.transfer(txData[i]);
        delayMicroseconds(10);  // 设备间延迟
    }
    
    // 模拟传输时间
    vTaskDelay(100 / portTICK_PERIOD_MS);
    
    // 保存结果
    SPIResult* result = &spiResults[resultIndex];
    result->deviceId = deviceId;
    result->dataLength = 4;
    memcpy(result->data, rxData, 4);
    result->success = (random(0, 100) < 90);  // 90% 成功率
    result->timestamp = millis();
    
    resultIndex = (resultIndex + 1) % 10;
    
    // 通知结果处理任务
    xSemaphoreGive(spiResultSemaphore);
    
    return result->success;
}

void spiResultProcessor(void *parameter) {
    for(;;) {
        // 等待SPI结果
        if (xSemaphoreTake(spiResultSemaphore, portMAX_DELAY) == pdTRUE) {
            // 处理最新的结果
            int currentIndex = (resultIndex - 1 + 10) % 10;
            SPIResult* result = &spiResults[currentIndex];
            
            Serial.printf("结果处理器:设备%d (%s) 的数据处理\n", 
                         result->deviceId, devices[result->deviceId].deviceName);
            
            if (result->success) {
                Serial.printf("接收数据: ");
                for (int i = 0; i < result->dataLength; i++) {
                    Serial.printf("0x%02X ", result->data[i]);
                }
                Serial.println();
                
                // 根据设备类型处理数据
                processDeviceData(result);
            } else {
                Serial.printf("设备%d 数据传输失败\n", result->deviceId);
            }
        }
    }
}

void processDeviceData(SPIResult* result) {
    switch (result->deviceId) {
        case 0:  // 温度传感器
            {
                float temperature = (result->data[0] << 8 | result->data[1]) / 10.0;
                Serial.printf("温度: %.1f°C\n", temperature);
            }
            break;
            
        case 1:  // 加速度计
            {
                int16_t accelX = (result->data[0] << 8 | result->data[1]);
                Serial.printf("加速度X: %d\n", accelX);
            }
            break;
            
        case 2:  // SD卡
            Serial.println("SD卡写入操作完成");
            break;
            
        case 3:  // 显示屏
            Serial.println("显示屏更新完成");
            break;
    }
}

void loop() {
    // 主循环可以执行其他任务
    static int counter = 0;
    counter++;
    
    if (counter % 10 == 0) {
        Serial.printf("主循环运行中... (计数: %d)\n", counter);
    }
    
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

WiFi连接状态的线程安全通知

WiFi连接状态的变化需要通知多个任务,这是一个典型的"一对多"通知场景。我们可以使用信号量来实现这种通知机制。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "WiFi.h"

// WiFi状态管理
typedef enum {
    WIFI_DISCONNECTED = 0,
    WIFI_CONNECTING = 1,
    WIFI_CONNECTED = 2,
    WIFI_FAILED = 3
} WiFiStatus;

// 同步原语
SemaphoreHandle_t wifiStatusMutex;
SemaphoreHandle_t wifiConnectedSemaphore;
SemaphoreHandle_t wifiDisconnectedSemaphore;

// 全局状态
WiFiStatus currentWiFiStatus = WIFI_DISCONNECTED;
char currentSSID[32] = "";
int connectionAttempts = 0;
unsigned long lastConnectionTime = 0;

// WiFi配置
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";

void setup() {
    Serial.begin(115200);
    
    // 创建同步原语
    wifiStatusMutex = xSemaphoreCreateMutex();
    wifiConnectedSemaphore = xSemaphoreCreateBinary();
    wifiDisconnectedSemaphore = xSemaphoreCreateBinary();
    
    if (wifiStatusMutex == NULL || wifiConnectedSemaphore == NULL || 
        wifiDisconnectedSemaphore == NULL) {
        Serial.println("WiFi同步原语创建失败!");
        return;
    }
    
    // 创建WiFi管理任务
    xTaskCreate(wifiManagerTask, "WiFi_Manager", 4096, NULL, 3, NULL);
    
    // 创建依赖WiFi的业务任务
    xTaskCreate(webServerTask, "Web_Server", 4096, NULL, 2, NULL);
    xTaskCreate(mqttClientTask, "MQTT_Client", 4096, NULL, 2, NULL);
    xTaskCreate(ntpSyncTask, "NTP_Sync", 2048, NULL, 1, NULL);
    xTaskCreate(wifiStatusMonitor, "WiFi_Monitor", 2048, NULL, 1, NULL);
    
    Serial.println("WiFi连接状态管理系统启动!");
}

void wifiManagerTask(void *parameter) {
    WiFi.mode(WIFI_STA);
    
    for(;;) {
        WiFiStatus currentStatus = getWiFiStatus();
        
        switch (currentStatus) {
            case WIFI_DISCONNECTED:
                Serial.println("WiFi管理器:开始连接WiFi...");
                setWiFiStatus(WIFI_CONNECTING);
                
                WiFi.begin(ssid, password);
                connectionAttempts++;
                
                // 等待连接结果
                {
                    int timeout = 20;  // 20秒超时
                    while (timeout > 0 && WiFi.status() != WL_CONNECTED) {
                        vTaskDelay(1000 / portTICK_PERIOD_MS);
                        timeout--;
                        Serial.printf("WiFi管理器:连接中... (%d秒)\n", 20 - timeout);
                    }
                    
                    if (WiFi.status() == WL_CONNECTED) {
                        Serial.printf("WiFi管理器:连接成功!IP: %s\n", WiFi.localIP().toString().c_str());
                        setWiFiStatus(WIFI_CONNECTED);
                        strcpy(currentSSID, ssid);
                        lastConnectionTime = millis();
                        connectionAttempts = 0;
                        
                        // 通知所有等待连接的任务
                        notifyWiFiConnected();
                    } else {
                        Serial.printf("WiFi管理器:连接失败 (第%d次尝试)\n", connectionAttempts);
                        setWiFiStatus(WIFI_FAILED);
                        
                        if (connectionAttempts >= 5) {
                            Serial.println("WiFi管理器:多次连接失败,等待更长时间后重试");
                            vTaskDelay(30000 / portTICK_PERIOD_MS);
                            connectionAttempts = 0;
                        }
                    }
                }
                break;
                
            case WIFI_CONNECTING:
                // 等待连接完成
                vTaskDelay(1000 / portTICK_PERIOD_MS);
                break;
                
            case WIFI_CONNECTED:
                // 检查连接状态
                if (WiFi.status() != WL_CONNECTED) {
                    Serial.println("WiFi管理器:检测到连接断开");
                    setWiFiStatus(WIFI_DISCONNECTED);
                    
                    // 通知所有依赖WiFi的任务
                    notifyWiFiDisconnected();
                } else {
                    // 连接正常,定期检查
                    vTaskDelay(5000 / portTICK_PERIOD_MS);
                }
                break;
                
            case WIFI_FAILED:
                Serial.println("WiFi管理器:等待后重新尝试连接");
                vTaskDelay(10000 / portTICK_PERIOD_MS);
                setWiFiStatus(WIFI_DISCONNECTED);
                break;
        }
    }
}

WiFiStatus getWiFiStatus() {
    WiFiStatus status;
    if (xSemaphoreTake(wifiStatusMutex, 100 / portTICK_PERIOD_MS) == pdTRUE) {
        status = currentWiFiStatus;
        xSemaphoreGive(wifiStatusMutex);
    } else {
        status = WIFI_DISCONNECTED;  // 默认值
    }
    return status;
}

void setWiFiStatus(WiFiStatus newStatus) {
    if (xSemaphoreTake(wifiStatusMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
        currentWiFiStatus = newStatus;
        xSemaphoreGive(wifiStatusMutex);
    }
}

void notifyWiFiConnected() {
    // 通知所有等待WiFi连接的任务
    // 注意:二进制信号量只能通知一个任务,所以我们需要多次调用
    xSemaphoreGive(wifiConnectedSemaphore);
    xSemaphoreGive(wifiConnectedSemaphore);
    xSemaphoreGive(wifiConnectedSemaphore);
}

void notifyWiFiDisconnected() {
    // 通知所有需要知道断开连接的任务
    xSemaphoreGive(wifiDisconnectedSemaphore);
    xSemaphoreGive(wifiDisconnectedSemaphore);
    xSemaphoreGive(wifiDisconnectedSemaphore);
}

void webServerTask(void *parameter) {
    bool serverRunning = false;
    
    for(;;) {
        if (!serverRunning) {
            Serial.println("Web服务器:等待WiFi连接...");
            
            // 等待WiFi连接
            if (xSemaphoreTake(wifiConnectedSemaphore, portMAX_DELAY) == pdTRUE) {
                Serial.println("Web服务器:WiFi已连接,启动HTTP服务器");
                
                // 模拟启动Web服务器
                serverRunning = true;
                
                Serial.printf("Web服务器:服务器运行在 http://%s/\n", 
                             WiFi.localIP().toString().c_str());
            }
        } else {
            // 检查WiFi状态
            if (getWiFiStatus() != WIFI_CONNECTED) {
                Serial.println("Web服务器:WiFi断开,停止服务器");
                serverRunning = false;
            } else {
                // 模拟处理HTTP请求
                Serial.println("Web服务器:处理HTTP请求");
                vTaskDelay(5000 / portTICK_PERIOD_MS);
            }
        }
        
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void mqttClientTask(void *parameter) {
    bool mqttConnected = false;
    
    for(;;) {
        if (!mqttConnected) {
            Serial.println("MQTT客户端:等待WiFi连接...");
            
            // 等待WiFi连接
            if (xSemaphoreTake(wifiConnectedSemaphore, portMAX_DELAY) == pdTRUE) {
                Serial.println("MQTT客户端:WiFi已连接,连接MQTT服务器");
                
                // 模拟MQTT连接
                vTaskDelay(2000 / portTICK_PERIOD_MS);
                
                if (random(0, 100) < 80) {  // 80% 成功率
                    mqttConnected = true;
                    Serial.println("MQTT客户端:连接成功");
                } else {
                    Serial.println("MQTT客户端:连接失败,稍后重试");
                }
            }
        } else {
            // 检查连接状态
            if (getWiFiStatus() != WIFI_CONNECTED) {
                Serial.println("MQTT客户端:WiFi断开,断开MQTT连接");
                mqttConnected = false;
            } else {
                // 模拟MQTT消息处理
                Serial.println("MQTT客户端:发送心跳消息");
                vTaskDelay(10000 / portTICK_PERIOD_MS);
            }
        }
        
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void ntpSyncTask(void *parameter) {
    bool timeSynced = false;
    
    for(;;) {
        if (!timeSynced) {
            Serial.println("NTP同步:等待WiFi连接...");
            
            // 等待WiFi连接
            if (xSemaphoreTake(wifiConnectedSemaphore, portMAX_DELAY) == pdTRUE) {
                Serial.println("NTP同步:WiFi已连接,开始时间同步");
                
                // 模拟NTP同步
                vTaskDelay(3000 / portTICK_PERIOD_MS);
                
                timeSynced = true;
                Serial.println("NTP同步:时间同步成功");
            }
        } else {
            // 定期重新同步
            if (getWiFiStatus() == WIFI_CONNECTED) {
                Serial.println("NTP同步:定期时间同步");
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            } else {
                timeSynced = false;
                Serial.println("NTP同步:WiFi断开,时间同步失效");
            }
        }
        
        vTaskDelay(60000 / portTICK_PERIOD_MS);  // 每分钟检查一次
    }
}

void wifiStatusMonitor(void *parameter) {
    for(;;) {
        WiFiStatus status = getWiFiStatus();
        const char* statusStrings[] = {"断开", "连接中", "已连接", "失败"};
        
        Serial.println("=== WiFi状态监控 ===");
        Serial.printf("状态: %s\n", statusStrings[status]);
        
        if (status == WIFI_CONNECTED) {
            Serial.printf("SSID: %s\n", currentSSID);
            Serial.printf("IP地址: %s\n", WiFi.localIP().toString().c_str());
            Serial.printf("信号强度: %d dBm\n", WiFi.RSSI());
            Serial.printf("连接时长: %lu 秒\n", (millis() - lastConnectionTime) / 1000);
        } else if (status == WIFI_FAILED) {
            Serial.printf("连接尝试次数: %d\n", connectionAttempts);
        }
        
        Serial.println("===================\n");
        
        vTaskDelay(15000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

传感器数据的缓冲区管理

在IoT项目中,传感器数据的采集和处理往往有不同的时间要求。采集需要实时性,处理可能需要更多时间。这时候就需要一个缓冲区来平衡两者的速度差异。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"

// 传感器数据结构
typedef struct {
    float temperature;
    float humidity;
    float pressure;
    float lightLevel;
    unsigned long timestamp;
    int sensorId;
} SensorData;

// 环形缓冲区
#define BUFFER_SIZE 50
SensorData sensorBuffer[BUFFER_SIZE];
int bufferHead = 0;
int bufferTail = 0;
int bufferCount = 0;

// 同步原语
SemaphoreHandle_t bufferMutex;
SemaphoreHandle_t dataAvailableSemaphore;
SemaphoreHandle_t bufferSpaceSemaphore;

// 统计信息
typedef struct {
    unsigned long totalSamples;
    unsigned long droppedSamples;
    unsigned long processedSamples;
    float averageProcessingTime;
} BufferStats;

BufferStats bufferStats = {0};

void setup() {
    Serial.begin(115200);
    
    // 创建同步原语
    bufferMutex = xSemaphoreCreateMutex();
    dataAvailableSemaphore = xSemaphoreCreateCounting(BUFFER_SIZE, 0);
    bufferSpaceSemaphore = xSemaphoreCreateCounting(BUFFER_SIZE, BUFFER_SIZE);
    
    if (bufferMutex == NULL || dataAvailableSemaphore == NULL || 
        bufferSpaceSemaphore == NULL) {
        Serial.println("传感器缓冲区同步原语创建失败!");
        return;
    }
    
    // 创建传感器采集任务
    xTaskCreate(temperatureSensorTask, "Temp_Sensor", 2048, (void*)1, 3, NULL);
    xTaskCreate(humiditySensorTask, "Humidity_Sensor", 2048, (void*)2, 3, NULL);
    xTaskCreate(pressureSensorTask, "Pressure_Sensor", 2048, (void*)3, 3, NULL);
    xTaskCreate(lightSensorTask, "Light_Sensor", 2048, (void*)4, 3, NULL);
    
    // 创建数据处理任务
    xTaskCreate(dataProcessorTask, "Data_Processor", 4096, NULL, 2, NULL);
    xTaskCreate(dataAnalyzerTask, "Data_Analyzer", 4096, NULL, 1, NULL);
    
    // 创建统计监控任务
    xTaskCreate(bufferStatsTask, "Buffer_Stats", 2048, NULL, 1, NULL);
    
    Serial.println("传感器数据缓冲区管理系统启动!");
}

bool addSensorData(SensorData* data) {
    // 尝试获取缓冲区空间
    if (xSemaphoreTake(bufferSpaceSemaphore, 100 / portTICK_PERIOD_MS) == pdTRUE) {
        // 获取缓冲区访问权限
        if (xSemaphoreTake(bufferMutex, 200 / portTICK_PERIOD_MS) == pdTRUE) {
            // 添加数据到缓冲区
            sensorBuffer[bufferHead] = *data;
            bufferHead = (bufferHead + 1) % BUFFER_SIZE;
            bufferCount++;
            
            bufferStats.totalSamples++;
            
            xSemaphoreGive(bufferMutex);
            
            // 通知数据可用
            xSemaphoreGive(dataAvailableSemaphore);
            
            return true;
        } else {
            // 无法获取互斥量,归还空间信号量
            xSemaphoreGive(bufferSpaceSemaphore);
            return false;
        }
    } else {
        // 缓冲区已满,丢弃数据
        bufferStats.droppedSamples++;
        return false;
    }
}

bool getSensorData(SensorData* data) {
    // 等待数据可用
    if (xSemaphoreTake(dataAvailableSemaphore, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
        // 获取缓冲区访问权限
        if (xSemaphoreTake(bufferMutex, 200 / portTICK_PERIOD_MS) == pdTRUE) {
            // 从缓冲区取出数据
            *data = sensorBuffer[bufferTail];
            bufferTail = (bufferTail + 1) % BUFFER_SIZE;
            bufferCount--;
            
            xSemaphoreGive(bufferMutex);
            
            // 释放缓冲区空间
            xSemaphoreGive(bufferSpaceSemaphore);
            
            return true;
        } else {
            // 无法获取互斥量,归还数据信号量
            xSemaphoreGive(dataAvailableSemaphore);
            return false;
        }
    }
    
    return false;
}

void temperatureSensorTask(void *parameter) {
    int sensorId = (int)parameter;
    float temperature = 25.0;
    
    for(;;) {
        // 模拟温度传感器读取
        temperature += (random(-20, 21) / 100.0);  // ±0.2°C 变化
        
        SensorData data = {
            .temperature = temperature,
            .humidity = 0,
            .pressure = 0,
            .lightLevel = 0,
            .timestamp = millis(),
            .sensorId = sensorId
        };
        
        if (addSensorData(&data)) {
            Serial.printf("温度传感器:添加数据 %.2f°C\n", temperature);
        } else {
            Serial.println("温度传感器:缓冲区满,数据丢失!");
        }
        
        vTaskDelay(2000 / portTICK_PERIOD_MS);  // 每2秒采集一次
    }
}

void humiditySensorTask(void *parameter) {
    int sensorId = (int)parameter;
    float humidity = 60.0;
    
    for(;;) {
        // 模拟湿度传感器读取
        humidity += (random(-50, 51) / 100.0);  // ±0.5% 变化
        if (humidity < 0) humidity = 0;
        if (humidity > 100) humidity = 100;
        
        SensorData data = {
            .temperature = 0,
            .humidity = humidity,
            .pressure = 0,
            .lightLevel = 0,
            .timestamp = millis(),
            .sensorId = sensorId
        };
        
        if (addSensorData(&data)) {
            Serial.printf("湿度传感器:添加数据 %.1f%%\n", humidity);
        } else {
            Serial.println("湿度传感器:缓冲区满,数据丢失!");
        }
        
        vTaskDelay(3000 / portTICK_PERIOD_MS);  // 每3秒采集一次
    }
}

void pressureSensorTask(void *parameter) {
    int sensorId = (int)parameter;
    float pressure = 1013.25;  // 标准大气压
    
    for(;;) {
        // 模拟气压传感器读取
        pressure += (random(-100, 101) / 100.0);  // ±1 hPa 变化
        
        SensorData data = {
            .temperature = 0,
            .humidity = 0,
            .pressure = pressure,
            .lightLevel = 0,
            .timestamp = millis(),
            .sensorId = sensorId
        };
        
        if (addSensorData(&data)) {
            Serial.printf("气压传感器:添加数据 %.2f hPa\n", pressure);
        } else {
            Serial.println("气压传感器:缓冲区满,数据丢失!");
        }
        
        vTaskDelay(5000 / portTICK_PERIOD_MS);  // 每5秒采集一次
    }
}

void lightSensorTask(void *parameter) {
    int sensorId = (int)parameter;
    float lightLevel = 500.0;  // 室内光照强度
    
    for(;;) {
        // 模拟光照传感器读取
        lightLevel += (random(-100, 101) / 10.0);  // ±10 lux 变化
        if (lightLevel < 0) lightLevel = 0;
        
        SensorData data = {
            .temperature = 0,
            .humidity = 0,
            .pressure = 0,
            .lightLevel = lightLevel,
            .timestamp = millis(),
            .sensorId = sensorId
        };
        
        if (addSensorData(&data)) {
            Serial.printf("光照传感器:添加数据 %.1f lux\n", lightLevel);
        } else {
            Serial.println("光照传感器:缓冲区满,数据丢失!");
        }
        
        vTaskDelay(4000 / portTICK_PERIOD_MS);  // 每4秒采集一次
    }
}

void dataProcessorTask(void *parameter) {
    SensorData data;
    
    for(;;) {
        if (getSensorData(&data)) {
            unsigned long startTime = millis();
            
            Serial.printf("数据处理器:处理传感器%d的数据 (时间戳: %lu)\n", 
                         data.sensorId, data.timestamp);
            
            // 根据传感器类型处理数据
            switch (data.sensorId) {
                case 1:  // 温度传感器
                    Serial.printf("  温度: %.2f°C", data.temperature);
                    if (data.temperature > 35.0) {
                        Serial.print(" [高温警告]");
                    }
                    Serial.println();
                    break;
                    
                case 2:  // 湿度传感器
                    Serial.printf("  湿度: %.1f%%", data.humidity);
                    if (data.humidity > 80.0) {
                        Serial.print(" [高湿警告]");
                    }
                    Serial.println();
                    break;
                    
                case 3:  // 气压传感器
                    Serial.printf("  气压: %.2f hPa", data.pressure);
                    if (data.pressure < 1000.0) {
                        Serial.print(" [低压警告]");
                    }
                    Serial.println();
                    break;
                    
                case 4:  // 光照传感器
                    Serial.printf("  光照: %.1f lux", data.lightLevel);
                    if (data.lightLevel < 100.0) {
                        Serial.print(" [光照不足]");
                    }
                    Serial.println();
                    break;
            }
            
            // 模拟数据处理时间
            vTaskDelay(500 / portTICK_PERIOD_MS);
            
            unsigned long processingTime = millis() - startTime;
            bufferStats.processedSamples++;
            bufferStats.averageProcessingTime = 
                (bufferStats.averageProcessingTime * (bufferStats.processedSamples - 1) + 
                 processingTime) / bufferStats.processedSamples;
        }
    }
}

void dataAnalyzerTask(void *parameter) {
    SensorData data;
    float tempSum = 0, humiditySum = 0, pressureSum = 0, lightSum = 0;
    int tempCount = 0, humidityCount = 0, pressureCount = 0, lightCount = 0;
    
    for(;;) {
        if (getSensorData(&data)) {
            Serial.printf("数据分析器:分析传感器%d的数据\n", data.sensorId);
            
            // 累积统计数据
            switch (data.sensorId) {
                case 1:  // 温度
                    tempSum += data.temperature;
                    tempCount++;
                    break;
                case 2:  // 湿度
                    humiditySum += data.humidity;
                    humidityCount++;
                    break;
                case 3:  // 气压
                    pressureSum += data.pressure;
                    pressureCount++;
                    break;
                case 4:  // 光照
                    lightSum += data.lightLevel;
                    lightCount++;
                    break;
            }
            
            // 每处理10个数据输出一次统计
            if ((tempCount + humidityCount + pressureCount + lightCount) % 10 == 0) {
                Serial.println("=== 数据分析统计 ===");
                if (tempCount > 0) {
                    Serial.printf("平均温度: %.2f°C (%d个样本)\n", 
                                 tempSum / tempCount, tempCount);
                }
                if (humidityCount > 0) {
                    Serial.printf("平均湿度: %.1f%% (%d个样本)\n", 
                                 humiditySum / humidityCount, humidityCount);
                }
                if (pressureCount > 0) {
                    Serial.printf("平均气压: %.2f hPa (%d个样本)\n", 
                                 pressureSum / pressureCount, pressureCount);
                }
                if (lightCount > 0) {
                    Serial.printf("平均光照: %.1f lux (%d个样本)\n", 
                                 lightSum / lightCount, lightCount);
                }
                Serial.println("===================\n");
            }
            
            // 模拟更复杂的分析处理
            vTaskDelay(800 / portTICK_PERIOD_MS);
        }
    }
}

void bufferStatsTask(void *parameter) {
    for(;;) {
        Serial.println("=== 缓冲区统计信息 ===");
        Serial.printf("缓冲区使用率: %d/%d (%.1f%%)\n", 
                     bufferCount, BUFFER_SIZE, 
                     (float)bufferCount / BUFFER_SIZE * 100);
        Serial.printf("总采样数: %lu\n", bufferStats.totalSamples);
        Serial.printf("已处理数: %lu\n", bufferStats.processedSamples);
        Serial.printf("丢失数: %lu\n", bufferStats.droppedSamples);
        
        if (bufferStats.totalSamples > 0) {
            Serial.printf("丢失率: %.2f%%\n", 
                         (float)bufferStats.droppedSamples / bufferStats.totalSamples * 100);
        }
        
        Serial.printf("平均处理时间: %.1f ms\n", bufferStats.averageProcessingTime);
        Serial.println("========================\n");
        
        vTaskDelay(20000 / portTICK_PERIOD_MS);  // 每20秒输出一次统计
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

性能优化:让你的ESP32飞起来

性能优化就像是给汽车调教引擎,合适的配置能让你的ESP32发挥出最佳性能。在多任务环境中,信号量和互斥量的使用策略直接影响系统的整体性能。

信号量vs互斥量的性能对比分析

首先,我们需要理解不同同步机制的性能特点。就像选择交通工具一样,不同的场景需要不同的选择。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 性能测试配置
#define TEST_ITERATIONS 10000
#define TEST_TASKS 4

// 测试用的同步原语
SemaphoreHandle_t binarySemaphore;
SemaphoreHandle_t mutex;
SemaphoreHandle_t recursiveMutex;

// 性能统计结构
typedef struct {
    unsigned long totalTime;
    unsigned long minTime;
    unsigned long maxTime;
    unsigned long averageTime;
    const char* testName;
} PerformanceStats;

void setup() {
    Serial.begin(115200);
    
    // 创建测试用的同步原语
    binarySemaphore = xSemaphoreCreateBinary();
    mutex = xSemaphoreCreateMutex();
    recursiveMutex = xSemaphoreCreateRecursiveMutex();
    
    // 初始化二进制信号量
    xSemaphoreGive(binarySemaphore);
    
    Serial.println("ESP32 同步机制性能测试开始!");
    
    // 运行各种性能测试
    runPerformanceTests();
}

void runPerformanceTests() {
    Serial.println("=== 性能测试结果 ===\n");
    
    // 测试二进制信号量
    PerformanceStats binaryStats = testBinarySemaphore();
    printPerformanceStats(&binaryStats);
    
    // 测试互斥量
    PerformanceStats mutexStats = testMutex();
    printPerformanceStats(&mutexStats);
    
    // 测试递归互斥量
    PerformanceStats recursiveStats = testRecursiveMutex();
    printPerformanceStats(&recursiveStats);
    
    // 测试无同步(基准测试)
    PerformanceStats noSyncStats = testNoSynchronization();
    printPerformanceStats(&noSyncStats);
    
    // 性能对比分析
    performanceComparison(&binaryStats, &mutexStats, &recursiveStats, &noSyncStats);
}

PerformanceStats testBinarySemaphore() {
    PerformanceStats stats = {0, ULONG_MAX, 0, 0, "二进制信号量"};
    
    Serial.println("测试二进制信号量性能...");
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        unsigned long startTime = micros();
        
        // 获取和释放二进制信号量
        xSemaphoreTake(binarySemaphore, portMAX_DELAY);
        xSemaphoreGive(binarySemaphore);
        
        unsigned long elapsedTime = micros() - startTime;
        
        stats.totalTime += elapsedTime;
        if (elapsedTime < stats.minTime) stats.minTime = elapsedTime;
        if (elapsedTime > stats.maxTime) stats.maxTime = elapsedTime;
    }
    
    stats.averageTime = stats.totalTime / TEST_ITERATIONS;
    return stats;
}

PerformanceStats testMutex() {
    PerformanceStats stats = {0, ULONG_MAX, 0, 0, "互斥量"};
    
    Serial.println("测试互斥量性能...");
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        unsigned long startTime = micros();
        
        // 获取和释放互斥量
        xSemaphoreTake(mutex, portMAX_DELAY);
        xSemaphoreGive(mutex);
        
        unsigned long elapsedTime = micros() - startTime;
        
        stats.totalTime += elapsedTime;
        if (elapsedTime < stats.minTime) stats.minTime = elapsedTime;
        if (elapsedTime > stats.maxTime) stats.maxTime = elapsedTime;
    }
    ```cpp
    stats.averageTime = stats.totalTime / TEST_ITERATIONS;
    return stats;
}

PerformanceStats testRecursiveMutex() {
    PerformanceStats stats = {0, ULONG_MAX, 0, 0, "递归互斥量"};
    
    Serial.println("测试递归互斥量性能...");
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        unsigned long startTime = micros();
        
        // 获取和释放递归互斥量
        xSemaphoreTakeRecursive(recursiveMutex, portMAX_DELAY);
        xSemaphoreGiveRecursive(recursiveMutex);
        
        unsigned long elapsedTime = micros() - startTime;
        
        stats.totalTime += elapsedTime;
        if (elapsedTime < stats.minTime) stats.minTime = elapsedTime;
        if (elapsedTime > stats.maxTime) stats.maxTime = elapsedTime;
    }
    
    stats.averageTime = stats.totalTime / TEST_ITERATIONS;
    return stats;
}

PerformanceStats testNoSynchronization() {
    PerformanceStats stats = {0, ULONG_MAX, 0, 0, "无同步(基准)"};
    volatile int dummyVariable = 0;
    
    Serial.println("测试无同步基准性能...");
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        unsigned long startTime = micros();
        
        // 简单的变量操作作为基准
        dummyVariable++;
        dummyVariable--;
        
        unsigned long elapsedTime = micros() - startTime;
        
        stats.totalTime += elapsedTime;
        if (elapsedTime < stats.minTime) stats.minTime = elapsedTime;
        if (elapsedTime > stats.maxTime) stats.maxTime = elapsedTime;
    }
    
    stats.averageTime = stats.totalTime / TEST_ITERATIONS;
    return stats;
}

void printPerformanceStats(PerformanceStats* stats) {
    Serial.printf("=== %s ===\n", stats->testName);
    Serial.printf("平均时间: %lu 微秒\n", stats->averageTime);
    Serial.printf("最小时间: %lu 微秒\n", stats->minTime);
    Serial.printf("最大时间: %lu 微秒\n", stats->maxTime);
    Serial.printf("总时间: %lu 微秒\n", stats->totalTime);
    Serial.println();
}

void performanceComparison(PerformanceStats* binary, PerformanceStats* mutex, 
                          PerformanceStats* recursive, PerformanceStats* noSync) {
    Serial.println("=== 性能对比分析 ===");
    
    Serial.printf("基准性能(无同步): %lu 微秒\n", noSync->averageTime);
    
    float binaryOverhead = (float)(binary->averageTime - noSync->averageTime) / noSync->averageTime * 100;
    float mutexOverhead = (float)(mutex->averageTime - noSync->averageTime) / noSync->averageTime * 100;
    float recursiveOverhead = (float)(recursive->averageTime - noSync->averageTime) / noSync->averageTime * 100;
    
    Serial.printf("二进制信号量开销: %.1f%%\n", binaryOverhead);
    Serial.printf("互斥量开销: %.1f%%\n", mutexOverhead);
    Serial.printf("递归互斥量开销: %.1f%%\n", recursiveOverhead);
    
    Serial.println("\n性能排序(从快到慢):");
    if (binary->averageTime <= mutex->averageTime && binary->averageTime <= recursive->averageTime) {
        Serial.println("1. 二进制信号量");
        if (mutex->averageTime <= recursive->averageTime) {
            Serial.println("2. 互斥量");
            Serial.println("3. 递归互斥量");
        } else {
            Serial.println("2. 递归互斥量");
            Serial.println("3. 互斥量");
        }
    } else if (mutex->averageTime <= binary->averageTime && mutex->averageTime <= recursive->averageTime) {
        Serial.println("1. 互斥量");
        if (binary->averageTime <= recursive->averageTime) {
            Serial.println("2. 二进制信号量");
            Serial.println("3. 递归互斥量");
        } else {
            Serial.println("2. 递归互斥量");
            Serial.println("3. 二进制信号量");
        }
    } else {
        Serial.println("1. 递归互斥量");
        if (binary->averageTime <= mutex->averageTime) {
            Serial.println("2. 二进制信号量");
            Serial.println("3. 互斥量");
        } else {
            Serial.println("2. 互斥量");
            Serial.println("3. 二进制信号量");
        }
    }
    
    Serial.println("\n推荐使用场景:");
    Serial.println("• 任务同步 → 二进制信号量");
    Serial.println("• 资源保护 → 互斥量");
    Serial.println("• 递归调用 → 递归互斥量");
    Serial.println("• 资源计数 → 计数信号量");
    Serial.println("========================\n");
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

内存占用与执行效率的权衡

在ESP32这样的嵌入式系统中,内存是珍贵的资源。我们需要在功能和内存占用之间找到平衡点。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 内存使用分析工具
void analyzeMemoryUsage() {
    Serial.println("=== ESP32 内存使用分析 ===");
    
    // 获取堆内存信息
    size_t freeHeap = esp_get_free_heap_size();
    size_t minFreeHeap = esp_get_minimum_free_heap_size();
    size_t totalHeap = ESP.getHeapSize();
    
    Serial.printf("总堆内存: %d 字节\n", totalHeap);
    Serial.printf("当前可用堆内存: %d 字节\n", freeHeap);
    Serial.printf("历史最小可用堆内存: %d 字节\n", minFreeHeap);
    Serial.printf("堆内存使用率: %.1f%%\n", 
                 (float)(totalHeap - freeHeap) / totalHeap * 100);
    
    // 获取任务栈信息
    UBaseType_t taskCount = uxTaskGetNumberOfTasks();
    Serial.printf("当前任务数量: %d\n", taskCount);
    
    // 分析不同同步原语的内存占用
    analyzeSynchronizationMemory();
    
    Serial.println("=============================\n");
}

void analyzeSynchronizationMemory() {
    Serial.println("\n--- 同步原语内存占用分析 ---");
    
    size_t heapBefore = esp_get_free_heap_size();
    
    // 创建各种同步原语
    SemaphoreHandle_t testBinary = xSemaphoreCreateBinary();
    size_t heapAfterBinary = esp_get_free_heap_size();
    
    SemaphoreHandle_t testMutex = xSemaphoreCreateMutex();
    size_t heapAfterMutex = esp_get_free_heap_size();
    
    SemaphoreHandle_t testRecursive = xSemaphoreCreateRecursiveMutex();
    size_t heapAfterRecursive = esp_get_free_heap_size();
    
    SemaphoreHandle_t testCounting = xSemaphoreCreateCounting(10, 0);
    size_t heapAfterCounting = esp_get_free_heap_size();
    
    // 计算内存占用
    Serial.printf("二进制信号量: %d 字节\n", heapBefore - heapAfterBinary);
    Serial.printf("互斥量: %d 字节\n", heapAfterBinary - heapAfterMutex);
    Serial.printf("递归互斥量: %d 字节\n", heapAfterMutex - heapAfterRecursive);
    Serial.printf("计数信号量: %d 字节\n", heapAfterRecursive - heapAfterCounting);
    
    // 清理资源
    vSemaphoreDelete(testBinary);
    vSemaphoreDelete(testMutex);
    vSemaphoreDelete(testRecursive);
    vSemaphoreDelete(testCounting);
    
    Serial.println("-----------------------------");
}

// 内存优化策略演示
void demonstrateMemoryOptimization() {
    Serial.println("=== 内存优化策略演示 ===");
    
    // 策略1:使用静态分配
    demonstrateStaticAllocation();
    
    // 策略2:合理的任务栈大小
    demonstrateStackOptimization();
    
    // 策略3:同步原语的复用
    demonstrateSynchronizationReuse();
    
    Serial.println("========================\n");
}

void demonstrateStaticAllocation() {
    Serial.println("策略1:静态内存分配");
    
    size_t heapBefore = esp_get_free_heap_size();
    
    // 动态分配(传统方式)
    SemaphoreHandle_t dynamicSemaphore = xSemaphoreCreateBinary();
    size_t heapAfterDynamic = esp_get_free_heap_size();
    
    // 静态分配
    StaticSemaphore_t staticSemaphoreBuffer;
    SemaphoreHandle_t staticSemaphore = xSemaphoreCreateBinaryStatic(&staticSemaphoreBuffer);
    size_t heapAfterStatic = esp_get_free_heap_size();
    
    Serial.printf("动态分配堆内存消耗: %d 字节\n", heapBefore - heapAfterDynamic);
    Serial.printf("静态分配堆内存消耗: %d 字节\n", heapAfterDynamic - heapAfterStatic);
    
    Serial.println("静态分配的优势:");
    Serial.println("• 不消耗堆内存");
    Serial.println("• 避免内存碎片");
    Serial.println("• 分配时间确定");
    Serial.println("• 适合内存受限的应用\n");
    
    // 清理资源
    vSemaphoreDelete(dynamicSemaphore);
    vSemaphoreDelete(staticSemaphore);
}

void demonstrateStackOptimization() {
    Serial.println("策略2:任务栈大小优化");
    
    Serial.println("不同任务类型的推荐栈大小:");
    Serial.println("• 简单控制任务: 1024-2048 字节");
    Serial.println("• 数据处理任务: 2048-4096 字节");
    Serial.println("• 网络通信任务: 4096-8192 字节");
    Serial.println("• 复杂算法任务: 8192+ 字节");
    
    Serial.println("\n栈使用监控示例:");
    
    // 创建一个测试任务来演示栈使用监控
    xTaskCreate(stackMonitorTask, "Stack_Monitor", 2048, NULL, 1, NULL);
    
    Serial.println("(栈监控任务已创建,将定期报告栈使用情况)\n");
}

void stackMonitorTask(void *parameter) {
    for(;;) {
        // 获取当前任务的栈使用情况
        UBaseType_t stackHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
        
        Serial.printf("栈监控:剩余栈空间 %d 字节\n", stackHighWaterMark * sizeof(StackType_t));
        
        if (stackHighWaterMark < 200) {
            Serial.println("警告:栈空间不足!");
        }
        
        vTaskDelay(30000 / portTICK_PERIOD_MS);  // 每30秒检查一次
    }
}

void demonstrateSynchronizationReuse() {
    Serial.println("策略3:同步原语复用");
    
    Serial.println("复用策略:");
    Serial.println("• 全局串口互斥量:所有任务共享一个");
    Serial.println("• 资源池信号量:按资源类型分组");
    Serial.println("• 状态通知:使用任务通知替代信号量");
    Serial.println("• 临时同步:使用栈上的局部变量");
    
    // 演示任务通知的使用(替代二进制信号量)
    demonstrateTaskNotification();
}

void demonstrateTaskNotification() {
    Serial.println("\n任务通知示例(替代二进制信号量):");
    
    // 创建演示任务
    TaskHandle_t notificationTask;
    xTaskCreate(taskNotificationDemo, "Notification_Demo", 2048, NULL, 1, &notificationTask);
    
    // 等待一段时间后发送通知
    vTaskDelay(2000 / portTICK_PERIOD_MS);
    
    Serial.println("发送任务通知...");
    xTaskNotifyGive(notificationTask);
    
    vTaskDelay(1000 / portTICK_PERIOD_MS);
    
    Serial.println("任务通知的优势:");
    Serial.println("• 不需要额外的内存分配");
    Serial.println("• 执行速度更快");
    Serial.println("• 每个任务内置支持");
    Serial.println("• 适合简单的通知场景\n");
}

void taskNotificationDemo(void *parameter) {
    Serial.println("任务通知演示:等待通知...");
    
    // 等待任务通知(类似于等待信号量)
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    
    Serial.println("任务通知演示:收到通知!");
    
    // 任务完成后删除自己
    vTaskDelete(NULL);
}

void setup() {
    Serial.begin(115200);
    
    Serial.println("ESP32 性能优化演示启动!");
    
    // 分析初始内存状态
    analyzeMemoryUsage();
    
    // 运行性能测试
    runPerformanceTests();
    
    // 演示内存优化策略
    demonstrateMemoryOptimization();
    
    // 创建内存监控任务
    xTaskCreate(memoryMonitorTask, "Memory_Monitor", 2048, NULL, 1, NULL);
}

void memoryMonitorTask(void *parameter) {
    for(;;) {
        analyzeMemoryUsage();
        vTaskDelay(60000 / portTICK_PERIOD_MS);  // 每分钟检查一次
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

任务切换开销的量化分析

任务切换是多任务系统的核心机制,但它也带来了开销。理解这些开销有助于我们优化系统性能。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 任务切换分析工具
SemaphoreHandle_t switchTestSemaphore;
volatile unsigned long switchStartTime;
volatile unsigned long switchEndTime;
volatile int switchCount = 0;

void setup() {
    Serial.begin(115200);
    
    switchTestSemaphore = xSemaphoreCreateBinary();
    
    Serial.println("任务切换开销分析开始!");
    
    // 创建任务切换测试任务
    xTaskCreate(taskSwitchProducer, "Switch_Producer", 2048, NULL, 2, NULL);
    xTaskCreate(taskSwitchConsumer, "Switch_Consumer", 2048, NULL, 1, NULL);
    
    // 创建性能监控任务
    xTaskCreate(performanceMonitor, "Performance_Monitor", 2048, NULL, 3, NULL);
}

void taskSwitchProducer(void *parameter) {
    for(;;) {
        // 记录开始时间
        switchStartTime = micros();
        
        // 释放信号量,这会导致任务切换
        xSemaphoreGive(switchTestSemaphore);
        
        // 等待消费者完成
        vTaskDelay(10 / portTICK_PERIOD_MS);
        
        switchCount++;
        
        if (switchCount >= 1000) {
            // 重置计数器
            switchCount = 0;
            vTaskDelay(5000 / portTICK_PERIOD_MS);
        }
    }
}

void taskSwitchConsumer(void *parameter) {
    for(;;) {
        // 等待信号量
        if (xSemaphoreTake(switchTestSemaphore, portMAX_DELAY) == pdTRUE) {
            // 记录结束时间
            switchEndTime = micros();
            
            // 计算任务切换时间
            unsigned long switchTime = switchEndTime - switchStartTime;
            
            // 每100次切换报告一次
            if (switchCount % 100 == 0) {
                Serial.printf("任务切换 #%d: %lu 微秒\n", switchCount, switchTime);
            }
        }
    }
}

void performanceMonitor(void *parameter) {
    TickType_t lastWakeTime = xTaskGetTickCount();
    
    for(;;) {
        // 获取系统统计信息
        TaskStatus_t *taskStatusArray;
        UBaseType_t taskCount = uxTaskGetNumberOfTasks();
        
        // 分配内存存储任务状态
        taskStatusArray = pvPortMalloc(taskCount * sizeof(TaskStatus_t));
        
        if (taskStatusArray != NULL) {
            // 获取任务状态信息
            UBaseType_t actualTaskCount = uxTaskGetSystemState(taskStatusArray, taskCount, NULL);
            
            Serial.println("=== 任务性能统计 ===");
            Serial.printf("任务数量: %d\n", actualTaskCount);
            
            for (UBaseType_t i = 0; i < actualTaskCount; i++) {
                Serial.printf("任务: %s\n", taskStatusArray[i].pcTaskName);
                Serial.printf("  状态: %s\n", getTaskStateName(taskStatusArray[i].eCurrentState));
                Serial.printf("  优先级: %d\n", taskStatusArray[i].uxCurrentPriority);
                Serial.printf("  栈剩余: %d 字节\n", 
                             taskStatusArray[i].usStackHighWaterMark * sizeof(StackType_t));
            }
            
            Serial.println("===================\n");
            
            // 释放内存
            vPortFree(taskStatusArray);
        }
        
        // 每10秒更新一次
        vTaskDelayUntil(&lastWakeTime, 10000 / portTICK_PERIOD_MS);
    }
}

const char* getTaskStateName(eTaskState state) {
    switch (state) {
        case eRunning:   return "运行中";
        case eReady:     return "就绪";
        case eBlocked:   return "阻塞";
        case eSuspended: return "挂起";
        case eDeleted:   return "已删除";
        default:         return "未知";
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

实时性要求下的最佳选择策略

在实时系统中,可预测性比平均性能更重要。我们需要确保关键任务能够在规定时间内完成。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 实时性分析配置
#define CRITICAL_TASK_PERIOD_MS 50    // 关键任务周期50ms
#define NORMAL_TASK_PERIOD_MS 200     // 普通任务周期200ms
#define BACKGROUND_TASK_PERIOD_MS 1000 // 后台任务周期1000ms

// 同步原语
SemaphoreHandle_t criticalResourceMutex;
SemaphoreHandle_t normalResourceMutex;
SemaphoreHandle_t realtimeNotification;

// 性能统计
typedef struct {
    unsigned long maxExecutionTime;
    unsigned long minExecutionTime;
    unsigned long totalExecutionTime;
    unsigned long executionCount;
    unsigned long deadlineMisses;
} RealtimeStats;

RealtimeStats criticalTaskStats = {0, ULONG_MAX, 0, 0, 0};
RealtimeStats normalTaskStats = {0, ULONG_MAX, 0, 0, 0};

void setup() {
    Serial.begin(115200);
    
    // 创建同步原语
    criticalResourceMutex = xSemaphoreCreateMutex();
    normalResourceMutex = xSemaphoreCreateMutex();
    realtimeNotification = xSemaphoreCreateBinary();
    
    Serial.println("实时性能分析系统启动!");
    
    // 创建不同优先级的任务
    xTaskCreate(criticalRealtimeTask, "Critical_RT", 2048, NULL, 5, NULL);  // 最高优先级
    xTaskCreate(normalRealtimeTask, "Normal_RT", 2048, NULL, 3, NULL);      // 中等优先级
    xTaskCreate(backgroundTask, "Background", 2048, NULL, 1, NULL);         // 低优先级
    xTaskCreate(realtimeMonitor, "RT_Monitor", 2048, NULL, 4, NULL);        // 监控任务
    
    // 启动实时性测试
    xTaskCreate(realtimeTestController, "RT_Controller", 2048, NULL, 2, NULL);
}

void criticalRealtimeTask(void *parameter) {
    TickType_t lastWakeTime = xTaskGetTickCount();
    
    for(;;) {
        unsigned long startTime = micros();
        
        // 关键实时任务的工作
        if (xSemaphoreTake(criticalResourceMutex, 5 / portTICK_PERIOD_MS) == pdTRUE) {
            // 模拟关键操作
            performCriticalOperation();
            
            xSemaphoreGive(criticalResourceMutex);
            
            unsigned long executionTime = micros() - startTime;
            updateRealtimeStats(&criticalTaskStats, executionTime, CRITICAL_TASK_PERIOD_MS * 1000);
            
        } else {
            // 无法获取资源,记录为错过截止时间
            criticalTaskStats.deadlineMisses++;
            Serial.println("关键任务:无法获取资源,错过截止时间!");
        }
        
        // 严格按周期执行
        vTaskDelayUntil(&lastWakeTime, CRITICAL_TASK_PERIOD_MS / portTICK_PERIOD_MS);
    }
}

void normalRealtimeTask(void *parameter) {
    TickType_t lastWakeTime = xTaskGetTickCount();
    
    for(;;) {
        unsigned long startTime = micros();
        
        // 普通实时任务的工作
        if (xSemaphoreTake(normalResourceMutex, 20 / portTICK_PERIOD_MS) == pdTRUE) {
            // 模拟普通操作
            performNormalOperation();
            
            xSemaphoreGive(normalResourceMutex);
            
            unsigned long executionTime = micros() - startTime;
            updateRealtimeStats(&normalTaskStats, executionTime, NORMAL_TASK_PERIOD_MS * 1000);
            
        } else {
            // 记录错过截止时间
            normalTaskStats.deadlineMisses++;
            Serial.println("普通任务:无法获取资源,错过截止时间!");
        }
        
        // 按周期执行
        vTaskDelayUntil(&lastWakeTime, NORMAL_TASK_PERIOD_MS / portTICK_PERIOD_MS);
    }
}

void backgroundTask(void *parameter) {
    for(;;) {
        Serial.println("后台任务:执行低优先级操作");
        
        // 模拟后台处理
        for (int i = 0; i < 1000; i++) {
            // 可中断的循环
            if (i % 100 == 0) {
                vTaskDelay(1 / portTICK_PERIOD_MS);  // 让出CPU
            }
        }
        
        vTaskDelay(BACKGROUND_TASK_PERIOD_MS / portTICK_PERIOD_MS);
    }
}

void performCriticalOperation() {
    // 模拟关键操作(必须在5ms内完成)
    volatile int calculation = 0;
    for (int i = 0; i < 10000; i++) {
        calculation += i;
    }
}

void performNormalOperation() {
    // 模拟普通操作(必须在20ms内完成)
    volatile int calculation = 0;
    for (int i = 0; i < 50000; i++) {
        calculation += i;
    }
}

void updateRealtimeStats(RealtimeStats* stats, unsigned long executionTime, unsigned long deadline) {
    stats->executionCount++;
    stats->totalExecutionTime += executionTime;
    
    if (executionTime > stats->maxExecutionTime) {
        stats->maxExecutionTime = executionTime;
    }
    
    if (executionTime < stats->minExecutionTime) {
        stats->minExecutionTime = executionTime;
    }
    
    // 检查是否错过截止时间
    if (executionTime > deadline) {
        stats->deadlineMisses++;
    }
}

void realtimeMonitor(void *parameter) {
    for(;;) {
        Serial.println("=== 实时性能监控报告 ===");
        
        // 关键任务统计
        Serial.println("关键任务统计:");
        if (criticalTaskStats.executionCount > 0) {
            Serial.printf("  执行次数: %lu\n", criticalTaskStats.executionCount);
            Serial.printf("  平均执行时间: %lu 微秒\n", 
                         criticalTaskStats.totalExecutionTime / criticalTaskStats.executionCount);
            Serial.printf("  最大执行时间: %lu 微秒\n", criticalTaskStats.maxExecutionTime);
            Serial.printf("  最小执行时间: %lu 微秒\n", criticalTaskStats.minExecutionTime);
            Serial.printf("  错过截止时间: %lu 次\n", criticalTaskStats.deadlineMisses);
            Serial.printf("  实时性成功率: %.2f%%\n", 
                         (float)(criticalTaskStats.executionCount - criticalTaskStats.deadlineMisses) / 
                         criticalTaskStats.executionCount * 100);
        }
        
        Serial.println();
        
        // 普通任务统计
        Serial.println("普通任务统计:");
        if (normalTaskStats.executionCount > 0) {
            Serial.printf("  执行次数: %lu\n", normalTaskStats.executionCount);
            Serial.printf("  平均执行时间: %lu 微秒\n", 
                         normalTaskStats.totalExecutionTime / normalTaskStats.executionCount);
            Serial.printf("  最大执行时间: %lu 微秒\n", normalTaskStats.maxExecutionTime);
            Serial.printf("  最小执行时间: %lu 微秒\n", normalTaskStats.minExecutionTime);
            Serial.printf("  错过截止时间: %lu 次\n", normalTaskStats.deadlineMisses);
            Serial.printf("  实时性成功率: %.2f%%\n", 
                         (float)(normalTaskStats.executionCount - normalTaskStats.deadlineMisses) / 
                         normalTaskStats.executionCount * 100);
        }
        
        Serial.println("========================\n");
        
        vTaskDelay(15000 / portTICK_PERIOD_MS);  // 每15秒报告一次
    }
}

void realtimeTestController(void *parameter) {
    for(;;) {
        Serial.println("实时性测试控制器:开始压力测试");
        
        // 创建额外的干扰任务来测试实时性
        TaskHandle_t interferenceTask;
        xTaskCreate(interferenceTask_func, "Interference", 2048, NULL, 2, &interferenceTask);
        
        // 运行10秒压力测试
        vTaskDelay(10000 / portTICK_PERIOD_MS);
        
        // 停止干扰任务
        vTaskDelete(interferenceTask);
        
        Serial.println("实时性测试控制器:压力测试结束");
        
        // 等待30秒后再次测试
        vTaskDelay(30000 / portTICK_PERIOD_MS);
    }
}

void interferenceTask_func(void *parameter) {
    for(;;) {
        // 创建CPU负载来测试实时性
        volatile int calculation = 0;
        for (int i = 0; i < 100000; i++) {
            calculation += i * i;
        }
        
        vTaskDelay(1 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

调试技巧:当信号量和互斥量不按套路出牌

调试多任务程序就像是当侦探,你需要从蛛丝马迹中找出问题的根源。当信号量和互斥量出现问题时,症状往往很明显,但原因却可能隐藏得很深。

常见错误模式的识别与解决

让我们来看看那些让程序员头疼的经典问题!

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 调试工具配置
#define DEBUG_ENABLE 1
#define DEBUG_PRINT(fmt, ...) do { if(DEBUG_ENABLE) Serial.printf("[DEBUG] " fmt "\n", ##__VA_ARGS__); } while(0)
#define ERROR_PRINT(fmt, ...) Serial.printf("[ERROR] " fmt "\n", ##__VA_ARGS__)
#define WARN_PRINT(fmt, ...) Serial.printf("[WARN] " fmt "\n", ##__VA_ARGS__)

// 错误检测和记录
typedef struct {
    const char* errorType;
    const char* taskName;
    unsigned long timestamp;
    const char* description;
} ErrorRecord;

#define MAX_ERROR_RECORDS 20
ErrorRecord errorLog[MAX_ERROR_RECORDS];
int errorLogIndex = 0;

// 测试用的同步原语
SemaphoreHandle_t problematicMutex;
SemaphoreHandle_t debugSemaphore;

void setup() {
    Serial.begin(115200);
    
    problematicMutex = xSemaphoreCreateMutex();
    debugSemaphore = xSemaphoreCreateBinary();
    
    Serial.println("多任务调试技巧演示开始!");
    
    // 演示各种常见错误
    demonstrateCommonErrors();
}

void demonstrateCommonErrors() {
    Serial.println("=== 常见错误模式演示 ===\n");
    
    // 错误1:忘记释放互斥量
    Serial.println("错误1:忘记释放互斥量");
    xTaskCreate(forgetfulTask, "Forgetful", 2048, NULL, 2, NULL);
    xTaskCreate(waitingTask, "Waiting", 2048, NULL, 1, NULL);
    
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    
    // 错误2:错误的任务释放信号量
    Serial.println("\n错误2:错误的任务释放信号量");
    xTaskCreate(wrongReleaseTask, "WrongRelease", 2048, NULL, 2, NULL);
    
    vTaskDelay(3000 / portTICK_PERIOD_MS);
    
    // 错误3:在中断中使用错误的API
    Serial.println("\n错误3:在中断中使用错误的API");
    demonstrateInterruptError();
    
    vTaskDelay(2000 / portTICK_PERIOD_MS);
    
    // 错误4:优先级反转
    Serial.println("\n错误4:优先级反转");
    demonstratePriorityInversion();
    
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    
    // 错误5:竞态条件
    Serial.println("\n错误5:竞态条件");
    demonstrateRaceCondition();
    
    // 创建错误监控任务
    xTaskCreate(errorMonitorTask, "Error_Monitor", 2048, NULL, 3, NULL);
}

// 错误1:忘记释放互斥量
void forgetfulTask(void *parameter) {
    DEBUG_PRINT("健忘任务:开始执行");
    
    if (xSemaphoreTake(problematicMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
        DEBUG_PRINT("健忘任务:获取到互斥量");
        
        // 模拟一些工作
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        
        // 故意"忘记"释放互斥量
        ERROR_PRINT("健忘任务:哎呀,忘记释放互斥量了!");
        recordError("MUTEX_LEAK", "Forgetful", "忘记释放互斥量");
        
        // 在实际代码中,这里应该有 xSemaphoreGive(problematicMutex);
    }
    
    // 任务结束但没有释放互斥量
    vTaskDelete(NULL);
}

void waitingTask(void *parameter) {
    vTaskDelay(1000 / portTICK_PERIOD_MS);  // 等待健忘任务先获取互斥量
    
    DEBUG_PRINT("等待任务:尝试获取互斥量...");
    
    if (xSemaphoreTake(problematicMutex, 3000 / portTICK_PERIOD_MS) == pdTRUE) {
        DEBUG_PRINT("等待任务:获取到互斥量");
        xSemaphoreGive(problematicMutex);
    } else {
        ERROR_PRINT("等待任务:获取互斥量超时!可能发生了互斥量泄漏");
        recordError("MUTEX_TIMEOUT", "Waiting", "获取互斥量超时");
    }
    
    vTaskDelete(NULL);
}

// 错误2:错误的任务释放信号量
void wrongReleaseTask(void *parameter) {
    DEBUG_PRINT("错误释放任务:开始执行");
    
    // 错误:尝试释放一个没有获取的信号量
    if (xSemaphoreGive(debugSemaphore) == pdTRUE) {
        DEBUG_PRINT("错误释放任务:释放信号量成功");
    } else {
        ERROR_PRINT("错误释放任务:释放信号量失败");
        recordError("WRONG_RELEASE", "WrongRelease", "释放未获取的信号量");
    }
    
    vTaskDelete(NULL);
}

// 错误3:在中断中使用错误的API
void demonstrateInterruptError() {
    DEBUG_PRINT("演示中断中的错误API使用");
    
    // 注意:这只是演示,实际的中断处理需要更复杂的设置
    Serial.println("在中断服务程序中应该使用:");
    Serial.println("• xSemaphoreGiveFromISR() 而不是 xSemaphoreGive()");
    Serial.println("• xSemaphoreTakeFromISR() 而不是 xSemaphoreTake()");
    Serial.println("• 避免在ISR中使用会阻塞的API");
    
    recordError("ISR_API_ERROR", "ISR", "在中断中使用了错误的API");
}

// 错误4:优先级反转演示
void demonstratePriorityInversion() {
    DEBUG_PRINT("演示优先级反转问题");
    
    // 创建一个普通信号量(不支持优先级继承)
    SemaphoreHandle_t normalSemaphore = xSemaphoreCreateBinary();
    xSemaphoreGive(normalSemaphore);
    
    // 创建不同优先级的任务
    xTaskCreate(lowPriorityTask, "LowPriority", 2048, (void*)normalSemaphore, 1, NULL);
    xTaskCreate(mediumPriorityTask, "MediumPriority", 2048, NULL, 2, NULL);
    xTaskCreate(highPriorityTask, "HighPriority", 2048, (void*)normalSemaphore, 3, NULL);
    
    Serial.println("优先级反转问题:");
    Serial.println("• 高优先级任务被低优先级任务阻塞");
    Serial.println("• 中优先级任务抢占了低优先级任务的CPU时间");
    Serial.println("• 导致高优先级任务等待时间过长");
    
    recordError("PRIORITY_INVERSION", "System", "检测到优先级反转");
}

void lowPriorityTask(void *parameter) {
    SemaphoreHandle_t semaphore = (SemaphoreHandle_t)parameter;
    
    DEBUG_PRINT("低优先级任务:获取信号量");
    if (xSemaphoreTake(semaphore, portMAX_DELAY) == pdTRUE) {
        DEBUG_PRINT("低优先级任务:开始长时间工作");
        
        // 长时间工作
        for (int i = 0; i < 10; i++) {
            DEBUG_PRINT("低优先级任务:工作中... %d/10", i + 1);
            vTaskDelay(500 / portTICK_PERIOD_MS);
        }
        
        DEBUG_PRINT("低优先级任务:释放信号量");
        xSemaphoreGive(semaphore);
    }
    
    vTaskDelete(NULL);
}

void mediumPriorityTask(void *parameter) {
    vTaskDelay(1000 / portTICK_PERIOD_MS);  // 延迟启动
    
    DEBUG_PRINT("中优先级任务:开始CPU密集型工作");
    
    for (int i = 0; i < 8; i++) {
        DEBUG_PRINT("中优先级任务:计算中... %d/8", i + 1);
        vTaskDelay(300 / portTICK_PERIOD_MS);
    }
    
    DEBUG_PRINT("中优先级任务:工作完成");
    vTaskDelete(NULL);
}

void highPriorityTask(void *parameter) {
    SemaphoreHandle_t semaphore = (SemaphoreHandle_t)parameter;
    
    vTaskDelay(2000 / portTICK_PERIOD_MS);  // 延迟启动
    
    DEBUG_PRINT("高优先级任务:紧急需要信号量!");
    
    unsigned long startTime = millis();
    
    if (xSemaphoreTake(semaphore, portMAX_DELAY) == pdTRUE) {
        unsigned long waitTime = millis() - startTime;
        
        if (waitTime > 1000) {  // 如果等待超过1秒
            ERROR_PRINT("高优先级任务:等待时间过长 (%lu ms),可能发生优先级反转", waitTime);
            recordError("PRIORITY_INVERSION", "HighPriority", "等待时间异常");
        }
        
        DEBUG_PRINT("高优先级任务:获取到信号量,等待了 %lu ms", waitTime);
        xSemaphoreGive(semaphore);
    }
    
    vTaskDelete(NULL);
}

// 错误5:竞态条件演示
volatile int sharedCounter = 0;
SemaphoreHandle_t counterMutex;

void demonstrateRaceCondition() {
    DEBUG_PRINT("演示竞态条件问题");
    
    counterMutex = xSemaphoreCreateMutex();
    sharedCounter = 0;
    
    // 创建多个任务同时修改共享变量
    xTaskCreate(unsafeCounterTask, "UnsafeCounter1", 2048, NULL, 1, NULL);
    xTaskCreate(unsafeCounterTask, "UnsafeCounter2", 2048, NULL, 1, NULL);
    xTaskCreate(safeCounterTask, "SafeCounter", 2048, NULL, 1, NULL);
    xTaskCreate(counterMonitorTask, "CounterMonitor", 2048, NULL, 2, NULL);
    
    Serial.println("竞态条件问题:");
    Serial.println("• 多个任务同时访问共享资源");
    Serial.println("• 没有适当的同步机制保护");
    Serial.println("• 导致数据不一致");
    
    recordError("RACE_CONDITION", "System", "检测到竞态条件");
}

void unsafeCounterTask(void *parameter) {
    for (int i = 0; i < 1000; i++) {
        // 不安全的操作:没有互斥量保护
        int temp = sharedCounter;
        vTaskDelay(1 / portTICK_PERIOD_MS);  // 模拟处理时间
        sharedCounter = temp + 1;
        
        if (i % 100 == 0) {
            DEBUG_PRINT("%s:不安全地增加计数器到 %d", pcTaskGetTaskName(NULL), sharedCounter);
        }
    }
    
    vTaskDelete(NULL);
}

void safeCounterTask(void *parameter) {
    for (int i = 0; i < 1000; i++) {
        // 安全的操作:使用互斥量保护
        if (xSemaphoreTake(counterMutex, portMAX_DELAY) == pdTRUE) {
            int temp = sharedCounter;
            vTaskDelay(1 / portTICK_PERIOD_MS);  // 模拟处理时间
            sharedCounter = temp + 1;
            
            if (i % 100 == 0) {
                DEBUG_PRINT("%s:安全地增加计数器到 %d", pcTaskGetTaskName(NULL), sharedCounter);
            }
            
            xSemaphoreGive(counterMutex);
        }
    }
    ```cpp
    vTaskDelete(NULL);
}

void counterMonitorTask(void *parameter) {
    int lastCounter = 0;
    int unexpectedChanges = 0;
    
    for(;;) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        
        int currentCounter = sharedCounter;
        int change = currentCounter - lastCounter;
        
        DEBUG_PRINT("计数器监控:当前值 %d,变化 %d", currentCounter, change);
        
        // 检测异常变化(理论上每秒应该增加约300,但由于竞态条件可能会更少)
        if (change < 200 && change > 0) {
            unexpectedChanges++;
            WARN_PRINT("计数器监控:检测到异常变化,可能存在竞态条件");
            
            if (unexpectedChanges > 3) {
                ERROR_PRINT("计数器监控:多次检测到异常,确认存在竞态条件");
                recordError("RACE_CONDITION", "CounterMonitor", "共享变量访问异常");
            }
        }
        
        lastCounter = currentCounter;
    }
}

// 错误记录函数
void recordError(const char* errorType, const char* taskName, const char* description) {
    ErrorRecord* record = &errorLog[errorLogIndex];
    
    record->errorType = errorType;
    record->taskName = taskName;
    record->timestamp = millis();
    record->description = description;
    
    errorLogIndex = (errorLogIndex + 1) % MAX_ERROR_RECORDS;
    
    ERROR_PRINT("记录错误:%s - %s - %s", errorType, taskName, description);
}

// 错误监控任务
void errorMonitorTask(void *parameter) {
    for(;;) {
        Serial.println("=== 错误监控报告 ===");
        
        // 统计错误类型
        int mutexErrors = 0;
        int priorityErrors = 0;
        int raceConditionErrors = 0;
        int otherErrors = 0;
        
        for (int i = 0; i < MAX_ERROR_RECORDS; i++) {
            if (errorLog[i].errorType != NULL) {
                if (strcmp(errorLog[i].errorType, "MUTEX_LEAK") == 0 || 
                    strcmp(errorLog[i].errorType, "MUTEX_TIMEOUT") == 0) {
                    mutexErrors++;
                } else if (strcmp(errorLog[i].errorType, "PRIORITY_INVERSION") == 0) {
                    priorityErrors++;
                } else if (strcmp(errorLog[i].errorType, "RACE_CONDITION") == 0) {
                    raceConditionErrors++;
                } else {
                    otherErrors++;
                }
            }
        }
        
        Serial.printf("互斥量相关错误: %d\n", mutexErrors);
        Serial.printf("优先级反转错误: %d\n", priorityErrors);
        Serial.printf("竞态条件错误: %d\n", raceConditionErrors);
        Serial.printf("其他错误: %d\n", otherErrors);
        
        // 显示最近的错误
        Serial.println("\n最近的错误记录:");
        for (int i = 0; i < 5; i++) {
            int index = (errorLogIndex - 1 - i + MAX_ERROR_RECORDS) % MAX_ERROR_RECORDS;
            if (errorLog[index].errorType != NULL) {
                Serial.printf("  [%lu] %s - %s: %s\n", 
                             errorLog[index].timestamp,
                             errorLog[index].errorType,
                             errorLog[index].taskName,
                             errorLog[index].description);
            }
        }
        
        Serial.println("===================\n");
        
        vTaskDelay(30000 / portTICK_PERIOD_MS);  // 每30秒报告一次
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

使用ESP-IDF调试工具追踪同步问题

ESP-IDF提供了强大的调试工具来帮助我们追踪同步问题。让我们看看如何使用这些工具。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_task_wdt.h"

// 调试标签
static const char* TAG = "SYNC_DEBUG";

// 高级调试配置
#define ENABLE_TASK_TRACE 1
#define ENABLE_MUTEX_TRACE 1
#define ENABLE_DEADLOCK_DETECTION 1

// 调试用的同步原语
SemaphoreHandle_t debugMutexA;
SemaphoreHandle_t debugMutexB;
SemaphoreHandle_t traceSemaphore;

// 任务跟踪信息
typedef struct {
    TaskHandle_t handle;
    const char* name;
    unsigned long lastActiveTime;
    SemaphoreHandle_t heldMutex;
    SemaphoreHandle_t waitingMutex;
} TaskTraceInfo;

#define MAX_TRACED_TASKS 10
TaskTraceInfo tracedTasks[MAX_TRACED_TASKS];
int tracedTaskCount = 0;

void setup() {
    Serial.begin(115200);
    
    // 配置ESP日志级别
    esp_log_level_set("*", ESP_LOG_INFO);
    esp_log_level_set(TAG, ESP_LOG_DEBUG);
    
    // 创建调试用的同步原语
    debugMutexA = xSemaphoreCreateMutex();
    debugMutexB = xSemaphoreCreateMutex();
    traceSemaphore = xSemaphoreCreateBinary();
    
    ESP_LOGI(TAG, "ESP-IDF调试工具演示开始");
    
    // 启用看门狗
    esp_task_wdt_init(30, true);  // 30秒超时
    
    // 创建调试演示任务
    xTaskCreate(debugTask1, "DebugTask1", 2048, NULL, 2, NULL);
    xTaskCreate(debugTask2, "DebugTask2", 2048, NULL, 2, NULL);
    xTaskCreate(deadlockDetectionTask, "DeadlockDetector", 2048, NULL, 3, NULL);
    xTaskCreate(taskTraceTask, "TaskTracer", 2048, NULL, 1, NULL);
    
    // 演示各种调试技巧
    demonstrateDebuggingTechniques();
}

void demonstrateDebuggingTechniques() {
    ESP_LOGI(TAG, "=== ESP-IDF调试技巧演示 ===");
    
    // 1. 使用ESP_LOG进行结构化日志
    demonstrateStructuredLogging();
    
    // 2. 使用任务看门狗检测死锁
    demonstrateWatchdogUsage();
    
    // 3. 使用堆栈跟踪
    demonstrateStackTrace();
    
    // 4. 使用内存调试
    demonstrateMemoryDebugging();
}

void demonstrateStructuredLogging() {
    ESP_LOGI(TAG, "=== 结构化日志演示 ===");
    
    // 不同级别的日志
    ESP_LOGD(TAG, "调试信息:系统初始化完成");
    ESP_LOGI(TAG, "信息:开始执行主要逻辑");
    ESP_LOGW(TAG, "警告:检测到潜在问题");
    ESP_LOGE(TAG, "错误:发生了严重错误");
    
    // 带参数的日志
    int taskCount = uxTaskGetNumberOfTasks();
    size_t freeHeap = esp_get_free_heap_size();
    
    ESP_LOGI(TAG, "系统状态 - 任务数: %d, 可用堆内存: %d 字节", taskCount, freeHeap);
    
    // 条件日志
    if (freeHeap < 10000) {
        ESP_LOGW(TAG, "内存不足警告:仅剩 %d 字节", freeHeap);
    }
}

void demonstrateWatchdogUsage() {
    ESP_LOGI(TAG, "=== 看门狗使用演示 ===");
    
    // 为当前任务添加看门狗
    esp_task_wdt_add(NULL);
    
    ESP_LOGI(TAG, "看门狗已启用,任务必须定期喂狗");
    
    // 模拟正常工作
    for (int i = 0; i < 5; i++) {
        ESP_LOGI(TAG, "正常工作中... %d/5", i + 1);
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        
        // 喂狗
        esp_task_wdt_reset();
    }
    
    ESP_LOGI(TAG, "看门狗演示完成");
    
    // 移除看门狗
    esp_task_wdt_delete(NULL);
}

void demonstrateStackTrace() {
    ESP_LOGI(TAG, "=== 堆栈跟踪演示 ===");
    
    // 获取当前任务信息
    TaskHandle_t currentTask = xTaskGetCurrentTaskHandle();
    char* taskName = pcTaskGetTaskName(currentTask);
    UBaseType_t stackRemaining = uxTaskGetStackHighWaterMark(currentTask);
    
    ESP_LOGI(TAG, "当前任务: %s", taskName);
    ESP_LOGI(TAG, "剩余栈空间: %d 字节", stackRemaining * sizeof(StackType_t));
    
    if (stackRemaining < 100) {
        ESP_LOGE(TAG, "栈空间不足!可能发生栈溢出");
        
        // 在实际应用中,这里可以触发堆栈转储
        // esp_backtrace_print(100);
    }
}

void demonstrateMemoryDebugging() {
    ESP_LOGI(TAG, "=== 内存调试演示 ===");
    
    // 获取详细的内存信息
    multi_heap_info_t info;
    heap_caps_get_info(&info, MALLOC_CAP_DEFAULT);
    
    ESP_LOGI(TAG, "堆内存统计:");
    ESP_LOGI(TAG, "  总大小: %d 字节", info.total_free_bytes + info.total_allocated_bytes);
    ESP_LOGI(TAG, "  已分配: %d 字节", info.total_allocated_bytes);
    ESP_LOGI(TAG, "  可用: %d 字节", info.total_free_bytes);
    ESP_LOGI(TAG, "  最大块: %d 字节", info.largest_free_block);
    ESP_LOGI(TAG, "  分配次数: %d", info.allocated_blocks);
    ESP_LOGI(TAG, "  空闲块数: %d", info.free_blocks);
    
    // 检测内存泄漏
    static size_t lastFreeHeap = 0;
    size_t currentFreeHeap = esp_get_free_heap_size();
    
    if (lastFreeHeap > 0) {
        int memoryChange = currentFreeHeap - lastFreeHeap;
        if (memoryChange < -1000) {  // 内存减少超过1KB
            ESP_LOGW(TAG, "检测到可能的内存泄漏:内存减少 %d 字节", -memoryChange);
        }
    }
    
    lastFreeHeap = currentFreeHeap;
}

void debugTask1(void *parameter) {
    addTaskToTrace(xTaskGetCurrentTaskHandle(), "DebugTask1");
    
    for(;;) {
        ESP_LOGD(TAG, "DebugTask1: 开始执行");
        updateTaskTrace(xTaskGetCurrentTaskHandle(), NULL, NULL);
        
        // 尝试获取互斥量A
        ESP_LOGD(TAG, "DebugTask1: 尝试获取互斥量A");
        updateTaskTrace(xTaskGetCurrentTaskHandle(), NULL, debugMutexA);
        
        if (xSemaphoreTake(debugMutexA, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
            ESP_LOGD(TAG, "DebugTask1: 获取互斥量A成功");
            updateTaskTrace(xTaskGetCurrentTaskHandle(), debugMutexA, NULL);
            
            // 持有互斥量A时尝试获取互斥量B
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            
            ESP_LOGD(TAG, "DebugTask1: 尝试获取互斥量B");
            updateTaskTrace(xTaskGetCurrentTaskHandle(), debugMutexA, debugMutexB);
            
            if (xSemaphoreTake(debugMutexB, 2000 / portTICK_PERIOD_MS) == pdTRUE) {
                ESP_LOGD(TAG, "DebugTask1: 获取互斥量B成功");
                
                // 同时持有两个互斥量
                vTaskDelay(500 / portTICK_PERIOD_MS);
                
                ESP_LOGD(TAG, "DebugTask1: 释放互斥量B");
                xSemaphoreGive(debugMutexB);
            } else {
                ESP_LOGW(TAG, "DebugTask1: 获取互斥量B超时");
            }
            
            ESP_LOGD(TAG, "DebugTask1: 释放互斥量A");
            xSemaphoreGive(debugMutexA);
            updateTaskTrace(xTaskGetCurrentTaskHandle(), NULL, NULL);
        } else {
            ESP_LOGW(TAG, "DebugTask1: 获取互斥量A超时");
        }
        
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

void debugTask2(void *parameter) {
    addTaskToTrace(xTaskGetCurrentTaskHandle(), "DebugTask2");
    
    vTaskDelay(1500 / portTICK_PERIOD_MS);  // 错开启动时间
    
    for(;;) {
        ESP_LOGD(TAG, "DebugTask2: 开始执行");
        updateTaskTrace(xTaskGetCurrentTaskHandle(), NULL, NULL);
        
        // 尝试获取互斥量B
        ESP_LOGD(TAG, "DebugTask2: 尝试获取互斥量B");
        updateTaskTrace(xTaskGetCurrentTaskHandle(), NULL, debugMutexB);
        
        if (xSemaphoreTake(debugMutexB, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
            ESP_LOGD(TAG, "DebugTask2: 获取互斥量B成功");
            updateTaskTrace(xTaskGetCurrentTaskHandle(), debugMutexB, NULL);
            
            // 持有互斥量B时尝试获取互斥量A
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            
            ESP_LOGD(TAG, "DebugTask2: 尝试获取互斥量A");
            updateTaskTrace(xTaskGetCurrentTaskHandle(), debugMutexB, debugMutexA);
            
            if (xSemaphoreTake(debugMutexA, 2000 / portTICK_PERIOD_MS) == pdTRUE) {
                ESP_LOGD(TAG, "DebugTask2: 获取互斥量A成功");
                
                // 同时持有两个互斥量
                vTaskDelay(500 / portTICK_PERIOD_MS);
                
                ESP_LOGD(TAG, "DebugTask2: 释放互斥量A");
                xSemaphoreGive(debugMutexA);
            } else {
                ESP_LOGW(TAG, "DebugTask2: 获取互斥量A超时");
            }
            
            ESP_LOGD(TAG, "DebugTask2: 释放互斥量B");
            xSemaphoreGive(debugMutexB);
            updateTaskTrace(xTaskGetCurrentTaskHandle(), NULL, NULL);
        } else {
            ESP_LOGW(TAG, "DebugTask2: 获取互斥量B超时");
        }
        
        vTaskDelay(3000 / portTICK_PERIOD_MS);
    }
}

// 任务跟踪函数
void addTaskToTrace(TaskHandle_t handle, const char* name) {
    if (tracedTaskCount < MAX_TRACED_TASKS) {
        tracedTasks[tracedTaskCount].handle = handle;
        tracedTasks[tracedTaskCount].name = name;
        tracedTasks[tracedTaskCount].lastActiveTime = millis();
        tracedTasks[tracedTaskCount].heldMutex = NULL;
        tracedTasks[tracedTaskCount].waitingMutex = NULL;
        tracedTaskCount++;
        
        ESP_LOGI(TAG, "添加任务到跟踪列表: %s", name);
    }
}

void updateTaskTrace(TaskHandle_t handle, SemaphoreHandle_t held, SemaphoreHandle_t waiting) {
    for (int i = 0; i < tracedTaskCount; i++) {
        if (tracedTasks[i].handle == handle) {
            tracedTasks[i].lastActiveTime = millis();
            tracedTasks[i].heldMutex = held;
            tracedTasks[i].waitingMutex = waiting;
            break;
        }
    }
}

void deadlockDetectionTask(void *parameter) {
    for(;;) {
        ESP_LOGI(TAG, "=== 死锁检测报告 ===");
        
        bool potentialDeadlock = false;
        
        // 检查所有跟踪的任务
        for (int i = 0; i < tracedTaskCount; i++) {
            TaskTraceInfo* task = &tracedTasks[i];
            
            ESP_LOGI(TAG, "任务 %s:", task->name);
            ESP_LOGI(TAG, "  最后活动: %lu ms 前", millis() - task->lastActiveTime);
            ESP_LOGI(TAG, "  持有互斥量: %p", task->heldMutex);
            ESP_LOGI(TAG, "  等待互斥量: %p", task->waitingMutex);
            
            // 检查是否长时间无活动
            if (millis() - task->lastActiveTime > 10000) {
                ESP_LOGW(TAG, "  警告:任务可能被阻塞");
                potentialDeadlock = true;
            }
            
            // 检查循环等待
            if (task->heldMutex && task->waitingMutex) {
                for (int j = 0; j < tracedTaskCount; j++) {
                    if (i != j && tracedTasks[j].heldMutex == task->waitingMutex && 
                        tracedTasks[j].waitingMutex == task->heldMutex) {
                        ESP_LOGE(TAG, "  检测到潜在死锁:%s 和 %s 相互等待", 
                                task->name, tracedTasks[j].name);
                        potentialDeadlock = true;
                    }
                }
            }
        }
        
        if (potentialDeadlock) {
            ESP_LOGE(TAG, "系统可能存在死锁!");
            
            // 在实际应用中,这里可以:
            // 1. 触发系统重启
            // 2. 记录详细的调试信息
            // 3. 发送警报
            // 4. 尝试自动恢复
        } else {
            ESP_LOGI(TAG, "未检测到死锁");
        }
        
        ESP_LOGI(TAG, "========================");
        
        vTaskDelay(15000 / portTICK_PERIOD_MS);
    }
}

void taskTraceTask(void *parameter) {
    for(;;) {
        ESP_LOGI(TAG, "=== 任务跟踪报告 ===");
        
        // 获取系统中所有任务的信息
        UBaseType_t taskCount = uxTaskGetNumberOfTasks();
        TaskStatus_t* taskStatusArray = (TaskStatus_t*)pvPortMalloc(taskCount * sizeof(TaskStatus_t));
        
        if (taskStatusArray != NULL) {
            UBaseType_t actualCount = uxTaskGetSystemState(taskStatusArray, taskCount, NULL);
            
            for (UBaseType_t i = 0; i < actualCount; i++) {
                TaskStatus_t* task = &taskStatusArray[i];
                
                ESP_LOGI(TAG, "任务: %s", task->pcTaskName);
                ESP_LOGI(TAG, "  状态: %d", task->eCurrentState);
                ESP_LOGI(TAG, "  优先级: %d", task->uxCurrentPriority);
                ESP_LOGI(TAG, "  栈剩余: %d", task->usStackHighWaterMark);
                ESP_LOGI(TAG, "  任务号: %d", task->xTaskNumber);
            }
            
            vPortFree(taskStatusArray);
        }
        
        ESP_LOGI(TAG, "========================");
        
        vTaskDelay(20000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

死锁检测的自动化方案

死锁检测是一个复杂的问题,但我们可以通过一些自动化的方案来及早发现和处理死锁。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"

// 死锁检测系统配置
#define MAX_MONITORED_MUTEXES 10
#define MAX_MONITORED_TASKS 20
#define DEADLOCK_TIMEOUT_MS 5000
#define WATCHDOG_INTERVAL_MS 1000

// 互斥量监控信息
typedef struct {
    SemaphoreHandle_t mutex;
    const char* name;
    TaskHandle_t holder;
    unsigned long acquireTime;
    bool isActive;
} MutexMonitorInfo;

// 任务监控信息
typedef struct {
    TaskHandle_t handle;
    const char* name;
    SemaphoreHandle_t waitingFor;
    unsigned long waitStartTime;
    unsigned long lastHeartbeat;
    bool isActive;
} TaskMonitorInfo;

// 死锁检测系统
typedef struct {
    MutexMonitorInfo mutexes[MAX_MONITORED_MUTEXES];
    TaskMonitorInfo tasks[MAX_MONITORED_TASKS];
    int mutexCount;
    int taskCount;
    TimerHandle_t watchdogTimer;
    bool systemEnabled;
} DeadlockDetector;

DeadlockDetector detector = {0};

// 死锁检测API
void deadlockDetector_init();
void deadlockDetector_registerMutex(SemaphoreHandle_t mutex, const char* name);
void deadlockDetector_registerTask(TaskHandle_t task, const char* name);
void deadlockDetector_mutexTaken(SemaphoreHandle_t mutex, TaskHandle_t task);
void deadlockDetector_mutexGiven(SemaphoreHandle_t mutex);
void deadlockDetector_taskWaiting(TaskHandle_t task, SemaphoreHandle_t mutex);
void deadlockDetector_taskResumed(TaskHandle_t task);
void deadlockDetector_heartbeat(TaskHandle_t task);

// 测试用的互斥量和任务
SemaphoreHandle_t testMutexA, testMutexB, testMutexC;

void setup() {
    Serial.begin(115200);
    
    // 初始化死锁检测系统
    deadlockDetector_init();
    
    // 创建测试互斥量
    testMutexA = xSemaphoreCreateMutex();
    testMutexB = xSemaphoreCreateMutex();
    testMutexC = xSemaphoreCreateMutex();
    
    // 注册互斥量到检测系统
    deadlockDetector_registerMutex(testMutexA, "TestMutexA");
    deadlockDetector_registerMutex(testMutexB, "TestMutexB");
    deadlockDetector_registerMutex(testMutexC, "TestMutexC");
    
    // 创建测试任务
    TaskHandle_t task1, task2, task3;
    xTaskCreate(deadlockTestTask1, "DeadlockTest1", 2048, NULL, 2, &task1);
    xTaskCreate(deadlockTestTask2, "DeadlockTest2", 2048, NULL, 2, &task2);
    xTaskCreate(deadlockTestTask3, "DeadlockTest3", 2048, NULL, 2, &task3);
    
    // 注册任务到检测系统
    deadlockDetector_registerTask(task1, "DeadlockTest1");
    deadlockDetector_registerTask(task2, "DeadlockTest2");
    deadlockDetector_registerTask(task3, "DeadlockTest3");
    
    Serial.println("死锁检测系统启动完成!");
}

void deadlockDetector_init() {
    detector.mutexCount = 0;
    detector.taskCount = 0;
    detector.systemEnabled = true;
    
    // 创建看门狗定时器
    detector.watchdogTimer = xTimerCreate(
        "DeadlockWatchdog",
        WATCHDOG_INTERVAL_MS / portTICK_PERIOD_MS,
        pdTRUE,  // 自动重载
        NULL,
        deadlockWatchdogCallback
    );
    
    if (detector.watchdogTimer != NULL) {
        xTimerStart(detector.watchdogTimer, 0);
        Serial.println("死锁检测器:初始化完成");
    } else {
        Serial.println("死锁检测器:初始化失败");
    }
}

void deadlockDetector_registerMutex(SemaphoreHandle_t mutex, const char* name) {
    if (detector.mutexCount < MAX_MONITORED_MUTEXES) {
        MutexMonitorInfo* info = &detector.mutexes[detector.mutexCount];
        info->mutex = mutex;
        info->name = name;
        info->holder = NULL;
        info->acquireTime = 0;
        info->isActive = true;
        detector.mutexCount++;
        
        Serial.printf("死锁检测器:注册互斥量 %s\n", name);
    }
}

void deadlockDetector_registerTask(TaskHandle_t task, const char* name) {
    if (detector.taskCount < MAX_MONITORED_TASKS) {
        TaskMonitorInfo* info = &detector.tasks[detector.taskCount];
        info->handle = task;
        info->name = name;
        info->waitingFor = NULL;
        info->waitStartTime = 0;
        info->lastHeartbeat = millis();
        info->isActive = true;
        detector.taskCount++;
        
        Serial.printf("死锁检测器:注册任务 %s\n", name);
    }
}

void deadlockDetector_mutexTaken(SemaphoreHandle_t mutex, TaskHandle_t task) {
    for (int i = 0; i < detector.mutexCount; i++) {
        if (detector.mutexes[i].mutex == mutex) {
            detector.mutexes[i].holder = task;
            detector.mutexes[i].acquireTime = millis();
            break;
        }
    }
    
    // 更新任务状态
    for (int i = 0; i < detector.taskCount; i++) {
        if (detector.tasks[i].handle == task) {
            detector.tasks[i].waitingFor = NULL;
            detector.tasks[i].waitStartTime = 0;
            break;
        }
    }
}

void deadlockDetector_mutexGiven(SemaphoreHandle_t mutex) {
    for (int i = 0; i < detector.mutexCount; i++) {
        if (detector.mutexes[i].mutex == mutex) {
            detector.mutexes[i].holder = NULL;
            detector.mutexes[i].acquireTime = 0;
            break;
        }
    }
}

void deadlockDetector_taskWaiting(TaskHandle_t task, SemaphoreHandle_t mutex) {
    for (int i = 0; i < detector.taskCount; i++) {
        if (detector.tasks[i].handle == task) {
            detector.tasks[i].waitingFor = mutex;
            detector.tasks[i].waitStartTime = millis();
            break;
        }
    }
}

void deadlockDetector_taskResumed(TaskHandle_t task) {
    for (int i = 0; i < detector.taskCount; i++) {
        if (detector.tasks[i].handle == task) {
            detector.tasks[i].waitingFor = NULL;
            detector.tasks[i].waitStartTime = 0;
            break;
        }
    }
}

void deadlockDetector_heartbeat(TaskHandle_t task) {
    for (int i = 0; i < detector.taskCount; i++) {
        if (detector.tasks[i].handle == task) {
            detector.tasks[i].lastHeartbeat = millis();
            break;
        }
    }
}

void deadlockWatchdogCallback(TimerHandle_t xTimer) {
    if (!detector.systemEnabled) return;
    
    unsigned long currentTime = millis();
    bool deadlockDetected = false;
    
    // 检查长时间等待
    for (int i = 0; i < detector.taskCount; i++) {
        TaskMonitorInfo* task = &detector.tasks[i];
        
        if (task->waitingFor != NULL && 
            (currentTime - task->waitStartTime) > DEADLOCK_TIMEOUT_MS) {
            
            Serial.printf("死锁检测器:任务 %s 长时间等待互斥量\n", task->name);
            deadlockDetected = true;
        }
        
        // 检查心跳超时
        if ((currentTime - task->lastHeartbeat) > (DEADLOCK_TIMEOUT_MS * 2)) {
            Serial.printf("死锁检测器:任务 %s 心跳超时\n", task->name);
            deadlockDetected = true;
        }
    }
    
    // 检查循环等待
    if (detectCircularWait()) {
        Serial.println("死锁检测器:检测到循环等待");
        deadlockDetected = true;
    }
    
    if (deadlockDetected) {
        handleDeadlock();
    }
}

bool detectCircularWait() {
    // 构建等待图并检测循环
    for (int i = 0; i < detector.taskCount; i++) {
        TaskMonitorInfo* task = &detector.tasks[i];
        
        if (task->waitingFor != NULL) {
            // 找到持有这个互斥量的任务
            TaskHandle_t holder = NULL;
            for (int j = 0; j < detector.mutexCount; j++) {
                if (detector.mutexes[j].mutex == task->waitingFor) {
                    holder = detector.mutexes[j].holder;
                    break;
                }
            }
            
            if (holder != NULL) {
                // 检查持有者是否在等待当前任务持有的互斥量
                if (isTaskWaitingForTaskMutex(holder, task->handle)) {
                    return true;  // 发现循环等待
                }
            }
        }
    }
    
    return false;
}

bool isTaskWaitingForTaskMutex(TaskHandle_t waitingTask, TaskHandle_t holdingTask) {
    // 找到等待任务正在等待的互斥量
    SemaphoreHandle_t waitingMutex = NULL;
    for (int i = 0; i < detector.taskCount; i++) {
        if (detector.tasks[i].handle == waitingTask) {
            waitingMutex = detector.tasks[i].waitingFor;
            break;
        }
    }
    
    if (waitingMutex == NULL) return false;
    
    // 检查这个互斥量是否被holdingTask持有
    for (int i = 0; i < detector.mutexCount; i++) {
        if (detector.mutexes[i].mutex == waitingMutex && 
            detector.mutexes[i].holder == holdingTask) {
            return true;
        }
    }
    
    return false;
}

void handleDeadlock() {
    Serial.println("=== 死锁检测器:检测到死锁 ===");
    
    // 打印系统状态
    printSystemState();
    
    // 死锁处理策略
    Serial.println("死锁处理选项:");
    Serial.println("1. 重启系统");
    Serial.println("2. 终止部分任务");
    Serial.println("3. 强制释放互斥量");
    Serial.println("4. 记录日志并继续");
    
    // 在这个演示中,我们选择记录日志并继续
    Serial.println("选择策略4:记录日志并继续监控");
    
    // 可以在这里实现更复杂的恢复策略
    // 例如:esp_restart() 重启系统
}

void printSystemState() {
    Serial.println("=== 系统状态快照 ===");
    
    Serial.println("互斥量状态:");
    for (int i = 0; i < detector.mutexCount; i++) {
        MutexMonitorInfo* mutex = &detector.mutexes[i];
        Serial.printf("  %s: 持有者=%p, 获取时间=%lu\n", 
                     mutex->name, mutex->holder, mutex->acquireTime);
    }
    
    Serial.println("任务状态:");
    for (int i = 0; i < detector.taskCount; i++) {
        TaskMonitorInfo* task = &detector.tasks[i];
        Serial.printf("  %s: 等待=%p, 等待时间=%lu, 心跳=%lu\n", 
                     task->name, task->waitingFor, task->waitStartTime, task->lastHeartbeat);
    }
    
    Serial.println("=====================");
}

// 安全的互斥量操作包装函数
BaseType_t safeMutexTake(SemaphoreHandle_t mutex, TickType_t timeout) {
    TaskHandle_t currentTask = xTaskGetCurrentTaskHandle();
    
    // 通知检测器任务开始等待
    deadlockDetector_taskWaiting(currentTask, mutex);
    
    BaseType_t result = xSemaphoreTake(mutex, timeout);
    
    if (result == pdTRUE) {
        // 通知检测器互斥量被获取
        deadlockDetector_mutexTaken(mutex, currentTask);
    } else {
        // 通知检测器任务停止等待
        deadlockDetector_taskResumed(currentTask);
    }
    
    return result;
}

BaseType_t safeMutexGive(SemaphoreHandle_t mutex) {
    // 通知检测器互斥量被释放
    deadlockDetector_mutexGiven(mutex);
    
    return xSemaphoreGive(mutex);
}

// 测试任务
void deadlockTestTask1(void *parameter) {
    TaskHandle_t currentTask = xTaskGetCurrentTaskHandle();
    
    for(;;) {
        // 发送心跳
        deadlockDetector_heartbeat(currentTask);
        
        Serial.println("任务1:尝试获取互斥量A");
        if (safeMutexTake(testMutexA, 3000 / portTICK_PERIOD_MS) == pdTRUE) {
            Serial.println("任务1:获取互斥量A成功");
            
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            
            Serial.println("任务1:尝试获取互斥量B");
            if (safeMutexTake(testMutexB, 2000 / portTICK_PERIOD_MS) == pdTRUE) {
                Serial.println("任务1:获取互斥量B成功");
                
                vTaskDelay(500 / portTICK_PERIOD_MS);
                
                Serial.println("任务1:释放互斥量B");
                safeMutexGive(testMutexB);
            } else {
                Serial.println("任务1:获取互斥量B超时");
            }
            
            Serial.println("任务1:释放互斥量A");
            safeMutexGive(testMutexA);
        } else {
            Serial.println("任务1:获取互斥量A超时");
        }
        
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

void deadlockTestTask2(void *parameter) {
    TaskHandle_t currentTask = xTaskGetCurrentTaskHandle();
    
    vTaskDelay(1500 / portTICK_PERIOD_MS);  // 错开启动时间
    
    for(;;) {
        // 发送心跳
        deadlockDetector_heartbeat(currentTask);
        
        Serial.println("任务2:尝试获取互斥量B");
        if (safeMutexTake(testMutexB, 3000 / portTICK_PERIOD_MS) == pdTRUE) {
            Serial.println("任务2:获取互斥量B成功");
            
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            
            Serial.println("任务2:尝试获取互斥量A");
            if (safeMutexTake(testMutexA, 2000 / portTICK_PERIOD_MS) == pdTRUE) {
                Serial.println("任务2:获取互斥量A成功");
                
                vTaskDelay(500 / portTICK_PERIOD_MS);
                
                Serial.println("任务2:释放互斥量A");
                safeMutexGive(testMutexA);
            } else {
                Serial.println("任务2:获取互斥量A超时");
            }
            
            Serial.println("任务2:释放互斥量B");
            safeMutexGive(testMutexB);
        } else {
            Serial.println("任务2:获取互斥量B超时");
        }
        
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}

void deadlockTestTask3(void *parameter) {
    TaskHandle_t currentTask = xTaskGetCurrentTaskHandle();
    
    for(;;) {
        // 发送心跳
        deadlockDetector_heartbeat(currentTask);
        
        Serial.println("任务3:正常工作中");
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

性能瓶颈的定位方法

当系统出现性能问题时,我们需要系统性的方法来定位瓶颈。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"

// 性能分析工具
typedef struct {
    const char* name;
    uint64_t totalTime;
    uint64_t minTime;
    uint64_t maxTime;
    uint32_t callCount;
    uint64_t lastStartTime;
} PerformanceCounter;

#define MAX_PERFORMANCE_COUNTERS 20
PerformanceCounter perfCounters[MAX_PERFORMANCE_COUNTERS];
int perfCounterCount = 0;

// 性能分析宏
#define PERF_START(name) \
    int perfIndex = getOrCreatePerfCounter(name); \
    perfCounters[perfIndex].lastStartTime = esp_timer_get_time();

#define PERF_END(name) \
    uint64_t endTime = esp_timer_get_time(); \
    int perfIndex = getPerfCounter(name); \
    if (perfIndex >= 0) { \
        uint64_t duration = endTime - perfCounters[perfIndex].lastStartTime; \
        updatePerfCounter(perfIndex, duration); \
    }

// 性能分析函数
int getOrCreatePerfCounter(const char* name);
int getPerfCounter(const char* name);
void updatePerfCounter(int index, uint64_t duration);
void printPerformanceReport();

// 测试用的同步原语
SemaphoreHandle_t perfTestMutex;
SemaphoreHandle_t perfTestSemaphore;

void setup() {
    Serial.begin(115200);
    
    perfTestMutex = xSemaphoreCreateMutex();
    perfTestSemaphore = xSemaphoreCreateCounting(5, 5);
    
    Serial.println("性能瓶颈定位工具演示开始!");
    
    // 创建性能测试任务
    xTaskCreate(performanceTestTask, "PerfTest", 2048, NULL, 2, NULL);
    xTaskCreate(performanceAnalysisTask, "PerfAnalysis", 2048, NULL, 1, NULL);
    
    // 运行性能基准测试
    runPerformanceBenchmarks();
}

void runPerformanceBenchmarks() {
    Serial.println("=== 性能基准测试 ===");
    
    // 测试互斥量性能
    testMutexPerformance();
    
    // 测试信号量性能
    testSemaphorePerformance();
    
    // 测试任务切换性能
    testTaskSwitchPerformance();
    
    // 测试内存分配性能
    testMemoryAllocationPerformance();
}

void testMutexPerformance() {
    Serial.println("测试互斥量性能...");
    
    const int iterations = 1000;
    
    for (int i = 0; i < iterations; i++) {
        PERF_START("mutex_take_give");
        
        xSemaphoreTake(perfTestMutex, portMAX_DELAY);
        xSemaphoreGive(perfTestMutex);
        
        PERF_END("mutex_take_give");
    }
    
    Serial.printf("互斥量获取/释放测试完成:%d 次迭代\n", iterations);
}

void testSemaphorePerformance() {
    Serial.println("测试信号量性能...");
    
    const int iterations = 1000;
    
    for (int i = 0; i < iterations; i++) {
        PERF_START("semaphore_take_give");
        
        xSemaphoreTake(perfTestSemaphore, portMAX_DELAY);
        xSemaphoreGive(perfTestSemaphore);
        
        PERF_END("semaphore_take_give");
    }
    
    Serial.printf("信号量获取/释放测试完成:%d 次迭代\n", iterations);
}

void testTaskSwitchPerformance() {
    Serial.println("测试任务切换性能...");
    
    const int iterations = 100;
    
```cpp
void testTaskSwitchPerformance() {
    Serial.println("测试任务切换性能...");
    
    const int iterations = 100;
    
    for (int i = 0; i < iterations; i++) {
        PERF_START("task_yield");
        
        taskYIELD();
        
        PERF_END("task_yield");
    }
    
    Serial.printf("任务切换测试完成:%d 次迭代\n", iterations);
}

void testMemoryAllocationPerformance() {
    Serial.println("测试内存分配性能...");
    
    const int iterations = 100;
    const int blockSize = 1024;  // 1KB
    
    for (int i = 0; i < iterations; i++) {
        PERF_START("memory_alloc_free");
        
        void* ptr = pvPortMalloc(blockSize);
        vPortFree(ptr);
        
        PERF_END("memory_alloc_free");
    }
    
    Serial.printf("内存分配/释放测试完成:%d 次迭代\n", iterations);
}

void performanceTestTask(void *parameter) {
    for(;;) {
        // 模拟各种同步操作的性能测试
        
        // 测试1:互斥量操作
        PERF_START("mutex_operation");
        if (xSemaphoreTake(perfTestMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
            // 模拟临界区操作
            vTaskDelay(1 / portTICK_PERIOD_MS);
            xSemaphoreGive(perfTestMutex);
        }
        PERF_END("mutex_operation");
        
        // 测试2:信号量操作
        PERF_START("semaphore_operation");
        if (xSemaphoreTake(perfTestSemaphore, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
            // 模拟资源使用
            vTaskDelay(2 / portTICK_PERIOD_MS);
            xSemaphoreGive(perfTestSemaphore);
        }
        PERF_END("semaphore_operation");
        
        // 测试3:任务延迟
        PERF_START("task_delay");
        vTaskDelay(10 / portTICK_PERIOD_MS);
        PERF_END("task_delay");
        
        // 测试4:内存操作
        PERF_START("memory_operation");
        void* buffer = pvPortMalloc(512);
        if (buffer != NULL) {
            // 模拟内存操作
            memset(buffer, 0, 512);
            vPortFree(buffer);
        }
        PERF_END("memory_operation");
        
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}

void performanceAnalysisTask(void *parameter) {
    for(;;) {
        // 定期打印性能报告
        printPerformanceReport();
        vTaskDelay(10000 / portTICK_PERIOD_MS);  // 每10秒报告一次
    }
}

int getOrCreatePerfCounter(const char* name) {
    // 查找现有计数器
    for (int i = 0; i < perfCounterCount; i++) {
        if (strcmp(perfCounters[i].name, name) == 0) {
            return i;
        }
    }
    
    // 创建新计数器
    if (perfCounterCount < MAX_PERFORMANCE_COUNTERS) {
        perfCounters[perfCounterCount].name = name;
        perfCounters[perfCounterCount].totalTime = 0;
        perfCounters[perfCounterCount].minTime = UINT64_MAX;
        perfCounters[perfCounterCount].maxTime = 0;
        perfCounters[perfCounterCount].callCount = 0;
        perfCounters[perfCounterCount].lastStartTime = 0;
        
        return perfCounterCount++;
    }
    
    return -1;  // 无法创建更多计数器
}

int getPerfCounter(const char* name) {
    for (int i = 0; i < perfCounterCount; i++) {
        if (strcmp(perfCounters[i].name, name) == 0) {
            return i;
        }
    }
    
    return -1;  // 未找到
}

void updatePerfCounter(int index, uint64_t duration) {
    if (index >= 0 && index < perfCounterCount) {
        perfCounters[index].totalTime += duration;
        perfCounters[index].callCount++;
        
        if (duration < perfCounters[index].minTime) {
            perfCounters[index].minTime = duration;
        }
        
        if (duration > perfCounters[index].maxTime) {
            perfCounters[index].maxTime = duration;
        }
    }
}

void printPerformanceReport() {
    Serial.println("=== 性能分析报告 ===");
    
    for (int i = 0; i < perfCounterCount; i++) {
        PerformanceCounter* counter = &perfCounters[i];
        
        if (counter->callCount > 0) {
            double avgTime = (double)counter->totalTime / counter->callCount;
            
            Serial.printf("操作: %s\n", counter->name);
            Serial.printf("  调用次数: %u\n", counter->callCount);
            Serial.printf("  平均时间: %.2f 微秒\n", avgTime);
            Serial.printf("  最小时间: %llu 微秒\n", counter->minTime);
            Serial.printf("  最大时间: %llu 微秒\n", counter->maxTime);
            Serial.printf("  总时间: %llu 微秒\n", counter->totalTime);
        }
    }
    
    // 找出性能瓶颈
    int worstIndex = -1;
    double worstAvgTime = 0;
    
    for (int i = 0; i < perfCounterCount; i++) {
        if (perfCounters[i].callCount > 0) {
            double avgTime = (double)perfCounters[i].totalTime / perfCounters[i].callCount;
            if (avgTime > worstAvgTime) {
                worstAvgTime = avgTime;
                worstIndex = i;
            }
        }
    }
    
    if (worstIndex >= 0) {
        Serial.printf("\n潜在性能瓶颈: %s (平均 %.2f 微秒)\n", 
                     perfCounters[worstIndex].name, worstAvgTime);
    }
    
    Serial.println("=====================\n");
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

高级话题:超越基础的深度探索

掌握了基础知识后,是时候深入探索一些高级话题了。这些高级技巧能够帮助你应对更复杂的多任务场景。

信号量集合的巧妙运用

在复杂的实时系统中,任务可能需要等待多个事件中的任意一个发生。信号量集合(Semaphore Sets)就是为这种场景设计的。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"

// 事件组标志位定义
#define BUTTON_PRESSED_BIT    (1 << 0)
#define SENSOR_DATA_BIT       (1 << 1)
#define TIMER_EXPIRED_BIT     (1 << 2)
#define NETWORK_DATA_BIT      (1 << 3)
#define ALL_EVENTS_BITS       (BUTTON_PRESSED_BIT | SENSOR_DATA_BIT | TIMER_EXPIRED_BIT | NETWORK_DATA_BIT)

// 事件组句柄
EventGroupHandle_t eventGroup;

// 传统信号量
SemaphoreHandle_t buttonSemaphore;
SemaphoreHandle_t sensorSemaphore;
SemaphoreHandle_t timerSemaphore;
SemaphoreHandle_t networkSemaphore;

void setup() {
    Serial.begin(115200);
    
    // 创建事件组
    eventGroup = xEventGroupCreate();
    
    // 创建传统信号量
    buttonSemaphore = xSemaphoreCreateBinary();
    sensorSemaphore = xSemaphoreCreateBinary();
    timerSemaphore = xSemaphoreCreateBinary();
    networkSemaphore = xSemaphoreCreateBinary();
    
    Serial.println("信号量集合演示开始!");
    
    // 创建事件产生任务
    xTaskCreate(buttonTask, "Button_Task", 2048, NULL, 2, NULL);
    xTaskCreate(sensorTask, "Sensor_Task", 2048, NULL, 2, NULL);
    xTaskCreate(timerTask, "Timer_Task", 2048, NULL, 2, NULL);
    xTaskCreate(networkTask, "Network_Task", 2048, NULL, 2, NULL);
    
    // 创建事件处理任务
    xTaskCreate(eventHandlerTask, "Event_Handler", 2048, NULL, 3, NULL);
    
    // 创建传统多信号量等待任务(用于对比)
    xTaskCreate(traditionalMultiWaitTask, "Traditional_Wait", 2048, NULL, 3, NULL);
}

// 按钮事件生成任务
void buttonTask(void *parameter) {
    for(;;) {
        // 模拟按钮按下
        vTaskDelay((2000 + random(3000)) / portTICK_PERIOD_MS);
        
        Serial.println("按钮:检测到按下");
        
        // 设置事件标志位
        xEventGroupSetBits(eventGroup, BUTTON_PRESSED_BIT);
        
        // 同时也释放传统信号量
        xSemaphoreGive(buttonSemaphore);
    }
}

// 传感器事件生成任务
void sensorTask(void *parameter) {
    for(;;) {
        // 模拟传感器数据到达
        vTaskDelay((3000 + random(4000)) / portTICK_PERIOD_MS);
        
        Serial.println("传感器:新数据可用");
        
        // 设置事件标志位
        xEventGroupSetBits(eventGroup, SENSOR_DATA_BIT);
        
        // 同时也释放传统信号量
        xSemaphoreGive(sensorSemaphore);
    }
}

// 定时器事件生成任务
void timerTask(void *parameter) {
    for(;;) {
        // 模拟定时器到期
        vTaskDelay(5000 / portTICK_PERIOD_MS);
        
        Serial.println("定时器:时间到");
        
        // 设置事件标志位
        xEventGroupSetBits(eventGroup, TIMER_EXPIRED_BIT);
        
        // 同时也释放传统信号量
        xSemaphoreGive(timerSemaphore);
    }
}

// 网络事件生成任务
void networkTask(void *parameter) {
    for(;;) {
        // 模拟网络数据到达
        vTaskDelay((4000 + random(5000)) / portTICK_PERIOD_MS);
        
        Serial.println("网络:收到数据包");
        
        // 设置事件标志位
        xEventGroupSetBits(eventGroup, NETWORK_DATA_BIT);
        
        // 同时也释放传统信号量
        xSemaphoreGive(networkSemaphore);
    }
}

// 使用事件组的事件处理任务
void eventHandlerTask(void *parameter) {
    for(;;) {
        Serial.println("事件处理器:等待任意事件...");
        
        // 等待任意事件发生
        EventBits_t bits = xEventGroupWaitBits(
            eventGroup,           // 事件组
            ALL_EVENTS_BITS,      // 等待的位
            pdTRUE,               // 清除位
            pdFALSE,              // 等待任意位
            portMAX_DELAY         // 无限等待
        );
        
        // 检查哪些事件发生
        if (bits & BUTTON_PRESSED_BIT) {
            Serial.println("事件处理器:处理按钮事件");
            // 处理按钮事件
        }
        
        if (bits & SENSOR_DATA_BIT) {
            Serial.println("事件处理器:处理传感器事件");
            // 处理传感器事件
        }
        
        if (bits & TIMER_EXPIRED_BIT) {
            Serial.println("事件处理器:处理定时器事件");
            // 处理定时器事件
        }
        
        if (bits & NETWORK_DATA_BIT) {
            Serial.println("事件处理器:处理网络事件");
            // 处理网络事件
        }
    }
}

// 使用传统信号量的多事件等待任务
void traditionalMultiWaitTask(void *parameter) {
    for(;;) {
        Serial.println("传统等待:轮询多个信号量...");
        
        bool eventProcessed = false;
        
        // 检查按钮信号量
        if (xSemaphoreTake(buttonSemaphore, 0) == pdTRUE) {
            Serial.println("传统等待:处理按钮事件");
            eventProcessed = true;
        }
        
        // 检查传感器信号量
        if (xSemaphoreTake(sensorSemaphore, 0) == pdTRUE) {
            Serial.println("传统等待:处理传感器事件");
            eventProcessed = true;
        }
        
        // 检查定时器信号量
        if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE) {
            Serial.println("传统等待:处理定时器事件");
            eventProcessed = true;
        }
        
        // 检查网络信号量
        if (xSemaphoreTake(networkSemaphore, 0) == pdTRUE) {
            Serial.println("传统等待:处理网络事件");
            eventProcessed = true;
        }
        
        if (!eventProcessed) {
            // 如果没有事件,短暂延迟后再次检查
            vTaskDelay(100 / portTICK_PERIOD_MS);
        }
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

条件变量与信号量的配合

条件变量是另一种同步机制,它允许任务等待特定条件满足。在FreeRTOS中,我们可以使用信号量和互斥量来实现类似的功能。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 条件变量模拟结构
typedef struct {
    SemaphoreHandle_t mutex;
    SemaphoreHandle_t signal;
    int waitingTasks;
} ConditionVariable;

// 共享数据结构
typedef struct {
    int value;
    bool dataReady;
    ConditionVariable dataCondition;
} SharedData;

SharedData sharedData;

// 条件变量操作函数
void conditionVariable_init(ConditionVariable* cv);
void conditionVariable_wait(ConditionVariable* cv);
void conditionVariable_signal(ConditionVariable* cv);
void conditionVariable_broadcast(ConditionVariable* cv);

void setup() {
    Serial.begin(115200);
    
    // 初始化共享数据
    sharedData.value = 0;
    sharedData.dataReady = false;
    conditionVariable_init(&sharedData.dataCondition);
    
    Serial.println("条件变量演示开始!");
    
    // 创建生产者和消费者任务
    xTaskCreate(producerTask, "Producer", 2048, NULL, 2, NULL);
    xTaskCreate(consumerTask, "Consumer1", 2048, (void*)1, 1, NULL);
    xTaskCreate(consumerTask, "Consumer2", 2048, (void*)2, 1, NULL);
    xTaskCreate(consumerTask, "Consumer3", 2048, (void*)3, 1, NULL);
}

void conditionVariable_init(ConditionVariable* cv) {
    cv->mutex = xSemaphoreCreateMutex();
    cv->signal = xSemaphoreCreateCounting(10, 0);  // 支持多个等待任务
    cv->waitingTasks = 0;
}

void conditionVariable_wait(ConditionVariable* cv) {
    // 增加等待计数
    cv->waitingTasks++;
    
    // 释放互斥锁
    xSemaphoreGive(cv->mutex);
    
    // 等待条件信号
    xSemaphoreTake(cv->signal, portMAX_DELAY);
    
    // 重新获取互斥锁
    xSemaphoreTake(cv->mutex, portMAX_DELAY);
    
    // 减少等待计数
    cv->waitingTasks--;
}

void conditionVariable_signal(ConditionVariable* cv) {
    // 如果有等待任务,发送一个信号
    if (cv->waitingTasks > 0) {
        xSemaphoreGive(cv->signal);
    }
}

void conditionVariable_broadcast(ConditionVariable* cv) {
    // 向所有等待任务发送信号
    for (int i = 0; i < cv->waitingTasks; i++) {
        xSemaphoreGive(cv->signal);
    }
}

void producerTask(void *parameter) {
    for(;;) {
        // 模拟数据生成
        vTaskDelay((2000 + random(3000)) / portTICK_PERIOD_MS);
        
        // 获取互斥锁
        xSemaphoreTake(sharedData.dataCondition.mutex, portMAX_DELAY);
        
        // 更新共享数据
        sharedData.value = random(1, 100);
        sharedData.dataReady = true;
        
        Serial.printf("生产者:生成新数据 %d,通知所有消费者\n", sharedData.value);
        
        // 通知所有等待的消费者
        conditionVariable_broadcast(&sharedData.dataCondition);
        
        // 释放互斥锁
        xSemaphoreGive(sharedData.dataCondition.mutex);
    }
}

void consumerTask(void *parameter) {
    int consumerId = (int)parameter;
    
    for(;;) {
        // 获取互斥锁
        xSemaphoreTake(sharedData.dataCondition.mutex, portMAX_DELAY);
        
        Serial.printf("消费者%d:检查数据状态\n", consumerId);
        
        // 如果数据未就绪,等待条件变量
        while (!sharedData.dataReady) {
            Serial.printf("消费者%d:数据未就绪,等待...\n", consumerId);
            conditionVariable_wait(&sharedData.dataCondition);
        }
        
        // 处理数据
        Serial.printf("消费者%d:处理数据 %d\n", consumerId, sharedData.value);
        
        // 如果是最后一个消费者,重置数据状态
        if (sharedData.dataCondition.waitingTasks == 0) {
            sharedData.dataReady = false;
            Serial.printf("消费者%d:重置数据状态\n", consumerId);
        }
        
        // 释放互斥锁
        xSemaphoreGive(sharedData.dataCondition.mutex);
        
        // 模拟处理时间
        vTaskDelay((500 + random(1000)) / portTICK_PERIOD_MS);
    }
}

void loop() {
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

中断服务程序中的同步机制

在中断服务程序(ISR)中使用同步机制需要特别小心,因为普通的API函数可能会导致系统崩溃。FreeRTOS为此提供了专门的"FromISR"版本的API。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 中断引脚定义
#define BUTTON_PIN 0  // 假设按钮连接到GPIO0

// 同步原语
SemaphoreHandle_t isrSemaphore;
SemaphoreHandle_t isrMutex;
SemaphoreHandle_t isrQueue;

// 中断计数器
volatile int interruptCount = 0;

void setup() {
    Serial.begin(115200);
    
    // 创建同步原语
    isrSemaphore = xSemaphoreCreateBinary();
    isrMutex = xSemaphoreCreateMutex();
    isrQueue = xQueueCreate(10, sizeof(int));
    
    if (isrSemaphore == NULL || isrMutex == NULL || isrQueue == NULL) {
        Serial.println("同步原语创建失败!");
        return;
    }
    
    Serial.println("中断同步机制演示开始!");
    
    // 设置中断引脚
    pinMode(BUTTON_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
    
    // 创建处理任务
    xTaskCreate(isrHandlerTask, "ISR_Handler", 2048, NULL, 3, NULL);
    xTaskCreate(normalTask, "Normal_Task", 2048, NULL, 1, NULL);
    
    Serial.println("按下按钮触发中断");
}

// 中断服务程序
void IRAM_ATTR buttonISR() {
    // 记录中断次数
    interruptCount++;
    
    // 发送信号量(使用ISR版本)
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR(isrSemaphore, &xHigherPriorityTaskWoken);
    
    // 向队列发送数据(使用ISR版本)
    int value = interruptCount;
    xQueueSendFromISR(isrQueue, &value, &xHigherPriorityTaskWoken);
    
    // 如果唤醒了更高优先级的任务,请求上下文切换
    if (xHigherPriorityTaskWoken == pdTRUE) {
        portYIELD_FROM_ISR();
    }
}

// 中断处理任务
void isrHandlerTask(void *parameter) {
    for(;;) {
        // 等待中断信号
        if (xSemaphoreTake(isrSemaphore, portMAX_DELAY) == pdTRUE) {
            Serial.println("中断处理任务:收到中断通知");
            
            // 从队列读取中断数据
            int value;
            if (xQueueReceive(isrQueue, &value, 0) == pdTRUE) {
                Serial.printf("中断处理任务:收到中断数据 %d\n", value);
            }
            
            // 处理中断
            processInterrupt();
        }
    }
}

void processInterrupt() {
    // 获取互斥量保护共享资源
    if (xSemaphoreTake(isrMutex, 100 / portTICK_PERIOD_MS) == pdTRUE) {
        Serial.printf("处理中断:中断计数 = %d\n", interruptCount);
        
        // 模拟处理时间
        vTaskDelay(100 / portTICK_PERIOD_MS);
        
        // 释放互斥量
        xSemaphoreGive(isrMutex);
    } else {
        Serial.println("处理中断:无法获取互斥量");
    }
}

// 普通任务
void normalTask(void *parameter) {
    for(;;) {
        // 获取互斥量访问共享资源
        if (xSemaphoreTake(isrMutex, 1000 / portTICK_PERIOD_MS) == pdTRUE) {
            Serial.printf("普通任务:访问共享资源,中断计数 = %d\n", interruptCount);
            
            // 模拟处理时间
            vTaskDelay(500 / portTICK_PERIOD_MS);
            
            // 释放互斥量
            xSemaphoreGive(isrMutex);
        } else {
            Serial.println("普通任务:无法获取互斥量");
        }
        
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

void loop() {
    // 模拟按钮按下(在实际项目中,这部分会被真实的按钮中断替代)
    static unsigned long lastSimulatedInterrupt = 0;
    
    if (millis() - lastSimulatedInterrupt > 5000) {
        lastSimulatedInterrupt = millis();
        
        Serial.println("模拟按钮按下...");
        buttonISR();
    }
    
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值