一、概述
      OSAL (Operating System Abstraction Layer) ,翻译为 “ 操作系统抽象层 ”。
    在 ZigBee 协议中,协议本身已经定义了大部分内容。在基于 ZigBee 协议的应用开发中,用户只需要实现应用程序框架即可。应用程序框架中包含了最多 240 个应用程序对象。如果我们把一个应用程序对象看做为一个任务的话,那么应用程序框架将包含一个支持多任务的资源分配机制。于是 OSAL 便有了存在的必要性,它正是 Z-Stack 为了实现这样一个机制而存在的。
     OSAL 就是以实现多任务为核心的系统资源管理机制。所以 OSAL 与标准的操作系统还是有很大的区别的。
  简单而言, OSAL实现了类似操作系统的某些功能,但并不能称之为真正意义上的操作系统 。
二、 OSAL运行方式
     在GenericApp的工程的workspace里面 可以看到三个文件,分别是 “GenericApp.c” 、 “GenericApp.h” 、 “OSAL_GenericApp.c” 。我们整个程序所实现的功能都在这三个文件当中。

首先打开GenericApp.c这个文件。我们首先看到的是比较重要的两个函数:GenericApp_Init和GenericApp_ProcessEvent。从函数名称上我们很容易得到的信息便是,GenericApp_Init是任务的初始化函数,而GenericApp_ProcessEvent则负责处理传递给此任务的事件,此函数的主要功能是判断由参数传递的事件类型,然后执行相应的事件处理函数。

当有一个事件发生的时候, OSAL 负责将此事件分配给能够处理此事件的任务,然后此任务判断事件的类型,调用相应的事件处理程序进行处理。
三、 OSAL的事件传递机制
      OSAL是如何传递事件给任务的?
首先介绍一下 tasksArr 、tasksEvents(在OSAL_GenericApp.c 文件中)。

const pTaskEventHandlerFn tasksArr[] = {
  macEventLoop,
  nwk_event_loop,
  Hal_ProcessEvent,
#if defined( MT_TASK )
  MT_ProcessEvent,
#endif
  APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_ProcessEvent,
#endif
  ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_event_loop,
#endif
   GenericApp_ProcessEvent
};
const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
uint16 *tasksEvents;
TaskArr这个数组里存放了所有任务的事件处理函数的地址,在这里事件处理函数就代表了任务本身,也就是说事件处理函数标识了与其对应的任务。 tasksCnt这个变量保存了当前的任务个数。
tasksEvents是一个指向数组的指针,此数组保存了当前任务的状态。osal每个任务可以有16个事件,其中SYS_EVENT_MSG定义为0x8000,为系统事件,用户可以定义剩余的15个事件。


   tasksEvents这个数组存放的是从序号为0到tasksCnt,每个任务在本次循环中是否要被运行,需要运行的任务其值非0(用橙色表示),否则为0。而tasksArr数组则存放了对应每个任务的入口地址,只有在tasksEvents中记录的需要运行的任务,在本次循环中才会被调用到。 tasksEvents和tasksArr[]里的顺序是一一对应的, tasksArr[ ]中的第i个  事件处理函数对应于tasksEvents中的第i个任务的事件. 
 
Zmain.c->osal_init_system() ->osal_initTasks()中:

void osalInitTasks( void )
{
  uint8 taskID = 0;

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

  macTaskInit( taskID++ );
  nwk_init( taskID++ );
  Hal_Init( taskID++ );
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
  APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_Init( taskID++ );
#endif
  ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_Init( taskID++ );
#endif
  SampleApp_Init( taskID );
}
 tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); 

