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无线传感器网络设计与实现》      王小强等人编著化学工业出版社

                                                                                                  

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • rpc接口测试方法_rpc服务接口测试

    rpc接口测试方法_rpc服务接口测试一、项目需求开发文档已明确要压测的接口是rpc接口,并给出了rpc接口和依赖,如下:RPC接口:<dependency><groupId>com.xueqiu.snowflake</groupId><artifactId>usercenter-client</artifactId><version>2.13.5</version></dependency>调用示例:List&

    2022年10月13日
  • MySQL数据库面试题(2020最新版)

    MySQL数据库面试题(2020最新版)文章目录数据库基础知识为什么要使用数据库什么是SQL?什么是MySQL?数据库三大范式是什么mysql有关权限的表都有哪几个MySQL的binlog有有几种录入格式?分别有什么区别?数据类型mysql有哪些数据类型引擎MySQL存储引擎MyISAM与InnoDB区别MyISAM索引与InnoDB索引的区别?InnoDB引擎的4大特性存储引擎选择索引什么是索引?索引有哪些优缺点?索引使用场景(重点)…

  • JavaScript匿名函数理解及应用[通俗易懂]

    JavaScript匿名函数理解及应用[通俗易懂]匿名函数匿名函数顾名思义就是没有名字的函数,在实际开发中经常会用到,也是JavaScript的重点。匿名函数又叫立即执行函数。由于这种函数是匿名的,所以它不能被调用。由于它不能被调用,所以如果它不立即执行的话就没有了意义。由于它需要立即执行,所以在执行完之后匿名函数就会被销毁。匿名自执行函数的作用就是用于闭包和创建独立的命名空间两个方面。匿名函数的基本形式为(function(){…})();前面的括号包含函数体,后面的括号就是给匿名函数传递参数然后立即执行。匿名函数的作用是避免全局变量

  • 一个程序员的蜕变(我是如何成为架构师的)

    一个程序员的蜕变(我是如何成为架构师的)

  • plsqldev8.0下载和注册码「建议收藏」

    plsqldev8.0下载和注册码「建议收藏」[b]关键词:PL/SQL,下载,plsqldev,注册码,plsqldev711,汉化文件[/b]PL/SQLDeveloper是一种集成的开发环境,专门用于开发、测试、调试和优化OraclePL/SQL存储程序单元,比如触发器等。PL/SQLDeveloper功能十分全面,大大缩短了程序员的开发周期。[url]http://www.kutoku.info/software…

  • Pytest(14)pytest.ini配置文件

    Pytest(14)pytest.ini配置文件前言pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行查看pytest.ini的配置选项pytest-h找到以下

发表回复

您的电子邮箱地址不会被公开。

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