os_mbox.c

  定位到uCOS-II/Source/os_mbox.c,该文件是消息邮箱管理的相关操作函数。

  任务与任务之间需要数据传递,那么为了适应传递的数据的不同类型,可以建立一个缓冲区(void*类型可以接收不同类型的数据),然后以该缓冲区为介质来实现任务间的切换,这就是消息邮箱的数据传输原理。

  消息邮箱的具体实现是:将数据缓冲区的指针赋给事件控制块(OS_EVENT)的成员OSEventPtr(OSEventPtr是个void* 类型),同时设置OSEventPtr中的用于表示事件类型的成员OSEventType为OS_EVENT_TYPE_MBOX。

1. 查看邮箱是否有需要的消息函数OSMboxAccept()

  OSMboxAccept()是非阻塞的,和OSMboxPend()函数不同(在下面解析)。OSMboxAccept()会从消息邮箱中试图取出消息,若没有消息可取,OSMboxAccept()函数并不挂起任务。若有消息可提取,则OSMboxAccept()函数提取消息完毕后会将该邮箱中存放消息的位置清空。因为该函数是非阻塞的,所以它允许在ISR中被调用。

#if OS_MBOX_ACCEPT_EN > 0u
void  *OSMboxAccept (OS_EVENT *pevent)
{
    void      *pmsg;
#if OS_CRITICAL_METHOD == 3u 
    OS_CPU_SR  cpu_sr = 0u;
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        return ((void *)0);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { //pevent类型检查
        return ((void *)0);
    }
    OS_ENTER_CRITICAL();
    pmsg               = pevent->OSEventPtr;    //取出消息中的内容
    pevent->OSEventPtr = (void *)0;
    OS_EXIT_CRITICAL();     //取出后将其清空
    return (pmsg);   
}
#endif

2. 创建一个邮箱函数OSMboxCreate()

  OSMboxCreate()函数用于建立并初始化一个邮箱信息,参数是消息指针,返回值是消息邮箱的指针。

OS_EVENT  *OSMboxCreate (void *pmsg)
{
    OS_EVENT  *pevent;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_EVENT *)0);
    }
#endif

    if (OSIntNesting > 0u) {
        return ((OS_EVENT *)0);
    }
    OS_ENTER_CRITICAL();

    //取出空闲事件节点 
    pevent = OSEventFreeList;
    if (OSEventFreeList != (OS_EVENT *)0) {
        //全局变量OSEventFreeList指向下一个空闲事件节点
        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
    }
    OS_EXIT_CRITICAL();
    //初始化该空闲事件节点为消息邮箱的相关设置
    if (pevent != (OS_EVENT *)0) {
        pevent->OSEventType    = OS_EVENT_TYPE_MBOX;
        pevent->OSEventCnt     = 0u;    //其实对mbox来说此参数没用
        pevent->OSEventPtr     = pmsg;
#if OS_EVENT_NAME_EN > 0u
        pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
        OS_EventWaitListInit(pevent);   //定义在os_core.c中,置空该事件的等待队列
    }
    return (pevent); 
}

  任务调用OSMboxCreate()时一般其参数msg为NULL,也可以事先定义一个邮箱,然后将该邮箱内的消息指针传递给OSMboxCreate()函数的msg,从而使得新建的邮箱从一开始就指代一个邮箱。

3. 删除邮箱函数OSMboxDel()

  删除消息邮箱,因为其他任务可能处于等待获取该消息邮箱,所以在删除之前应该要安置好这些等待任务。

