zigbee协议栈 任务、事件与轮询机制

zigbee协议栈 任务、事件与轮询机制typedefunsignedcharuint8  只占一个字节,即二进制的8位,0b00000000,16进制的两位0x00;typedefunsignedshortuint16只占两个字节,即二进制的16位,0b0000000000000000,16进制的四位0x0000 协议栈中有三个变量至关重要:l tasksCnt保存了任务的总个数uint8ta

大家好,又见面了,我是你们的朋友全栈君。

typedef unsigned char uint8   只占一个字节,即二进制的8位,0b00000000,16进制的两位0x00;

typedef unsigned short uint16 只占两个字节,即二进制的16位,0b0000000000000000,16进制的四位 0x0000

 

协议栈中有三个变量至关重要:

tasksCnt保存了任务的总个数

uint8 tasksCnt    

tasksEvents这是一个指针(可以看做数组),作为事件表,数组的索引是任务ID号,每一元素对应了该任务下的所有事件,这个事件可以拆分为小事件;

uint16 *tasksEvents 

tasksArr——这是一个数组,数组中的每一项都是一个函数指针,指向了任务事件的处理函数。数组的索引是任务的ID号,该ID号下的元素就是对应任务的事件处理函数,事件处理函数利用switch将该任务下的所有事件处理;

pTaskEventHandlerFntasksArr[] ,pTaskEventHandlerFn 是函数指针

typedef unsignedshort (*pTaskEventHandlerFn) (unsigned char task_id, unsigned short event)

表明,pTaskEventHandlerFn是一个指向返回值为(unsigned short)形参为(unsigned char task_id ,unsigned short event)的函数的指针

 

tasksEvents中的元素为一个16位二进制数,zigbee协议栈用一位二进制来定义事件,为1表示有事件,为0表示无事件,如任务ZDAppTaskID下的事件为

事件

十六进制

二进制

ZDO_NETWORK_INIT

0x0001

0b0000000000000001

ZDO_NETWORK_START

0x0002

0b0000000000000010

ZDO_DEVIEC_RESET

0x0004

0b0000000000000100

ZDO_COMMAND_CNF

0x0008

0b0000000000001000

ZDO_START_CHANGE_EVT

0x0010

0b0000000000010000

ZDO_ROUTER_START

0x0020

0b0000000000100000

ZDO_NEW_DEVICE

0x0040

0b0000000001000000

ZDO_DEVICE_AUTH

0x0080

0b0000000010000000

ZDO_SCEMGR_EVENT

0x0100

0b0000000100000000

ZDO_NWK_UPDATE_NV

0x0200

0b0000001000000000

ZDO_FRAMECOUNTER_CHANGE

0x0400

0b0000010000000000

如上图,这样二进制的每一位的1可以定义为一个事件,理论上一个任务下可以定义16个事件。这样的好处是,事件与事件之间可以用二进制加法处理即异或算法相加。

比如:tasksEvents[ZDAppTaskID]=0x0003 ,由于 0x0003=0x0001^0x0002,所以可以看做ZDO_NETWORK_INT+ZDO_NETWORK_START,所以此时该任务下的事件有两个,即ZDO_NETWORK_INT和ZDO_NETWORK_START。提取的时候可以利用与运算来提取。 “与”运算能用来判断二进制数的某一位是否为1。由于二进制的减法运算与加法运算相同,所以也可以通过加法异或运算来清零某一已经处理过的事件。

 

全zigbee协议栈最重要的就是  void osal_start_system( void )函数,整个轮询机制也在这个函数中被完成

zigbee协议栈 任务、事件与轮询机制

第5行,定义了一个变量idx,用来标识任务,(如任务0),用来在事件表和函数表中索引;

第6、7行,更新系统时钟,同时查看硬件方面是否有事件发生,如串口是否接收到数据、是否有按键按下等信息,这部分内容在此暂不考虑;

第9~15行,idx从零增大到tasksCnt,依次遍历事件表,查看哪个任务idx对应的事件不为空,当找到任务idx的事件不为空时停止遍历,转而去调用该任务的事件处理函数,不妨设此时的idx = 8;

第19~22行,将该任务8的事件取出来放到events变量中,由于事件值被取出来了,所以对应事件表中的元素值要清零,即tasksEvents[8] = 0;

第25行,由于任务事件处理函数表的元素索引与任务事件表的索引一一对应,所以直接将8作为索引带入任务事件处理函数表就可以调用任务8的事件处理函数(tasksArr[8])(8,events),而且由于函数表中的事件处理函数是以函数指针的形式给出的,所以还需要填写形参,分别是任务id号8,和从任务8的事件events。形参中有events天经地义,因为需要分辨events中含有哪些具体事件,形参中为什么有任务id号呢?是因为,在处理任务的时候可能需要调用其他与任务绑定的函数。同时,任务处理函数的返回值也是任务值,UINT16,返回的是未被处理的具体事件。

第32行,将任务8未被处理的具体事件放回事件表中任务8对应的事件元素中。

 

然而,以上的机制可以解释Zigbee是怎样处理一个任务下面的事件的,但处理后的事件表是被清零的,那是谁来给任务的这些事件来置一呢?

osal_set_event(uint8 task_id , uint16 event_flag ) ;由它来给事件表中的元素赋值。

函数原型为:

uint8osal_set_event( uint8 task_id , uint16 event_flag )

