我是靠谱客的博主 迅速小蘑菇,最近开发中收集的这篇文章主要介绍OSAL之消息管理转载请注明出处,尊重原创;本文基于蓝牙1.3.2版本总述:背景知识:在OSAL.H文件中API函数上面这些完成了一个消息从创建到发送,目标任务获取消息,处理消息,释放消息空间整个详细介绍过程。下面介绍消息处理的其他API函数,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

转载请注明出处,尊重原创;

本文基于蓝牙1.3.2版本


总述:

消息处理机制流程
消息处理机制流程
OSAL中实现对消息管理的功能实现是在osal.cosal.h 文件中,对于操作系统来说,不可缺少的就是任务之间信息的传递,消息包括:信号量,互斥量,消息邮箱、消息队列等。在OSAL中仅仅实现了消息队列的功能,系统可以发送或接收消息,并对消息进行管理。

背景知识:在OSAL.H文件中

1、定义消息管理的消息首部,所有的消息构成都有它这部分;

typedef struct
{   //用于消息首部
  void   *next;
  uint16 len;
  uint8  dest_id;
} osal_msg_hdr_t;

其中next指针将消息组织成有序的链表,len为消息的长度,dest_id是目标任务id。
2、定义管理消息队列的指针类型,从这里也可以看出管理消息队列指针类型是空指针,也说明了消息结构的类型是变化的,不是固定的。
typedef void * osal_msg_q_t;
3、定义几个消息操作的宏

获得下一个消息
#define OSAL_MSG_NEXT(msg_ptr)      ((osal_msg_hdr_t *) (msg_ptr) - 1)->next

获得一个消息的消息长度
#define OSAL_MSG_LEN(msg_ptr)      ((osal_msg_hdr_t *) (msg_ptr) - 1)->len
获得消息发送的目标任务
#define OSAL_MSG_ID(msg_ptr)      ((osal_msg_hdr_t *) (msg_ptr) - 1)->dest_id

下面几个 *(q_ptr)计算,说明必须传过来的是消息链表头指针的地址,
typedef void *osal_msg_q_t;
osal_msg_q_t osal_qHead;//定义的消息头指针
//调用入队列函数osal_msg_enqueue( &osal_qHead, msg_ptr );
//它的定义处是void osal_msg_enqueue( osal_msg_q_t *q_ptr, void *msg_ptr ),这里说明q_ptr是一个二级指针。
初始化一个消息队列,将消息头指针置空
#define OSAL_MSG_Q_INIT(q_ptr)      *(q_ptr) = NULL
判断消息队列是否为空
#define OSAL_MSG_Q_EMPTY(q_ptr)     (*(q_ptr) == NULL)
获得消息队列头指针
#define OSAL_MSG_Q_HEAD(q_ptr)      (*(q_ptr))

4、定义一个管理消息队列的变量用于管理消息队列
osal_msg_q_t osal_qHead;
最后消息结构如图
从这个图可以看出osa_qHead是指向消息空间的,但是因为消息空间不能给出一个固定的结构模式,所以他是一个空指针的形式。

API函数

1、创建消息

osal_msg_allocate() 给消息分配内存空间,该函数由发送消息的任务调用以创建一个消息
参数:
len-消息的长度
1、分配内存空间(消息管理首部大小+消息的长度)
hdr=(osal_msg_hdr_t*)osal_mem_alloc((short)(len+sizeof( osal_msg_hdr_t )) );
2、填充消息首部

{
    hdr->next = NULL;
    hdr->len = len;
    hdr->dest_id = TASK_NO_TASK;
    return ( (uint8 *) (hdr + 1) );//这里返回的是消息信息空间的地址
  }

创建一个消息缓冲区后,下一步是在调用函数里填充消息信息空间了。然后调用osal_msg_send()函数将一个消息发送到消息队列队末尾。

2、发送消息

osal_msg_send()函数是向消息队列发送一个消息,同时还会向目标任务的事件列表里置位消息事件。
参数:
destination_task:目标任务id
*msg_ptr: 指向消息空间

uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
  return ( osal_msg_enqueue_push( destination_task, msg_ptr, FALSE ) );
}

