ucosii任务间的同步和通信都可以通过“事件”的方式来搞定,这个事件包括:信号量、互斥量、邮箱和消息队列
其中信号量和互斥量用于同步,邮箱和消息队列可用于通信。
任务中有任务控制块,同样,在“事件”中有事件控制块ECB
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;
OSEventType:事件类型,包括
#define OS_EVENT_TYPE_UNUSED 0u
#define OS_EVENT_TYPE_MBOX 1u //邮箱
#define OS_EVENT_TYPE_Q 2u //消息
#define OS_EVENT_TYPE_SEM 3u //信号量
#define OS_EVENT_TYPE_MUTEX 4u //互斥量
#define OS_EVENT_TYPE_FLAG 5u
OSEventPtr:指向消息的指针
OSEventCnt:信号量值,只有在信号量中才使用
OSEventGrp:等待事件的任务组,作用相当于OSRdyGrp,和线面的任务等代表配合使用
OSEventTbl:任务等待表,跟任务就绪表的使用方法是一样的,有哪个任务在等待这个“事件”,就把相应的bit位置1
把一个任务置于等待状态调用void OS_EventTaskWait (OS_EVENT *pevent),信号量、互斥量、邮箱和消息队列的等待函数OSXXXPend()都会调用它。
如果一个等待任务具备了运行条件,那么就使它进入就绪状态,这时候调用的是INT8U OS_EventTaskRdy(),就是事件控制块中的任务等待表相应位清零,就绪任务表相应位置0
系统在初始化的时候也会创建一个空事件控制块链表,作用和空任务控制块链表一样,有ECB被创建时,就从空事件控制块链表中拿走一个,删除一个事件时,再将事件控制块归还空事件控制块链表。
1、创建信号量
函数原型:OS_EVENT *OSSemCreate (INT16U cnt)
参数:cnt,初始化信号量的值
返回值:返回一个ECB控制块
OS_EVENT *OSSemCreate (INT16U cnt)
{
OS_EVENT *pevent;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
if (OSIntNesting > 0u) { /* 中断中禁止创建信号量 */
return ((OS_EVENT *)0); /* ... can't CREATE from an ISR */
}
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; /* 从空ECB链表中获取一个ECBGet next free event control block */
if (OSEventFreeList != (OS_EVENT *)0) { /* See if pool of free ECB pool was empty */
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) { /* Get an event control block */
pevent->OSEventType = OS_EVENT_TYPE_SEM; /*设置为信号量*/
pevent->OSEventCnt = cnt; /* Set semaphore value */
pevent->OSEventPtr = (void *)0; /* Unlink from ECB free list */
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
OS_EventWaitListInit(pevent); /* Initialize to 'nobody waiting' on sem.等待列表清零 */
}
return (pevent);
}
2、获取信号量
函数原型:void OSSemPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr)
参数:pevent:ECB指针
timerout:超时时间,0表示无线等待
perr:返回的错误信息
void OSSemPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */
*perr = OS_ERR_PEVENT_NULL;
return;
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
*perr = OS_ERR_EVENT_TYPE;
return;
}
if (OSIntNesting > 0u) { /* See if called from ISR ... */
*perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */
return;
}
if (OSLockNesting > 0u) { /* See if called with scheduler locked ... */
*perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */
return;
}
OS_ENTER_CRITICAL();
if (pevent->OSEventCnt > 0u) { /* If sem. is positive, resource available ... */
pevent->OSEventCnt--; /* ... decrement semaphore only if positive. */
OS_EXIT_CRITICAL(); /*信号量值大于0,也就是信号量可以获取成功,直接返回继续执行*/
*perr = OS_ERR_NONE;
return;
}
/*获取信号量失败,那就得切换任务了*/ /* Otherwise, must wait until event occurs */
OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* Resource not available, pend on semaphore */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout; /* Store pend timeout in TCB */
OS_EventTaskWait(pevent); /* 把当前任务放到等待列表当中Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next highest priority task ready */
OS_ENTER_CRITICAL();
switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */
case OS_STAT_PEND_OK: /*信号量被释放了,获取成功了*/
*perr = OS_ERR_NONE;
break;
case OS_STAT_PEND_ABORT: /*信号量被中止了*/
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */
break;
case OS_STAT_PEND_TO: /*超时了*/
default:
OS_EventTaskRemove(OSTCBCur, pevent);
*perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event within TO */
break;
} /*再把任务放回到任务就绪列表中*/
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */
#if (OS_EVENT_MULTI_EN > 0u)
OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
OS_EXIT_CRITICAL();
}
3、释放信号量
函数原型:INT8U OSSemPost (OS_EVENT *pevent)
参数:pevent:时间指针
返回值:OS_ERR_NONE 成功
OS_ERR_SEM_OVF 信号量的值溢出
OS_ERR_EVENT_TYPE 传入的参数不是一个信号量
OS_ERR_PEVENT_NULL 传入参数为NULL
释放信号量就是要把信号量的值加1,在这之前,先检查是否有任务在等待这个信号量,如果有,则去切换到高优先级任务。(其实我不理解为啥要先去切换任务,再去把信号量的值++,先++再切换任务ok不呢,请各位网友指教哦)
INT8U OSSemPost (OS_EVENT *pevent)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */
return (OS_ERR_PEVENT_NULL);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
return (OS_ERR_EVENT_TYPE);
}
OS_ENTER_CRITICAL(); /*是否有任务在等待这个信号量,如有,清除等待队列,切换任务*/
if (pevent->OSEventGrp != 0u) { /* See if any task waiting for semaphore */
/* Ready HPT waiting on event */
(void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);
OS_EXIT_CRITICAL();
OS_Sched(); /* Find HPT ready to run */
return (OS_ERR_NONE);
} /*对信号量++*/
if (pevent->OSEventCnt < 65535u) { /* Make sure semaphore will not overflow */
pevent->OSEventCnt++; /* Increment semaphore count to register event */
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
OS_EXIT_CRITICAL(); /* Semaphore value has reached its maximum */
return (OS_ERR_SEM_OVF);
}
4 信号删除
函数原型:OS_EVENT *OSSemDel (OS_EVENT *pevent,
INT8U opt,
INT8U *perr)
参数:pevent 事件指针
opt:OS_DEL_NO_PEND 没有任务处于此信号量的等待表中时方可删除
OS_DEL_ALWAYS 直接删除
perr:成功返回OS_ERR_NONE
从中断中删除信号量返回OS_ERR_DEL_ISR
opt错误返回OS_ERR_INVALID_OPT
还有任务在等待信号量OS_ERR_TASK_WAITING
传递事件指针错误,不是信号量指针返回OS_ERR_EVENT_TYPE
事件指针为空OS_ERR_PEVENT_NULL
OS_EVENT *OSSemDel (OS_EVENT *pevent,
INT8U opt,
INT8U *perr)
{
BOOLEAN tasks_waiting;
OS_EVENT *pevent_return;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#if OS_ARG_CHK_EN > 0u
if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */
*perr = OS_ERR_PEVENT_NULL;
return (pevent);
}
#endif
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* Validate event block type */
*perr = OS_ERR_EVENT_TYPE;
return (pevent);
}
if (OSIntNesting > 0u) { /* See if called from ISR ... */
*perr = OS_ERR_DEL_ISR; /* ... can't DELETE from an ISR */
return (pevent);
}
OS_ENTER_CRITICAL();
if (pevent->OSEventGrp != 0u) { /* See if any tasks waiting on semaphore */
tasks_waiting = OS_TRUE; /* Yes */
} else {
tasks_waiting = OS_FALSE; /* No */
}
switch (opt) {
case OS_DEL_NO_PEND: /* Delete semaphore only if no task waiting */
if (tasks_waiting == OS_FALSE) { /*是否有任务在等待*/
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
pevent->OSEventType = OS_EVENT_TYPE_UNUSED; /*类型改为空*/
pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list返回到空ECB链表 */
pevent->OSEventCnt = 0u;
OSEventFreeList = pevent; /* Get next free event control block */
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
pevent_return = (OS_EVENT *)0; /* Semaphore has been deleted */
} else {
OS_EXIT_CRITICAL();
*perr = OS_ERR_TASK_WAITING; /*还有任务在等待,别删*/
pevent_return = pevent;
}
break;
case OS_DEL_ALWAYS: /* Always delete the semaphore */
while (pevent->OSEventGrp != 0u) { /* Ready ALL tasks waiting for semaphore 清楚等待表,都放到就绪表中 */
(void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);
}
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */
pevent->OSEventCnt = 0u;
OSEventFreeList = pevent; /* Get next free event control block */
OS_EXIT_CRITICAL(); /*有等待的情况下,要去切换任务*/
if (tasks_waiting == OS_TRUE) { /* Reschedule only if task(s) were waiting */
OS_Sched(); /* Find highest priority task ready to run */
}
*perr = OS_ERR_NONE;
pevent_return = (OS_EVENT *)0; /* Semaphore has been deleted */
break;
default:
OS_EXIT_CRITICAL();
*perr = OS_ERR_INVALID_OPT;
pevent_return = pevent;
break;
}
return (pevent_return);
}
互斥量与信号量最大的不同是,互斥量可以防止出现优先级反转的问题。。
看一下互斥量如何防止出现优先级反转的
一个互斥量的结构如下,通过OSEventCnt来实现防止优先级反转功能。分成低八位和高八位,低八位为0XFF表示资源可以获得,否则获取资源失败,高八位表示的是要提升的优先级
1、创建互斥量
函数原型:OS_EVENT *OSMutexCreate (INT8U prio,
INT8U *perr)
参数:1、优先级,必须保证优先级是未使用的
2、错误码
疑惑:1、为何要自己设定这个优先级,为何不直接将使用互斥量任务的优先级复制到ECB中呢?2、为何不能使用已存在的优先级呢?欢迎大神们解答
返回值:成功返回一个ECB指针
OS_EVENT *OSMutexCreate (INT8U prio,
INT8U *perr)
{
OS_EVENT *pevent;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == OS_TRUE) {
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#if OS_ARG_CHK_EN > 0u
if (prio >= OS_LOWEST_PRIO) { /* Validate PIP */
*perr = OS_ERR_PRIO_INVALID;
return ((OS_EVENT *)0);
}
#endif
if (OSIntNesting > 0u) { /* See if called from ISR ... */
*perr = OS_ERR_CREATE_ISR; /* ... can't CREATE mutex from an ISR */
return ((OS_EVENT *)0);
}
OS_ENTER_CRITICAL(); /*设定的优先级必须是不存在的(why?)*/
if (OSTCBPrioTbl[prio] != (OS_TCB *)0) { /* Mutex priority must not already exist */
OS_EXIT_CRITICAL(); /* Task already exist at priority ... */
*perr = OS_ERR_PRIO_EXIST; /* ... inheritance priority */
return ((OS_EVENT *)0);
}
OSTCBPrioTbl[prio] = OS_TCB_RESERVED; /* Reserve the table entry */
pevent = OSEventFreeList; /* Get next free event control block */
if (pevent == (OS_EVENT *)0) { /* See if an ECB was available */
OSTCBPrioTbl[prio] = (OS_TCB *)0; /* No, Release the table entry */
OS_EXIT_CRITICAL();
*perr = OS_ERR_PEVENT_NULL; /* No more event control blocks */
return (pevent);
}
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; /* Adjust the free list */
OS_EXIT_CRITICAL();
pevent->OSEventType = OS_EVENT_TYPE_MUTEX; /*对ECB进行赋值*/
pevent->OSEventCnt = (INT16U)((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE; /* Resource is avail. */
pevent->OSEventPtr = (void *)0; /* No task owning the mutex */
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
OS_EventWaitListInit(pevent);
*perr = OS_ERR_NONE;
return (pevent);
}
消息队列可以传递多条消息,消息队列由3部分组成:ECB,消息队列和消息。
消息队列比上述几个多了两个数据结构:队列控制块OS_Q和一个指针数组MsgTbl[],数组内的指针指向消息,这个数组是自己创建的
这几项的关系如下,OSEventPtr指向了消息控制块,消息控制块中的变量又指向数组MsgTbl的各个部分
消息控制块中各个变量含义:
用户创建的消息指针数组中存放的是传输消息的地址,这个数组在使用的时候是一个环形buffer,OSQOut指向的是将要被取出的消息地址。OSQIn存放的则是下一个能被存放消息的地址。
1、创建消息队列
函数原型:OS_EVENT *OSQCreate (void **start,
INT16U size)
参数:start,消息指针数组的起始地址
size:消息队列长度
返回值:事件指针
OS_EVENT *OSQCreate (void **start,
INT16U size)
{
OS_EVENT *pevent;
OS_Q *pq;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == OS_TRUE) {
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
if (OSIntNesting > 0u) { /* See if called from ISR ... */
return ((OS_EVENT *)0); /* ... can't CREATE from an ISR */
}
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; /* Get next free event control block */
if (OSEventFreeList != (OS_EVENT *)0) { /* See if pool of free ECB pool was empty */
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) { /* See if we have an event control block */
OS_ENTER_CRITICAL();
pq = OSQFreeList; /* Get a free queue control block */
if (pq != (OS_Q *)0) { /* Were we able to get a queue control block ? */
OSQFreeList = OSQFreeList->OSQPtr; /* Yes, Adjust free list pointer to next free*/
OS_EXIT_CRITICAL();
pq->OSQStart = start; /* Initialize the queue */
pq->OSQEnd = &start[size];
pq->OSQIn = start;
pq->OSQOut = start;
pq->OSQSize = size;
pq->OSQEntries = 0u;
pevent->OSEventType = OS_EVENT_TYPE_Q;
pevent->OSEventCnt = 0u;
pevent->OSEventPtr = pq;
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
OS_EventWaitListInit(pevent); /* Initalize the wait list */
} else {
pevent->OSEventPtr = (void *)OSEventFreeList; /* No, Return event control block on error */
OSEventFreeList = pevent;
OS_EXIT_CRITICAL();
pevent = (OS_EVENT *)0;
}
}
return (pevent);
}