ZigBee学习之37——osalInitTasks()分析
- 格式:doc
- 大小:45.00 KB
- 文档页数:7
本文来自飞比论坛,作者:xingqing帖子地址:/bbs/viewthread.php?tid=393膜拜大作,虚心学习。
看了outm an的关于Rem oTI的教程,受益匪浅,然后找了下以前学习所做的笔记,共享出来,大家共同学习,如果有哪里理解错误请大家给我指出来:Rem oTI遥控板子上按键的发射程序,整个流程及其相对应的程序我都贴上了,好了,首先上个图:很不幸的是,从刚开始分析我就没留意这个图,造成我花了很长时间来想这个按键的流程,看他的流程,已经很明白了:开始(start),所有的列配置为输出,所有的行配置为中断使能输入,这里很简单就是个矩阵键盘,51中是用查询法来检测按键,这里只不过用的是中断方式来触发而已,然后调用消除抖动定时器(start key de-bounce tim er),当定时器溢出的时候,开始扫描按键,如果没有检测到按键,那么有返回到开始之下,重新来,如果有按键被检测到,这时候启动轮询定时器,启动这个定时器的原因是为了能够连发,如果你按下这个键一直不放那么就是靠这个定时器,来实现连发的,连发的时间间隔这个值我们是可以改动的,如果这个时候又检测不到按键了,那么又回到开始之下,当你再次按键的时候是靠中断来触发的。
有很多人觉得读取按键还有无线发送按键命令是在端口0的中断处理函数中进行的,其实不是这样的,中断中只是给这个任务添加了一个定时器而已这里先插一句:分析程序的时候我们要忽略次要的,看主要的。
以上是整个按键的流程了,下面分析下具体的程序:凡是要找个开始,不用说肯定先看主函数,:int m ain(void){/* Initialize hardware 初始化硬件,选择时钟,还有灯的方向选择*/HAL_BOARD_INIT();/* Initialze the HAL driver 初始化板上的硬件*/HalDriverInit();/* Initialize NV system 初始化nv系统????*/osal_nv_init(NULL);/* Initialize MAC 被封掉了 */MAC_InitRf4ce();/* Initialize the operating system 初始化操作系统*/osal_init_system();/* Enable interrupts 使能总中断*/HAL_ENABLE_INTERRUPTS();/* Setup Keyboard callback 这里是选择了按键的检测方式*///最重要的是指向了按键回调函数HalKeyConfig(RSA_KEY_INT_ENABLED, RSA_KeyCbac k);/* Start OSAL 进入操作系统,不能返回 */osal_start_system(); // No Return from herereturn 0;}以上我已经做了注释了,这个主函数很简单,下面看看具体跟按键相关的程序:函数HalDriverInit中,有这么一句/* KEY */#if (defined HAL_KEY) && (HAL_KEY == TRUE)HalKeyInit();#endif我们再看看这个HalKeyInit();函数到底做了什么,void HalKeyInit( void ){#if (HAL_KEY == TRUE)/* Initialize previous key to 0 初始化先前的键值为0*/halKeySavedKeys = HAL_KEY_CODE_NOKEY;//列端口1除了P1_1没有使用之外其他都用到了,设置为普通的IO,方向输出HAL_KEY_COL_SEL &= (uint8) ~HAL_KEY_COL_BITS; // set pin function to GPIOHAL_KEY_COL_DIR |= HAL_KEY_COL_BITS; // set pin direction to output//以下几句是在选择他的触发方式,上升沿触发还是下降沿触发,如果学过单片机的同学//这个应该不是问题if (HAL_KEY_ROW_PULLDOWN)//如果是下拉的话,进来设置所有的引脚为高电平{HAL_KEY_COL_PORT |= HAL_KEY_COL_BITS; // output high on all colum ns}else//如果是上拉,进来设置所有的引脚为低电平{HAL_KEY_COL_PORT &= (uint8) ~HAL_KEY_COL_BITS; // output low on all colum ns }//行端口0设置为普通的IO(所有端口都用到了),方向输入HAL_KEY_ROW_SEL &= ~(HAL_KEY_ROW_BITS); // set pin function to GPIOHAL_KEY_ROW_DIR &= ~(HAL_KEY_ROW_BITS); // set pin direction to input// pull down setup if necessary 如果有必要的话设置为下拉if (HAL_KEY_ROW_PULLDOWN){HAL_KEY_ROW_INP |= HAL_KEY_ROW_PDUPBIT;//设置端口0为下拉}//以下三句程序现在不用管他是做什么用的,后面我们会看到他的作用,/* Initialize callback function 这个要好好关注下,开始的时候这个回调时空的 */pHalKeyProcessFunction = NULL;//这里初始化回调函数是空的/* Start with key is not configured */HalKeyConfigured = FALSE;//起始的时候键值没有被配置halKeyTim erRunning = FALSE;#endif /* HAL_KEY */}上面这个函数只是对按键的初始化,注意这里初始化只是将按键配置成了:普通的IO口,列为输出,行为输入,并没有让行的中断使能,再就是对回调函数的清空,这个回调是重点,要注意!!!!在osal_init_system这个函数中,osalInitTasks这个函数,具体程序如下:void osalInitTasks( void ){uint8 taskID = 0;//注意这里任务ID是从0开始的/*这里返回的是分配内存空间的首地址,注意这个首地址的上一个地址就是他的内存控制头,关于他内存的分配可以看看我的下一教程*/tasksEvents = (uint16 *)osal_m em_alloc( sizeof( uint16 ) * tasksCnt);//分配了足够的空间osal_m em set( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));//全部初始化为0m acTaskInit( taskID++ ); //添加m ac层的任务被封掉了RCN_Init( taskID++ ); //添加RCN的任务RCN:RF4CE network layer 被封掉了RTI_Init( taskID++ ); //添加RTI的任务R TI:Rem oTI appli cation fram eworkRSA_Init( taskID++ ); //添加RSA的任务Hal_Init( taskID ); //添加硬件抽象层的任务,这里就是对按键处理任务的添加}还有一个数组要十分注意的是:// The order in this table m ust be identi cal to the task initialization calls below in osalInitTask.//这里的顺序很重要,要和osalInitTasks这里添加任务的顺序一样const pTaskEventHandlerFn tasksArr[] ={m acEventLoop,RCN_ProcessEvent,RTI_ProcessEvent,RSA_ProcessEvent,Hal_ProcessEvent};这个数组的顺序和上面的taskID要对应上,也就是说他给每个处理函数排了个队,并且给他们编了个号,而且有个标志位,这个标志位很重要,标志位是靠下层触发的(这句话现在看有点不明白,看到最后的时候你会明白),究竟怎么触发的我们先不用管,还是举个例子吧,比如无线接收到数据了,你要从下层传到上层,最后就是靠下层触发这些标志位,这样上层才知道有事情发生了,通过taskID 找到你相应的函数,这样比较简单,系统在轮询事件的时候是按照这个顺序来走了,你的taskID和相应的程序对不上号那不就乱了吗,简单说下,以后详再说。
OSAL你想知道的都在这里近日,21ic论坛TI无线连接论坛板块分享了一张OSAL调度机制的图,图片如下OSAL调度机制是何方神圣?OSAL为Operating System Abstraction Layer,即操作系统抽象层,支持多任务运行,它并不是一个传统意义上的操作系统,但是实现了部分类似操作系统的功能。
OSAL概念是由TI公司在ZIGBEE协议栈引入,他的意思是”模拟操作系统”,此OS,并非一个真正的OS,而是模拟OS的一些方法为广大编程者提供一种写MCU程序的方法.当有一个事件发生的时候,OSAL负责将此事件分配给能够处理此事件的任务,然后此任务判断事件的类型,调用相应的事件处理程序进行处理。
现有的嵌入式操作系统可以分为两类,即通用的多任务操作系统(General—purpose Multi-tasking OS)和事件驱动的操作系统(Event-driven OS)。
前者能够很好地支持多任务或者多线程,但是会随着内部任务切换频率的增加而产生很大的开销,这类操作系统有:uC /OS-II、嵌入式Linux、WinCE等。
后者支持数据流的高效并发,并且考虑了系统的低功耗要求,在功耗、运行开销等方面具有优势。
典型的代表如TinyOSl291。
目前TinyOS操作系统支持的平台有ATMEL公司的A VR系列、TI公司的MSP430系列。
由于TinyOS操作系统还没有对Chipcon公司(才知道TI把它收购了)提供CC2430开发平台提供支持,因此,要在CC2430开发平台上使用TinyOS系统来开发Zigbee协议栈软件,就必须首先对TinyOS进行移植。
因此Chipcon公司为自己设计的ZStack协议栈中提供了一个名为操作系统抽象层OSAL 的协议栈调度程序。
Osal主要提供如下功能:任务注册、任务间同步互斥、中断处理存储器分配和管理、提供定时器功能。
ZIGBEE技术规范与协议栈分析篇一:ZigBee知识无线龙1.协议栈工作流程和无线收发控制 LED 实验内容:1. ZigBee 协议栈简介2. 如何使用 ZigBee 协议栈3. ZigBee 协议栈的安装、编译与下载4. 协议栈无线收发控制 LED5. 协议栈工作流程实现现象:协调器、终端上电,组网成功后 D1 灯闪烁 1. ZigBee 协议栈简介什么是 ZigBee 协议栈呢?它和 ZigBee 协议有什么关系呢?协议是一系列的通信标准,通信双方需要共同按照这一标准进行正常的数据发射和接收。
协议栈是协议的具体实现形式,通俗点来理解就是协议栈是协议和用户之间的一个接口,开发人员通过使用协议栈来使用这个协议的,进而实现无线数据收发。
图 1 展示了 ZigBee 无线网络协议层的架构图。
ZigBee 的协议分为两部分,IEEE 802.15.4 定义了 PHY(物理层)和 MAC(介质访问层)技术规范;ZigBee联盟定义了NWK(网络层)、APS(应用程序支持子层)、APL(应用层)技术规范。
ZigBee协议栈就是将各个层定义的协议都集合在一直,以函数的形式实现,并给用户提供 API(应用层),用户可以直接调用。
图 1 ZigBee 无线网络协议层 2. 如何使用 ZigBee 协议栈协议栈是协议的实现,可以理解为代码,函数库,供上层应用调用,协议较底下的层与应用是相互独立的。
商业化的协议栈就是给你写好了底层的代码,符合协议标准,提供给你一个功能模块给你调用。
你需要关心的就是你的应用逻辑,数据从哪里到哪里,怎么存储,处理;还有系统里的设备之间的通信顺序什么的,当你的应用需要数据通信时,调用组网函数给你组建你想要的网络;当你想从一个设备发数据到另一个设备时,调用无线数据发送函数;当然,接收端就调用接收函数;当你的设备没事干的时候,你就调用睡眠函数;要干活的时候就调用唤醒函数。
所以当你做具体应用时,不需要关心协议栈是怎么写的,里面的每条代码是什么意思。
Zstack中如何实现自己的任务在Zstack(TI的Zigbee协议栈)中,对于每个用户自己新建立的任务通常需要两个相关的处理函数,包括:(1).用于初始化的函数,如:SampleApp_Init(), 这个函数是在osalInitTasks()这个osal(Zstack中自带的小操作系统)中去调用的,其目的就是把一些用户自己写的任务中的一些变量,网络模式,网络终端类型等进行初始化;(2).用于引起该任务状态变化的事件发生后所需要执行的事件处理函数,如:SampleApp_ProcessEvent(),这个函数是首先在const pTaskEventHandlerFn tasksArr[ ] 中进行设置(绑定),然后在osalInitTasks()中如果发生事件进行调用绑定的事件处理函数.下面分3个部分分析.1.用户自己设计的任务代码在Zstack中的调用过程(1).main() 执行(在ZMain.c中)main() ---> osal_init_system()(2). osal_init_system()调用osalInitTasks(), (在OSAL.c中)osal_init_system() ---> osalInitTasks()(3). osalInitTasks()调用SampleApp_Init() , (在OSAL_SampleApp.c中)osalInitTasks() ---> SampleApp_Init()在osalInitTasks()中实现了多个任务初始化的设置,其中macTaskInit( taskID++ )到ZDApp_Init( taskID++ )的几行代码表示对于几个系统运行初始化任务的调用,而用户自己实现的SampleApp_Init()在最后,这里taskID随着任务的增加也随之递增.所以用户自己实现的任务的初始化操作应该在osalInitTasks()中增加.void osalInitTasks( void ){uint8 taskID = 0;//这里很重要, 调用osal_mem_alloc()为当前OSAL中的各任务分配存储空间(实际上是一个任务数组),并用tasksEvents指向该任务数组(任务队列).tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt)); //将taskSEvents所指向的空间清零macTaskInit( taskID++ );nwk_init( taskID++ );Hal_Init( taskID++ );#if defined( MT_TASK )MT_TaskInit( taskID++ );#endifAPS_Init( taskID++ );ZDApp_Init( taskID++ );SampleApp_Init( taskID ); //用户自己需要添加的任务}2.任务处理调用的重要数据结构这里要解释一下,在Zstack里,对于同一个任务可能有多种事件发生,那么需要执行不同的事件处理,为了方便,对于每个任务的事件处理函数都统一在一个事件处理函数中实现,然后根据任务的ID号(task_id)和该任务的具体事件(events)调用某个任务的事件处理函数,进入了该任务的事件处理函数之后,再根据events再来判别是该任务的哪一种事件发生,进而执行相应的事件处理.pTaskEventHandlerFn 是一个指向函数(事件处理函数)的指针,这里实现的每一个数组元素各对应于一个任务的事件处理函数,比如SampleApp_ProcessEvent对于用户自行实现的事件处理函数uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ),所以这里如果我们实现了一个任务,还需要把实现的该任务的事件处理函数在这里添加.const pTaskEventHandlerFn tasksArr[] = {macEventLoop,nwk_event_loop,Hal_ProcessEvent,#if defined( MT_TASK ) //一个MT任务命令MT_ProcessEvent,#endifAPS_event_loop,ZDApp_event_loop,SampleApp_ProcessEvent};注意, tasksEvents和tasksArr[]里的顺序是一一对应的, tasksArr[]中的第i个事件处理函数对应于tasksEvents中的第i个任务的事件.//计算出任务的数量const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );uint16 *tasksEvents;3. 对于不同事件发生后的任务处理函数的调用osal_start_system() 很重要,决定了当某个任务的事件发生后调用对应的事件处理函数void osal_start_system(void){#if !defined ( ZBIT )for(;;) // Forever Loop#endif{uint8 idx = 0;Hal_ProcessPoll(); // This replaces MT_SerialPoll() and osal_check_timer().//这里是轮询任务队列,并检查是否有某个任务的事件发生do {if (tasksEvents[idx]) // Task is highest priority that is ready.//序号低的优先级高{break;}} while (++idx < tasksCnt);if (idx < tasksCnt){uint16 events;halIntState_t intState;HAL_ENTER_CRITICAL_SECTION(intState); //进入临界区,来提取出需要处理的任务中的事件events = tasksEvents[idx]; //处理该idx的任务事件, 是第idx个任务的事件发生了tasksEvents[idx] = 0; // Clear the Events for this task.HAL_EXIT_CRITICAL_SECTION(intState); //退出临界区,保存尚未处理的事件//对应调用第idx个任务的事件处理函数,用events说明是什么事件events = (tasksArr[idx])( idx, events );//当没有处理完,把返回的events继续放到tasksEvents[idx]当中HAL_ENTER_CRITICAL_SECTION(intState);tasksEvents[idx] |= events; // Add back unprocessed events to the current task.HAL_EXIT_CRITICAL_SECTION(intState);}#if defined( POWER_SA VING )else // Complete pass through all task events with no activity?{osal_pwrmgr_powerconserve(); // Put the processor/system into sleep}#endif}}//临界区资源管理先看一个临界区代码保护的例子:HAL_ENTER_CRITICAL_SECTION(intState);events = activeTask->events;activeTask->events = 0; //清楚任务的事件HAL_EXIT_CRITICAL_SECTION(intState);其中:中断宏定义如下#define HAL_ENABLE_INTERRUPTS() st( EA = 1; )#define HAL_DISABLE_INTERRUPTS() st( EA = 0; )#define HAL_INTERRUPTS_ARE_ENABLED() (EA)typedef unsigned char halIntState_t;#define HAL_ENTER_CRITICAL_SECTION(x) st( x =EA; HAL_DISABLE_INTERRUPTS(); )#define HAL_EXIT_CRITICAL_SECTION(x) st( EA = x; )#define HAL_CRITICAL_STATEMENT(x) st( halIntState_t s;HAL_ENTER_CRITICAL_SECTION(s); x; HAL_EXIT_CRITICAL_SECTION(s); )以及相关的st宏:#define st(x) do { x } while (__LINE__ == -1)(1)cc2430芯片中的中断使能的特殊功能寄存器(SFRs):IEN0,IEN1和IEN2,(见cc2430 datasheet: P49)。
Zigbee学习过程1前言学习zigbee之前需要熟悉掌握C语言的高级编程,而不是简单的普通的C语言编程。
做zigbee开发,需要相当熟悉掌握c语言,并且有c语言大程序的制作经验,不然非得吃不少苦头不可。
我本身并没有大程序的开发经验,导致我费了很大力气,一方面去补习c语言,一方面慢慢适应zigbee的开发,结果导致周博士主掌zigbee程序开发,我只负责开发C4驱动……。
不服气也得看自己实力。
至于C51是否够用,答案是肯定的也是否定的。
在刚开始接触zigbee时,C51是完全够用的,因为TI 最初的开发板C2530就是基于C51的,所以够用;但是如果涉及到深层次的zigbee开发,就要上升到ARM 的级别,因此C51就不够使了。
2轮询机制Osal.c文件里有个osal-run-system函数,这个函数就是负责轮询的,通过轮询查找到要处理的函数和事件,继而传到上层去处理。
上层处理函数是通过events = (tasksArr[idx])( idx, events )这个程序调用的,tasksArr[idx]存储的是事件处理函数,如下图。
以上图中的Sample_ProcessEvents函数为例,从参数中就可以发现这个参数和events = (tasksArr[idx])( idx, events )的参数恰恰一一对应。
3登记任务有了轮询函数,还是不够的,咱们还需要一个登记任务的机制,不然有事件发生,但是没有登记任务,那么该事件依然不会被处理……。
在OSAL_SampleAPP.c文件里有下面这个函数。
这个函数就是用来登记任务的。
这个任务在前面的轮询机制提到过。
轮询其实就是查任务,看是否有任务要求执行。
一旦有任务执行且没有其它执行的任务,则找到任务相应函数进行相应的处理。
3.1任务事件任务事件要用到一个函数,osal_set_event,如下面截图所述,这个函数主要是为某个任务设臵一个事件,比如说串口事件,或者按键事件。
ZigBee 学习之1515——————ZStackZStack API 解读3应用框架(AF)应用框架层是应用道APS 层的OTA 数据接口。
此层也接收数据消息的终端多路复用器。
AF 为应用提供以下功能:•终端(Endpoint)管理•发送和接收数据哈哈,这里的函数应该就是我们经常要用到的函数了。
终端管理每个设备都是Zigbee 中的节点,每个节点有长地址和短地址,短地址被其他设备用来发送数据。
每个节点又241个终端(0保留,1-240可分配给应用)。
每个终端可以独立设置地址;当设备发送数据时必须指定目标设备的短地址和接收终端。
一个应用必须注册一个或多个终端用来接收或者发送数据。
简单描述符-SimpleDescriptionFormat_t每个终端都必须有一个Zigbee 简单描述。
这些描述对Zigbee 网络刻画了这个终端,其他设备可以询问这个终端以知道这个设备的类型。
typedef struct{byte EndPoint;uint16AppProfId;uint16AppDeviceId;byte AppDevVer:4;byte Reserved:4;//AF_V1_SUPPORT uses for AppFlags:4.byte AppNumInClusters;cId_t *pAppInClusterList;byte AppNumOutClusters;cId_t *pAppOutClusterList;}SimpleDescriptionFormat_t;EndPoint –终端号:1-240这是节点的子地址,用来接收数据AppProfId –定义了这个终端上支持的Profile ID(剖面ID),ID 最好遵循由ZigBee 联盟的分配。
AppDeviceId –终端支持的设备ID,ID 最好遵循ZigBee 联盟的分配。
AppDevVer –此终端上设备执行的设备描述的版本:0x00为Version 1.0.Reserved –保留AppNumInClusters –终端支持的输入簇数目pAppInClusterList –指向输入Cluster ID 列表的指针AppNumOutClusters –终端支持的输出簇数目pAppOutClusterList –指向输出Cluster ID 列表的指针终端描述符-endPointDesc_t节点中的每一个终端都必须有一个终端描述符typedef struct{byte endPoint;byte *task_id;//Pointer to location of the Application task ID.SimpleDescriptionFormat_t*simpleDesc;afNetworkLatencyReq_t latencyReq;}endPointDesc_t;task_id-任务ID指针,当接收到消息时,此任务ID将指示消息传递目的。
OSAL(操作系统抽象层简介) (2010-11-19 20:00)分类:ZigBee 技术学习Z-Stack1.4.3及以后的版本中引入了一个OSAL(Operating System Abstraction Layer 操作系统抽象层),但在我们整个的ZigBee协议栈的结构图中,我并没有能够发现这个层在哪个位置。
但是整个的协议栈都要在OS的基础上才能运行。
OSAL和我们通常所说的RTOS,pc上的操作系统还是有很大的不同,ZigBee2006中只是利用了操作系统的概念和思想,利用OS把Z-Stack软件组件从特殊的处理过程相分离,并将软件成分保护了起来。
它提供了如下的管理功能:◆任务的注册、初始化、开始◆ 任务间的消息交换◆任务同步◆ 中断处理◆时间管理◆ 内存分配在ZigBee协议中,协议本身已经定义了大部分内容。
在基于ZigBee协议的应用开发中,用户只需要实现应用程序框架即可。
从ZigBee的协议架构图中我们也可以看到,其中的应用程序框架中包含了最多的240个应用程序对象,如果我们把一个应用程序对象看做为一个任务的话,那么应用程序框架将包含一个支持多任务的资源分配机制。
于是OSAL便有了存在的必要性,它正是Z-Stack为了实现这样一个机制而存在的。
OSAL主要是这样一种机制,一种任务分配资源的机制,从而形成了一个简单多任务的操作系统。
首先,osal初始化系统,包括软件系统初始化和资源初始化.其中软件系统初始化就是初始化一些变量,比如osal重要的组成部分任务表,任务结构体和序列号.资源初始化主要包括内存,中断,NV等各种设备模块资源.这就和我们嵌入式系统中的RTOS操作系统μC/OS-II有了很大的相似处。
μC/OS-II中也是通过建立任务把一个问题进行分解,任务之间可以通过消息队列的方式进行通信。
接着,osal通过osal_add_task添加任务到任务表中,形成一个任务链表.这个任务链表是以任务的优先级先后排序的.优先级高的排在前,低者排于后.最后,开始运行系统,系统是以一个死循环的形式工作的.在循环体当中不断地检测各个任务,看是否要运行等.这就相当于我们平时用的linux和window等多任务系统,把CPU分成N个时间片(有多少任务就分成多少时间片),只要处理频率高,就相当于多任务同时运行了总之,OSAL实现了类似操作系统的某些功能,但我认为并不能称之为真正意义上的操作系统。
要试验的:1串口查看数据2用自己的温度传感器看3串口通信,该代码,发送自己想发的数据。
如果修改数据长度看路由代码。
修改路由代码弄串口这边原理。
两头拼。
要解决的问题:1温度采集代码2zigbee传输代码3串口传输数据代码4endpoint与任务的关系。
与按键串口服务关系。
已经解决的问题:1开发的流程2如何添加新任务3osal工作原理,任务调度机制。
难点:几个关键回调函数的理解关键点(总体规划):1、数据采集,这部分的关键是I-WIRE协议的理解。
传感器与51的通信问题。
自己必须先把这部分搞定,现在要解决这个问题,即在TI提供的协议栈的基础上开发自己的应用。
要弄懂温度,湿度,PH值传感器的工作原理,一般采用I-WIRE总线的协议。
2、将数据传输到协调器(控制节点),这部分关键是zigbee通信协议,要充分利用TI的Z-STACK 协议栈来进行二次开发。
主要关注数据的收发模块,数据的格式、特点。
3、数据从协调器传到PC,这部分关键是串口通信协议。
正确的将数据传到上位机。
4、PC控制显示界面,这部分关键是找到关键的API,然后取出自己想要的数据显示。
如果需要存储数据,要操纵数据库,选择数据库。
解决这几个问题这个项目就算完成了。
现在猜想:一、开发一个新的应用应该做什么呢?1获取模板标识符,簇标识符,设备标识符的相关信息,我要进一步了解这两个关键的概念。
2在1基础上我要能注册application,taskID,endpoint,以建立自己应用与操作系统交互。
这是一个关键的点。
二、必须弄懂传感器采集的原理,代码。
三、必须弄懂串口的原理,代码。
关键的概念1、PAN标识符,PAN ID2、模板标识符,profileID3、簇标识符,clusterID 8bit4、节点,ieee地址(扩展地址)网络地址(短地址)64bit/16bit5、端点,endpoint 8bit6、设备标识符,Device Description 16bit7、应用任务ID,taskID8、属性Attribute 16bit9、Taskevents envents 16bit申请到模板标识符后,可以为模板定义设备描述符、簇标识符、服务类型(KVP或MSG)属性(Attribute)。
ZigBee学习之37——osalInitTasks()分析 此函数完成了任务ID的分配,以及所有任务的初始化,如果我们要加入自己的应用必须在这里加入自己的任务初始化函数,以使系统可以自动为任务分配一个ID号。如果是想要自己创建一个应用,那么必须首先实现两个东西,一是任务回调函数队列,另一个是系统任务初始化及ID分配。任务队列是一个pTaskEventHandlerFn结构的数组,只要把每个任务相关的回调函数按初始化队列中的顺序填到数组中就可以了,他们的实现一般是写在自己的应用源码里面。介绍一个例子(SimpleApp): const pTaskEventHandlerFn tasksArr[] = { macEventLoop, nwk_event_loop, Hal_ProcessEvent, #if defined( MT_TASK ) MT_ProcessEvent, #endif APS_event_loop, ZDApp_event_loop,
SAPI_ProcessEvent };
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++ ); ZDApp_Init( taskID++ ); SAPI_Init( taskID ); } 注意这里两个实体里面的顺序要一一对应! macTaskInit( taskID++ ),nwk_init( taskID++ ),APS_Init( taskID++ )定义在mac_api.h,nwk.h,APS.h中,但没有找到其实现,可能被打包在链接库文件里面了。 void Hal_Init( uint8 task_id ) { /*将系统分配的任务的ID传给变量*/ Hal_TaskID = task_id; } //开始器件的启动【ZDApp.c】 void ZDApp_Init( byte task_id ) { uint8 capabilities;
//保存系统分配的任务ID ZDAppTaskID = task_id;
//初始化ZDO全局的短地址,并配置成无效的一个地址 ZDAppNwkAddr.addrMode = Addr16Bit; ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR;
// ZDAppNwkAddr为一个地址数据结构:addrMode只有几种:
AddrNotPresent = 0, AddrGroup = 1, Addr16Bit = 2, Addr64Bit = 3, AddrBroadcast = 15 typedef struct { union { uint16 shortAddr; ZLongAddr_t extAddr; } addr; byte addrMode; } zAddrType_t; (void)NLME_GetExtAddr(); //这是个ZstackAPI,返回指向此器件64位地址的指针
//检查"Hold Auto Start",如果启动的时候按着SW1,此函数就会设置设备的状态为:DEV_HOLD ZDAppCheckForHoldKey();
//初始化ZDO物件,并且设置设备 ZDO_Init();
//【ZDObject.c】
void ZDO_Init( void ) { #if defined ( REFLECTOR ) //如果器件定义的是“反射器”则需要定义个路由的设备结构,REFLECTOR是一个编译选项,如果定义了这个编译选项则使用“源绑定”,源绑定的定义有这样一句话In the Zigbee 2006 release,the binding mechanism is implemented in all devices and is called source binding(在Zigbee2006中,绑定机制在所有的设备中实现,这就叫做源绑定),如果使用源绑定则绑表是存放在源设备中的,这样就不用为找绑定入口而先向协调器提交绑定请求,但是方面又增大了源节点的开销,因为要为保存绑定表而开辟一段静态内存。默认是不使用源绑定。
ZDO_EDBind = NULL;
#endif
//设置创建设备的类型 ZDODeviceSetup(); //根据设置的编译选项来调用不同的网络层管理函数 // ZDO_COORDINATOR:设备作为协调器 // SOFT_START:如果没有协调器则设备以协调器启动,否则以路由器启动 //这里面的几个函数都做成了静态库了看不到源码!看来TI的所谓开放也不是完全开放 static void ZDODeviceSetup( void )
{ #if defined( ZDO_COORDINATOR ) NLME_CoordinatorInit(); #endif
#if defined ( REFLECTOR ) #if defined ( ZDO_COORDINATOR ) APS_ReflectorInit( APS_REFLECTOR_PUBLIC ); #else APS_ReflectorInit( APS_REFLECTOR_PRIVATE ); #endif #endif
#if !defined( ZDO_COORDINATOR ) || defined( SOFT_START ) NLME_DeviceJoiningInit(); #endif } } // 向AF层注册endpoint(终端) afRegister( (endPointDesc_t *)&ZDApp_epDesc );
endPointDesc_t ZDApp_epDesc =
{ ZDO_EP, //0,ZDO为终端0 &ZDAppTaskID, //指向应用任务号 (SimpleDescriptionFormat_t *)NULL, // 终端的简单描述,No Simple description for ZDO (afNetworkLatencyReq_t)0 // No Network Latency req };
#if defined( ZDO_USERDESC_RESPONSE ) ZDApp_InitUserDesc(); #endif // ZDO_USERDESC_RESPONSE
//从NIB中读取属性 NLME_GetRequest(nwkCapabilityInfo, 0, &capabilities); //设置设备能处理的广播消息 NLME_SetBroadcastFilter( capabilities );
//根据设备状态启动设备,如果不是DEV_HOLD则启动ZDO设备,否则闪烁LED1 if ( devState != DEV_HOLD ) { ZDOInitDevice( 0 ); //启动网络中的设备,在这个函数中会调用ZDApp_NetworkInit()来启动网络,并发送ZDO_NETWORK_INIT事件,当ZDO事件循环ZDApp_event_loop()接收到这个事件后会执行ZDO_StartDevice()来启动设备 } else { // Blink LED to indicate HOLD_START HalLedBlink ( HAL_LED_4, 0, 50, 500 ); }
ZDApp_RegisterCBs(); //注册ZDO消息,只有注册了的消息才能以ZDO_CB_MSG消息的形式发送给指定的任务 } /* ZDO_Init() */
事件处理函数SAPI_ProcessEvent(),在任何地方只要调用osal_set_event()对这个处理函数对应的任务ID设置了事件的话就会启动这个事件处理函数。 系统消息事件SYS_EVENT_MSG包含几个子类的消息(子类可以在文档【Z-Stack Sample Applications (F8W-2006-0023).pdf】中找到),SYS_EVENT_MS由函数osal_msg_send()【OSAL.C】发送,osal_msg_send()首先将传过来的任务ID加入到消息包中,然后再把这个消息包通过osal_msg_enqueue加入到消息队列中,(osal_qHead是消息队列的头指针),然后我们再来查找调用osal_msg_send()的函数,这样我们就可以知道系统消息SYS_EVENT_MSG是在什么情况下如何发出的了。afDataConfirm()【AF.c】会调用osal_msg_send(),并且构造的消息中包含事件AF_DATA_CONFIRM_CMD(此事件说明由AF_DataRequest()初始化的数据已成功发送,若AF_DataRequest()的选项中要求有回应,则此事件表示目标已收到数据) void afDataConfirm( uint8 endPoint, uint8 transID, ZStatus_t status ) { endPointDesc_t *epDesc; afDataConfirm_t *msgPtr;
//每个终端都需要一个描述,如果没有则直接返回 epDesc = afFindEndPointDesc( endPoint ); if ( epDesc == NULL ) return;
„„„„„„ // Determine the incoming command type msgPtr = (afDataConfirm_t *)osal_msg_allocate( sizeof(afDataConfirm_t) ); if ( msgPtr ) { // Build the Data Confirm message msgPtr->hdr.event = AF_DATA_CONFIRM_CMD; //设置消息的事件为AF_DATA_CONFIRM_CMD msgPtr->hdr.status = status; msgPtr->endpoint = endPoint; msgPtr->transID = transID;