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不能等待信号量。