Z-Stack + OSAL操作系统

Zigbee协议栈与Zigbee协议

协议是一系列的通信标准,通信双方需要共同按照这一标准进行正常的数据发射和接收。协议栈是协议的具体实现形式,简单地说就是协议栈是协议和用户之间的一个接口。开发人员通过协议栈来使用这个协议。进而实现数据的收发。

Zigbee的体系结构由称为层的各模块组成。每一层为其上层提供特定的服务:即由数据服务实体提供数据传输服务;管理实体提供所有的其他管理服务。毎个服务实体懣过相应的服务接入点(SAP为其上层提供一个接口,每个服务接入点通过服务原语来完成所对应的功能, ZigBee协议的体系结构如下图所示:

Zigbee基本概念

设备类型(一个网络必须最少有一个协调器,多个路由器和多个终端设备)

  • Coordinator(协调器)

    负责启动整个网络,它也是网络的第一个设备。协调器选择一个信道和一个网络ID(也称之为PAN ID),随后启动整个网络,协调器也可以用来协助建立网络中安全层和应用层绑定(bingings)。

  • Router(路由器)

    允许其他设备加入网路,多眺路由和协助它自己的由电池供电的终端设备的通讯。

    通常,路由器希望一直处于活动状态,因此他必须使用主电源供电。但是当使用树状网络拓扑结构时,允许路由间隔一定的周期操作一次,这样就可以使用电磁给其供电。

  • End-Device(终端设备)

    终端设备没有特定的维持网络结构的责任,它可以睡眠或唤醒,终端设备对存储空间(特别是RAM的需要)比较小。

网络结构

上图是一个简单的Zigbee网络示意图,其中红色节点为Coordinator,黄色节点为Router,绿色节点为End-Device。

协议栈规范

协议栈规范由Zigbee联盟定义指定。在同一网络中的设备必须符同一个协议栈规范。

Zigbee联盟为Zigbee协议栈2007定义了两个规范:Zigbee个Zigbee PRO。所有的设备只要遵守了该规范,即使在不同的厂商买的不同的设备同样可以形成网络。

如果开发正改变了规范,它的设备只能在自己的产品中使用。不能与其他的产品通信,更改网络之后的规范称之为“特定网络”规范。

协议栈ID号可以通过查询设备发送的beacon帧获得。在设备加入网络之前,首先需要确认协议栈规范的ID,“特定网络”规范ID号为0;Zigbee协议栈规范的ID号为1;Zigbee PRO协议的ID号为2.协议栈规范的ID(STACK_PROFLLE_ID)在nwk_globals.h中定义:

// nwk_globals.h// Controls various stack parameter settings
#define NETWORK_SPECIFIC      0
#define HOME_CONTROLS         1
#define ZIGBEEPRO_PROFILE     2
#define GENERIC_STAR          3
#define GENERIC_TREE          4#if defined ( ZIGBEEPRO )#define STACK_PROFILE_ID      ZIGBEEPRO_PROFILE
#else#define STACK_PROFILE_ID      HOME_CONTROLS
#endif// 在fwConfig.cfg文件定义
/* Enable Zigbee-Pro */
-ZIGBEEPRO

拓扑结构

支持星状、树(簇)状和网状三种网络拓扑结构。

  • 星型拓扑

    最简单的一种拓扑形式,包含一个Coordinator(协调者)节点和一系列的End Device(终端)节点,每一个End Device节点只能和Coordinator节点进行通信。两个End Device节点之间通讯必须通过Coordintor节点进行信息的转发。

  • 树型拓扑

  • Mesh拓扑(网状拓扑)

在Z-Stack中网络拓扑结构定义如下:

// Controls the operational mode of network
#define NWK_MODE_STAR         0
#define NWK_MODE_TREE         1
#define NWK_MODE_MESH         2#if ( STACK_PROFILE_ID == ZIGBEEPRO_PROFILE )#define MAX_NODE_DEPTH      20#define NWK_MODE            NWK_MODE_MESH  // 网状网络#define SECURITY_MODE       SECURITY_COMMERCIAL#if   ( SECURE != 0  )#define USE_NWK_SECURITY    1   // true or false#define SECURITY_LEVEL      5#else#define USE_NWK_SECURITY    0   // true or false#define SECURITY_LEVEL      0#endif

信标与非信标模式

Zigbee网络的工作模式可分为信标(Beaeon)和非信标(Non-beaeon)两种模式。

  • 信标模式

    实现了我网络中所有设备的同步工作和同步休眠。以达到最大程度节省功耗。

    协调器负责以一定的时间间隔(一般在15ms-mins之间)想网络广播发送信标帧。

  • 非信标模式

    只允许终端设备进行周期性休眠,协调器和所有路由器设备必须长期处于工作状态。

    父节点为终端设备子节点焕春数据,终端设备主动向其父节点提取数据的机制。实现终端设备的周期性(周期可设置)休眠。

地址定义

Zigbee设备有两种类型的地址。一种是64位IEEE地址,即MAC地址,另一种是16位网络地址。

  • 64位地址

    全球唯一地址(MAC地址)由设备制造商设置,设备在生命周期一直拥有。

  • 16位网络地址

    设备加入网络后分配的,在网络中唯一。用来在网路哟中鉴别设备和发送数据。协调器的网络地址为0x0000。

地址分配机制,Zigbee 2007 PRo使用随机地址分配机制,对新加入的节点使用随机地址分配,为保证网络内地址分配不重复,使用其余的随机地址再进行分配。当一个节点加入时,将接收到父节点的的随机分配地址然后后产生”设备声明“(包含分配到的网络地址和IEEE地址)发送至网络中的其余节点。如果另一个节点有着同样的网络地址,则通过路由器广播“网络状态-地址冲突”至网络中的所有节点。所有发生网络地址冲突的节点更改自己的网络地址,然后再发起“设备声明”检测新的网络地址是否冲突。

终端设备不会广播“地址冲突”,他们的父节点会帮助完成。如果一个终端设备发生了”地址冲突“,他们的父节点发送”重新加入“消息至终端设设备,并要求他们更改网络地址,然后终端设备再发起”设备声明“检测新的网络地址是否冲突。

当接收到“设备声明”后,关联表和绑定表将被更新使用心得网络地址,但是路由表不会被更新。

在每个路由加如网络之前,寻址方案需要知道和配是一些参数。

  • MAX_DEPTH(最大网络深度)
  • MAX_ROUTERS(最多路由数)
  • MAX_CHILDREN(最多子节点数)
#if ( STACK_PROFILE_ID == ZIGBEEPRO_PROFILE )uint8 CskipRtrs[1] = {0};uint8 CskipChldrn[1] = {0};
  • Cm(nwkMaxChildren):每个父节点可以联接的子节点的总个数;
  • Rm(nwkMaxRouters):在Cm中可以连接的子节点的总个数;
  • Lm:最大网络深度,协调器深度为0;

这三个参数的值在Z-stack中分别由变量CskipChlarn、CskipRtrs、MAX_NODE_DEPTH决定。这三个变量可以在NWK中的nwk_globals.c和nwk_globals.h两个文件中找到。

寻址

为了向一个在ZigBee网络中的设备发送数据,应用程序通常使用AF_DataRequest()函数。数据包将要发送给一个afAddType_t(在AF.h中定义)类型的目标设备。

typedef struct
{union{uint16      shortAddr;ZLongAddr_t extAddr;} addr;afAddrMode_t addrMode;uint8 endPoint;uint16 panId;  // used for the INTER_PAN feature
} afAddrType_t;

注意,除了网络地址之外,还要制定地址模式参数。目的地址模式可以设置为以下几个值:

typedef enum
{afAddrNotPresent = AddrNotPresent,afAddr16Bit      = Addr16Bit,afAddr64Bit      = Addr64Bit,afAddrGroup      = AddrGroup,afAddrBroadcast  = AddrBroadcast
} afAddrMode_t;

因为在ZigBee网络中,数据包可以单点传送(unicast),多点传送(multicast)或则广播传送,所以必须有地址模式参数。一个单点传送数据包只发送给一个设备,多点传送数据包则要传送一组设备。二广播数据则要发送给网络中的所有节点。

  • 单点传送( Unicast)

    Dicast是标准寻址模式,它将数据包发送给一个已经知道网络地址的网络设备。将afAddrMode设置为Addr16Bit并且在数据包中携带目标设备地址。

  • 间接传送( ndirect)

    当应用程序不知道数据包的目标设备在哪里的时候使用的模式。将模式设为AddrNotPresent并且目标地址没有指定。取代它的是从发送设备的栈的绑定表中査找目标设备。这种特点称之为源绑定。当数据向下发送到达栈中,从绑定表中査找并且使用该目标地址。这样,数据包将被处理成为一个标准的单点传送数据包。如果在绑定表中找到多个设备,则向每个设备都发送一个数据包的拷贝。

  • 广播传送( broadcast)

    当应用程序需要将数据包发送给网络的每一个设备时,使用这种模式。地址模式设置为AddrBroadcast。目标地址可以设置为下面广播地址的种:NWK BROADCAST SHORTADDR DEVALL(0xFFF数据包将被传送到网络上的所有设备,包括睡眠中的设备。对于睡眠中的设备,数据包将被保留在其父亲节点直到査询到它,或者消息超时( NWK INDIRECT MSG TIMEOUT在f8 wConifg cfg中NWK BROADCAST SHORTADDR DEVRXON( OXFFFD)数据包将被传送到网络上的所有在空闲时打开接收的设备( RXONWHENIDLE),也就是说,除了睡眠中的所有设备。NWK BROADCAST SHORTADDR DEVZCZR(0xFFC)数据包发送给所有的路由器,包括协调器。

  • 组寻址( Group Addressing)

    当应用程序需要将数据包发送给网络上的一组设备时,使用该模式。地址模式设置为afAddr Group并且addr.shortAddr设置为组|D。在使用这个功能呢之前必须在网络中定义组。(参见Z- -stack Ap|文档中的 aps AddGroup0函数注意组可以用来关联间接寻址。再绑定表中找到的目标地址可能是是单点传送或者是一个组地址。另外,广播发送可以看做是一个组寻址的特例。下面的代码是一个设备怎样加入到个D为1的组当中:

    // Group Table Element
    typedef struct
    {uint16 ID;                       // Unique to this tableuint8  name[APS_GROUP_NAME_LEN]; // Human readable name of group
    } aps_Group_t;
    

设备

一个节点就是一个设备,对应一个无线单片机(CC2530);一个设备有一个射频终端,具有唯一的IEEE地址(64位)和网络地址(16位)在协议栈中不同的设备有相应的配置文件:

  • 协调器配置文件:f8wCoord.cfg
  • 路由器配置文件:f8wRouter.cfg
  • 终端设备配置文件:f8wEndev.cfg

重要设备地址

应用程序可能需要知道它的设备地址和父亲地址。使用下面的函数获取设备地址(在 ZStackAP中定义)

  • NLME GetshortAddr(返回本设备的16位网络地址
  • NLME GetExtAddr0—返回本设备的64位扩展地址
  • 使用下面的函数获取该设备的父亲设备的地址:
  • NLME GetCoordShortAddri0—返回本设备的父亲设备的16位网络地址
  • NLME GetCoord ExtAddrt0——返回本设备的父亲设备的64位扩展地址

如何使用ZigBee协议栈(一般步骤)

  1. 组网:调用协议栈的组网函数、加入网络函数,实现网络的建立与节点的加入。
  2. 发送:发送节点调用协议栈的无线数据发送函数,实现无线数据发送。
  3. 接收:接收节点调用协议栈的无线数据接收函数,实现无线数据接收
协议栈工作流程

自己添加的应用任务程序在Zstack中的调用过程:

main() → osal_init_system() → osalInitTask() → SampleApp_Init()

  • osal_init_system:初始化操作系统

      // Initialize the Memory Allocation Systemosal_mem_init();// Initialize the message queueosal_qHead = NULL;// Initialize the timersosalTimerInit();// Initialize the Power Management Systemosal_pwrmgr_init();  // Initialize the system tasks.osalInitTasks();    /*重要 任务初始化函数,我们只关心这个函数*/// Setup efficient search for the first free block of heap.osal_mem_kick();
    
  • osalInitTasks(); 任务初始化函数,

    void osalInitTasks( void )  // 初始化任务,在OSAL_SampleApp.c文件中
    {uint8 taskID = 0;// 为当前OSAL中各任务分配存储空间,tasksEvents指向任务数组tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);// 给分配的空间清0osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));// 任务优先级由高向低依次排列,高优先级对应的TaskID的值反而小macTaskInit( taskID++ ); // macTaskInit(0),初始化网络层任务 用户无需考虑nwk_init( taskID++ );Hal_Init( taskID++ );
    #if defined( MT_TASK )MT_TaskInit( taskID++ );
    #endifAPS_Init( taskID++ );
    #if defined ( ZIGBEE_FRAGMENTATION )APSF_Init( taskID++ );
    #endifZDApp_Init( taskID++ ); // ZDApp_Init(4) 用户需考虑
    #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )ZDNwkMgr_Init( taskID++ );// 初始化网络管理任务
    #endif// 用户创建的任务GenericApp_Init( taskID );  // SampleApp_Init_Init(5),用户需考虑 。重要!!!!!
    }
    

    任务初始化,就是为系统个任务分配纯初空间,在为各任务分配TaskID;这里的孙旭要注意。系统主循环函数里 tasksEvents[idx] 和 taskArr[idx] 的 idx 与这里taskUD是一一对应关系。

    指针数组tasksEvents[] 里面最终分别指向的是各任务存储空间,指针数组tasksArr[] 里面最终分别指向的是各任务事件处理韩数,这两个数组里面的各元素孙媳要一一对应,后面任务调会调用相应的事件处理函数。

    SampleApp_Init() 是我们应用协议栈例程的必要函数,用户通常在这里初始化自己的东西。

  • 轮询查询

    在OSAL_SampleApp.c文件中,taskArr[] 事件数组。

      // The order in this table must be identical to the task initialization calls below in osalInitTask.const pTaskEventHandlerFn tasksArr[] = {macEventLoop,nwk_event_loop,Hal_ProcessEvent,#if defined( MT_TASK )MT_ProcessEvent,#endifAPS_event_loop,#if defined ( ZIGBEE_FRAGMENTATION )APSF_ProcessEvent,#endifZDApp_event_loop,#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )ZDNwkMgr_event_loop,#endifSampleApp_ProcessEvent};
    

    Z-Stack中操作系统是基于优先级的轮转查询式操作系统,执行流程图如下:

  • osal_start_system() 运行操作系统

    osal_start_system() 最终调用 osal_run_system()

    void osal_run_system( void )
    {uint8 idx = 0;osalTimeUpdate(); // 扫描那个事件被触发了,然后设置相应的标志位Hal_ProcessPoll(); // 轮询TIMER 与 UARTdo {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];             // 提取本次需要处理的任务中的事件tasksEvents[idx] = 0;  // 清除本次任务的事件HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区activeTaskID = idx;events = (tasksArr[idx])( idx, events );  //通过指针调用任务处理函数,关键!!!!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 )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
    }
    

    events = tasksEvents[idx] 进入tasksEvents[idx] 数组定义,发现恰好是osalInitTasks() 函数里面分配空间初始化过的tasksEvents。而且taskID一一对应。这就是初始化与调用的关系。taskID把任务联系起来了。

  • SampleApp_Init() 用户应用任务初始化函数

    void SampleApp_Init( uint8 task_id )
    { SampleApp_TaskID = task_id;   //osal分配的任务ID随着用户添加任务的增多而改变SampleApp_NwkState = DEV_INIT;//设备状态设定为ZDO层中定义的初始化状态SampleApp_TransID = 0;        //消息发送ID(多消息时有顺序之分)// Device hardware initialization can be added here or in main() (Zmain.c).// If the hardware is application specific - add it here.// If the hardware is other parts of the device add it in main().#if defined ( BUILD_ALL_DEVICES )// The "Demo" target is setup to have BUILD_ALL_DEVICES and HOLD_AUTO_START// We are looking at a jumper (defined in SampleAppHw.c) to be jumpered// together - if they are - we will start up a coordinator. Otherwise,// the device will start as a router.if ( readCoordinatorJumper() )zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR;elsezgDeviceLogicalType = ZG_DEVICETYPE_ROUTER;
    #endif // BUILD_ALL_DEVICES//该段的意思是,如果设置了HOLD_AUTO_START宏定义,将会在启动芯片的时候会暂停启动
    //流程,只有外部触发以后才会启动芯片。其实就是需要一个按钮触发它的启动流程。
    #if defined ( HOLD_AUTO_START )// HOLD_AUTO_START is a compile option that will surpress ZDApp//  from starting the device and wait for the application to//  start the device.ZDOInitDevice(0);
    #endif// Setup for the periodic message's destination address 设置发送数据的方式和目的地址寻址模式// Broadcast to everyone 发送模式:广播发送SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//广播SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的网络地址为广播地址// Setup for the flash command's destination address - Group 1 组播发送SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; //组寻址SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;//组号0x0001// Fill out the endpoint description. 定义本设备用来通信的APS层端点描述符SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号SampleApp_epDesc.task_id = &SampleApp_TaskID;   //SampleApp 描述符的任务IDSampleApp_epDesc.simpleDesc= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp简单描述符SampleApp_epDesc.latencyReq = noLatencyReqs;    //延时策略// Register the endpoint description with the AFafRegister( &SampleApp_epDesc );    //向AF层登记描述符// Register for all key events - This app will handle all key eventsRegisterForKeys( SampleApp_TaskID ); // 登记所有的按键事件// By default, all devices start out in Group 1SampleApp_Group.ID = 0x0001;//组号osal_memcpy( SampleApp_Group.name, "Group 1", 7  );//设定组名aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );//把该组登记添加到APS中#if defined ( LCD_SUPPORTED )HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 ); //如果支持LCD,显示提示信息
    #endif
    }
    
  • SampleApp_ProcessEvent() 用户应用任务的事件处理函数

    //用户应用任务的事件处理函数
    uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
    {afIncomingMSGPacket_t *MSGpkt;(void)task_id;  // Intentionally unreferenced parameterif ( events & SYS_EVENT_MSG ) //接收系统消息再进行判断{//接收属于本应用任务SampleApp的消息,以SampleApp_TaskID标记MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );while ( MSGpkt ){switch ( MSGpkt->hdr.event ){// Received when a key is pressedcase KEY_CHANGE://按键事件SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );break;// Received when a messages is received (OTA) for this endpointcase AF_INCOMING_MSG_CMD://接收数据事件,调用函数AF_DataRequest()接收数据SampleApp_MessageMSGCB( MSGpkt );//调用回调函数对收到的数据进行处理break;// Received whenever the device changes state in the networkcase ZDO_STATE_CHANGE://只要网络状态发生改变,就通过ZDO_STATE_CHANGE事件通知所有的任务。//同时完成对协调器,路由器,终端的设置SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);//if ( (SampleApp_NwkState == DEV_ZB_COORD)//实验中协调器只接收数据所以取消发送事件if ( (SampleApp_NwkState == DEV_ROUTER) || (SampleApp_NwkState == DEV_END_DEVICE) ){// Start sending the periodic message in a regular interval.//这个定时器只是为发送周期信息开启的,设备启动初始化后从这里开始//触发第一个周期信息的发送,然后周而复始下去osal_start_timerEx( SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );}else{// Device is no longer in the network}break;default:break;}// Release the memory 事件处理完了,释放消息占用的内存osal_msg_deallocate( (uint8 *)MSGpkt );// Next - if one is available 指针指向下一个放在缓冲区的待处理的事件,//返回while ( MSGpkt )重新处理事件,直到缓冲区没有等待处理事件为止MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );}// return unprocessed events 返回未处理的事件return (events ^ SYS_EVENT_MSG);}// Send a message out - This event is generated by a timer//  (setup in SampleApp_Init()).if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ){// Send the periodic message 处理周期性事件,//利用SampleApp_SendPeriodicMessage()处理完当前的周期性事件,然后启动定时器//开启下一个周期性事情,这样一种循环下去,也即是上面说的周期性事件了,//可以做为传感器定时采集、上传任务SampleApp_SendPeriodicMessage();// Setup to send message again in normal period (+ a little jitter)osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT,(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );// return unprocessed events 返回未处理的事件return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);}// Discard unknown eventsreturn 0;
    }
    
  • 分析接收数据函数SampleApp_MessageMSGCB

    //接收数据,参数为接收到的数据
    void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
    {uint16 flashTime;byte buf[3]; switch ( pkt->clusterId ) //判断簇ID{case SAMPLEAPP_PERIODIC_CLUSTERID: //收到广播数据osal_memset(buf, 0 , 3);osal_memcpy(buf, pkt->cmd.Data, 2); //复制数据到缓冲区中if(buf[0]=='D' && buf[1]=='1')      //判断收到的数据是否为"D1"         {HalLedBlink(HAL_LED_1, 0, 50, 500);//如果是则Led1间隔500ms闪烁
    #if defined(ZDO_COORDINATOR) //协调器收到"D1"后,返回"D1"给终端,让终端Led1也闪烁SampleApp_SendPeriodicMessage();
    #endif}else{HalLedSet(HAL_LED_1, HAL_LED_MODE_ON);                   }break;case SAMPLEAPP_FLASH_CLUSTERID: //收到组播数据flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );break;}
    }
    
  • 分析发送周期信息 SampleApp_SendPeriodicMessage

    //分析发送周期信息
    void SampleApp_SendPeriodicMessage( void )
    {byte SendData[3]="D1";// 调用AF_DataRequest将数据无线广播出去if( AF_DataRequest( &SampleApp_Periodic_DstAddr,//发送目的地址+端点地址和传送模式&SampleApp_epDesc,//源(答复或确认)终端的描述(比如操作系统中任务ID等)源EPSAMPLEAPP_PERIODIC_CLUSTERID, //被Profile指定的有效的集群号2,       // 发送数据长度SendData,// 发送数据缓冲区&SampleApp_TransID,     // 任务ID号AF_DISCV_ROUTE,      // 有效位掩码的发送选项AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )  //传送跳数,通常设置为AF_DEFAULT_RADIUS{}else{HalLedSet(HAL_LED_1, HAL_LED_MODE_ON);// Error occurred in request to send.}
    }
    
  • AF_DataRequest 发送函数

    AF_DataRequest( &SampleApp_Periodic_DstAddr,//发送目的地址+端点地址和传送模式&SampleApp_epDesc,//源(答复或确认)终端的描述(比如操作系统中任务ID等)源EPSAMPLEAPP_PERIODIC_CLUSTERID, //被Profile指定的有效的集群号2,       // 发送数据长度SendData,// 发送数据缓冲区&SampleApp_TransID,     // 任务ID号AF_DISCV_ROUTE,      // 有效位掩码的发送选项AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )  //传送跳数,通常设置为AF_DEFAULT_RADIUS
    

    整体流程

Z-Stack + OSAL操作系统相关推荐

  1. 5、CC2541芯片中级教程-OSAL操作系统(PWM+看门狗)

    本文根据一周CC2541笔记汇总得来-- 适合概览和知识快速索引-- 全部链接: 中级教程-OSAL操作系统\OSAL操作系统-实验01 OSAL初探 [插入]SourceInsight-工程建立方法 ...

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

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

  3. 简述Z-Stack的基本工作原理与流程(OSAL操作系统)

    首先上图,跟着图中的函数顺序来感受Z-Stack的工作流程: Z-Stack协议栈总的来说做了两件事,系统的初始化和启动OSAL操作系统. 系统初始化:从main函数看,首先是调用了osal_init ...

  4. ZigBee学习之7——OSAL(操作系统抽象层)API解读

    根据Z-Stack1.4.3-1.2.0中OSAL API_F8W-2003-0002_.pdf文档翻译. Z-Stack1.4.3及以后的版本中引入了一个OS的概念,把应用层和堆栈层进行了分离,但是 ...

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

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

  6. zigbee之OSAL操作系统

    概述 OSAL(Operating System AbstractionLayer),即操作系统抽象层. OSAL是一种基于事件驱动的轮询式操作系统,所提供的管理功能有: (1)任务登记.任务初始化. ...

  7. 安装ubuntu系统操作系统详细流程、ubuntu管理包命令apt和dpkg命令详细说明、一键部署openstack环境、DBeaver下载驱动报错和登录提示RSA public key.. 解决方法

    文章目录 安装ubuntu操作系统 安装vmware,我这的版本是16 . 创建虚拟机 设置网络.[你没有啥特殊需求,忽略该步骤] 开启处理器虚拟化 开始安装ubuntu系统 配置sshd和修改roo ...

  8. Java里的堆(heap)栈(stack)和方法区(method)

    http://imiduo.iteye.com/blog/616310 Java里的堆(heap)栈(stack)和方法区(method)  <一> 基础数据类型直接在栈空间分配, 方法的 ...

  9. java中堆栈(stack)和堆(heap)

    http://www.ej38.com/showinfo/java-172156.html 堆栈是一种先进后出的数据结构,只能在一端进行输入或输出数据的操作  Stack类在java.util包中 向 ...

最新文章

  1. 正向最大匹配算法 python代码_中文分词算法之最大正向匹配算法(Python版)
  2. Linux 下安装 Android Studio
  3. Dubbo Cloud Native 之路的实践与思考
  4. Linux - 搭建FastDFS分布式文件系统
  5. 理解JavaScript的运行
  6. 2018-6-19bash编程之循环
  7. 用计算机画出方格表,方格造型图_怎么做这种颜色相间的方格图(有图)_彩妆阁...
  8. JavaWeb一些常用操作
  9. HDU 5934 2016CCPC杭州 B: Bomb(Trajan强连通)
  10. python 爬取 js渲染_scrapy 爬取 javascript 动态渲染页面
  11. 九宫格C语言递归程序,九宫格程序代码 共享并希望大家多提意见
  12. iOS 新浪微博-1.1框架升级
  13. 有趣的JS加密(一)AA加密颜文字加密
  14. win11电脑上如何设置微信双开(打开两个微信)
  15. 学计算机好还是学数学好,大学专业学计算机好还是学数学好
  16. LiveMedia视频平台是如何实现基于网页的语音对讲
  17. Android IOS平台AE动画库
  18. 浅学 web安全知识(好奇)
  19. 微信小程序 家校通 中小学家校联系电子作业系统
  20. 什么是相位,为什么会有相位?

热门文章

  1. 港科夜闻|香港科技大学与英国思克莱德大学深化研究合作
  2. 基于局部均方差的人脸磨皮matlab程序
  3. 兵法三十六计第三计-借刀杀人。
  4. 计算机启动不能马上联网,电脑开机慢不能联网
  5. 【算力网络】算力网络的技术创新——绿色与安全关键技术
  6. linux su -c 命令
  7. 谷歌浏览器倍速播放视频方法
  8. 用了 VS Code、IDEA 等十几款编辑器后,我总结出优秀编辑器的特质
  9. Web of Science如何导出参考文献
  10. ubuntu安装chrome浏览器64位