上一节学习了计数信号量它可以记录事件发生的次数也可以管理多个相同资源。这一节学习另一个非常重要的同步机制——互斥量Mutex。很多初学者会认为互斥量和二值信号量差不多其实它们虽然使用方法相似但设计目的完全不同。互斥量主要用于保护共享资源避免多个任务同时访问同一个资源导致数据混乱。本节通过两个任务共同使用串口输出的例子学习互斥量的基本使用。一、什么是共享资源共享资源就是多个任务都会访问的资源。例如USART串口 LCD显示屏 SPI总线 I2C总线 全局变量 SD卡假设有两个任务Task1负责打印Hello Task1Task2负责打印Hello Task2如果两个任务同时访问串口就有可能出现Hello TaHello Task2 sk1甚至出现乱码。这就是多个任务同时访问共享资源造成的问题。二、什么是互斥量互斥量可以理解为一把锁谁先获得这把锁谁先使用共享资源。例如Task1 ↓ 获得Mutex ↓ 使用串口 ↓ 释放Mutex随后Task2才能获得互斥量继续使用串口。因此同一时刻 只有一个任务 能够访问共享资源。三、创建互斥量首先包含#include semphr.h定义互斥量句柄SemaphoreHandle_t Mutex;创建互斥量Mutex xSemaphoreCreateMutex();创建成功Mutex ! NULL失败Mutex NULL因此通常写成if(Mutex NULL) { printf(Create Mutex Failed\r\n); }四、获得互斥量获得互斥量仍然使用xSemaphoreTake();例如xSemaphoreTake( Mutex, portMAX_DELAY );表示如果互斥量已经被其他任务占用就一直等待。只有获得互斥量以后当前任务 才能访问共享资源。五、释放互斥量共享资源使用完成以后必须释放互斥量。使用xSemaphoreGive();例如xSemaphoreGive(Mutex);释放以后其他等待这个互斥量的任务就可以继续运行。因此Take ↓ 使用资源 ↓ Give必须成对出现。六、完整实验创建两个任务任务1void Task1(void *pvParameters) { while(1) { xSemaphoreTake( Mutex, portMAX_DELAY ); printf(Task1 Running\r\n); vTaskDelay(pdMS_TO_TICKS(500)); xSemaphoreGive(Mutex); vTaskDelay(pdMS_TO_TICKS(500)); } }任务2void Task2(void *pvParameters) { while(1) { xSemaphoreTake( Mutex, portMAX_DELAY ); printf(Task2 Running\r\n); vTaskDelay(pdMS_TO_TICKS(500)); xSemaphoreGive(Mutex); vTaskDelay(pdMS_TO_TICKS(500)); } }程序运行时Task1获得Mutex ↓ Task2等待 ↓ Task1释放Mutex ↓ Task2获得Mutex两个任务轮流使用串口不会同时输出数据。七、如果忘记释放互斥量例如xSemaphoreTake( Mutex, portMAX_DELAY ); printf(Task1\r\n);但是忘记xSemaphoreGive(Mutex);那么Mutex一直被Task1占用此时Task2永远等待Mutex程序表现为Task2一直不运行因此Take以后 一定要Give。这是使用互斥量最容易犯的错误。八、互斥量和二值信号量有什么区别很多初学者都会问互斥量 二值信号量是不是一样从接口来看都使用xSemaphoreTake(); xSemaphoreGive();但是作用不同。二值信号量任务同步 通知事件发生例如ADC完成 按键按下 串口接收完成互斥量保护共享资源例如USART LCD Flash I2C另外互斥量还支持优先级继承这是二值信号量没有的功能。下一节会详细介绍它的重要作用。九、实际工程中的应用互斥量几乎出现在所有FreeRTOS工程中。例如多个任务共同打印串口使用Mutex例如多个任务访问I2C一次只能有一个任务访问总线。例如多个任务写Flash也必须保证同一时刻只有一个任务执行写操作。因此只要看到多个任务 共同使用 同一个资源首先想到的就是互斥量。十、总结这一节学习了FreeRTOS中的互斥量。创建互斥量使用xSemaphoreCreateMutex();获取互斥量xSemaphoreTake();释放互斥量xSemaphoreGive();互斥量最大的作用就是保护共享资源保证同一时刻只有一个任务能够访问串口、LCD、I2C、SPI、Flash等公共资源避免多个任务同时访问造成数据混乱。需要特别注意的是互斥量使用时必须遵循Take ↓ 使用共享资源 ↓ Give如果获取以后忘记释放就可能导致其他任务一直等待。