信号量简单理解
信号量可以分为两种:一种是二值型信号量(0和1),另外一种是 N 值信号量。
二值型信号量好比家里的座机,任何时候,只能有一个人占用。而 N 值信号量,则好比公共电话亭,可以同时有N个人排队使用。
UCOSII 将二值型信号量称之为也叫互斥型信号量,将 N 值信号量称之为计数型信号量,也就是普通的信号量。
下面介绍的是普通信号量
1) 创建信号量函数
用函数 OSSemCreate 来创建一个信号量,该函数的原型为:OS_EVENT *OSSemCreate (INT16U cnt);
其中,该函数返回值为已创建的信号量的指针,而参数 cnt 则是信号量计数器(OSEventCnt)的初始值。
例如:sem_beep=OSSemCreate(0);//创建信号量
2) 请求信号量函数
任务通过调用函数 OSSemPend 请求信号量,该函数原型如下:void OSSemPend ( OS_EVENT *pevent, INT16U timeout, INT8U *err);
其中,参数 pevent 是被请求信号量的指针, timeout 为等待时限, err 为错误信息。
为防止任务因得不到信号量而处于长期的等待状态,函数 OSSemPend 允许用参数timeout 设置一个等待时间的限制,当任务等待的时间超过 timeout 时可以结束等待状态而进入就绪状态。如果参数 timeout 被设置为 0,则表明任务的等待时间为无限长。
例如:OSSemPend(sem_beep,0,&err);//请求信号量
3) 发送信号量函数
任务获得信号量,并在访问共享资源结束以后,必须要释放信号量,释放信号量也叫做发送信号量,发送信号通过 OSSemPost 函数实现 。 OSSemPost 函数在对信号量的计数器操作之前,首先要检查是否还有等待该信号量的任务。如果没有,就把信号量计数器OSEventCnt 加一;如果有,则调用调度器 OS_Sched( )去运行等待任务中优先级别最高的任务。
函数 OSSemPost 的原型为:INT8U OSSemPost(OS_EVENT *pevent);
其中, pevent 为信号量指针,该函数在调用成功后,返回值为 OS_ON_ERR,否则会根据具体错误返回 OS_ERR_EVENT_TYPE、OS_SEM_OVF。
例如:OSSemPost(sem_beep);//发送信号量
4) 删除信号量函数
应用程序如果不需要某个信号量了,那么可以调用函数 OSSemDel 来删除该信号量,
该函数的原型为:OS_EVENT *OSSemDel (OS_EVENT *pevent,INT8U opt, INT8U *err);
其中, pevent 为要删除的信号量指针, opt 为删除条件选项, err 为错误信息。
工作机制举例说明:ucosii创建了C任务,C任务中创建了信号量sem_beep=OSSemCreate(0),则sem_beep就是信号量的指针,信号量计数器(OSEventCnt)的初始值为0,另外在C任务中又创建了A和B任务,A任务中调用了发送信号量函数OSSemPost(sem_beep),B任务中调用了请求信号量函数OSSemPend(sem_beep,0,&err),一个任务在运行状态下每调用一次发送信号量函数,则信号量计数器(OSEventCnt)的值加1,只有信号量计数器的值不为0,另一个任务在调用该信号量的请求信号量函数后才能执行,否则信号量计数器的值为0,调用请求信号量函数的任务无法执行。如果信号量计数器的值不为0,则调用请求信号量函数的任务可以执行,且每调用一次请求信号量函数则信号量计数器(OSEventCnt)的值减1,任务多次执行直至信号量计数器的值为0。即只有A任务运行后信号量计数器(OSEventCnt)的值加1,不为0,B任务才能运行,且使之减1。
由于信号量计数器(OSEventCnt)在事件控制块结构体中定义的,且sem_beep被定义为结构体指针(OS_EVENT * sem_beep;),所以如果相要对信号量计数器(OSEventCnt)进行使用,则应表示为sem_beep->OSEventCnt,如下。
事件控制块结构体定义如下:
typedef struct os_event {
INT8U OSEventType; /* Type of event control block (see OS_EVENT_TYPE_xxxx) */
void *OSEventPtr; /* Pointer to message or queue structure */
INT16U OSEventCnt; /* Semaphore Count (not used if other EVENT type)信号量计数器 */
OS_PRIO OSEventGrp; /* Group corresponding to tasks waiting for event to occur */
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur */
#if OS_EVENT_NAME_EN > 0u
INT8U *OSEventName;
#endif
} OS_EVENT;
OS_EVENT * sem_beep;
if(sem_beep->OSEventCnt==0)
{
...
}
参考原子103zet6开发板中的ucosii实验例程