Z-Stack的OSAL的消息传递机制

5. 消息传递机制

这一部分,我们将综合任务、事件和消息,进行一个完整过程的阐述。

5.1 消息的发送与接收

5.1.1 消息的发送

消息的发送是通过定义 OSAL.c 文件中的 osal_msg_send() 函数定义的:

uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr ) {
    if (msg_ptr == NULL) // 指针为空返回错误信息
        return (INVALID_MSG_POINTER);
     // 如果要发送的任务号大于系统定义的最大任务号
    if (destination_task >= tasksCnt) {
        osal_msg_deallocate( msg_ptr );
        return ( INVALID_TASK );
    }
    // 检查元数据,是否为错误的消息
    if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
       OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK ) {
        osal_msg_deallocate( msg_ptr );
        return ( INVALID_MSG_POINTER );
    }
    // 设置其元数据的目的地址
    OSAL_MSG_ID( msg_ptr ) = destination_task;
    // 将该消息进队
    osal_msg_enqueue( &osal_qHead, msg_ptr );
    // 告知任务有消息需要处理
    osal_set_event( destination_task, SYS_EVENT_MSG );
    return (SUCCESS)
}

这一过程可用图1 表示:

做一系列的检查
设置消息元数据目的地址
将消息进队
告知任务有消息需要处理
图1 消息的发送过程

注意函数 osal_set_event() 的使用,这是一个十分关键的函数,我们首先分析其源代码:

uint8 osal_set_event( uint8 task_id, uint16 event_flag ) {
    if (task_id < tasksCnt) {
        halIntState_t   intState;
        HAL_ENTER_CRITICAL_SECTION(intState); // 关中断
        tasksEvents[task_id] |= event_flag; 
        HAL_EXIT_CRITICAL_SECTION(intState);// 开中断
    }
    else {
        return (INVALID_TASK);
    }
}

这个函数有两个参数,一个是 task_id,另一个是 event_flag,执行了一个特别重要的操作:

tasksEvents[task_id] |= event_flag; // 按位或并赋值

这样就通过了这个函数,改变了 tasksEvents[idx] 的值,从而在下一次扫描的时候,处理该事件。按位或的目的在于不能够影响其他已经存在的未经处理的事件。

其次,在消息发送的函数中第二个参数的值为 SYS_EVENT_MSG。其值为 0x8000。从这一点,我们必须认识到消息系统层次的事件,也就是说,消息是只能够存在在系统事件中的,这是消息发送这个函数所规定的。

5.1.2 消息的接收

消息的接收是通过定义 OSAL.c 文件中的 osal_msg_receive() 函数定义的:

uint8 *osal_msg_receive( uint8 task_id ) {
    osal_msg_hdr_t *listHdr; // 定义一个消息结构体指针
    osal_msg_hdr_t *prevHdr = NULL;
    osal_msg_hdr_t *foundHdr = NULL;
    halIntState_t   intState;
    HAL_ENTER_CRITICAL_SECTION(intState); // 进入临界区,关中断。
    listHdr = osal_qHead; // 指向队首指针
    // 遍历队列
    while ( listHdr != NULL ) {
        // 如果某个消息的目的地是 task_id
        if ((listHdr - 1)->dest_id == task_id) {
            // 第一次找到
            if ( foundHdr == NULL ) {
                 foundHdr = listHdr;
             }
            else break;
        }
        if (foundHdr == NULL) {
            // 保存找到的消息之前的消息
            // 方便进行删除操作
            prevHdr = listHdr; 
        }
        listHdr = OSAL_MSG_NEXT( listHdr ); // 遍历
    }
    // 不为空,证明为遍历完,也就是存在多个消息
    if ( listHdr != NULL ) {
        // 告知任务还有消息在等待处理
        osal_set_event( task_id, SYS_EVENT_MSG );
    }
    else {
        osal_clear_event( task_id, SYS_EVENT_MSG );
    }
    // 找到了消息
    if (foundHdr != NULL) {
        // 提取出这个消息
        osal_msg_extract( &osal_qHead, foundHdr, prevHdr );
    }
    HAL_EXIT_CRITICAL_SECTION(intState);// 退出临界区,开中断。
    return ( (uint8*) foundHdr ); // 返回消息
}

消息的接收过程可以用图2 表示:

Yes
yes
yes
No
No
No
Yes
No
listHdr != NULL
判断有无存在要发送给该任务的消息
是否第一次找到
foundHdr = listHdr
prevHdr = listHdr
listHdr = OSAL_MSG_NEXT( listHdr )
listHdr != NULL
osal_set_event( task_id, SYS_EVENT_MSG )
清除消息
提取消息
图2 消息的接收
5.1.3 事件的捕获

通过上述的过程,我们已经可以总结出事件是如何捕获的了,事件的捕获是通过函数 osal_set_event() 实现的。

5.2 任务处理函数

在第4 部分中,我们仅仅简单地讲述了任务处理函数,下面我们通过一个具体的例子进行说明:

UINT16 GenericApp_ProcessEvent( byte x, UINT16 events ) {
    if ( events & SYS_EVENT_MSG ) {
        MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
        while (MSGpkt) {
            switch(MSGpkt) {
                    case ZDO_CB_MSG:
                    	GenericApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
                    	break;
                    case ZDO_CB_MSG:
                    	GenericApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
                    	break;
                	default: break;
            	}
            osal_msg_deallocate( (uint8 *)MSGpkt ); // 释放消息
            // 处理该事件的下一个消息
            MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
        }
        return (events ^ SYS_EVENT_MSG) // 返回未处理的事件
    }  
}

我们从上面的代码可以看出,首先进入了任务处理函数,会去优先处理系统事件,再次强调,消息只能在系统时间中处理,这是OSAL 规定好了的。首先利用 osal_msg_receive() 函数从消息队列中获得有关该层任务的消息,然后根据发送的消息提供的信息,作出相应的操作,然后释放该消息并去处理下一个消息。

5.3 小结

至此,我们已经基本讲清了事件驱动机制的一个实现过程,希望对读者了解这个机制有所帮助。

你可能感兴趣的:(Z-Stack,OSAL分析)