1.开发背景
FreeRTOS 提供了多线程控制,并且是支持高低优先级抢占,这就意味着低优先级线程在执行任务时有可能被高优先级线程打断,如果两个线程共同操作同一个资源可能会导致不可意料的结果,因此,访问共享内存时需要添加互斥操作,因此互斥锁就有了。
有人可能会考虑用信号量去实现互斥的功能,实时上也是可以的,FreeRTOS的互斥锁是二值信号量的一个特例,相对于二值信号量,互斥锁会有优先级继承的特性:
【如果另一个更高优先级的任务尝试获取相同的互斥锁, 则将暂时提高“获取”互斥锁的任务的优先级。 拥有互斥锁的任务 “继承”试图“获取”相同 互斥锁的任务的优先级。 这意味着必须始终“归还”互斥锁,否则 优先级较高的任务将始终无法获得互斥锁,而优先级较低 的始终无法“取消继承”优先级。】
2.开发需求
设计实验1:利用互斥锁实现资源保护
1)创建低优先级和高优先级线程
2)低优先级线程先 100ms 获取互斥锁,在互斥锁期间 1000ms 后释放互斥锁
3)高优先级线程后 100ms 尝试获取互斥锁
设计实验2:验证优先级继承现象
1)创建低优先级和高优先级线程
2)低优先级线程取锁后获取当前线程优先级
3)低优先级线程等待高优先级线程取锁后,获取当前线程优先级
4)等待高优先级线程获取互斥锁后,再获取当前线程优先级
3.开发环境
window10 + MDK + STM32F429 + FreeRTOS10.3.1
4.实现步骤
4.1 互斥锁资源保护
4.1.1 软件编码
#include "appTest.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mspDwt.h"
#include "mspGpio.h"
#include "mspExti.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "appLog.h"
typedef struct
{
/* 信号量 */
SemaphoreHandle_t lock; // 信号锁
/* 创建任务 */
TaskHandle_t taskLow; // 低优先级线程
TaskHandle_t taskHigh; // 高优先级线程
}Ctrl_t;
/* 文件指针 */
static Ctrl_t s_ctrl = {0};
static Ctrl_t *p = &s_ctrl;
static void TaskLow(void *pvParameters);
static void TaskHigh(void *pvParameters);
/* 接收线程 */
static void TaskLow(void *pvParameters)
{
vTaskDelay(100);
for ( ; ; )
{
/* 取锁 */
xSemaphoreTake(p->lock, portMAX_DELAY);
Log_Debug("%s 取锁成功\r\n", __func__);
/* 等待高优先级线程取锁 */
vTaskDelay(1000);
Log_Debug("%s 解锁成功\r\n", __func__);
xSemaphoreGive(p->lock);
}
}
/* 发送线程 */
static void TaskHigh(void *pvParameters)
{
vTaskDelay(200);
for ( ; ; )
{
xSemaphoreTake(p->lock, portMAX_DELAY);
Log_Debug("%s 取锁成功\r\n", __func__);
vTaskDelay(1000);
Log_Debug("%s 解锁成功\r\n", __func__);
xSemaphoreGive(p->lock);
vTaskDelay(100);
}
}
/* 测试初始化 */
void aTest_Init(void)
{
/* 创建信号锁 */
p->lock = xSemaphoreCreateMutex();
/* 创建动态任务 */
xTaskCreate(TaskLow, "TaskLow", 500, NULL, 4, &p->taskLow);
xTaskCreate(TaskHigh, "TaskHigh", 500, NULL, 5, &p->taskHigh);
}
/* Key2 PC13 Key0 PH3 Key1 PH2 */
void Exti13_TriggerInterrupt(void)
{
mspExti_Close(13);
if (mspGpio_GetInput("PC13") == 0)
{
}
}
4.1.2 结果显示
4.2 互斥锁优先级继承
4.2.1 软件编码
#include "appTest.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mspDwt.h"
#include "mspGpio.h"
#include "mspExti.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "appLog.h"
typedef struct
{
/* 信号量 */
SemaphoreHandle_t lock; // 信号锁
/* 创建任务 */
TaskHandle_t taskLow; // 低优先级线程
TaskHandle_t taskHigh; // 高优先级线程
}Ctrl_t;
/* 文件指针 */
static Ctrl_t s_ctrl = {0};
static Ctrl_t *p = &s_ctrl;
static void TaskLow(void *pvParameters);
static void TaskHigh(void *pvParameters);
/* 接收线程 */
static void TaskLow(void *pvParameters)
{
vTaskDelay(100);
UBaseType_t priority = 0;
for ( ; ; )
{
/* 取锁前查看一下线程优先级 */
priority = uxTaskPriorityGet(NULL);
Log_Debug("%s 低优先级线程取锁前查看优先级 priority = %d\r\n", __func__, priority);
/* 取锁 */
xSemaphoreTake(p->lock, portMAX_DELAY);
Log_Debug("%s 取锁成功\r\n", __func__);
/* 取锁前查看一下线程优先级 */
priority = uxTaskPriorityGet(NULL);
Log_Debug("%s 低优先级线程取锁后查看优先级 priority = %d\r\n", __func__, priority);
/* 等待高优先级线程取锁 */
vTaskDelay(1000);
/* 查看线程优先级 */
priority = uxTaskPriorityGet(NULL);
Log_Debug("%s 低优先级线程等高优先级线程取锁后查看优先级 priority = %d\r\n", __func__, priority);
xSemaphoreGive(p->lock);
}
}
/* 发送线程 */
static void TaskHigh(void *pvParameters)
{
vTaskDelay(200);
for ( ; ; )
{
xSemaphoreTake(p->lock, portMAX_DELAY);
Log_Debug("%s 取锁成功\r\n", __func__);
vTaskDelay(1000);
// xSemaphoreGive(p->lock);
}
}
/* 测试初始化 */
void aTest_Init(void)
{
/* 创建信号锁 */
p->lock = xSemaphoreCreateMutex();
/* 创建动态任务 */
xTaskCreate(TaskLow, "TaskLow", 500, NULL, 4, &p->taskLow);
xTaskCreate(TaskHigh, "TaskHigh", 500, NULL, 5, &p->taskHigh);
}
/* Key2 PC13 Key0 PH3 Key1 PH2 */
void Exti13_TriggerInterrupt(void)
{
mspExti_Close(13);
if (mspGpio_GetInput("PC13") == 0)
{
}
}