{

 if( task_id < tasksCnt)

{

  halIntState_t   intState ;

  HAL_ENTER_CRITICALL_SECTION( inState ) ;   //关中断

  tasksEvents[task_id] |= event_falg ;           //给任务task_id设置事件event_flag

   HAL_EXTI_CRITICALL_SECTION(inState ) ;     //开中断

   return( SUCCESS )

}

else

{

  return( INVALID_TASK )

}

 

}

 

如,原来的tasksEvents[ZDAppTaskID] = 0x0000,即此任务下无事件可以处理,当我调用

osal_set_event(uint8 ZDAppTaskID , ZDO_NETWORK_INIT ) tasksEvents[ZDAppTaskID] = 0x0001,这样当进入轮询时就会调用事件处理函数处理该事件。

 

 

知道了任务下的事件是如何被设置、处理的,我们知道任务ID是将事件表和事件处理函数联系起来的关键,那么任务本身是怎样被设置的呢?

添加任务的关键是

新任务的初始化函数

新任务的事件处理函数

如要添加任务GenericApp则其初始化函数GenericApp_Init( taskID ),事件处理函数GenericApp_ProcessEvent应该分别放在osalInitTasks函数、事件处理函数表tasksArr[]的最后,而tasksEvent[]的大小会随着tasksArr[]的大小一起变化,即当在tasksArr[]最后一位加入GenericApp_ProcessEvent时,tasksEvent[]也随着增加一个元素用来安放对应的事件。

       为了方便看出原理,将taskArr[]、osalInitTasks()简化处理得到

zigbee协议栈 任务、事件与轮询机制

值得一提的是,taskArr[]是先于tasksEvents[]声明定义的,所以可以根据taskArr[]的长度来设置tasksEvents[]的长度与任务相匹配。

由osalInitTask(),任务ID号从0开始,按先后顺序分别分配给各个任务,所以这个ID号与数组的索引也是相匹配的,设备必须的任务优先,从上图看到,用户自定义的任务ID应该是8。正是借助这个ID号将任务事件表中的事件元素和事件处理函数表中的事件处理函数对应了起来。

zigbee协议栈 任务、事件与轮询机制

                                                                                                  2018年1月22日星期一

                                                                                                         武汉大学青楼

本文参考自:《ZigBee无线传感器网络设计与实现》      王小强等人编著化学工业出版社

                                                                                                  

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/140472.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 四叉树空间索引原理及其实现

    四叉树空间索引原理及其实现四叉树索引的基本思想是将地理空间递归划分为不同层次的树结构 它将已知范围的空间等分成四个相等的子空间 如此递归下去 直至树的层次达到一定深度或者满足某种要求后停止分割 四叉树的结构比较简单 并且当空间数据对象分布比较均匀时 具有比较高的空间数据插入和查询效率 因此四叉树是 GIS 中常用的空间索引之一 常规四叉树的结构如图所示 地理空间对象都存储在叶子节点上 中间节点以及根节点不存储地理空间对象

    2026年1月31日
    0
  • 图解原型和原型链

    图解原型和原型链前言知识 结论 原型 把一个函数称为类的话 原型就是每个类都有的属性 作用 给这类添加属性 分为两类 显式原型 prototype 和隐式原型 proto 所有函数对象 Function 都有显式原型 所有对象 如 Object Function Array 等对象 都有隐式原型原型链 本质是链表 proto 相当于链表的指针 指向下一个对象 每个对象都有 proto 属性 它指向构造函数的 prototype 属性 构造函数的 prototype 属性的 proto 属性

    2025年9月27日
    3
  • 前向传播算法(Forward propagation)与反向传播算法(Back propagation)「建议收藏」

    前向传播算法(Forward propagation)与反向传播算法(Back propagation)「建议收藏」虽然学深度学习有一段时间了,但是对于一些算法的具体实现还是模糊不清,用了很久也不是很了解。因此特意先对深度学习中的相关基础概念做一下总结。先看看前向传播算法(Forwardpropagation)与反向传播算法(Backpropagation)。1.前向传播如图所示,这里讲得已经很清楚了,前向传播的思想比较简单。举个例子,假设上一层结点i,j,k,…等一些结点与本层的结点w有连接,那么结点

    2022年4月28日
    48
  • MAX31865和PT100 PT1000的小白避坑攻略

    MAX31865和PT100 PT1000的小白避坑攻略关于例程和资料已上传,大家可以免费下载:https://download.csdn.net/download/winux123/12106121百度下载链接:https://pan.baidu.com/s/1gU_k_XRmGoJ4MORQoysuEQPT100/PT1000两线硬件接法:PT100/PT1000两线程序修改两处:PT100/PT1000三线硬…

    2022年6月15日
    26
  • Stata字符型与数值型数据的转换——上市公司证券代码字符型与数值型的互换

    Stata字符型与数值型数据的转换——上市公司证券代码字符型与数值型的互换实现效果假设 000001 000012 000123 002345 为某些上市公司证券代码 上市公司实证研究中 常常需要用到上市公司证券代码 将字符型转为数值型比较容易 直接用 destring 命令即可实现 但是数值型转为字符型并且补齐 6 位数的命令比较少见 字符型转为数值型 destringStkc replace 数值型转为字符型 tostringStkc

    2025年9月19日
    3
  • SPI接口调试

    SPI接口调试以MCP2515芯片的SPI接口为例说明。

    2022年6月18日
    31

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号