为当前osal中的各任务分配  缓冲区 (实际上是一个数组,每个任务的大小是sizeof( uint16 )即 4个字节,tasksCnt为任务数,sizeof( uint16 ) * tasksCnt为分配缓冲区的字节数),函数返回指向任务缓冲区的指针,此指针的基本类型是  uint16。
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
把开辟的内存全部设置为0;sizeof( uint16 )是2个字节,即一个任务的长度(同样是uint16定义),乘以任务数量tasksCnt,即全部内存空间 , tasksEvents 是缓冲区的首地址。
Zmain.c->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专门建立了一个任务来对硬件资源进行管理,这个任务的事件处理函数是Hal_ProcessEvent。在这个函数中通过调用osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);这个函数使得每隔100毫秒就会执行一次HalKeyPoll()函数。HalKeyPoll()获取当前按键的状态,并且通过调用OnBoard_KeyCallback函数向GenericApp任务发送一个按键消息,并且设置tasksEvents中GenericApp 所对应的值为非零。

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 函数, 在接近函数末尾的地方, keys 变量(在函数起始位置定义的)获得了当前按键的状态。最后,有一个十分重要的函数调用。
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
pHalKeyProcessFunction 这个函数指针指向了void  OnBoard_KeyCallback ( uint8 keys, uint8 state ),因为在HalKeyConfig函数中有一句话pHalKeyProcessFunction = cback; cback是HalKeyConfig所传进来的参数,所以,想要知道它所指向的函数,必须找到其调用的地方。经过简单的搜索我们不难找出答案。在main函数中有这样一个函数调用:InitBoard( OB_READY );此函数中做了如下调用:
  {
        HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  }

在 void  OnBoard_KeyCallback 函数中 按键的状态信息被封装到了一个消息结构体中。最后有一个极其重要的函数被调用了。osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );其中 registeredKeysTaskID 指的就是GenericApp。
在osal_msg_send 函数中 osal_set_event( destination_task,  SYS_EVENT_MSG )设置了任务事件。
通过调用 osal_msg_send 函数向 GenericApp 发送了一个消息,这个消息记录了这个事件的附加信息。在 GenericApp_ProcessEvent 中,通过
{
        MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
}
获取了这样一个消息,然后再通过GenericApp_HandleKeys进一步处理。
五、系统时钟

      首先介绍一下定时器,协议栈里面有两类定时器:一类是硬件定时器,对应几个Timer,硬件时钟定时器即硬件时钟。另一类是软件定时 器, 通过osal_start_timerEx()添加到软定时器链表,再由系统时钟进行统一减计数。

时间管理

对事件进行时间管理,OSAL采用了链表的方式进行,有时发生一个要被处理的事件,就启动一个逻辑上的定时器,并将此定时器添加到链表当中。利用硬件定时器作为时间操作的基本单元。设置时间操作的最小精度为1ms,每1ms硬件定时器便产生一个时间中断,在时间中断处理程序中去更新定时器链表。每次更新,就将链表中的每一项时间计数减1,如果发现定时器链表中有某一表项时间计数已减到0,则将这个定时器从链表中删除,并设置相应的事件标志。这样任务调度程序便可以根据事件标志进行相应的事件处理。

时间管理函数:
extern byte osal_start_timerEx(byte task_id, uint16 event_id, uint16 timeout_value);
这个函数为事件event_id设置超时等待时间timeout_value。一旦等待结束,便为task_id所对应的任务设置相应的事件发生标记,进行相应处理.
这段话中的硬件定时器,即为系统时钟定时器,定时时间为“TICK_TIME—系统时间片”。

系统时间是多少?
在OnBoard.h中定义:
/* OSAL timer defines */
#define TICK_TIME 1000 // Timer per tick - in micro-sec
1000us,即1ms,

系统时钟什么时候初始化?

在OnBoard.c中InitBoard():
/***************************************
void InitBoard( byte level )//在zmain.c中先OB_COLD启动,后OB_READY启动
{
if ( level == OB_COLD )//冷启动
{
…………
/* Timer2 for Osal timer
* This development board uses ATmega128 Timer/Counter3 to provide
* system clock ticks for the OSAL scheduler. These functions perform
* the hardware specific actions required by the OSAL_Timers module.
*/
OnboardTimerIntEnable = FALSE; //对系统时钟定时器来说,默认为非中断方式 
HalTimerConfig (OSAL_TIMER, // 8bit timer2
HAL_TIMER_MODE_CTC, // Clear Timer on Compare
HAL_TIMER_CHANNEL_SINGLE, // Channel 1 - default
HAL_TIMER_CH_MODE_OUTPUT_COMPARE, // Output Compare mode
OnboardTimerIntEnable, // FALSE
Onboard_TimerCallBack); // Channel Mode

}
…………
}
/***************************************

OnBoard.h中初始化
/* OSAL Timer define */
#define OSAL_TIMER HAL_TIMER_2
而通过halTimerRemap()知道HAL_TIMER_2对应HW Timer 4 

定时器非中断方式函数调用流程:
osal_timer_activate( TRUE )开启系统时钟定时器——系统主循环函数osal_start_system()——调用Hal_ProcessPoll()轮询硬件——调用HalTimerTick()轮询定时器——定时器采用非中断方式则调用halProcessTimer()判断定时器是否溢出——溢出则调用halTimerSendCallBack()来发送消息给相应定时器的回调函数——调用各定时器的回调函数,如系统时钟定时器的Onboard_TimerCallBack()——调用osal_update_timers()来更新软件定时器链表中各定时器的计数值(每次减1ms)——如有软件定时器溢出调用osal_set_event()触发事件。
 
总结起来就是每1ms系统时钟都会跑到软件定时器链表中去把各定时器的计数值减1.

看下 osalTimerUpdate()这个函数