#if OS_MBOX_DEL_EN > 0u
OS_EVENT  *OSMboxDel (OS_EVENT  *pevent,    //指定邮箱
                      INT8U      opt,       //删除操作选项
                      INT8U     *perr)      //输出型参数,用于指定出错信息
{
    BOOLEAN    tasks_waiting;
    OS_EVENT  *pevent_return;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_EVENT *)0);
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return (pevent);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {
        *perr = OS_ERR_EVENT_TYPE;
        return (pevent);
    }
    if (OSIntNesting > 0u) {
        *perr = OS_ERR_DEL_ISR;
        return (pevent);
    }
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0u) { //有任务在阻塞等待该MBox上的消息
        tasks_waiting = OS_TRUE;    //Yes
    } else {
        tasks_waiting = OS_FALSE;   //No
    }
    switch (opt) {
        case OS_DEL_NO_PEND:    //opt等于OS_DEL_NO_PEND表示无任务在等待才删除该Mbox
             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;      //OSEventFreeList原先是指向具体消息的,现指向下一个空闲事件节点
                 pevent->OSEventCnt  = 0u;                   //计数器,Mbox没用处
                 OSEventFreeList     = pevent;
                 OS_EXIT_CRITICAL();
                 *perr               = OS_ERR_NONE;
                 pevent_return       = (OS_EVENT *)0;
             } else {   //有任务等待,出错返回
                 OS_EXIT_CRITICAL();
                 *perr               = OS_ERR_TASK_WAITING;
                 pevent_return       = pevent;
             }
             break;

        case OS_DEL_ALWAYS:     //尽管有任务在等待还是要删除
             while (pevent->OSEventGrp != 0u) {  //有任务在等待,先逐一置为Rdy状态
                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MBOX, OS_STAT_PEND_ABORT);
             }
#if OS_EVENT_NAME_EN > 0u
             pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
             //一样的删除操作
             pevent->OSEventType    = OS_EVENT_TYPE_UNUSED;
             pevent->OSEventPtr     = OSEventFreeList;
             pevent->OSEventCnt     = 0u;
             OSEventFreeList        = pevent; 
             OS_EXIT_CRITICAL();
             if (tasks_waiting == OS_TRUE) { 
                 OS_Sched();  //若系统已经在运行,发起调度
             }
             *perr         = OS_ERR_NONE;
             pevent_return = (OS_EVENT *)0;
             break;

        default:  //操作选项有误,出错返回
             OS_EXIT_CRITICAL();
             *perr         = OS_ERR_INVALID_OPT;
             pevent_return = pevent;
             break;
    }
    return (pevent_return);
}
#endif

4. 阻塞请求消息邮箱函数OSMboxPend()

  任务请求邮箱时调用OSMboxPend()函数,该函数是带阻塞的:查看邮箱内存放消息的指针OSEventPtr是否为NULL,若不为NULL则将邮箱中的消息指针返回给调用者,若为NULL则使任务进入挂起状态,并会引发调度。

void  *OSMboxPend (OS_EVENT  *pevent,    //指定MBox
                   INT32U     timeout,   //超时时间设置
                   INT8U     *perr)
{
    void      *pmsg;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((void *)0);
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return ((void *)0);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {
        *perr = OS_ERR_EVENT_TYPE;
        return ((void *)0);
    }
    if (OSIntNesting > 0u) {
        *perr = OS_ERR_PEND_ISR;
        return ((void *)0);
    }
    if (OSLockNesting > 0u) {
        *perr = OS_ERR_PEND_LOCKED;
        return ((void *)0);
    }
    OS_ENTER_CRITICAL();
    pmsg = pevent->OSEventPtr;      //取出邮箱内的消息
    if (pmsg != (void *)0) {        //邮箱中的消息不为NULL
        pevent->OSEventPtr = (void *)0; //读取完毕后清空该消息指针
        OS_EXIT_CRITICAL();
        *perr = OS_ERR_NONE;
        return (pmsg);
    }

    //执行到这里说明邮箱内的消息为空,需要阻塞
    OSTCBCur->OSTCBStat     |= OS_STAT_MBOX;        //进入等待消息邮箱状态,表只能通过消息邮箱唤醒  
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;     //挂起
    OSTCBCur->OSTCBDly       = timeout;             //超时时间
    OS_EventTaskWait(pevent);                       //定义在os_core.c中,将自身添加到事件等待列表中
    OS_EXIT_CRITICAL();
    OS_Sched();                                     //重新调度,调度后会有一个优先级比当前优先级低的任务得到执行
                                                    //而当前任务在等待有任务向Mbox发消息
    OS_ENTER_CRITICAL();

    //能执行到这里,说明其他任务:
    //a. 调用了OSMboxPostOpt()释放该Mbox
    //b. 调用OSMboxDel()、OSMboxPendAbort()删除了该Mbox
    //c. 等待超时
    switch (OSTCBCur->OSTCBStatPend) {
        case OS_STAT_PEND_OK:   //有任务调用了OSMboxPostOpt进而唤醒当前任务
             pmsg =  OSTCBCur->OSTCBMsg;  //提取任务的消息指针
            *perr =  OS_ERR_NONE;
             break;

        case OS_STAT_PEND_ABORT:    //MBox被删除了
             pmsg = (void *)0;
            *perr =  OS_ERR_PEND_ABORT;
             break;

        case OS_STAT_PEND_TO:       //超时,不再等待
        default:
             OS_EventTaskRemove(OSTCBCur, pevent);  //定义在os_core.c中,将当前任务从event等待列表中清除
             pmsg = (void *)0;
            *perr =  OS_ERR_TIMEOUT; 
             break;
    }

    //设置任务状态
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY; 
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;
    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;
#if (OS_EVENT_MULTI_EN > 0u)
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
    OSTCBCur->OSTCBMsg           = (void      *)0; 
    OS_EXIT_CRITICAL();
    return (pmsg);
}

  在超时时间范围内收到消息后,任务是通过”OSTCBCur->OSTCBMsg”提取消息并继续执行的,为什么不是”pevent->OSEventPtr”提取?
  这是因为多任务消息传递时可以将消息直接赋给各个任务的TCB的OSTCBMsg成员。

