Z-Stack + OSAL操作系统
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协议栈(一般步骤)
- 组网:调用协议栈的组网函数、加入网络函数,实现网络的建立与节点的加入。
- 发送:发送节点调用协议栈的无线数据发送函数,实现无线数据发送。
- 接收:接收节点调用协议栈的无线数据接收函数,实现无线数据接收
协议栈工作流程
自己添加的应用任务程序在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操作系统相关推荐
- 5、CC2541芯片中级教程-OSAL操作系统(PWM+看门狗)
本文根据一周CC2541笔记汇总得来-- 适合概览和知识快速索引-- 全部链接: 中级教程-OSAL操作系统\OSAL操作系统-实验01 OSAL初探 [插入]SourceInsight-工程建立方法 ...
- CC2540/CC2541/CC254x之OSAL操作系统抽象层
测试环境 协议栈版本:BLE-CC254x-1.4.0 开发环境IAR版本:IAR 8.20 硬件设备:CC2540/CC2541开发板 示例测试Demo工程:simpleBLEPeripheral工 ...
- 简述Z-Stack的基本工作原理与流程(OSAL操作系统)
首先上图,跟着图中的函数顺序来感受Z-Stack的工作流程: Z-Stack协议栈总的来说做了两件事,系统的初始化和启动OSAL操作系统. 系统初始化:从main函数看,首先是调用了osal_init ...
- ZigBee学习之7——OSAL(操作系统抽象层)API解读
根据Z-Stack1.4.3-1.2.0中OSAL API_F8W-2003-0002_.pdf文档翻译. Z-Stack1.4.3及以后的版本中引入了一个OS的概念,把应用层和堆栈层进行了分离,但是 ...
- OSAL操作系统实验学习笔记04
相信很多人接触了OSAL操作系统之后对它的任务资源分配机制还是很模糊,我细看了很多遍也还是略知一二,现在分享一篇我觉得写得特别好的文章. 深入浅出Z-Stack OSAL多任务资源分配机制 一 概述 ...
- zigbee之OSAL操作系统
概述 OSAL(Operating System AbstractionLayer),即操作系统抽象层. OSAL是一种基于事件驱动的轮询式操作系统,所提供的管理功能有: (1)任务登记.任务初始化. ...
- 安装ubuntu系统操作系统详细流程、ubuntu管理包命令apt和dpkg命令详细说明、一键部署openstack环境、DBeaver下载驱动报错和登录提示RSA public key.. 解决方法
文章目录 安装ubuntu操作系统 安装vmware,我这的版本是16 . 创建虚拟机 设置网络.[你没有啥特殊需求,忽略该步骤] 开启处理器虚拟化 开始安装ubuntu系统 配置sshd和修改roo ...
- Java里的堆(heap)栈(stack)和方法区(method)
http://imiduo.iteye.com/blog/616310 Java里的堆(heap)栈(stack)和方法区(method) <一> 基础数据类型直接在栈空间分配, 方法的 ...
- java中堆栈(stack)和堆(heap)
http://www.ej38.com/showinfo/java-172156.html 堆栈是一种先进后出的数据结构,只能在一端进行输入或输出数据的操作 Stack类在java.util包中 向 ...
最新文章
- 正向最大匹配算法 python代码_中文分词算法之最大正向匹配算法(Python版)
- Linux 下安装 Android Studio
- Dubbo Cloud Native 之路的实践与思考
- Linux - 搭建FastDFS分布式文件系统
- 理解JavaScript的运行
- 2018-6-19bash编程之循环
- 用计算机画出方格表,方格造型图_怎么做这种颜色相间的方格图(有图)_彩妆阁...
- JavaWeb一些常用操作
- HDU 5934 2016CCPC杭州 B: Bomb(Trajan强连通)
- python 爬取 js渲染_scrapy 爬取 javascript 动态渲染页面
- 九宫格C语言递归程序,九宫格程序代码 共享并希望大家多提意见
- iOS 新浪微博-1.1框架升级
- 有趣的JS加密(一)AA加密颜文字加密
- win11电脑上如何设置微信双开(打开两个微信)
- 学计算机好还是学数学好,大学专业学计算机好还是学数学好
- LiveMedia视频平台是如何实现基于网页的语音对讲
- Android IOS平台AE动画库
- 浅学 web安全知识(好奇)
- 微信小程序 家校通 中小学家校联系电子作业系统
- 什么是相位,为什么会有相位?