/*********************************************************************
* @fn osalTimerUpdate
*
* @brief Update the timer structures for a timer tick.
*
* @param none
*
* @return none
*********************************************************************/
static void osalTimerUpdate( uint16 updateTime )
{
…………

// Update the system time
osal_systemClock += updateTime; //系统时间,1ms往上加

// Look for open timer slot
if ( timerHead != NULL )
{
// Add it to the end of the timer list
srchTimer = timerHead;
prevTimer = (void *)NULL;

// Look for open timer slot
while ( srchTimer )
{
// Decrease the correct amount of time
if (srchTimer->timeout <= updateTime) //小于等于
srchTimer->timeout = 0;
else //大于
srchTimer->timeout = srchTimer->timeout - updateTime; //减1ms

// When timeout, execute the task
if ( srchTimer->timeout == 0 )
{
osal_set_event( srchTimer->task_id, srchTimer->event_flag ); //设置事件发生标志

…………
}
…………
}

…………
}

/*********************************************************************

osalTimerUpdate()来以ms为单位对软定时器链表中的“软定时器”减计数,溢出时,即调用osal_set_event触发事件。

系统时钟定时器什么时候开启?  
osalTimerInit()函数中初始化timerActive = false,因此会在osal_start_timerEx()中执行osal_timer_activate( TRUE )。

系统什么时候开始运行第一个osal_start_timerEx(),就什么时候开启系统时钟。即当系统的软定时器链表中添加第一个软件定时器时,就开启系统时钟。那什么时候开始运行第一个osal_start_timerEx() ?一般是在任务的ProcessEvent函数中,如SampleApp_ProcessEvent和Hal_ProcessEvent。

转自:点击链接打开

OSAL多任务资源分配机制相关推荐

  1. OSAL操作系统实验学习笔记04

    相信很多人接触了OSAL操作系统之后对它的任务资源分配机制还是很模糊,我细看了很多遍也还是略知一二,现在分享一篇我觉得写得特别好的文章. 深入浅出Z-Stack OSAL多任务资源分配机制 一 概述 ...

  2. 用ajax修改成功怎么返回页面,jquery操作ajax返回的页面元素

    这两天工作不忙,正好从朋友那里拿到一个某个应用的开发文档,相关数据放在了mongodb里,自己电脑可以本地开启服务器然后通过给的借口来获取数据.由于这是一个比较大比较全的一个完整项目,也没有那么多经历 ...

  3. ZigBee无线网络的温湿度测量系统

    目前,随着工业生产自动化程度不断提升,温湿度等生产环境要素监控智能化程度也在不断发展.传统测量设备功能单一,采用线缆连接各测量节点,测量系统架设复杂,数据处理实时性不高.随着短距离无线通信技术.嵌入式 ...

  4. ZigBee无线传感器的网络协议栈

    小结: Z-Stack协议栈 = OSAL操作系统 + CC2530硬件模块 + AF无线网络应用 协议定义的是一系列的通信标准,通信双方需要共同按照这一标准进行正常的数据收发;协议栈是协议的具体实现 ...

  5. 浅析CC2540的OSAL原理

    http://www.baidu.com/link?url=SIaYicr9vNVSFxvVwlIbueni2hnjmb0d5U3mntLWnPiP9fXDc6ApIHZy1XaK89wdlLodRn ...

  6. CC2540/CC2541/CC254x之OSAL操作系统抽象层

    测试环境 协议栈版本:BLE-CC254x-1.4.0 开发环境IAR版本:IAR 8.20 硬件设备:CC2540/CC2541开发板 示例测试Demo工程:simpleBLEPeripheral工 ...

  7. BlueTooth: 浅析CC2540的OSAL原理

    http://www.baidu.com/link?url=SIaYicr9vNVSFxvVwlIbueni2hnjmb0d5U3mntLWnPiP9fXDc6ApIHZy1XaK89wdlLodRn ...

  8. IAR平台移植TI OSAL到STC8A8K64S4A12单片机中

    玩过TI 的ZigBee或者BLE的人,都会接触到OSAL.OSAL是什么?OSAL英文全称:operating system abstraction layer(操作系统抽象层).基于OSAL的调度 ...

  9. OSAL(操作系统抽象层)

    OSAL为Operating System Abstraction Layer,即操作系统抽象层,支持多任务运行,所有的应用程序(app)都在其上运行,它并不是一个传统意义上的操作系统,但是实现了部分 ...

最新文章

  1. Mysql是时候学习一个存储过程了
  2. hadoop命令帮助
  3. Java 基础之 Random类和Math.random()方法
  4. BZOJ 2038: [2009国家集训队]小Z的袜子(莫队算法例题)
  5. activemq主从配置_使用ActiveMQ –具有故障转移协议的“主/从”配置
  6. c++ vs release没有exe_未来安全 | 第一次Geant4培训总结 | 有没有你关注的问题呢?...
  7. C语言程序设计教程的读后感,《高质量c语言编程》读后感
  8. mysql常用基础操作语法(八)~~多表查询合并结果和内连接查询【命令行模式】...
  9. 设计素材模板|艺术感中国风海报
  10. A query was run and no Result Maps were found for the Mapped Statement
  11. Json学习总结(3)——Jsonp跨域及Rest接口实现
  12. [导入]用Windows自带的媒体播放器抓视频截图
  13. Everything本地文件检索 快速搜索/共享神器
  14. 跟着迪哥学python电子书pdf-跟着迪哥学Python数据分析与机器学习实战
  15. 记录linux deploy如何进行分区安装centos7
  16. [bzoj4199][后缀数组][后缀自动机]品酒大会
  17. java零基础Ⅰ-- 1.java 概述
  18. gitter 卸载_最佳Gitter频道:数学
  19. openssl 1.0.2 漏洞修复指南
  20. 两条线段的交点 交点

热门文章

  1. 【有利可图网】PS实战教程25:巧用PS设计制作滑落一半的海报
  2. 关于sap的Logon的几个参数
  3. Kali Linux 安装Nvidia显卡驱动(二)CUDA, Pyrit and Cpyrit-cuda
  4. 长沙银河计算机中等职业学校图片,长沙银河中等职业学校
  5. 练习记录-用FSL工具对DTI数据进行FDT预处理
  6. 36个非常有用的电脑知识?
  7. JS XML在线格式化、压缩、校验、XML转JSON工具-toolfk程序员工具网
  8. Domain Adaption3
  9. 布衣联盟XP SP2之国兴奥运版
  10. 解析鸿峰智能软件-------恒指