uC/OS-III之信号量

1.信号量更多的被用于实现任务间的同步以及任务和ISR间的同步。

2.信号量的数据结构类型为OS_SEM,它的定义位于os.h中。

typedef  struct  os_sem              OS_SEM;                // 631行

struct  os_sem {                                            // 858行 - 869行
    OS_OBJ_TYPE          Type;                              // 类型,必须被设置成OS_OBJ_TYPE_SEM
    CPU_CHAR            *NamePtr;                           // 名称
    OS_PEND_LIST         PendList;                          // 任务挂起表
#if OS_CFG_DBG_EN > 0u                                      // 与调试相关
    OS_SEM              *DbgPrevPtr;
    OS_SEM              *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
    OS_SEM_CTR           Ctr;                               // 信号量计数,它的最大值取决于它的类型OS_SEM_CTRO
    CPU_TS               TS;                                // 上次被释放的时间
};

3.创建信号量使用函数OSSemCreate(),它的定义位于os_sem.c中。

void  OSSemCreate (OS_SEM      *p_sem,                      // 72行 - 123行,声明的信号量的地址
                   CPU_CHAR    *p_name,                     // 名称
                   OS_SEM_CTR   cnt,                        // 计数初始值
                   OS_ERR      *p_err)                      // 错误代码
{
    CPU_SR_ALLOC();                                         // 声明变量cpu_sr,用来临时存储CPU的状态寄存器

#ifdef OS_SAFETY_CRITICAL                                   // 安全检查
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508                          // 安全检查-IEC61508协议
    if (OSSafetyCriticalStartFlag == DEF_TRUE) {
        *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
        return;
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u                      // 允许进行ISR调用检查
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              // 不允许在ISR中调用该函数
        *p_err = OS_ERR_CREATE_ISR;
        return;
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u                                  // 允许进行参数检查
    if (p_sem == (OS_SEM *)0) {                             // 无效的参数
        *p_err = OS_ERR_OBJ_PTR_NULL;
        return;
    }
#endif

    CPU_CRITICAL_ENTER();                                   // 进入临界区
    p_sem->Type    = OS_OBJ_TYPE_SEM;                       // 该结构体的类型为信号量
    p_sem->Ctr     = cnt;                                   // 设置计数初值
    p_sem->TS      = (CPU_TS)0;
    p_sem->NamePtr = p_name;                                // 保存信号量的名称
    OS_PendListInit(&p_sem->PendList);                      // 初始化挂起表

#if OS_CFG_DBG_EN > 0u                                      // 允许进行调试
    OS_SemDbgListAdd(p_sem);
#endif
    OSSemQty++;                                             // 信号量的数目加1

    CPU_CRITICAL_EXIT();                                    // 退出临界区
    *p_err = OS_ERR_NONE;
}

4.等待信号量使用函数OSSemPend(),它的定义位于os_sem.c中。

OS_SEM_CTR  OSSemPend (OS_SEM   *p_sem,                     // 301行 - 427行
                       OS_TICK   timeout,                   // 超时时间:0表示任务将一直等待下去
                       OS_OPT    opt,                       // 选项
                       CPU_TS   *p_ts,
                       OS_ERR   *p_err)
{
    OS_SEM_CTR    ctr;
    OS_PEND_DATA  pend_data;
    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 OS_CFG_ARG_CHK_EN > 0u                                  // 允许进行参数检查
    if (p_sem == (OS_SEM *)0) {                             // 信号量无效
        *p_err = OS_ERR_OBJ_PTR_NULL;
        return ((OS_SEM_CTR)0);
    }
    switch (opt) {                                          
        case OS_OPT_PEND_BLOCKING:
        case OS_OPT_PEND_NON_BLOCKING:
             break;
        default:                                            // 选项无效
             *p_err = OS_ERR_OPT_INVALID;
             return ((OS_SEM_CTR)0);
    }
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u                             // 允许进行内核对象类型检查
    if (p_sem->Type != OS_OBJ_TYPE_SEM) {                   // 保证信号量已被创建
        *p_err = OS_ERR_OBJ_TYPE;
        return ((OS_SEM_CTR)0);
    }
#endif

    if (p_ts != (CPU_TS *)0) {
       *p_ts  = (CPU_TS)0;                                  // 初始化返回时间戳
    }
    CPU_CRITICAL_ENTER();                                   // 进入临界区
    if (p_sem->Ctr > (OS_SEM_CTR)0) {                       // 计数值>0,信号量可用
        p_sem->Ctr--;                                       // 计数值减1
        if (p_ts != (CPU_TS *)0) {
           *p_ts  = p_sem->TS;                              // 获取最近一次信号量的释放时间
        }
        ctr    = p_sem->Ctr;
        CPU_CRITICAL_EXIT();                                // 退出临界区
        *p_err = OS_ERR_NONE;
        return (ctr);
    }

    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    // 信号量不可用,用户不想阻塞
        ctr    = p_sem->Ctr;
        CPU_CRITICAL_EXIT();                                // 退出临界区
        *p_err = OS_ERR_PEND_WOULD_BLOCK;
        return (ctr);
    } 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(&pend_data,                                     // 把该任务放入到挂起表中
            (OS_PEND_OBJ *)((void *)p_sem),
            OS_TASK_PEND_ON_SEM,
            timeout);
    OS_CRITICAL_EXIT_NO_SCHED();                            // 退出临界区,不调度
    OSSched();                                              // 执行调度程序

// 在任务执行OSSched()函数后,该任务被挂起,其他任务开始执行;当某一个时刻,切换到该任务后才执行下面的代码。

    CPU_CRITICAL_ENTER();                                   // 进入临界区
    switch (OSTCBCurPtr->PendStatus) {                      // 判断当前任务的状态
        case OS_STATUS_PEND_OK:                             // 信号量被发布了
             if (p_ts != (CPU_TS *)0) {
                *p_ts  =  OSTCBCurPtr->TS;
             }
             *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;
        case OS_STATUS_PEND_DEL:                            // 信号量被删除
             if (p_ts != (CPU_TS *)0) {
                *p_ts  =  OSTCBCurPtr->TS;
             }
             *p_err = OS_ERR_OBJ_DEL;
             break;
        default:                                            // 其他情况
             *p_err = OS_ERR_STATUS_INVALID;
             CPU_CRITICAL_EXIT();
             return ((OS_SEM_CTR)0);
    }
    ctr = p_sem->Ctr;
    CPU_CRITICAL_EXIT();                                    // 退出临界区
    return (ctr);
}

