跟着韦东山老师FreeRTOS教学资料的学习记录
FreeRTOS全部项目代码链接(更新中)
https://gitee.com/chenshao777/free-rtos_-study
了解互斥量的使用场景
例如多个任务对同一个数组或者变量进行操作时,往往会发生冲突,可能任务A访问arr数组的过程中被任务B打断,任务B恰好也对arr数组进行了操作,当任务A继续执行时,此时的arr数组已经不是当时的arr数组了,这就造成了访问冲突。
即非原子化访问,也就是操作的过程可能被打断
为了避免此问题,可以在访问变量前,上锁,访问之后,开锁
- 即A使用变量前上个锁
- A使用完再开锁
- 再此期间B必须等A开锁后才能使用该变量,这样就不会产生访问冲突了
- 但实际上FreeRTOS的代码并没有实现谁上的锁只能由谁来开
- 所以A上锁后,B实际上也是可以自己开锁的
- 所以谁上锁,谁才能开锁需要由程序员自己约定了
- 这个锁就是我们说的互斥量,互斥量是一种特殊的二进制信号量。
要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:
#define configUSE_MUTEXES 1
一、创建互斥量
二、互斥量的操作函数(获取、释放、删除)
三、互斥量使用实例
四、盗锁
一、创建互斥量
QueueHandle_t xSemaphoreMutex; //互斥量句柄
/* 创建互斥量,返回它的句柄 */
xSemaphoreMutex = xSemaphoreCreateMutex();
二、互斥量的操作函数(获取、释放、删除)
/*
* xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
/* 释放 */
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
/* 释放(ISR版本) */
BaseType_t xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
/* 获取 */
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait
);
/* 获取 (ISR版本) */
xSemaphoreGiveFromISR(
SemaphoreHandle_t xSemaphore,
BaseType_t *pxHigherPriorityTaskWoken
);
三、互斥量使用实例
创建两个任务
- 任务1一直发送0~99数字
- 任务2一直发送100~199数字
1. 不使用互斥量的情况(USE_Mutex_Flag 置0)
实验结果,发送数据混乱在一起:
2. 使用互斥量的情况(USE_Mutex_Flag 置1)
实验结果,发送数据不产生错乱了:
代码如下:
void systemInit(void); //各模块初始化
QueueHandle_t xSemaphoreMutex; //互斥量句柄
void systemInit(void); //各模块初始化
QueueHandle_t xSemaphoreMutex; //互斥量句柄
/* 使用互斥量标志 */
#define USE_Mutex_Flag 0
/*
* 任务1一直发送0~99数字
*/
void vSemphrMutex_Task1(void *pvParameters)
{
const TickType_t xDelay = 50 / portTICK_PERIOD_MS;
BaseType_t result;
for(;;)
{
#if (USE_Mutex_Flag == 1)
/* 上锁 */
xSemaphoreTake(xSemaphoreMutex, portMAX_DELAY);
#endif
/* 打印 0 ~ 99 */
for(int i=0; i < 100 ; i++){
printf("%d ", i);
}
printf("\r\n");
#if (USE_Mutex_Flag == 1)
/* 开锁 */
xSemaphoreGive(xSemaphoreMutex);
#endif
vTaskDelay(xDelay);
}
}
/*
* 任务2一直发送100~199数字
*/
void vSemphrMutex_Task2(void *pvParameters)
{
const TickType_t xDelay = 50 / portTICK_PERIOD_MS;
BaseType_t result; //获取信号量结果变量
for(;;)
{
#if (USE_Mutex_Flag == 1)
/* 上锁 */
xSemaphoreTake(xSemaphoreMutex, portMAX_DELAY);
#endif
/* 打印 100 ~ 199 */
for(int i=100; i < 200 ; i++){
printf("%d ", i);
}
printf("\r\n");
#if (USE_Mutex_Flag == 1)
/* 开锁 */
xSemaphoreGive(xSemaphoreMutex);
#endif
vTaskDelay(xDelay);
}
}
int main()
{
systemInit(); //初始化各个模块
// 互斥量任务一
xTaskCreate(vSemphrMutex_Task1, "vSemphrMutex_Task1", 200, NULL, 2, NULL);
// 互斥量任务二
xTaskCreate(vSemphrMutex_Task2, "vSemphrMutex_Task2", 200, NULL, 2, NULL);
vTaskStartScheduler(); //开启任务调度
}
/*
* 初始化各个模块
*/
void systemInit(void)
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
USART1_Init(115200);
printf("串口初始化成功!\r\n");
/* 创建互斥量 */
xSemaphoreMutex = xSemaphoreCreateMutex();
}
四、盗锁
-
任务2上锁后,开始打印
-
任务1来上锁(Take),发现失败,不等待
-
任务1直接开锁(Give),简称盗锁(因为本来约定谁上的锁只能由谁开)
-
任务1开锁(Give)成功,开始打印
-
由于优先级相同,任务1和2交替打印
- 任务1盗锁代码如下:
- 盗锁结果: