邮箱是UCOS的一种通讯机制, 它可以使一个任务或者一个中断服务程序向另一个任务发送一个void *OSEventPtr指针变量,该指针指向一个特定的数据结构.即通过该指针传递消息.

本文使用的UCOS版本:V2.91.

事件控制块

说到消息邮箱, 首先需要讲解一下UCOS的事件控制块.UCOS的信号量,消息邮箱,消息队列.都是使用OS_EVENT结构体来标识的. 结构体成员变量OSEventType标识不同的事件类型(信号量,消息队列等).

OS_EVENT  *OSMboxCreate (void *pmsg)
{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_IEC61508if (OSSafetyCriticalStartFlag == OS_TRUE) {OS_SAFETY_CRITICAL_EXCEPTION();}
#endifif (OSIntNesting > 0u) {                     /* See if called from ISR ...                         */return ((OS_EVENT *)0);                   /* ... can't CREATE from an ISR                       */}OS_ENTER_CRITICAL();/*在事件控制块链表上找到一个可用的事件控制块,(实质为 OSEventTbl[OS_MAX_EVENTS]数组中的一个可用项.)设置对应的pevent结构体成员变量的属性值. 最后调用OS_EventWaitListInit清除 OSEventGrp OSEventTbl值.OSMboxCreate执行完成后,我们得到了一个OS_EVENT*类型的消息邮箱.*/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) {pevent->OSEventType    = OS_EVENT_TYPE_MBOX;pevent->OSEventCnt     = 0u;pevent->OSEventPtr     = pmsg;           /* Deposit message in event control block             */
#if OS_EVENT_NAME_EN > 0upevent->OSEventName    = (INT8U *)(void *)"?";
#endifOS_EventWaitListInit(pevent);}return (pevent);                             /* Return pointer to event control block              */
}

消息邮箱创建

消息邮箱的创建主要是从事件控制块数组中取出一个空闲的事件控制块. 设置事件控制块的相关属性.

OS_EVENT  *OSMboxCreate (void *pmsg)
{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_IEC61508if (OSSafetyCriticalStartFlag == OS_TRUE) {OS_SAFETY_CRITICAL_EXCEPTION();}
#endifif (OSIntNesting > 0u) {                     /* See if called from ISR ...                         */return ((OS_EVENT *)0);                   /* ... can't CREATE from an ISR                       */}OS_ENTER_CRITICAL();/*在事件控制块链表上找到一个可用的事件控制块,(实质为 OSEventTbl[OS_MAX_EVENTS]数组中的一个可用项.)设置对应的pevent结构体成员变量的属性值. 最后调用OS_EventWaitListInit清除 OSEventGrp OSEventTbl值.OSMboxCreate执行完成后,我们得到了一个OS_EVENT*类型的消息邮箱.*/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) {pevent->OSEventType    = OS_EVENT_TYPE_MBOX;pevent->OSEventCnt     = 0u;pevent->OSEventPtr     = pmsg;           /* Deposit message in event control block             */
#if OS_EVENT_NAME_EN > 0upevent->OSEventName    = (INT8U *)(void *)"?";
#endifOS_EventWaitListInit(pevent);}return (pevent);                             /* Return pointer to event control block              */
}

消息邮箱请求

OSMboxPend函数的详解在代码内以注释的形式体现.

void  *OSMboxPend (OS_EVENT  *pevent,INT32U     timeout,INT8U     *perr)
{void      *pmsg;
#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */OS_CPU_SR  cpu_sr = 0u;
#endif#ifdef OS_SAFETY_CRITICALif (perr == (INT8U *)0) {OS_SAFETY_CRITICAL_EXCEPTION();}
#endif#if OS_ARG_CHK_EN > 0uif (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */*perr = OS_ERR_PEVENT_NULL;return ((void *)0);}
#endifif (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {  /* Validate event block type                     */*perr = OS_ERR_EVENT_TYPE;return ((void *)0);}if (OSIntNesting > 0u) {                          /* See if called from ISR ...                    */*perr = OS_ERR_PEND_ISR;                      /* ... can't PEND from an ISR                    */return ((void *)0);}if (OSLockNesting > 0u) {                         /* See if called with scheduler locked ...       */*perr = OS_ERR_PEND_LOCKED;                   /* ... can't PEND when locked                    */return ((void *)0);}OS_ENTER_CRITICAL();/*如果有可用的消息,就返回消息指针, ucos的消息邮箱采用的是直接传递指针的形式,没有使用memcpy.如果在发送消息的时候 pevent->OSEventPtr指向的内存是使用malloc申请的.记得在接收处理完消息释放内存.*/pmsg = pevent->OSEventPtr;if (pmsg != (void *)0) {                          /* See if there is already a message             */pevent->OSEventPtr = (void *)0;               /* Clear the mailbox                             */OS_EXIT_CRITICAL();*perr = OS_ERR_NONE;return (pmsg);                                /* Return the message received (or NULL)         */}/*如果还没有消息,会运行到这里. 设置当前任务控制块的相关状态属性.OS_EventTaskWait函数将当前任务的优先级放入此消息邮箱的结构体成员变量,并将此任务的优先级从就绪表中移除.然后触发一次任务调度.CPU转到其他任务运行.此任务即处于了挂起等待消息邮箱的状态.*/OSTCBCur->OSTCBStat     |= OS_STAT_MBOX;          /* Message not available, task will pend         */OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;OSTCBCur->OSTCBDly       = timeout;               /* Load timeout in TCB                           */OS_EventTaskWait(pevent);                         /* Suspend task until event or timeout occurs    */OS_EXIT_CRITICAL();// 执行完OS_Sched();任务被切换.OS_Sched();                                       /* Find next highest priority task ready to run  *//*当收到消息或超时时间到导致任务被放入就绪表, 且被调度器调度时,会从这里开始接着运行.先检查pend到消息的原因, 如果是等待超时,就将此任务的优先级从信号量的等待事件中移除.最后设置任务的相关状态.*/OS_ENTER_CRITICAL();switch (OSTCBCur->OSTCBStatPend) {                /* See if we timed-out or aborted                */case OS_STAT_PEND_OK:pmsg =  OSTCBCur->OSTCBMsg;*perr =  OS_ERR_NONE;break;case OS_STAT_PEND_ABORT:pmsg = (void *)0;*perr =  OS_ERR_PEND_ABORT;               /* Indicate that we aborted                      */break;case OS_STAT_PEND_TO:default:OS_EventTaskRemove(OSTCBCur, pevent);pmsg = (void *)0;*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;
#endifOSTCBCur->OSTCBMsg           = (void      *)0;    /* Clear  received message                       */OS_EXIT_CRITICAL();return (pmsg);                                    /* Return received message                       */
}

消息邮箱发送

INT8U  OSMboxPost (OS_EVENT  *pevent,void      *pmsg)
{
#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */OS_CPU_SR  cpu_sr = 0u;
#endif#if OS_ARG_CHK_EN > 0uif (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */return (OS_ERR_PEVENT_NULL);}if (pmsg == (void *)0) {                          /* Make sure we are not posting a NULL pointer   */return (OS_ERR_POST_NULL_PTR);}
#endifif (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {  /* Validate event block type                     */return (OS_ERR_EVENT_TYPE);}OS_ENTER_CRITICAL();/*OSEventGrp != 0 时表示有其他任务在等待此消息. OS_EventTaskRdy函数的作用是通过pevent的结构体成员找到等待此信号量的那个任务(假定为任务A),并将任务A的优先级放入就绪表,且从等待事件表移除.然后触发一次任务调度.   */if (pevent->OSEventGrp != 0u) {                   /* See if any task pending on mailbox            *//* Ready HPT waiting on event                    */(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK);OS_EXIT_CRITICAL();OS_Sched();                                   /* Find highest priority task ready to run       */return (OS_ERR_NONE);}/*消息邮箱满,直接返回. 因为消息邮箱只能携带一条消息.pevent->OSEventPtr非空就表示有数据.消息邮箱满.*/if (pevent->OSEventPtr != (void *)0) {            /* Make sure mailbox doesn't already have a msg  */OS_EXIT_CRITICAL();return (OS_ERR_MBOX_FULL);}//保存要发送的消息到消息邮箱事件控制块指针.pevent->OSEventPtr = pmsg;                        /* Place message in mailbox                      */OS_EXIT_CRITICAL();return (OS_ERR_NONE);
}

问答环节

1.问: UCOS中消息邮箱有哪些应用场景.
答: 消息邮箱一般用于任务与任务之间,或者是中断向任务传递消息.使用消息邮箱也间接性的做到了共享数据的互斥 比如任务A采集各个传感器数据,任务B上送各个传感器数据.直接在任务A,B使用全局变量的形式处理传感器数据.会出现 可能A采集了一半,跳转到了B运行,造成数据一致性缺失的问题. 如果是任务A发送消息给任务B,就不会有这种情况发生.

2.问: UCOS消息邮箱可以传递哪些类型的数据
答: UCOS消息邮箱可以传递任意类型的数据, 消息邮箱参数是一个void *类型的指针,它可以执行任意数据类型的数据. 如果只是传递一个简单的数据,可以使用基本数据类型,如果传递的数据较多,可以自定义一个结构体.接收方按照结构体约定 的数据格式解析即可.

UCOS你问我答系列之消息邮箱详解相关推荐

  1. UCOS你问我答系列之消息队列详解

    消息队列是UCOS系统的一种通讯机制,它可以使任务向任务或者中断向任务发送一个指针变量.指针变量指向的数据结构由用户自定义,即我们常说的消息.首先来看一下UCOS消息队列结构体的设计. 本文使用的UC ...

  2. UCOS你问我答系列之系统时钟节拍详解

    前言 系统时钟节拍是多任务得以正常运行的基石,UCOS的系统时钟节拍一般依赖于MCU的硬件定时器.硬件定时器产生固定时间间隔的中断,中断中调用UCOS的系统函数,完成多任务操作系统的基本调度功能. 本 ...

  3. 从源码分析RocketMQ系列-RocketMQ消息设计详解

    1 消息存储   消息存储是RocketMQ中最为复杂和最为重要的一部分,本节将分别从RocketMQ的消息存储整体架构.PageCache与Mmap内存映射以及RocketMQ中两种不同的刷盘方式三 ...

  4. 大型网站架构系列:负载均衡详解(4)

    原文:大型网站架构系列:负载均衡详解(4) 本文是负载均衡详解的第四篇,主要介绍了LVS的三种请求转发模式和八种负载均衡算法,以及Haproxy的特点和负载均衡算法.具体参考文章,详见最后的链接. 三 ...

  5. MQ消息队列详解、四大MQ的优缺点分析

    MQ消息队列详解.四大MQ的优缺点分析 前言 面试题切入 面试官心理分析 面试题剖析 ①为什么要使用MQ 系统解耦 异步调用 流量削峰 消息队列的优缺点 四大主流MQ(kafka.ActiveMQ.R ...

  6. sip消息类型和消息代码详解-转

    在学习asterisk的时候,经常遇到一些远程服务器传回的代码,这些代码都有很重要的信息,让我们了解到对方的sip是如何响应我们这边的sip消息的,于是网上找到了这些sip消息类型和消息代码,自己收藏 ...

  7. 英飞凌 AURIX 系列单片机的HSM详解(1)——何为HSM

    本系列的其它几篇文章: <英飞凌 AURIX 系列单片机的HSM详解(2)--与HSM相关的UCB和寄存器> <英飞凌 AURIX 系列单片机的HSM详解(3)--开发方法> ...

  8. h2 不能访问localhost_SpringBoot2.x系列教程44--H2数据库详解及搭建Web控制台

    SpringBoot2.x系列教程44--H2数据库详解及搭建Web控制台 作者:一一哥 我在上一章节中讲解了Spring Boot中整合Mybatis,接下来我给大家介绍一款内存数据库--H2. H ...

  9. 大型网站系统架构系列:负载均衡详解(一)

    大型网站系统架构系列:负载均衡详解(一) 2016-03-20 架构说 面对大量用户访问.高并发请求,海量数据,可以使用高性能的服务器.大型数据库,存储设备,高性能Web服务器,采用高效率的编程语言比 ...

最新文章

  1. android studio同步代码块,Android Studio快捷键大全
  2. ESXi6.5环境搭建(一:VMware Workstations 12 Pro 环境的安装及配置)
  3. 为Delphi程序添加事件和事件处理器
  4. 哈尔滨商业大学计算机与信息工程学院地址,计算机与信息工程学院
  5. 开源FPGA硬件模拟游戏机,原汁原味的复古游戏体验带你回童年
  6. 如何自动判断域名是否被微信拦截 被微信屏蔽的域名网址如何正常打开使用
  7. 罗莎琳德·富兰克林:隐于幕后的DNA之母,以及她被误解却又伟大的短暂一生...
  8. 加载类_JVM类加载
  9. 西瓜书+实战+吴恩达机器学习(十五)无监督学习之关联分析(Apriori, FP-growth)
  10. 在delphi原有控件基础上画图
  11. 转AndroidThings技术资料
  12. mysql2005安装_安装SQL Server 2005的详细步骤
  13. 地籍图宗记注记标注实现
  14. elf文件反编译C语言,ELF文件解析和反汇编
  15. 22道接口测试面试题答案。
  16. gcforest 深度森林原理及实现
  17. Euraka启动记录
  18. 【支持向量机SVM系列教程3】支持向量回归SVR
  19. recoil error Cannot assign to read only property of object ‘#‘
  20. GIT 清理远程已删除本地还存在的分支

热门文章

  1. DP——2008 APAC local onsites C Millionaire
  2. 用java面向对象的内容建立学生姓名,学号,id,班级,每一科的成绩,以及对管理员实现(对老师只实现查看):通过名字查询成绩,通过学号查询成绩,通过id修改姓名,通过姓名修改成绩
  3. 美白-用通道计算实现人物祛斑美白
  4. SQL server 2012连接不上怎么办?
  5. wwhhuu (思维
  6. 怎么证明一个一维函数连续
  7. 3D建模可以做什么工作呢
  8. 全国公共英语等级考试复习指导总论zt
  9. android webview onkeydown,WebView中的OnKeyDown事件未处理 - Android
  10. 基于html的奥特曼资料大全的页面设计