µC/OSII是通过优先级先后顺序进行就绪任务调度的系统,这样导致任务多的时候不好进行任务调度,容易出现乱序。这个时候使用信号量能做到任务之间的同步。
前情提要:信号量的控制在os_cfg.h中的宏进行更改。
OS_SEM_EN 此为信号量使能宏定义,使用信号量功能需要置1
目录
信号量管理函数
OSSemAccept()
作用:无需等待查看是否有信号量
原型:INT16U OSSemAccept(OS_EVENT *pevent);
注意:该函数不会导致任务被挂起(挂起:任务在列表中,但是不执行),中断也可以调用此函数查询信号量
参数:OS_EVENT *pevent:信号量的控制块指针(就是创建信号量函数的返回值)
返回值:INT16U 返回信号量的值,大于0任务就绪。此函数会消耗一个信号量。
OSSemCreate ()
作用:创建信号量
原型:OS_EVENT *OSSemCreate(INT16U value);
注意:此函数不允许在中断中调用!必须在使用信号量前创建。
参数:INT16U value:初始信号量值(每经过一个等待信号量函数OSSemPend(),此值会递减,当为0时,等待信号量函数的任务不会往下继续执行)
返回值:OS_EVENT 返回信号量的控制块指针,其他函数需要关联此信号量都需要控制块。
OSSemDel ()
作用:删除信号量
原型:OS_EVENT *OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *perr);
注意:必须先创建信号量。
参数:OS_EVENT *pevent:信号量控制块指针
INT8U opt:OS_DEL_ALWAYS直接删除、OS_DEL_NO_PEND不存在任务挂起时删除
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:如果信号量被成功删除,则返回空指针;否则需要根据错误代码进行检查。
OSSemPend()
作用:申请一个信号量
原型:void OSSemPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr);
注意:此函数会挂起任务等待信号量。
参数:OS_EVENT *pevent:信号量控制块指针
INT32U timeout:写入0时表示一直等待,写入正数值表示周期性递减到0时往下执行任务
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:无
OSSemPendAbort()
作用:使正在等待该信号量的任务取消挂起。不再等待。
原型:void OSSemPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr);
注意:此函数执行一次,等待任务就取消挂起一次
参数:OS_EVENT *pevent:信号量控制块指针
INT32U timeout:写入0时表示一直等待,写入正数值表示周期性递减到0时往下执行任务
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:无
OSSemPost()
作用:发送信号量,每执行一次信号量增加1个数
原型:INT8U OSSemPost(OS_EVENT *pevent);
注意: 信号量会优先给正在等待就绪优先级高的任务,如果高优先级任务执行,信号量就会被消耗。所以一定要注意调度问题。比如两个任务都等待信号量且信号量只有一个,高优先级任务先执行,第二个任务没有信号量就没办法执行了。
参数:OS_EVENT *pevent:信号量控制块指针
INT32U timeout:写入0时表示一直等待,写入正数值表示周期性递减到0时往下执行任务
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:无
OSSemQuery()
作用:获取一个信号量的相关信息。
原型:INT8U OSSemQuery(OS_EVENT *pevent, OS_SEM_DATA *p_sem_data);
注意: 信号量会优先给正在等待就绪优先级高的任务,如果高优先级任务执行,信号量就会被消耗。所以一定要注意调度问题。比如两个任务都等待信号量且信号量只有一个,高优先级任务先执行,第二个任务没有信号量就没办法执行了。
参数:OS_EVENT *pevent:信号量控制块指针
OS_SEM_DATA *p_sem_data:存放获取到信息的结构体
返回值:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
OSSemSet()
作用:改变当前信号量的计数值。
原型:void OSSemSet(OS_EVENT *pevent, INT16U cnt, INT8U *perr);
注意: 信号量被用于表示某事件发生了多少次的情况下会使用。这个函数只能修改当信号量>0 的情况,也就是没有任务等待信号量的情况。
参数:OS_EVENT *pevent:信号量控制块指针
INT16U cnt:期望的数值。 类似刷新值
INT8U *perr:错误码 自己查 ucos_ii.h OS_ERR_XXX表示错误码:OS_ERR_NONE
返回值:无
示例代码
例如:创建3个任务,当任务2执行后3次后,给任务1发送一个信号量使每3次执行任务2时任务1都能执行,当任务3执行10次将挂起任务2。导致任务2任务1都不执行。其余的函数各位自己去验证
#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 *DispSem;//信号量控制块指针
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;//返回标志
parameter = parameter;//防止编译器报警告
printf("Perform the initial task\r\n");
OSStatInit();
DispSem=OSSemCreate(0);//创建信号量 ,初始0个信号量,任务直接等待状态
// DispSem=OSSemCreate(5);//创建信号量 ,初始有5个信号量,被任务执行五次后直接进入等待状态
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)
{
INT8U err;//错误码
parameter = parameter;//防止编译器报警告
u32 flag=0;
while(1)
{
OSSemPend(DispSem, 0, &err);//等待信号量,第二参数写入0表示一直等待
printf("Mission_One_Func execute\r\n");
OSTimeDlyHMSM(0,0,0,500); //500ms 释放CPU
}
}
void Mission_Two_Func(void *parameter)
{
INT8U err;//错误码
parameter = parameter;//防止编译器报警告
u32 Sem_flag=0;
while(1)
{
if(++Sem_flag==3)//每执行3次任务2发送一次信号量
{
Sem_flag=0;
OSSemPost(DispSem);//发送一个信号量,错误码返回值不成功就自己去添加打印查看
}
printf("Mission_Two_Func execute\r\n");
OSTimeDlyHMSM(0,0,0,500); //500ms 释放CPU
}
}
void Mission_Three_Func(void *parameter)
{
parameter = parameter;//防止编译器报警告
u32 Sem_flag=0;
while(1)
{
if(++Sem_flag==10)
{
Sem_flag=0;
OSTaskSuspend(Mission_Two_PRO);//执行10次后挂起任务2,任务1获取不到信号量,只执行任务3
}
printf("Mission_Three_Func execute\r\n");
OSTimeDlyHMSM(0,0,0,500); //500ms 释放CPU
}
}
//此代码没意义就是为了更简单验证,不加入按键获取LED等功能
代码验证结果
Mission_One_Func //创建任务1
Mission_Two_Func //创建任务2
Mission_Three_Func //创建任务3
Mission_Two_Func execute
Mission_Three_Func execute
Mission_Two_Func execute
Mission_Three_Func execute
Mission_One_Func execute //第一次获得信号量,任务1优先级比任务2高,当任务2第三次进入发送信号量时,立马被任务1抢占CPU执行完后再回到任务2
Mission_Two_Func execute
Mission_Three_Func execute
Mission_Two_Func execute
Mission_Three_Func execute
Mission_Two_Func execute
Mission_Three_Func execute
Mission_One_Func execute//第二次获得信号量Mission_Two_Func execute
Mission_Three_Func execute
Mission_Two_Func execute
Mission_Three_Func execute
Mission_Two_Func execute
Mission_Three_Func execute
Mission_One_Func execute//第三次获得信号量
Mission_Two_Func execute
Mission_Three_Func execute
Mission_Two_Func execute
Mission_Three_Func execute//任务2被挂起,导致任务1获取不到信号量不执行
Mission_Three_Func execute
Mission_Three_Func execute,,,,,
总结:信号量主要用于任务同步,我通过任务3可以一次调用两个发送信号量函数,分别给任务1任务2等待信号量函数,那么只要任务3想要获取数据便发送信号量,可以通过任务1与任务2获得信号量取消等待进行执行获取数据,实现3个任务联动。