1.什么是信号量
在UCOSIII中,信号量分为两种:二值信号量和计数信号量
二值信号量就是只有两个值(0和1)的信号量,当它为1的时候,与它绑定的资源就可以被访问,当它为0的时候,与它绑定的资源不可以被访问。试图访问一个信号量为0的资源的任务会被放入到等待信号量的任务表中,在等待信号量的时候也可以设置超时处理,如果设定的时间任务没有等到信号量的话那么该任务就会进入就绪态。可以看出,一个信号量如果为二值信号量的话,一次只能有一个任务可以访问这个资源,其实和单片机程序中的flag一样,做标志位的。
计数信号量可以有多种数值,每发送一次信号量时,该信号量+1,每请求一次信号量时,该信号量-1。任务同步使用这个。
2.怎么使用信号量
要使用信号量肯定要先创建一个信号量
2.1创建信号量的函数
void OSSemCreate (OS_SEM *p_sem, //信号量 CPU_CHAR *p_name, //信号量名字 OS_SEM_CTR cnt, //信号量的值 OS_ERR *p_err) //返回值,记录错误信号
第一个参数需要先定义一个OS_SEM的变量
OS_SEM MySem; //定义一个信号量,用于任务同步
第二个参数随便写一个字符串
第三个参数用来表示信号量的初始值。有2种写法, 如果用于共享资源,则应初始化为可用资源数量。如果用于表示事件的发生,则应将其初始化为0。用于任务同步的话,使用事件发生次数。
第四个参数记录返回值,其他函数也有这样的一个返回值。
调用OSSemCreate创建一个信号量。创建信号量应该放在第一个任务中(用来创建其他任务的哪个任务)
OSSemCreate ((OS_SEM* )&MySem, (CPU_CHAR* )"SemName", (OS_SEM_CTR)0, //初始化为0,表示事件的发生次数 (OS_ERR* )&err);
2.2发送信号量的函数
OS_SEM_CTR OSSemPost (OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
第一个参数是上面定义的信号量结构体变量MySem
第二个参数决定发送模式,
2.3请求信号量的函数
OS_SEM_CTR OSSemPend (OS_SEM *p_sem, OS_TICK timeout, OS_OPT opt, CPU_TS *p_ts, OS_ERR *p_err)
第一个参数是上面定义的信号量结构体变量MySem
第二个参数是设置超时时间,设置为0则一直等待(阻塞)
第三个参数是设置是否阻塞等待这个信号量
第四个参数可以记录请求到信号量时的时间(时间戳),为空则不需要时间戳
第五个参数记录返回值,其他函数也有这样的一个返回值。
3.信号量完成任务同步
信号量现在更多的被用来实现任务的同步以及任务和ISR间的同步,信号量用于任务同步。
图1 信号量用于任务同步
图1中用一个小旗子代表信号量,小旗子旁边的数值N为信号量计数值,表示发布号量的次数累积值,ISR可以多次发布信号量,发布的次数会记录为N。一般情况下,N的
初始值是0,表示事件还没有发生过。在初始化时,也可以将N的初值设为大于零的某个值,来表示初始情况下有多少信号量可用。等待信号量的任务旁边的小沙漏表示等待任务可以设定超时时间。超时的意思是该任务只会等待一定时间的信号量,如果在这段时间内没有等到信号量,UCOSⅡ就会将任务置于就绪表中,并返回错误码
3.1 使用信号量完成任务同步
任务1每1.5s发送一个信号量。任务2每0.5s请求一次信号量。记录这个信号量数值的变量是上面定义的的信号量结构体下的一个ctr变量
发送信号量和请求信号量的过程:
每当任务1发送一次信号量是ctr+1,每当任务2请求到一次信号量时ctr-1。当ctr=0时,任务2阻塞等待任务1发送信号量。原理就是只有任务1发送了信号量时,任务2才会请求到一次信号量,任务2才会执行一次。当任务1发送了10次信号量,任务2也就会执行10次。这样完成任务同步。
//任务1的任务函数 void task1_task(void *p_arg) { u8 key=0; OS_ERR err; //printf("当前任务1进来了 " ); while(1) { keey++; if(次数==10){OSTaskDel((OS_TCB *)0,&err);}//任务1发送10次信号量后删除自己,不再发送信号量 if(key<10){ printf("当前任务1while1 进来了key=%d ", key); OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err);//发送信号量 printf("当前任务1的信号量:%d ",MySem.Ctr); OSTimeDlyHMSM(0,0,1,500,OS_OPT_TIME_PERIODIC,&err); //延时1.5s }} }
//任务2的任务函数 void task2_task(void *p_arg) { u8 num; OS_ERR err; //printf("当前任务2进来了 " ); while(1) {printf("当前任务2while1 进来了 " ); OSSemPend(&SYNC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量 num++; printf("当前任务2的信号量:%d ",MySem.Ctr); OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } }
结果:
由于任务1 2里面的延时时间不同,串口打印的数据也很错乱。当任务1发送了9次信号量后,停止发送,任务2也会请求到9次信号量。也就是说任务1和任务2的执行次数是相同的。这样完成任务同步。