5. 唤醒等待消息邮箱的任务函数OSMboxPendAbort()

  其实这个函数并非pend(获取),而是类似post(释放)MBox,具体实现唤醒单个/所有等待任务。

#if OS_MBOX_PEND_ABORT_EN > 0u
INT8U  OSMboxPendAbort (OS_EVENT  *pevent,
                        INT8U      opt,
                        INT8U     *perr)
{
    INT8U      nbr_tasks;       //用于记录有多少个任务在等待该事件(Mbox)
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return (0u);
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return (0u);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {
        *perr = OS_ERR_EVENT_TYPE;
        return (0u);
    }
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0u) {  //有任务在等待
        nbr_tasks = 0u;
        switch (opt) {
            case OS_PEND_OPT_BROADCAST:  //广播,唤醒所有在等待的任务
                 while (pevent->OSEventGrp != 0u) {
                     (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MBOX, OS_STAT_PEND_ABORT);
                     nbr_tasks++;
                 }
                 break;

            case OS_PEND_OPT_NONE:      /只唤醒一个任务,即优先级最高的一个
            default:
                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MBOX, OS_STAT_PEND_ABORT);
                 nbr_tasks++;
                 break;
        }
        OS_EXIT_CRITICAL();
        OS_Sched();1            //调度
        *perr = OS_ERR_PEND_ABORT;
        return (nbr_tasks);
    }
    OS_EXIT_CRITICAL();
    *perr = OS_ERR_NONE;
}
#endif

6. 向消息邮箱发送消息函数OSMboxPost()

  任务可以通过OSMboxPost()函数向目标消息邮箱发送消息,参数二为消息缓冲区的指针。

#if OS_MBOX_POST_EN > 0u
INT8U  OSMboxPost (OS_EVENT  *pevent,
                   void      *pmsg)
{
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        return (OS_ERR_PEVENT_NULL);
    }
    if (pmsg == (void *)0) {
        return (OS_ERR_POST_NULL_PTR);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0u) {  //有任务在等待

        //唤醒等待任务中优先级最高的任务,并将消息填充到该任务的TCB的OSTCBMsg成员中
        (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK);
        OS_EXIT_CRITICAL();
        OS_Sched();         //调度
        return (OS_ERR_NONE);
    }

    //若消息邮箱中的消息指针已经存有数据,出错返回
    if (pevent->OSEventPtr != (void *)0) {
        OS_EXIT_CRITICAL();
        return (OS_ERR_MBOX_FULL);
    }

    //没任务等待且消息邮箱中的消息指针不为空,将pmsg赋给消息邮箱的消息指针
    pevent->OSEventPtr = pmsg; 
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}
#endif

  这里需要注意,定义在OS_EventTaskRdy()的函数的具体实现并非将pmsg消息赋给消息邮箱的消息指针而是赋给最高优先级任务的TCB的OSTCBMsg成员,这就跟前面OSMboxPend()函数中提取消息操作”pmsg = OSTCBCur->OSTCBMsg”对应上了。

  由此可见,uCOS-II为了效率,还可以将数据赋给即将被调度运行的目标任务的TCB的OSTCBMsg成员。

7. 向邮箱发送消息的另一函数OSMboxPostOpt()

  OSMboxPostOpt()函数可以以广播的方式向MBox时间等待任务列表中的所有任务发送消息。

#if OS_MBOX_POST_OPT_EN > 0u
INT8U  OSMboxPostOpt (OS_EVENT  *pevent,
                      void      *pmsg,
                      INT8U      opt)
{
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        return (OS_ERR_PEVENT_NULL);
    }
    if (pmsg == (void *)0) {
        return (OS_ERR_POST_NULL_PTR);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0u) {
        if ((opt & OS_POST_OPT_BROADCAST) != 0x00u) { //含有OS_POST_OPT_BROADCAST操作选项
            while (pevent->OSEventGrp != 0u) { // 有任务在等待
                 //唤醒任务并将消息pmsg填充到该任务的TCB的OSTCBMsg中
                (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK);
            }
        } else {
            //唤醒单个任务,优先级最高的
            (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK);
        }
        OS_EXIT_CRITICAL();

        if ((opt & OS_POST_OPT_NO_SCHED) == 0u) { //为0表示没有包含OS_POST_OPT_NO_SCHED操作选项
            OS_Sched();         //需要调度
        }
        return (OS_ERR_NONE);
    }

    //若消息邮箱中的消息指针已经存有数据,出错返回
    if (pevent->OSEventPtr != (void *)0) { 
        OS_EXIT_CRITICAL();
        return (OS_ERR_MBOX_FULL);
    }

    //没任务等待且消息邮箱中的消息指针不为空,将pmsg赋给消息邮箱的消息指针
    pevent->OSEventPtr = pmsg;
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}
#endif

8. 查询邮箱状态函数OSMboxQuery()

  OSMboxQuery()可用于查看MBox的状态,其状态信息保存在函数的输入型参数p_mbox_data中,p_mbox_data为OS_MBOX_DATA类型的指针,OS_MBOX_DATA原型为:

typedef struct os_mbox_data {
    void   *OSMsg;                          //消息邮箱的有效信息

    //任务等待列表,存放等待该MBox的所有任务
#if OS_LOWEST_PRIO <= 63
    INT8U   OSEventTbl[OS_EVENT_TBL_SIZE];
    INT8U   OSEventGrp;
#else
    INT16U  OSEventTbl[OS_EVENT_TBL_SIZE];
    INT16U  OSEventGrp; 
#endif
} OS_MBOX_DATA;

  函数原型为:

#if OS_MBOX_QUERY_EN > 0u
INT8U  OSMboxQuery (OS_EVENT      *pevent,
                    OS_MBOX_DATA  *p_mbox_data)
{
    INT8U       i;
    OS_PRIO    *psrc;
    OS_PRIO    *pdest;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR   cpu_sr = 0u;
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        return (OS_ERR_PEVENT_NULL);
    }
    if (p_mbox_data == (OS_MBOX_DATA *)0) {
        return (OS_ERR_PDATA_NULL);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {
        return (OS_ERR_EVENT_TYPE);
    }

    OS_ENTER_CRITICAL();
    p_mbox_data->OSEventGrp = pevent->OSEventGrp;

    //(等待任务)数组赋值,需要用for()语句逐一赋值
    psrc                    = &pevent->OSEventTbl[0];
    pdest                   = &p_mbox_data->OSEventTbl[0];
    for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) {
        *pdest++ = *psrc++;
    }
    p_mbox_data->OSMsg = pevent->OSEventPtr; 
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}
#endif
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值