一、基本概念
互斥锁又称互斥信号量,是一种特殊的二值信号量,常用于实现对临界资源的独占式处理。任务使用时,互斥锁处于闭锁状态,使用完毕,处于开锁状态。可以用于两个任务同时向串口发送等情况。
二、互斥锁的优先级继承机制
任务的优先级,在创建时已经指定,高优先级的任务可以打断低优先级的任务。低优先级的任务在使用互斥锁时,可能被高优先级的任务打断,那么暂时提高低优先级的任务的优先级,让它和高优先级的一样,避免了被抢占,使用完后,任务优先级恢复。
三、互斥锁的运行机制
(1)互斥锁保护的资源被占用时,无论何种优先级任务想要使用都会被阻塞
(2)正在使用互斥锁的任务1比阻塞中的任务2优先级低,暂时把1的优先级提升到2的
(3)当任务1使用完资源后会释放互斥锁,任务1的优先级恢复
(4)任务2此时可以获得互斥锁,并访问资源。
四、互斥锁的使用
4.1 互斥锁控制块
typedef struct
{
UINT8 ucMuxStat; /**< 互斥锁状态:OS_MUX_UNUSED或OS_MUX_USED */
UINT16 usMuxCount; /**< 互斥锁持有次数 */
UINT32 ucMuxID; /**< 互斥锁ID*/
LOS_DL_LIST stMuxList; /**< 互斥锁阻塞列表*/
LOS_TASK_CB *pstOwner; /**< 任务控制块指针*/
UINT16 usPriority; /**< 记录互斥锁任务的初始优先级 */
} MUX_CB_S;
4.2 互斥锁错误代码
在los_mux.h文件中有定义
4.3 互斥锁函数
(1)互斥锁创建函数LOS_MuxCreate
(2)互斥锁删除函数LOS_MuxDelete
(3)互斥锁释放函数LOS_MuxPost
(4)互斥锁获取函数LOS_MuxPend
4.4 互斥锁使用注意事项
(1)两个任务不能获取同一个互斥锁
(2)不能在中断服务程序中使用
(3)获得后,尽快释放
(4)任务持有时,不允许再更改持有互斥锁的任务的优先级
(5)互斥锁和信号量的区别在于,互斥锁可以被已经持有互斥锁的任务重复获取,而不会死锁
五、互斥锁实验main文件
(1)模拟优先级翻转实验(使用二值信号量 )
创建了三个任务,分别为高优先级、中优先级、低优先级。程序依次执行高—中—低。串口打印流程如下:
高:获取二值信号量,释放
中:运行中
低:获取二值信号量,占用非常久,此时,高堵塞
中:运行中
中:运行中
低:释放二值信号量,高就绪,LOS_TaskYield函数将低任务释放CPU,将其移到相同优先级就绪队列的队尾
高:获取二值信号量,释放
中:运行中
高:获取二值信号量,释放
低:获取二值信号量,占用非常久
……
/* LiteOS 头文件 */
#include "los_sys.h"
#include "los_task.ph"
#include "los_queue.h"
#include "los_sem.h"
/* 板级外设头文件 */
#include "bsp_usart.h"
#include "bsp_led.h"
#include "bsp_key.h"
/* 定义任务句柄 */
UINT32 HighPriority_Task_Handle;
UINT32 MidPriority_Task_Handle;
UINT32 LowPriority_Task_Handle;
/*定义二值信号量的ID*/
UINT32 BinarySem_Handle;
/* 函数声明 */
static UINT32 AppTaskCreate(void);
static UINT32 Creat_HighPriority_Task(void);
static UINT32 Creat_MidPriority_Task(void);
static UINT32 Creat_LowPriority_Task(void);
static void HighPriority_Task(void);
static void MidPriority_Task(void);
static void LowPriority_Task(void);
static void BSP_Init(void);
int main(void)
{
UINT32 uwRet = LOS_OK; //定义一个任务创建的返回值,默认为创建成功
/* 板载相关初始化 */
BSP_Init();
printf("串口打印出-Sucessful-表明实验创建成功\r\n");
/* LiteOS 内核初始化 */
uwRet = LOS_KernelInit();
if (uwRet != LOS_OK)
{
printf("LiteOS 核心初始化失败!失败代码0x%X\n",uwRet);
return LOS_NOK;
}
uwRet = AppTaskCreate();
if (uwRet != LOS_OK)
{
printf("AppTaskCreate创建任务失败!失败代码0x%X\n",uwRet);
return LOS_NOK;
}
/* 开启LiteOS任务调度 */
LOS_Start();
//正常情况下不会执行到这里
while(1);
}
static UINT32 AppTaskCreate(void)
{
/* 定义一个返回类型变量,初始化为LOS_OK */
UINT32 uwRet = LOS_OK;
/*创建一个二值信号量*/
uwRet = LOS_BinarySemCreate(1,&BinarySem_Handle);
if(uwRet!=LOS_OK)
{
printf("Test_Queue队列创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
uwRet = Creat_HighPriority_Task();
if (uwRet != LOS_OK)
{
printf("Receive_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
uwRet = Creat_MidPriority_Task();
if (uwRet != LOS_OK)
{
printf("Send_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
uwRet = Creat_LowPriority_Task();
if (uwRet != LOS_OK)
{
printf("Send_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
return LOS_OK;
}
static UINT32 Creat_HighPriority_Task()
{
//定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
//定义一个用于创建任务的参数结构体
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 3; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = " HighPriority_Task";/* 任务名 */
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC) HighPriority_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(& HighPriority_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
static UINT32 Creat_MidPriority_Task()
{
// 定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 4; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "MidPriority_Task"; /* 任务名*/
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)MidPriority_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&MidPriority_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
static UINT32 Creat_LowPriority_Task()
{
// 定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 5; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "LowPriority_Task"; /* 任务名*/
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)LowPriority_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&LowPriority_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
static void HighPriority_Task(void)
{
UINT32 uwRet = LOS_OK;
/* 任务都是一个无限循环,不能返回 */
while(1)
{
uwRet = LOS_SemPend(BinarySem_Handle, LOS_WAIT_FOREVER);
//获取二值信号量(BinarySem_Handle,未获取到会一直等待
if (uwRet == LOS_OK)
{
printf("高优先级任务运行中\n");
}
LED1_TOGGLE;
LOS_SemPost(BinarySem_Handle);//释放二值信号量BinarySem_Handle
LOS_TaskDelay(1000);
}
}
static void MidPriority_Task(void)
{
UINT32 uwRet = LOS_OK;
/* 任务都是一个无限循环,不能返回 */
while(1)
{
printf("中优先级任务运行中\n");
LOS_TaskDelay(1000);
}
}
static void LowPriority_Task(void)
{
UINT32 uwRet = LOS_OK;
static uint32_t i;
/* 任务都是一个无限循环,不能返回 */
while(1)
{
uwRet = LOS_SemPend(BinarySem_Handle, LOS_WAIT_FOREVER);
//获取二值信号量(BinarySem_Handle,未获取到会一直等待
if (uwRet == LOS_OK)
{
printf("低优先级任务运行中\n");
}
LED2_TOGGLE;
for (i = 0;i<200000;i++){//模拟低优先级任务占用信号量
LOS_TaskYield();//放弃剩余时间片,进行一次任务切换
}
printf("低优先任务释放信号量\r\n");
LOS_SemPost(BinarySem_Handle);//释放二值信号量
LOS_TaskDelay(1000);
}
}
/*******************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
******************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* KEY初始化 */
Key_GPIO_Config();
}
/******************************END OF FILE*******************/
(2)互斥锁实验
低优先级任务运行时,中优先级任务无法抢占低优先级的任务
/* LiteOS 头文件 */
#include "los_sys.h"
#include "los_task.ph"
#include "los_queue.h"
#include "los_sem.h"
#include "los_mux.h"
/* 板级外设头文件 */
#include "bsp_usart.h"
#include "bsp_led.h"
#include "bsp_key.h"
/* 定义任务句柄 */
UINT32 HighPriority_Task_Handle;
UINT32 MidPriority_Task_Handle;
UINT32 LowPriority_Task_Handle;
/*定义互斥锁的ID变量*/
UINT32 Mutex_Handle;
/* 函数声明 */
static UINT32 AppTaskCreate(void);
static UINT32 Creat_HighPriority_Task(void);
static UINT32 Creat_MidPriority_Task(void);
static UINT32 Creat_LowPriority_Task(void);
static void HighPriority_Task(void);
static void MidPriority_Task(void);
static void LowPriority_Task(void);
static void BSP_Init(void);
int main(void)
{
UINT32 uwRet = LOS_OK; //定义一个任务创建的返回值,默认为创建成功
/* 板载相关初始化 */
BSP_Init();
printf("串口打印出-Sucessful-表明实验创建成功\r\n");
/* LiteOS 内核初始化 */
uwRet = LOS_KernelInit();
if (uwRet != LOS_OK)
{
printf("LiteOS 核心初始化失败!失败代码0x%X\n",uwRet);
return LOS_NOK;
}
uwRet = AppTaskCreate();
if (uwRet != LOS_OK)
{
printf("AppTaskCreate创建任务失败!失败代码0x%X\n",uwRet);
return LOS_NOK;
}
/* 开启LiteOS任务调度 */
LOS_Start();
//正常情况下不会执行到这里
while(1);
}
static UINT32 AppTaskCreate(void)
{
/* 定义一个返回类型变量,初始化为LOS_OK */
UINT32 uwRet = LOS_OK;
/*创建一个互斥锁*/
uwRet = LOS_MuxCreate(&Mutex_Handle);
if(uwRet!=LOS_OK)
{
printf("Mutex创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
uwRet = Creat_HighPriority_Task();
if (uwRet != LOS_OK)
{
printf("Receive_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
uwRet = Creat_MidPriority_Task();
if (uwRet != LOS_OK)
{
printf("Send_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
uwRet = Creat_LowPriority_Task();
if (uwRet != LOS_OK)
{
printf("Send_Task任务创建失败!失败代码0x%X\n",uwRet);
return uwRet;
}
return LOS_OK;
}
static UINT32 Creat_HighPriority_Task()
{
//定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
//定义一个用于创建任务的参数结构体
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 3; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = " HighPriority_Task";/* 任务名 */
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC) HighPriority_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(& HighPriority_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
static UINT32 Creat_MidPriority_Task()
{
// 定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 4; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "MidPriority_Task"; /* 任务名*/
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)MidPriority_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&MidPriority_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
static UINT32 Creat_LowPriority_Task()
{
// 定义一个创建任务的返回类型,初始化为创建成功的返回值
UINT32 uwRet = LOS_OK;
TSK_INIT_PARAM_S task_init_param;
task_init_param.usTaskPrio = 5; /* 任务优先级,数值越小,优先级越高 */
task_init_param.pcName = "LowPriority_Task"; /* 任务名*/
task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)LowPriority_Task;/* 任务函数入口 */
task_init_param.uwStackSize = 1024; /* 堆栈大小 */
uwRet = LOS_TaskCreate(&LowPriority_Task_Handle, &task_init_param);/* 创建任务 */
return uwRet;
}
static void HighPriority_Task(void)
{
UINT32 uwRet = LOS_OK;
/* 任务都是一个无限循环,不能返回 */
while(1)
{
uwRet = LOS_MuxPend(Mutex_Handle, LOS_WAIT_FOREVER);
//获取Mutex,未获取到会一直等待
if (uwRet == LOS_OK)
{
printf("高优先级任务运行中\n");
}
LED1_TOGGLE;
LOS_MuxPost(Mutex_Handle);//释放
LOS_TaskDelay(1000);
}
}
static void MidPriority_Task(void)
{
UINT32 uwRet = LOS_OK;
/* 任务都是一个无限循环,不能返回 */
while(1)
{
printf("中优先级任务运行中\n");
LOS_TaskDelay(1000);
}
}
static void LowPriority_Task(void)
{
UINT32 uwRet = LOS_OK;
static uint32_t i;
/* 任务都是一个无限循环,不能返回 */
while(1)
{
uwRet = LOS_MuxPend(Mutex_Handle, LOS_WAIT_FOREVER);
//获取Mutex,未获取到会一直等待
if (uwRet == LOS_OK)
{
printf("低优先级任务运行中\n");
}
LED2_TOGGLE;
for (i = 0;i<200000;i++){//模拟低优先级任务占用信号量
LOS_TaskYield();//放弃剩余时间片,进行一次任务切换
}
printf("低优先任务释放互斥锁\r\n");
LOS_MuxPost(Mutex_Handle);//释放互斥锁
LOS_TaskDelay(1000);
}
}
/*******************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
******************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
/* LED 初始化 */
LED_GPIO_Config();
/* 串口初始化 */
USART_Config();
/* KEY初始化 */
Key_GPIO_Config();
}
/******************************END OF FILE*******************/