(通信 电子)ZigBee学习资料

2019-04-22 16:48

一、概述

OSAL (Operating System Abstraction Layer)?翻译为“操作系统抽象层”。如何理解这个复杂的名词呢?表面上看它是作为操作系统存在的?可是为什么又加上“抽象层”呢?它的本质是什么?在Z-Stack协议栈中?它又扮演了什么角色呢?要解答这些问题?我们必须先从宏观入手?渐渐深入探究,最后答案自然会浮出水面。

从这幅图中?我们可以很清楚地从宏观上了解ZigBee协议的结构。可是?经过粗略的浏览?我们并没有发现任何OSAL的踪迹。当然?我们都知道?Z-Stack与ZigBee之间并不能完全划等号。Z-Stack是ZigBee的具体实现?所以存在于Z-Stack中的OSAL并不一定出现在ZigBee中。但是?我们可以在ZigBee中找到些许OSAL的踪影。

在ZigBee协议中?协议本身已经定义了大部分内容。在基于ZigBee协议的应用开发中?用户只需要实现应用程序框架即可。从上图可以看出应用程序框架中包含了最多240个应用程序对象。如果我们把一个应用程序对象看做为一个任务的话?那么应用程序框架将包含一个支持多任务的资源分配机制。于是OSAL便有了存在的必要性?它正是Z-Stack为了实现这样一个机制而存在的。

OSAL就是以实现多任务为核心的系统资源管理机制。所以OSAL与标准的操作系统还是有很大的区别的。简单而言?OSAL实现了类似操作系统的某些功能?但并不能称之为真正意义上的操作系统。 二、OSAL任务运行方式

弄明白了OSAL是何方神圣?接下来我们将深入Z-Stack?进一步研究OSAL。 为了方便?我们使用Z-Stack所提供的GenericApp这个例程为例来进行分析。此例程的默认路径为

C:\\Texas Instruments\\ZStack-1.4.3-1.2.1\\Projects\\zstack\\Samples\\GenericApp。 首先我们去繁就简?先来了解应用程序的运行方式。

在右侧工作空间窗口打开App文件夹?我们可以看到三个文件?分别是“GenericApp.c”、“GenericApp.h”、“OSAL_GenericApp.c”。我们整个程序所实现的功能都在这三个文件当中。

首先打开GenericApp.c这个文件。我们首先看到的是比较重要的两个函数:

GenericApp_Init和GenericApp_ProcessEvent。从函数名称上我们很容易得到的信息便是?GenericApp_Init是任务的初始化函数?而GenericApp_ProcessEvent则负责处理传递给此任务的事件。

大概浏览一下GenericApp_ProcessEvent这个函数?我们可以发现?此函数的主要功能是判断由参数传递的事件类型?然后执行相应的事件处理函数。我们可以由此推断Z-Stack应用程序的运行机制如下图所示:

当有一个事件发生的时候?OSAL负责将此事件分配给能够处理此事件的任务?然后此任务判断事件的类型?调用相应的事件处理程序进行处理。

明白了这个问题?新的问题又摆在了我们的面前:OSAL是如何传递事件给任务的。 三、OSAL的事件传递机制

在试图弄清楚这个问题之前?我们需要弄清楚另外一个十分基础而重要的问题。那就是如何向我们的应用程序中添加一个任务。

我们先来看看GenericApp是如何添加任务的。

我们打开OSAL_GenericApp.c文件。这里我们可以找到一个很重要的数组tasksArr和一个同样很重要的函数osalInitTasks。

TaskArr这个数组里存放了所有任务的事件处理函数的地址?在这里事件处理函数就代表了任务本身?也就是说事件处理函数标识了与其对应的任务。

osalInitTasks是OSAL的任务初始化函数?所有任务的初始化工作都在这里面完成?并且自动给每个任务分配一个ID。

要添加新任务?我们需要编写新任务的事件处理函数和初始化函数?然后将事件处理函数的地址加入此数组。然后在osalInitTasks中调用此任务的初始化函数。在此例中?我们此前提到过的GenericApp_ProcessEvent这个函数被添加到了数组的末尾? GenericApp_Init这个函数在osalInitTasks中被调用。

值得注意的是?TaskArr数组里各任务函数的排列顺序要与osalInitTasks函数中调用各任务初始化函数的顺序必须一直?只有这样才能够保证每个任务能够通过初始化函数接收到正确的任务ID。

另外?为了保存任务初始化函数所接收的任务ID?我们需要给每一个任务定义一个全

局变量来保存这个ID。在GenericApp中GenericApp.c中定义了一个全局变量GenericApp_TaskID;并且在GenericApp_Init函数中进行了赋值 {

GenericApp_TaskID = task_id; }

这条语句将分配给GenericApp的任务ID保存了下来。 到此?我们就给应用程序中完整的添加了一个任务。