//他是调用了一个入队列函数,最后参赛FALSE是指明入队列末尾。也就是说发送消息只是入队列而已

3、将消息入队列

osal_msg_enqueue_push( uint8 destination_task, uint8 *msg_ptr, uint8 push )函数功能是将一则消息入消息队列,同时设置目的任务的消息事件。
参数:
destination_task:目标任务id
*msg_ptr: 指向消息空间
push 指定是入队头还是队尾;TRUE是对头,FALSE是队尾
主要函数代码:

OSAL_MSG_ID( msg_ptr ) = destination_task;//将目标id复制给消息头dest_id

  if ( push == TRUE )
  {
    // prepend the message如果push为TRUE则将消息插入到队列头部
    //这样说明osal_msg_push函数是仅仅将消息插入队列头部
    osal_msg_push( &osal_qHead, msg_ptr );
  }
  else
  {
    // append the message这里就是将消息插入队列尾部
    osal_msg_enqueue( &osal_qHead, msg_ptr );
  }

  // Signal the task that a message is waiting置位相应任务的消息事件
  osal_set_event( destination_task, SYS_EVENT_MSG );

对入队列调用的API讲解

osal_msg_push( &osal_qHead, msg_ptr );
将消息插入到队列对头,当push为TRUE时候执行。&osal_qHead是队列头指针的地址,msg_ptr是指向消息空间的指针
osal_msg_enqueue( &osal_qHead, msg_ptr );
将消息插入到队列末尾,&osal_qHead是队列头指针的地址,msg_ptr是指向消息空间的指针。

4发送消息的另外一种方式

osal_msg_push_front方式

uint8 osal_msg_push_front( uint8 destination_task, uint8 *msg_ptr )
{
  return ( osal_msg_enqueue_push( destination_task, msg_ptr, TRUE ) );//最后一个参数TRUE表明插入到队列头
}

它与osal_msg_send唯一不同的是它将消息插入到队列头

5以上完成了一个消息的创建,发送,入队列的过程。下面就是任务消息事件的处理了。


6目标任务执行处理系统消息事件

1、获取该任务的消息uint8 *osal_msg_receive( uint8 task_id )

osal_msg_hdr_t *listHdr;          //定义用来遍历消息队列,指向当前遍历的元素   
  osal_msg_hdr_t *prevHdr = NULL;     //用来遍历时候使用,指向foundHdr指针前一个元素
  osal_msg_hdr_t *foundHdr = NULL;    //指向发现符合该任务的消息
  halIntState_t   intState;

  // Hold off interrupts
  HAL_ENTER_CRITICAL_SECTION(intState);

  // Point to the top of the queue      osal_qHead指向的是消息信息结构体,不是消息头结构体
  listHdr = osal_qHead;

  // Look through the queue for a message that belongs to the asking task
  while ( listHdr != NULL )
  {
    if ( (listHdr - 1)->dest_id == task_id )
    {//找到了该任务的消息
      if ( foundHdr == NULL )
      {
        // Save the first one
        foundHdr = listHdr;
      }
      else
      {//如果有两个,则调出循环下次在执行
        // Second msg found, stop looking
        break;
      }
    }
    //若没找到符合的消息,那么prevHdr向下遍历,如果找到了,foundHdr不为0,prevHdr不再往下遍历了,指向foundHdr前一个元素
     if ( foundHdr == NULL )
    {                      
      prevHdr = listHdr;
    }
    listHdr = OSAL_MSG_NEXT( listHdr );
  }

该段代码主要是在消息队列中寻找目标任务的消息;跳出这个while循环有两种情况,1、找到了消息,同时listHdr也到了队列末尾,那么是因为while条件不满足listHdr = NULL而结束循环;2、找到了消息但是是通过break结束while循环,那么说明该任务不止一个消息,OSAL在这里的处理方式是先处理完这个事件,然后在过来处理下一个该任务的消息事件。基于这两种情况,马上就有下面的处理代码了。

 // Is there more than one?
  if ( listHdr != NULL )
  {
    // Yes, Signal the task that a message is waiting如果该任务不止一个消息事件
    osal_set_event( task_id, SYS_EVENT_MSG );
  }
  else
  {
    // No more,listHdr遍历到链表末尾了那么就只有一个这个任务的事件
    osal_clear_event( task_id, SYS_EVENT_MSG );
  }

基于上面第一段代码,找到了该任务的消息,那么下一步就是提取这个消息,本质上在数据结构里处理方式来说就是在单链表里删除一个元素,同时返回指向该消息的指针。

if ( foundHdr != NULL )
  {
    // Take out of the link list
    osal_msg_extract( &osal_qHead, foundHdr, prevHdr );
  }

  // Release interrupts
  HAL_EXIT_CRITICAL_SECTION(intState);

  return ( (uint8*) foundHdr );

osal_msg_extract( osal_msg_q_t *q_ptr, void *msg_ptr, void *prev_ptr )接口

主要完成的功能是将一个消息从消息队列中删除,所以函数里面主要处理的是对后继指针的操作,
参数:
q_ptr指向队列头指针
msg_ptr指向待提取的消息
prev_ptr 指向待提取消息前一个元素

 if ( msg_ptr == *q_ptr )
  {
    // remove from first待取的消息是队列第一个元素
    *q_ptr = OSAL_MSG_NEXT( msg_ptr );
  }
  else
  {
    // remove from middle
    OSAL_MSG_NEXT( prev_ptr ) = OSAL_MSG_NEXT( msg_ptr );
  }
  //下面语句的意思是将提取的消息设为无效
  OSAL_MSG_NEXT( msg_ptr ) = NULL;
  OSAL_MSG_ID( msg_ptr ) = TASK_NO_TASK;//将抽取的消息设为不为任务占用,等待取消消息空间

从队列中间删除一个元素的过程如图
这里写图片描述

2提取消息空间后就是调用消息处理函数

3处理完之后就是删除消息空间

osal_msg_deallocate( uint8 *msg_ptr )

//这里就是将消息空间以及消息头都包括在内
x = (uint8 *)((uint8 *)msg_ptr - sizeof( osal_msg_hdr_t ));
//释放消息空间+消息头部空间
  osal_mem_free( (void *)x );

上面这些完成了一个消息从创建到发送,目标任务获取消息,处理消息,释放消息空间整个详细介绍过程。

下面介绍消息处理的其他API函数

1 osal_msg_dequeue( osal_msg_q_t *q_ptr )

函数功能是将一个消息出列。
参数:q_ptr指向一个消息结构体的指针

 if ( *q_ptr != NULL )
  {
    // Dequeue message
    msg_ptr = *q_ptr;
    *q_ptr = OSAL_MSG_NEXT( msg_ptr );
    OSAL_MSG_NEXT( msg_ptr ) = NULL;
    OSAL_MSG_ID( msg_ptr ) = TASK_NO_TASK;
  }
return msg_ptr;//返回的指向该消息结构体的指针

2 osal_msg_find(uint8 task_id, uint8 event)

函数实现的功能是根据task_id, event寻找满足这两个条件的消息

pHdr = osal_qHead;  // Point to the top of the queue.

  // Look through the queue for a message that matches the task_id and event parameters.
while (pHdr != NULL)
  {
    if (((pHdr-1)->dest_id == task_id) && (((osal_event_hdr_t *)pHdr)->event == event))
    {
      break;
    }

    pHdr = OSAL_MSG_NEXT(pHdr);
  }
return (osal_event_hdr_t *)pHdr;

最后

以上就是迅速小蘑菇为你收集整理的OSAL之消息管理转载请注明出处,尊重原创;本文基于蓝牙1.3.2版本总述:背景知识:在OSAL.H文件中API函数上面这些完成了一个消息从创建到发送,目标任务获取消息,处理消息,释放消息空间整个详细介绍过程。下面介绍消息处理的其他API函数的全部内容,希望文章能够帮你解决OSAL之消息管理转载请注明出处,尊重原创;本文基于蓝牙1.3.2版本总述:背景知识:在OSAL.H文件中API函数上面这些完成了一个消息从创建到发送,目标任务获取消息,处理消息,释放消息空间整个详细介绍过程。下面介绍消息处理的其他API函数所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(69)

评论列表共有 0 条评论

立即
投稿
返回
顶部