µC/OSII是通过优先级先后顺序进行就绪任务调度的系统,优先级反转在可剥夺内核中是非常常见的,在实时系统中不允许出现这种现象,这样会破坏任务的预期顺序,可能会导致严重的后果。这个时候使用互斥信号量能做到防止优先级反转。互斥信号量的作用是临时提高某个任务的优先级,实现次任务不被其他任务抢占,互斥信号量只有1和0。
前情提要:互斥信号量的控制在os_cfg.h中的宏进行更改。
OS_MUTEX_DEL_EN 此为互斥信号量使能宏定义,使用互斥信号量功能需要置1
目录
互斥信号量管理函数
OSMutexCreate()
作用:创建一个互斥信号量
原型:OS_EVENT *OSMutexCreate (INT8U prio, INT8U *perr);
注意:互斥型信号量的使用与信号量的使用差不多,但还需要 一个较高的空闲优先级,这个优先级级要比使用这个互斥型信号量的所有任务优先级都高(数字更小并且没被占用)。
参数:INT8U prio:互斥信号量需要提升到的优先级
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:一个指向分配给互斥事件控制块指针。
OSMutexAccept ()
作用:无等待请求互斥型信号量
原型:BOOLEAN OSMutexAccept (OS_EVENT *pevent, INT8U *perr);
注意:此函数不能被中断调用
参数:OS_EVENT *pevent:指向要请求的互斥信号量的指针(控制块指针)。
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:OS_TRUE:互斥信号量可用。
OSMutexDel ()
作用:删除一个互斥信号量
原型:OS_EVENT *OSMutexDel (OS_EVENT *pevent, INT8U opt, INT8U *perr);
注意:使用这个函数是很危险的,因为多个任务可能是依赖于互斥信号量而运行的。所以在删除互斥信号量之前,必须先删除所有访问互斥信号量的任务。
参数:OS_EVENT *pevent:指向要请求的互斥信号量的指针。
INT8U opt:需要删除的类型
OS_DEL_NO_PEND:只有当不存在悬而未决的任务删除时才删除,否则不删除。 OS_DEL_ALWAYS: 直接删除。
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:如果互斥锁被成功删除返回 NULL 指针。
OSMutexPend()
作用:阻塞任务等待互斥型信号量。
原型:void OSMutexPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr);
注意:互斥信号量被其他任务持有时,OSMutexPend()函数挂起当前任务直到其他的任务或中断置起信号量或超出等待的预期时间。该互斥信号号的任务的持有者 会把自己的优先级提升到一个比较高的优先级(在创建互斥量时指定),来保证他不被其他任务中止,来确保在等待该信号的高优先级任务能尽快得到互斥信号。
参数:OS_EVENT *pevent:指向要请求的互斥信号量的指针。
INT32U timeout:如果非零,你的任务将等待资源的时间不超过此参数指定的时间。如果指定0,则任务将永远等待互斥信号量,直到资源可用为止。
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:无
OSMutexPost ()
作用:释放一个互斥型信号量。
原型:INT8U OSMutexPost (OS_EVENT *pevent);
注意:中断不能调用该函数。
参数:OS_EVENT *pevent:指向要请求的互斥信号量的指针。
返回值:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
OSMutexQuery ()
作用:获取一个互斥信号量的相关信息。
原型:INT8U OSMutexQuery (OS_EVENT *pevent, OS_MUTEX_DATA *p_mutex_data);
注意:此函数不能被中断调用。
参数:OS_EVENT *pevent:指向要请求的互斥信号量的指针。
OS_MUTEX_DATA *p_mutex_data: 存放查询到的状态信息。
返回值:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
示例代码
例如:创建三个任务都等待同一个互斥信号量,执行完次数后才释放信号量。实现了控制优先级反转问题。没有互斥信号量作用时,只要遇到时间函数,就会释放CPU执行其他任务,用互斥没有释放之前,不会被其他任务抢夺CPU使用权。
#include "stm32f4xx.h"
#include "usart1.h"
#include "delay.h"
#include "includes.h"
/*初始任务*/
#define Start_TASK_PRO 10 /*任务优先级*/
#define Start_STACK_SIZE 64 /*任务栈大小*/
OS_STK Start_STask[Start_STACK_SIZE]; //设置一个数组用于存放缓冲数据(入栈时的数据)
void Start_Task(void *parameter); //任务声明
/*任务一*/
#define Mission_One_PRO 12 /*任务优先级*/
#define Mission_One_SIZE 64 /*任务栈大小*/
OS_STK Mission_One[Mission_One_SIZE]; //设置一个数组用于存放缓冲数据(入栈时的数据)
void Mission_One_Func(void *parameter); //任务声明
/*任务二*/
#define Mission_Two_PRO 14 /*任务优先级*/
#define Mission_Two_SIZE 64 /*任务栈大小*/
OS_STK Mission_Two[Mission_Two_SIZE]; //设置一个数组用于存放缓冲数据(入栈时的数据)
void Mission_Two_Func(void *parameter); //任务声明
/*任务三*/
#define Mission_Three_PRO 16 /*任务优先级*/
#define Mission_Three_SIZE 64 /*任务栈大小*/
OS_STK Mission_Three[Mission_Three_SIZE];//设置一个数组用于存放缓冲数据(入栈时的数据)
void Mission_Three_Func(void *parameter);//任务声明
OS_EVENT *DispMutex;//互斥信号量
int main()//一个工程只能有一个main函数
{
//优先级分组,代码中只执行一次
INT8U res;//返回标志
NVIC_SetPriorityGrouping(5);//优先级分组,组2 101
Systick_Inti(168);
Usart1_Init(115200);
OSInit(); // 初始化 uC/OS-II 必须要有
res=OSTaskCreate(Start_Task,(void *)10,&Start_STask[Start_STACK_SIZE-1],Start_TASK_PRO);//创建初始任务
if(res==OS_ERR_NONE)
printf("Start_Task\r\n"); //判断任务创建成功
OSStart(); //启动多任务内核 调度
//后面的代码都不会执行
while(1);
}
//初始任务,任务中可以创建任务,并且初始任务只执行一次
void Start_Task(void *parameter)
{
INT8U res;//返回标志
INT8U err;//错误码
parameter = parameter;//防止编译器报警告
printf("Perform the initial task\r\n");
OSStatInit();
DispMutex = OSMutexCreate(8, &err); /* 创建互斥信号量*/
res=OSTaskCreate(Mission_One_Func,0,&Mission_One[Mission_One_SIZE-1],Mission_One_PRO);//创建任务
if(res==OS_ERR_NONE)
printf("Mission_One_Func\r\n"); //任务创建成功
res=OSTaskCreate(Mission_Two_Func,0,&Mission_Two[Mission_Two_SIZE-1],Mission_Two_PRO);//创建任务
if(res==OS_ERR_NONE)
printf("Mission_Two_Func\r\n"); //任务创建成功
res=OSTaskCreate(Mission_Three_Func,0,&Mission_Three[Mission_Three_SIZE-1],Mission_Three_PRO);//创建任务
if(res==OS_ERR_NONE)
printf("Mission_Three_Func\r\n"); //任务创建成功
OSTaskDel(OS_PRIO_SELF);//自杀 删除自己
}
//任务1
void Mission_One_Func(void *parameter)
{
u16 i=0;//返回标志
INT8U err;//错误码
parameter = parameter;//防止编译器报警告
while(1)
{
OSMutexPend(DispMutex,0,&err);//等待互斥信号量 提升任务1的优先级
for(i=0;i<3;i++)
{
printf("Mission_One_Func execute\r\n");
OSTimeDlyHMSM(0,0,0,300); //300ms 释放CPU
}
OSMutexPost(DispMutex);//释放互斥信号量
}
}
void Mission_Two_Func(void *parameter)
{
u16 i=0;//返回标志
INT8U err;//错误码
parameter = parameter;//防止编译器报警告
while(1)
{
OSMutexPend(DispMutex,0,&err);//等待互斥信号量 提升任务2的优先级
for(i=0;i<3;i++)
{
printf("Mission_Two_Func execute\r\n");
OSTimeDlyHMSM(0,0,0,300); //300ms 释放CPU
}
OSMutexPost(DispMutex);//释放互斥信号量
}
}
void Mission_Three_Func(void *parameter)
{
u16 i=0;//返回标志
INT8U err;//错误码
parameter = parameter;//防止编译器报警告
while(1)
{
OSMutexPend(DispMutex,0,&err);//等待互斥信号量 提升任务3的优先级
for(i=0;i<3;i++)
{
printf("Mission_Three_Func execute\r\n");
OSTimeDlyHMSM(0,0,0,300); //300ms 释放CPU
}
OSMutexPost(DispMutex);//释放互斥信号量
}
}
代码验证结果
Mission_One_Func
Mission_Two_Func
Mission_Three_Func
Mission_One_Func execute
Mission_One_Func execute
Mission_One_Func execute
Mission_Two_Func execute
Mission_Two_Func execute
Mission_Two_Func execute
Mission_One_Func execute
Mission_One_Func execute
Mission_One_Func execute
Mission_Two_Func execute
Mission_Two_Func execute
Mission_Two_Func execute
Mission_One_Func execute
Mission_One_Func execute
Mission_One_Func execute
Mission_Two_Func execute
Mission_Two_Func execute
Mission_Two_Func execute
Mission_One_Func execute
Mission_One_Func execute
Mission_One_Func execute
Mission_Two_Func execute
Mission_Two_Func execute
Mission_Two_Func execute.......
任务3不会执行,因为任务2执行完毕后释放互斥信号量,任务1已经就绪优先级比任务3高所以任务3不会执行。