- /*第一步*/
- void process_init(void)
- {
- lastevent = PROCESS_EVENT_MAX;
- nevents = fevent = 0;
- process_current = process_list = NULL;
- }
- /*第二步*/
- void autostart_start(struct process * const processes[])
- {
- int i;
- for(i = 0; processes[i] != NULL; ++i) {
- process_start(processes[i], NULL);
- }
- }
- /*第三步*/
- void ctimer_init(void) /*ctimer是contiki里很重要的概念,具体说来就是一种定时器模型:Active timer,calls a function when it expires.*/
- {
- initialized = 0;
- list_init(ctimer_list);
- process_start(&ctimer_process, NULL);
- }
Z-stack的OSAL初始化过程也差不多,由于好久没搞cc2530,就不贴代码了。
完成平台和OS的初始化后,就是协议栈的初始化了,Zigbee协议栈的初始化是件挺复杂的工作。协议栈的初始化就是各个Layer的初始化,aps、af、zdo、nwk、mac。实际顺序如下:
- aps_init();
- nwk_init();
- mac_init();
- af_init();
- zdo_init();
在这些函数之前还有一些内存管理的工作要做。
/* 对nv_save_tbl[]的初始化。zigbee协议栈中的长地址、短地址、节点属性等重要数据需要保存下来, nv_save_tbl 用来保存这些数据。*/
- memset((void*)nv_save_tbl,0,sizeof(nv_save_tbl));
- nv_save_tbl[0].start_addr = (uint32_t)save_flag; /* static const uint8_t save_flag[] = "saved"; */
- nv_save_tbl[0].len = sizeof(save_flag);
- nv_save_tbl[item].start_addr = (uint32_t)&aib;
- nv_save_tbl[item].len = (uint16_t)(sizeof(aps_aib_t));
/* The memory pointer pool contains an array of all the memory pointers that
are available to the stack. Each memory pointer can contain one block
of memory allocated from the heap. Needless to say, if we run out of
memory pointers, then that is just as bad as running out of memory. */
- memset(mem_ptr_pool, 0, MAX_MEM_PTR_POOL * sizeof(mem_ptr_t));
/* Init the frame buffer pool . buf_pool[] is the frame buffer data structure that is used for TX and RX.
*/
- for (i=0; i<MAX_BUF_POOL_SIZE; i++)
- {
- memset(&buf_pool[i], 0, sizeof(buffer_t));
- }
现在我们接着来看aps_init()。APS(应用支持层)是应用层的核心,APS子层是负责上层应用程序对象与下层网络层的协调。其工作有:
维护Binding表,这是用来配对两网络节点间所需服务的对应表;
转发已配对设备间的信息;
处理64位IEEE地址与16位NWK地址间的对应。
APS层使用一个结构体来记录该层的信息:
- typedef struct
- {
- bool desig_coord; ///< Device is the designated coordinator for this network
- U16 desig_parent; ///< Address of parent we're forcing the device to join
- U32 chan_mask; ///< Channel mask for scans
- U64 use_ext_pan_id; ///< Use this extended PAN ID (0 means use extended address
- U8 dev_type;
- bool sleep; ///< If is end device this will use
- bool use_desig_pan; ///< pan id of net we're forcing the device to join
- U16 pan_id; ///< pan id
- bool use_desig_ch; ///< pan id of net we're forcing the device to join
- U8 channel; ///< pan id
- U64 ieee_addr; ///< IEEE address
- } aps_aib_t;
因此,APS层的初始化也就是对 aps_aib_t 结构体成员的初始化:
- void aps_init()
- {
- // init the aib
- memset(&aib, 0, sizeof(aps_aib_t));
- aib.aps_ctr =(U8) drvr_get_rand();
- aib.chan_mask = PhyChannelsSupported;
- aib.use_insec_join = true;
- aib.use_desig_parent = false;
- aib.desig_coord = false;
- aib.ieee_addr = 0xFFFFFFFFFFFFFFFF;
- aib.pan_id = 0xFFFF;
- aib.use_desig_ch = false;
- aib.channel = MAC_PHY_CHANNEL_OFFSET;
- aib.sleep = false;
- aib.dev_type = NWK_ROUTER;
- aib.traxn_persist_time = ZIGBEE_POLL_INTERVAL+2;
- aps_retry_init(); // Init the retry list.
- aps_dupe_init(); // Init the dupe table.
- }
What is the retry list ?
Implement a retry queue to handle the APS retries on outgoing frames that have their ACK request flag set. The retry queue is just for reliable transfers that require an APS level ACK. The frame is buffered in the retry queue and if an ACK does not come back within the specified time, the frame will be re-sent from the retry queue. The APS retry queue will be removed later and the application will perform the retry. Since we are at such a high level, there is not much savings to buffer the frame here, versus rebuilding it. It would be more efficient to save the RAM that the APS retry queue takes up and just have the endpoint profiles handle the retry. It is actually easier since most application implementations won't increment the app's data pointer until a SUCCESS confirmation is received. Hence if no SUCCESS confirmation is received, that would be the signal to re-send the data .
And what is the dupe table ?
Implement the APS duplicate rejection table. The dupe table checks for duplicate data received by the ACK layer. It may be possible that an APS ACK wasn't sent out in time from a received frame, so the frame is retried by the remote node. Hence, we will get two identical frames. This table is used to check if we receive a duplicate, and if so, discard the dupe frame.
nwk_init() 和 aps_init()类似,也是对一些结构体成员初始化。由于 nwk layer较复杂,需要存储NKW相关的许多配置信息,如节点类型、网络深度、最大孩子数、最大路由数等 ;还需要使用控制块(Protocol Block,类似于Linux的进程控制块的概念)来处理NWK层的广播、网络发现、网络管理、加入网络等,此外还要初始化NWK Layer中的邻居表(neighbor table)、路由表(route table)、路由发现表(route discovery table)、待定表(pending list)。 所以会涉及到很多变量。
- /*******************************************************************
- NWK Information Base. Holds all the configuration information for the NWK layer.
- *******************************************************************/
- typedef struct _nwk_nib_t
- {
- U8 seq_num; ///< Current sequence number
- U8 max_children; ///< Max children this device will support (used to determine tree addr allocation)
- U8 max_depth; ///< Max network depth this device will support (used to determine tree addr allocation)
- U8 max_routers; ///< Max routers this device will support (used to determine tree addr allocation)
- bool report_const_cost; ///< Use constant path cost for all links
- bool sym_link; ///< Enable symmetric links
- U8 capability_info; ///< Capability info for this device
- U8 traxn_persist_time; ///< Length of time before an indirect transaction is expired
- U16 short_addr; ///< Network address for this device
- U8 stack_profile; ///< Stack profile (Zigbee or Zigbee pro)
- U64 ext_pan_ID; ///< Extended PAN ID
- U8 depth; ///< Device depth
- U8 rtr_cap; ///< Remaining available addresses for routers to join
- U8 ed_cap; ///< Remaining available addreses for end devices to join
- U16 cskip; ///< CSkip value for tree addressing
- U8 dev_type; ///< Device type (coordinator, router, end device)
- U8 rreq_id; ///< Current route request ID number (used to select the rreq ID)
- bool joined; ///< Joined to the network
- U8 nwk_scan_attempts; ///< Number of scan attempts
- U16 time_betw_scans; ///< Time between scan attempts
- U16 nwk_mngr; ///< Address of this network's network manager
- //Added byLiutian min
- U8 nbor_min_lqi; ///< Node can be added in nbor table with min lqi
- U32 child_cap_mask; ///< 0-unused 1-used,0~19 bit for end device 20~31 for router
- //Added byLiutian min
- } nwk_nib_t;
- /*******************************************************************
- Network Protocol Block. This holds miscellaneous variables needed by the NWK layer.
- *******************************************************************/
- typedef struct _nwk_pcb_t
- {
- // these are to handle broadcasts
- ...
- // nwk discovery
- U32 channel_mask; ///< Channel mask for network scan
- U8 duration; ///< Duration for network scan
- // these are to handle network management
- U8 nlme_state; ///< Network management state
- U8 *energy_list; ///< Pointer to MAC layer energy list
- // nwk join
- mem_ptr_t *curr_join_target; ///< Current parent we are trying to join
- bool join_as_rtr; ///< Joining as router or end device
- } nwk_pcb_t;
- /*******************************************************************
- 结构体 nwk_capab_info_t 存储 节点的 Capability information,当请求加入父节点时会被用到 。
- *******************************************************************/
- typedef struct
- {
- bool dev_type; ///< Device type
- bool pwr_src; ///< Power source (Mains, battery)
- bool rx_on_idle; ///< Receive always on
- bool alloc_addr; ///< Allocate address
- } nwk_capab_info_t;
nwk_init()的具体实现如下:
/*1:声明pcb、pcb、info*/
- static nwk_pcb_t pcb;
- static nwk_nib_t pcb;
- nwk_capab_info_t info;
/*2: IS_RFD 是有用户自行定义的标志位,用来设置 NWK Information Base Struct 中节点类型 */
- if(IS_RFD == true)
- {
- nib.dev_type = NWK_END_DEVICE;
- }
- else
- {
- nib.dev_type = aib->dev_type;
- }
- if(nib.dev_type != NWK_END_DEVICE)
- {
- aib->sleep = false;
- }
- info.rx_on_idle = aib->sleep ? false : true;
- if(nib.dev_type == NWK_END_DEVICE && aib->sleep)
- {
- info.pwr_src = false;
- }
- else
- {
- info.pwr_src = true;
- }
- nib.capability_info = nwk_gen_capab_info(&info);
- nib.seq_num = (U8)drvr_get_rand();
- nib.rreq_id = (U8)drvr_get_rand();
- nib.report_const_cost = true;
- nib.traxn_persist_time = aib->traxn_persist_time;
- nib.short_addr = 0xFFFF;
- nib.stack_profile = ZIGBEE_STACK_PROFILE;
- nib.max_routers = ZIGBEE_MAX_ROUTERS;
- nib.max_children = ZIGBEE_MAX_CHILDREN;
- nib.max_depth = ZIGBEE_MAX_DEPTH;
- nib.rtr_cap = nib.max_routers;
- nib.ed_cap = nib.max_children - nib.max_routers;
- nib.joined = false;
- nib.nbor_min_lqi = ZIGBEE_NBOR_MIN_LQI;
- nib.child_cap_mask = 0;
前面曾提到 NWK Layer中包含各种表( xxx table ... ),现在就来看下这些table是怎样完成初始化的。首先介绍contiki的链表操作。
在Contiki中,采用一种宏的机制来声明一个链表:
- #define LIST(name) \
- static void *LIST_CONCAT(name,_list) = NULL; \
- static list_t name = (list_t)&LIST_CONCAT(name,_list)
声明之后,又使用特定的接口函数对这个链表进行初始化:
- void list_init(list_t list)
- {
- *list = NULL;
- }
所以,当你想声明并初始化某个链表时,可以这样写:
- LIST(my_list);
- list_init(my_list);
然后你就可以调用contiki提供的各种链表操作接口对my_list进行各种操作了,如list_insert()、list_remove()、list_add().
知道了这,NWK Layer中邻居表(neighbor table)、路由表(route table)、路由发现表(route discovery table)、待定表(pending list)、广播表(broadcast table)的初始化就简单多了。
/*6: 邻居表的初始化*/
- LIST(nbor_tbl);
- list_init(nbor_tbl);
/*7: 路由表的初始化*/
- LIST(rte_tbl);
- list_init(rte_tbl);
/*8: 路由发现表的初始化*/
- LIST(disc_tbl);
- list_init(disc_tbl);
/*9: 待定表的初始化*/
- LIST(pend_list);
- list_init(pend_list);
/*10: 广播表的初始化*/
- LIST(brc_list);
- list_init(brc_list);
到这里,nwk_init()的内容大致就讲完了。除了APS和NWK的之外,协议栈初始化还包括MAC、AF、ZDO的初始化,这些内容这里不再赘述。