5.发布信号量使用OSSemPost(),它的定义位于os_sem.c中。

OS_SEM_CTR  OSSemPost (OS_SEM  *p_sem,                      // 573行 - 625行,信号量的地址
                       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

#if OS_CFG_ARG_CHK_EN > 0u                                  // 允许进行参数检查
    if (p_sem == (OS_SEM *)0) {                             // 无效的信号量
       *p_err  = OS_ERR_OBJ_PTR_NULL;
        return ((OS_SEM_CTR)0);
    }
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u                             // 允许进行内核对象类型检查
    if (p_sem->Type != OS_OBJ_TYPE_SEM) {                   // 保证信号量已被创建
        *p_err = OS_ERR_OBJ_TYPE;
        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_SEM,           // 发布到中断服务队列中
                    (void      *)p_sem,
                    (void      *)0,
                    (OS_MSG_SIZE)0,
                    (OS_FLAGS   )0,
                    (OS_OPT     )opt,
                    (CPU_TS     )ts,
                    (OS_ERR    *)p_err);
        return ((OS_SEM_CTR)0);
    }
#endif

    ctr = OS_SemPost(p_sem,                                 // 发送信号量
                     opt,
                     ts,
                     p_err);

    return (ctr);
}

发布信号量的方式有以下几种选项:
OS_OPT_POST_1表明只给一个任务发布信号;
OS_OPT_POST_ALL表明信号会发布给所有等待该信号量的任务;
OS_OPT_POST_NO_SCHED表明在发布信号量后不会进行任务调度,可以添加到前两种选项的后面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值