1.给任务发布信号量是一种非常常用的同步方法,因此,在uC/OS-III中,每个任务都有它自己的内嵌信号量。
2.当创建任务时,任务信号量会被自动创建,且初始计数为零。
3.等待任务信号量使用函数OSTaskSemPend(),它的定义位于os_task.c中。
OS_SEM_CTR OSTaskSemPend (OS_TICK timeout, // 1190行 - 1293行,超时时间 OS_OPT opt, // 等待的方式 CPU_TS *p_ts, // 时间戳 OS_ERR *p_err) // 错误码 { OS_SEM_CTR ctr; CPU_SR_ALLOC(); // 声明变量cpu_sr,用来临时存储CPU的状态寄存器 #ifdef OS_SAFETY_CRITICAL // 允许进行系统安全性检查 if (p_err == (OS_ERR *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return ((OS_SEM_CTR)0); } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u // 允许进行ISR调用检查 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 不允许在ISR中调用 *p_err = OS_ERR_PEND_ISR; return ((OS_SEM_CTR)0); } #endif if (p_ts != (CPU_TS *)0) { *p_ts = (CPU_TS )0; // 初始化返回的时间戳 } CPU_CRITICAL_ENTER(); // 进入临界区 if (OSTCBCurPtr->SemCtr > (OS_SEM_CTR)0) { // 计数值 > 0,表示信号量可用 OSTCBCurPtr->SemCtr--; // 计数值减1 ctr = OSTCBCurPtr->SemCtr; if (p_ts != (CPU_TS *)0) { *p_ts = OSTCBCurPtr->TS; } #if OS_CFG_TASK_PROFILE_EN > 0u // ??? OSTCBCurPtr->SemPendTime = OS_TS_GET() - OSTCBCurPtr->TS; if (OSTCBCurPtr->SemPendTime > OSTCBCurPtr->SemPendTimeMax) { OSTCBCurPtr->SemPendTimeMax = OSTCBCurPtr->SemPendTime; } #endif CPU_CRITICAL_EXIT(); // 退出临界区 *p_err = OS_ERR_NONE; return (ctr); } // 计数值 == 0,信号量不可用 if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { // 非阻塞型 CPU_CRITICAL_EXIT(); // 退出临界区 *p_err = OS_ERR_PEND_WOULD_BLOCK; return ((OS_SEM_CTR)0); } else { // 阻塞型 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 任务调度器锁定,无法挂起任务 CPU_CRITICAL_EXIT(); // 退出临界区 *p_err = OS_ERR_SCHED_LOCKED; return ((OS_SEM_CTR)0); } } OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); // 锁定调度器,使能中断 OS_Pend((OS_PEND_DATA *)0, // 把任务添加到挂起表中 (OS_PEND_OBJ *)0, (OS_STATE )OS_TASK_PEND_ON_TASK_SEM, (OS_TICK )timeout); OS_CRITICAL_EXIT_NO_SCHED(); // 退出临界区,不调度 OSSched(); // 执行任务调度程序 CPU_CRITICAL_ENTER(); // 进入临界区 switch (OSTCBCurPtr->PendStatus) { // 查看等待信号量的结果 case OS_STATUS_PEND_OK: // 信号量发布了 if (p_ts != (CPU_TS *)0) { *p_ts = OSTCBCurPtr->TS; #if OS_CFG_TASK_PROFILE_EN > 0u OSTCBCurPtr->SemPendTime = OS_TS_GET() - OSTCBCurPtr->TS; if (OSTCBCurPtr->SemPendTime > OSTCBCurPtr->SemPendTimeMax) { OSTCBCurPtr->SemPendTimeMax = OSTCBCurPtr->SemPendTime; } #endif } *p_err = OS_ERR_NONE; break; case OS_STATUS_PEND_ABORT: // 等待操作被其他任务取消了 if (p_ts != (CPU_TS *)0) { *p_ts = OSTCBCurPtr->TS; } *p_err = OS_ERR_PEND_ABORT; break; case OS_STATUS_PEND_TIMEOUT: // 在特定的超时时间内,信号量没有被发布 if (p_ts != (CPU_TS *)0) { *p_ts = (CPU_TS )0; } *p_err = OS_ERR_TIMEOUT; break; default: // 其他情况 *p_err = OS_ERR_STATUS_INVALID; break; } ctr = OSTCBCurPtr->SemCtr; CPU_CRITICAL_EXIT(); // 退出临界区 return (ctr); }4.发布任务信号量使用OSTaskSemPost(),它的定义位于os_task.c中。
OS_SEM_CTR OSTaskSemPost (OS_TCB *p_tcb, // 1401行 - 1439行 OS_OPT opt, // 发布任务信号量的方式 OS_ERR *p_err) { OS_SEM_CTR ctr; CPU_TS ts; #ifdef OS_SAFETY_CRITICAL // 允许进行系统安全性检查 if (p_err == (OS_ERR *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); return ((OS_SEM_CTR)0); } #endif ts = OS_TS_GET(); // 获取时间戳 #if OS_CFG_ISR_POST_DEFERRED_EN > 0u // 延迟发布模式 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 查看是否是在ISR中调用该函数 OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_TASK_SIGNAL, // 写入到中断队列中 (void *)p_tcb, (void *)0, (OS_MSG_SIZE)0, (OS_FLAGS )0, (OS_OPT )0, (CPU_TS )ts, (OS_ERR *)p_err); return ((OS_SEM_CTR)0); } #endif ctr = OS_TaskSemPost(p_tcb, // 发布信号量 opt, ts, p_err); return (ctr); }发布任务信号量的方式有以下两种: OS_OPT_POST_NONE表明发布任务信号量之后调用调度程序; OS_OPT_POST_NO_SCHED表明在OSTaskSemPost()的末尾不会调用调度程序。
5.任务信号量做双向同步时,不能用于任务和ISR之间的同步,因为ISR不能等待信号量。
