ZigBee学习——浅析协议栈

✨记录学习过程

文章目录

  • 一、初识OSAL
    • [1.1 Z-Stack和Zigbee的OSAL是什么关系?](#1.1 Z-Stack和Zigbee的OSAL是什么关系?)
    • [1.2 OSAL可以解决Z-stack在不同厂商的芯片上的使用吗?](#1.2 OSAL可以解决Z-stack在不同厂商的芯片上的使用吗?)
  • 二、协议栈运行机制
    • [2.1 初始化涉及内容](#2.1 初始化涉及内容)
    • [2.2 初始化过程](#2.2 初始化过程)

一、初识OSAL

OSAL,全称是操作系统抽象层(Operating System Abstraction Layer),是一种在操作系统上建立的软件架构。OSAL就是基于事件的轮询查询系统 ,它提供了一种方法,使得应用程序可以在多种操作系统上运行,而不需要修改代码。在一个典型的OSAL实现中,你会在应用程序代码和特定操作系统代码之间看到一个OSAL。

在Zigbee中,OSAL被用来解决在不同芯片、不同操作系统上运行Zigbee Stack的问题。无论是基于RTOS的单片机,还是复杂的嵌入式Linux系统,只要实现了相应的OSAL,Zigbee Stack就可以在上面快速移植。

基于OSAL的设计,软件在从一个操作系统移植到另一个操作系统时,开发者不需要修改每个操作系统的底层设备驱动,而只需要关注OSAL提供的、对外统一的接口就可以了。

以Silicon Labs的Zigbee协议栈为例,它的OSAL提供了一套用于实现任务控制、内存管理、定时器控制、消息队列、中断管理等功能的API,开发者可以使用这些API来实现对Zigbee应用的开发,而无需关心具体硬件和操作系统的差异。这就使得在不同的硬件平台和操作系统上复用Zigbee应用代码变得可能。

总的来说,OSAL在Zigbee中的作用主要是提供了一种抽象的软件接口,使得Zigbee应用能在不同的硬件平台和操作系统上运行,而无需做大量的修改工作。

1.1 Z-Stack和Zigbee的OSAL是什么关系?

Z-Stack是Texas Instruments(TI)开发的一款Zigbee协议栈,它允许开发者在TI的微处理器上开发和应用Zigbee的解决方案。Z-Stack在设计之初,就特意采用了操作系统抽象层(OSAL)的结构来组织软件构成。

所以,简单来说,Z-Stack是Zigbee的一个实现,而OSAL则是Z-Stack中的一部分。在搭建过程中,OSAL扮演的是一个抽象层的角色,为Z-Stack上的应用提供了一套统一的API接口,让开发者无需关心底层硬件和操作系统的具体实现。通过OSAL,Z-Stack可以在TI的多款微处理器上运行。

同时,OSAL也容许Z-Stack具有更好的跨平台和可移植性。根据OSAL提供的API,可以在不同的硬件设备和操作系统上复用应用代码,极大地提高了代码的复用性和开发效率。

简单概括一下,Z-Stack是TI的一款Zigbee协议栈实现,而Zigbee的OSAL则是构成Z-Stack的重要组件,提供了一套统一的API接口,使得应用能兼容并在不同的硬件平台和操作系统上运行。

1.2 OSAL可以解决Z-stack在不同厂商的芯片上的使用吗?

答案是不能

操作系统抽象层(OSAL)的主要目标是为上层应用提供一个标准的API集合,使得应用开发者无需关心特定的硬件和操作系统实现。然而,尽管OSAL能够提供一种机制使得应用代码变得更加可移植,但是这并不意味着Zigbee的实现可以轻易地在不同厂商的芯片上运行。

当我们谈到Zigbee和其他类似的无线通信技术时,它们所涉及到的通信协议和硬件要求通常比操作系统接口更为复杂和特定。无论是RF模块还是微处理器本身,都需要特定的驱动程序和底层固件才能正常工作。这些驱动和固件通常由芯片制造商提供,且它们的实现除了需要考虑硬件的特性,也需要满足Zigbee协议的要求。

因此,即使使用了OSAL,我们也无法直接将一个为特定芯片设计的Zigbee实现移植到另一款芯片上。这通常需要芯片制造商参与,为他们的设备提供特定的Zigbee实现,或者提供足够的底层访问权以便其他厂商或开发者自行实现。

所以,简而言之,OSAL可以提高Zigbee实现的代码可移植性,但并不能解决所有的移植问题。要在不同的芯片上运行Zigbee,仍然需要对特定的硬件和Zigbee协议有深入的理解,同时也需要有适当的驱动和固件支持。

二、协议栈运行机制

2.1 初始化涉及内容

操作系统抽象层(OSAL):OSAL是Z-Stack的核心部分,它为使用不同操作系统的应用提供了通用的API接口。这些API包括任务调度、内存管理、计时器管理、中断管理等。

硬件抽象层(HAL):HAL提供了硬件驱动的抽象,使Z-Stack可以运行在不同的TI芯片上,并且无需修改上层应用代码。

Z-Stack服务:Z-Stack包含一套底层服务,例如网络管理、设备管理、安全管理等。这些服务负责实现Zigbee协议栈的核心功能。

应用框架:Z-Stack也为用户提供了一个应用框架,用户可以在这个框架下开发自己的Zigbee应用。应用框架处于OSAL之上,可以直接使用OSAL提供的API。

2.2 初始化过程

Z-Stack协议栈的运行可以分为以下三个步骤:

  1. 初始化:Z-Stack启动时,它首先会执行硬件初始化,包括初始化微控制器、RF模块等硬件设备,然后加载并初始化Zigbee协议栈的软件服务。如下图(未截全):

  2. 循环处理:一旦初始化完成,Z-Stack会进入主循环。在主循环中,它会通过OSAL管理并调度不同的任务运行,包括接收和处理来自硬件的中断,执行网络管理、设备管理等各种服务,处理来自上层应用的请求,以及通过RF模块发送和接收数据等。

  3. 事件处理:除了主循环处理,Z-Stack还通过OSAL的事件队列机制处理任务之间的通信。每个任务都可以生成事件并发送到其他任务的事件队列中,接收任务在主循环中周期性地查看和处理自己的事件队列。

OSAL系统的两个关键词:任务与事件

三个关键参数:

  1. TaskCnt:任务个数
  2. taskEvents:指向事件表首地址的指针
  3. taskArray:数组,数组中的每一个元素都是指向事件处理函数的指针
c 复制代码
void osal_run_system( void )
{
  uint8 idx = 0;

#ifdef USE_ICALL
  uint32 next_timeout_prior = osal_next_timeout();
#else /* USE_ICALL */
#ifndef HAL_BOARD_CC2538
  osalTimeUpdate();
#endif
  Hal_ProcessPoll();	// 查看硬件方面是否有事件发生,例如是否有按键按下、串口是否收到数据等等
#endif /* USE_ICALL */

#ifdef USE_ICALL
  {
    /* Update osal timers to the latest before running any OSAL processes
     * regardless of wakeup callback from ICall because OSAL timers are added
     * relative to the current time. */
    unsigned long newtimestamp = ICall_getTicks();
    uint32 milliseconds;

    if (osal_tickperiod == 1000)
    {
      milliseconds = newtimestamp - osal_last_timestamp;
      osal_last_timestamp = newtimestamp;
    }
    else
    {
      unsigned long long delta = (unsigned long long)
        ((newtimestamp - osal_last_timestamp) & 0xfffffffful);
      delta *= osal_tickperiod;
      delta /= 1000;
      milliseconds = (uint32) delta;
      osal_last_timestamp += (uint32) (delta * 1000 / osal_tickperiod);
    }
    osalAdjustTimer(milliseconds);
    /* Set a value that will never match osal_next_timeout()
     * return value so that the next time can be scheduled.
     */
    next_timeout_prior = 0xfffffffful;
  }
  if (osal_eventloop_hook)
  {
    osal_eventloop_hook();
  }

  for (;;)
  {
    void *msg;
    ICall_EntityID src, dst;
    osal_msg_hdr_t *hdr;
    uint8 dest_id;

    if (ICall_fetchMsg(&src, &dst, &msg) != ICALL_ERRNO_SUCCESS)
    {
      break;
    }
    hdr = (osal_msg_hdr_t *) msg - 1;
    dest_id = osal_dispatch2id(dst);
    if (dest_id == TASK_NO_TASK)
    {
      /* Something wrong */
      ICall_abort();
    }
    else
    {
      /* Message towards one of the tasks */
      /* Create a proxy task ID if necessary and
       * queue the message to the OSAL internal queue.
       */
      uint8 proxyid = osal_alien2proxy(hdr->srcentity);

      if (hdr->format == ICALL_MSG_FORMAT_1ST_CHAR_TASK_ID)
      {
        uint8 *bytes = msg;
        *bytes = proxyid;
      }
      else if (hdr->format == ICALL_MSG_FORMAT_3RD_CHAR_TASK_ID)
      {
        uint8 *bytes = msg;
        bytes[2] = proxyid;
      }
      /* now queue the message to the OSAL queue */
      osal_msg_send(dest_id, msg);
    }
  }
#endif /* USE_ICALL */

  do {
    if (tasksEvents[idx])  // 判断是否有事件发生,当事件发生时,对应的元素会被置1,此时idx表示的就是哪一个事件被触发
    {
      break;	// 跳出do...while
    }
  } while (++idx < tasksCnt);

  if (idx < tasksCnt)		// idx检查合法性
  {
    uint16 events;
    halIntState_t intState;

    HAL_ENTER_CRITICAL_SECTION(intState);	// 进入临界区,这是操作系统的常规操作,避免由于中断影响程序的正常运行
    events = tasksEvents[idx];
    tasksEvents[idx] = 0;  // 置0,表示处理器接下来会去处理该事件对应的任务。
    HAL_EXIT_CRITICAL_SECTION(intState);	//退出临界区

    activeTaskID = idx;
    events = (tasksArr[idx])( idx, events );	// taskArr的每个元素都指向对应事件的处理函数,括号就是处理函数的形参
    activeTaskID = TASK_NO_TASK;

    HAL_ENTER_CRITICAL_SECTION(intState);	// 进入临界区
    tasksEvents[idx] |= events;   // Add back unprocessed events to the current task.
    //把未处理的事件返回给事件表,因为触发的事件可能不止一个,且一次循环只会完成一次事件处理,所以需要返回未处理事件
    HAL_EXIT_CRITICAL_SECTION(intState);	// 关闭临界区
  }
#if defined( POWER_SAVING ) && !defined(USE_ICALL)
  else  // Complete pass through all task events with no activity?
  {
    osal_pwrmgr_powerconserve();  // Put the processor/system into sleep
  }
#endif

  /* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
  {
    osal_task_yield();
  }
#endif

#if defined USE_ICALL
  /* Note that scheduling wakeup at this point instead of
   * scheduling it upon ever OSAL start timer request,
   * would only work if OSAL start timer call is made
   * from OSAL tasks, but not from either ISR or
   * non-OSAL application thread.
   * In case, OSAL start timer is called from non-OSAL
   * task, the scheduling should be part of OSAL_Timers
   * module.
   * Such a change to OSAL_Timers module was not made
   * in order not to diverge the OSAL implementations
   * too drastically between pure OSAL solution vs.
   * OSAL upon service dispatcher (RTOS).
   * TODO: reconsider the above statement.
   */
  {
    halIntState_t intState;

    uint32 next_timeout_post = osal_next_timeout();
    if (next_timeout_post != next_timeout_prior)
    {
      /* Next wakeup time has to be scheduled */
      if (next_timeout_post == 0)
      {
        /* No timer. Set time to the max */
        next_timeout_post = OSAL_TIMERS_MAX_TIMEOUT;
      }
      if (next_timeout_post > osal_max_msecs)
      {
        next_timeout_post = osal_max_msecs;
      }
      /* Restart timer */
      HAL_ENTER_CRITICAL_SECTION(intState);
      ICall_stopTimer(osal_timerid_msec_timer);
      ICall_setTimerMSecs(next_timeout_post, osal_msec_timer_cback,
                          (void *) (++osal_msec_timer_seq),
                          &osal_timerid_msec_timer);
      HAL_EXIT_CRITICAL_SECTION(intState);
    }
  }
#endif /* USE_ICALL */
}

Z-Stack的这种设计,使得应用代码可以在不同的TI微控制器系列产品上运行,而无需做任何修改。同时,通过服务和事件的设计,使得任务间的通信更加简洁高效。

相关推荐
东京老树根2 分钟前
SAP学习笔记 - 开发27 - 前端Fiori开发 Routing and Navigation(路由和导航)
笔记·学习
阿阳微客6 小时前
Steam 搬砖项目深度拆解:从抵触到真香的转型之路
前端·笔记·学习·游戏
Chef_Chen11 小时前
从0开始学习R语言--Day18--分类变量关联性检验
学习
键盘敲没电11 小时前
【IOS】GCD学习
学习·ios·objective-c·xcode
海的诗篇_12 小时前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
AgilityBaby12 小时前
UE5 2D角色PaperZD插件动画状态机学习笔记
笔记·学习·ue5
AgilityBaby12 小时前
UE5 创建2D角色帧动画学习笔记
笔记·学习·ue5
武昌库里写JAVA13 小时前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
一弓虽14 小时前
git 学习
git·学习
Moonnnn.16 小时前
【单片机期末】串行口循环缓冲区发送
笔记·单片机·嵌入式硬件·学习