我们回到OSAL如何将事件分配给任务这个问题上来

在OSAL_GenericApp.c这个文件中?在定义TaskArr这个数组之后?又定义了两个全局变量。

tasksCnt这个变量保存了当前的任务个数。

tasksEvents是一个指向数组的指针?此数组保存了当前任务的状态。在任务初始化函数中做了如下操作 {

tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt)); }

我们可以看出所有任务的状态都被初始化为0。代表了当前任务没有需要响应的事件。 紧接着?我们来到了main()函数。此函数在ZMain文件夹的ZMain.c文件中。略过许多对当前来说并非重要的语句?我们先来看osal_init_system()这个函数。在此函数中?osalInitTasks()被调用?从而tasksEvents中的所有内容被初始化为0。

之后?在main()函数中?我们进入了osal_start_system()函数?此函数为一个死循环?在这个循环中?完成了所有的事件分配。

首先我们来看这样一段代码: {

do {

if (tasksEvents[idx]) {

break; }

} while (++idx < tasksCnt); }

当tasksEvents这个数组中的某个元素不为0?即代表此任务有事件需要相应?事件类

型取决于这个元素的值。这个do-while循环会选出当前优先级最高的需要响应的任务?

{

events = (tasksArr[idx])( idx, events )?

}

此语句调用tasksArr数组里面相应的事件处理函数来响应事件。如果我们新添加的任务有了需要响应的事件?那么此任务的事件处理程序将会被调用。

就这样?OSAL就将需要响应的事件传递给了对应的任务处理函数进行处理。 四、事件的捕获

不过接下来就有了更加深入的问题了?事件是如何被捕获的?直观一些来说就是?tasksEvents这个数组里的元素是什么时候被设定为非零数?来表示有事件需要处理的?为了详细的说明这个过程?我将以GenericApp这个例程中响应按键的过程来进行说明。其他的事件虽然稍有差别?却是大同小异。

按键在我们的应用里面应该属于硬件资源?所以OSAL理应为我们提供使用和管理这些硬件的服务。稍微留意一下我们之前说过的tasksArr这样一个数组?它保存了所有任务的事件处理函数。我们从中发现了一个很重要的信息:Hal_ProcessEvent。HAL(Hardware Abstraction Layer)翻译为“硬件抽象层”。许多人在这里经常把将Z-Stack的硬件抽象层与ZigBee的物理层混为一谈。在这里?我们应该将Z-Stack的硬件抽象层与ZigBee的物理层区分开来。硬件抽象层所包含的范围是我们当前硬件电路上面所有对于系统可用的设备资源。而ZigBee中的物理层则是针对无线通信而言?它所包含的仅限于支持无线通讯的硬件设备。

通过这个重要的信息?我们可以得出这样一个结论:OSAL将硬件的管理也作为一个任务来处理。那么我们很自然的去寻找Hal_ProcessEvent这个事件处理函数?看看它究竟是如何管理硬件资源的。

在“HAL\\Commen\\ hal_drivers.c”这个文件中?我们找到了这个函数。我们直接分析与按键有关的一部分。 {

if (events & HAL_KEY_EVENT) {

#if (defined HAL_KEY) && (HAL_KEY == TRUE) /* Check for keys */ HalKeyPoll();

/* if interrupt disabled, do next polling */ if (!Hal_KeyIntEnable) {

osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100); }

#endif // HAL_KEY

return events ^ HAL_KEY_EVENT; } }

在事件处理函数接收到HAL_KEY_EVENT这样一个事件后?首先执行HalKeyPoll()函数。由于这个例程的按键采用查询的方法获取?所以是禁止中断的?于是表达式(!Hal_KeyIntEnable)的值为真。那么osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100)得以执行。osal_start_timerEx这是一个很常用的函数?它在这里的功能是经过100毫秒后?向Hal_TaskID这个ID所标示的任务(也就是其本身)发送一个HAL_KEY_EVENT事件。这样以来?每经过100毫秒?Hal_ProcessEvent这个事件处理函数都会至少执行一次来处理HAL_KEY_EVENT事件。也就是说每隔100毫秒都会执行HalKeyPoll()函数。 那么我们来看看HalKeyPoll函数到底在搞什么鬼! 代码中给的注释为: /* Check for keys */ HalKeyPoll();

于是我们推断这个函数的作用是检查当前的按键情况。进入函数一看?果不其然。虽然这个函数很长

很复杂?不过凭借着非凡的聪明才智?我们还是十分清楚的明白了?经过一系列的if语句和赋值语句?在

接近函数末尾的地方? keys变量(在函数起始位置定义的)获得了当前按键的状态。最后?有一个十分


(通信 电子)ZigBee学习资料.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:教学常规自查报告

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: