Zstack中如何实现自己的任务
更新时间:2024-04-30 06:02:01 阅读量: 综合文库 文档下载
- zstack协议栈推荐度:
- 相关推荐
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++ );
#endif
APS_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,
#endif
APS_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_SAVING )
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)。这三个寄存器的不同的位控制了不同的硬件的中断使能,比如IEN2中的第五位WDTIE控制着看门狗时钟的中断使能。这其中有一个比较特殊的位是IEN0的第7位,名称为EA,控制着所有中断的使能,为0时
将没有中断相应,为1时每一个中断源的使能受相应的位的控制。上面的宏即是用芯片的EA=0来关中断实现临界资源的保护。
(2)set宏定义如下,表示执行x指令,注意x是一个完整的语句,需要加分号。
#define st(x) do { x } while (__LINE__ == -1)
而整个宏的定义结束时没有分号,而是在最后的应用时加的分号,如:HAL_ENTER_CRITICAL_SECTION(intState);
(3)HAL_ENABLE_INTERRUPTS()和HAL_DISABLE_INTERRUPTS()这两个宏分别实现了cc2430的所有中断的开和关。HAL_ENTER_CRITICAL_SECTION(x)宏首先将EA的值保存在变量x中,然后关闭所有中断,进行后面的临街资源处理。HAL_EXIT_CRITICAL_SECTION(x)宏则是回复刚才保存在x中的EA的值。
HAL_CRITICAL_STATEMENT(x)宏的功能是将x作为临界代码执行,首先声明了用于保存EA值的变量,然后调用进入临界区宏,执行临界代码x,最后执行退出临界区的宏。
(4)注意HAL_CRITICAL_STATEMENT(x)这个宏,因为st宏的实现中x是一些可以执行的完整c语句,更主要的是写在do{}while()中,它值一个子的程序片段,因此x可以做很多事,比如声明变量等。否则你会奇怪,这样定义宏在宏展开的时候如果使用多个这个宏,会不会出现重复定义(HAL_CRITICAL_STATEMENT(x) 实现代码中的halIntState_t s;),会不会出现在程序的中间来定义变量(c语言要求要使用的变量需在最前面定义)等问题。其实这些问题是不会出现的,真是因为HAL_CRITICAL_STATEMENT(x)的x的执行在do-while中的do子句中。
下面是一个类似的验证例子程序: #include
#define st(x) do{x}while(__LINE__==-1)
#define enable() st(EA = 1;) //使能所有中断 #define disable() st(EA = 0;) //关闭所有中断
#define enter(x) st(x = EA; disable();) //进入临界区 #define exit(x) st(EA = x;) //退出临界区 //简写临界代码的执行
#define critical(s) st(int temp; enter(temp); s; exit(temp);) //模拟控制所有中断的变量 int EA = 5; int main() {
int a;
enter(a);
printf(\ exit(a);
//验证多次执行宏不会出现重复定义变量的问题 critical(printf(\ critical(printf(\
//上面的critical(printf(\world-first\\n\展开后的等价代码
do {
int temp; do{
temp = EA;
do{ EA = 0; }while(__LINE__==-1); }while(__LINE__==-1); printf(\
do{ EA =temp; }while(__LINE__==-1); }while(__LINE__==-1);
//验证在子模块中可以再次声明变量 {
int a = 12;
printf(\ {
int a = 89;
printf(\ } }
return 0; }
执行结果为: EA=0, a=5
hello world-first hello world-second hello world 12 89
PS: (1)c程序中的各个宏定义的顺序任意。
(1)c程序中要求变量需先定义所有要使用的变量,然后才使用,是对用一个层次模块来说,在子层次中可以遵循这个规则再次定义变量。一个花括号中的括起来的内容{...}可以看作一个子模块。
Zstack设置发送功率(CC2530)
在mac_radio.c中找到macRadioSetTxPower函数,其具体内容复制如下:
#ifndef HAL_MAC_USE_REGISTER_POWER_VALUES
MAC_INTERNAL_API void macRadioSetTxPower(uint8 txPower) {
halIntState_t s;
#if defined MAC_RUNTIME_CC2591 || defined MAC_RUNTIME_CC2590 const uint8 CODE *pTable =
macRadioDefsTxPwrTables[macRadioDefsRefTableId >> 4]; #elif defined HAL_PA_LNA || defined HAL_PA_LNA_CC2590 const uint8 CODE *pTable = macRadioDefsTxPwrTables[0]; #else
const uint8 CODE *pTable = macRadioDefsTxPwrBare;
//该table中含有txPower的设置值,将该table的首地址赋给指针pTable, //macRadioDefsTxPwrBare[]定义在mac_radio_defs.c中 #endif
if ((int8)txPower >
(int8)pTable[MAC_RADIO_DEFS_TBL_TXPWR_FIRST_ENTRY]) {
txPower = pTable[MAC_RADIO_DEFS_TBL_TXPWR_FIRST_ENTRY];//发送功率上限值 }
else if ((int8)txPower <
(int8)pTable[MAC_RADIO_DEFS_TBL_TXPWR_LAST_ENTRY]) {
txPower = pTable[MAC_RADIO_DEFS_TBL_TXPWR_LAST_ENTRY];//发送功率下限值 }
HAL_ENTER_CRITICAL_SECTION(s); {
uint8 index = pTable[MAC_RADIO_DEFS_TBL_TXPWR_FIRST_ENTRY] - txPower
+ MAC_RADIO_DEFS_TBL_TXPWR_ENTRIES; reqTxPower = pTable[index]; }
//通过计算转换查表index,得到发送功率值,将其赋给reqTxPower, //函数macRadioUpdateTxPower中用于更新发送功率
HAL_EXIT_CRITICAL_SECTION(s);
macRadioUpdateTxPower(); } #else
MAC_INTERNAL_API void macRadioSetTxPower(uint8 txPower) {//直接获得reqTxPower halIntState_t s;
HAL_ENTER_CRITICAL_SECTION(s); reqTxPower = txPower;
HAL_EXIT_CRITICAL_SECTION(s);
macRadioUpdateTxPower(); }
下面了解一下macRadioUpdateTxPower函数,其函数体如下:
MAC_INTERNAL_API void macRadioUpdateTxPower(void) {
halIntState_t s;
HAL_ENTER_CRITICAL_SECTION(s);//进入临界区
if (reqTxPower != macPhyTxPower)//macPhyTxPower即为当前实际的发送功率 {
if (!macRxOutgoingAckFlag && !MAC_TX_IS_PHYSICALLY_ACTIVE()) //当有发送任务正在进行时,不能改变发送功率。
//当前的发送任务完成后,将重新调用该函数进行发送功率设置。 {
macPhyTxPower = reqTxPower;
MAC_RADIO_SET_TX_POWER(macPhyTxPower);//设置寄存器TXPOWER为macPhyTxPower,即reqTxPower }
}
HAL_EXIT_CRITICAL_SECTION(s);//离开临界区 }
通过上面的函数,我们根据自己的需要,适当对发送功率进行设置。
关于ZStack-CC2530-2.3.0-1.4.0中simpleApp例子的组网(一)
所有的C语言编写的程序,入口函数一定是main函数,首先看一下ZMain.c函数。
int main( void ) {
osal_int_disable( INTS_ALL ); HAL_BOARD_INIT(); zmain_vdd_check(); InitBoard( OB_COLD ); HalDriverInit(); osal_nv_init( NULL ); ZMacInit(); zmain_ext_addr(); zgInit(); #ifndef NONWK afInit(); #endif
osal_init_system();
osal_int_enable( INTS_ALL ); InitBoard( OB_READY );
zmain_dev_info(); #ifdef LCD_SUPPORTED zmain_lcd_init(); #endif
#ifdef WDT_IN_PM1
WatchDogEnable( WDTIMX ); #endif
osal_start_system(); return 0; }
主函数要做的事情非常简单,首先进行了一些初始化,包括各层的初始化,硬件初始化,以及任务的初始化等,然后就进入到操作系统当中,即
osal_start_system(); 就再也出不来了。操作系统的作用就是如果有事件发生,就把这个消息通知给处理该事件的事件处理函数去执行,然后一直的循环查找有没有事件发生。另外说一下,事件是定义在任务当中的,即一个任务可能有多个事件,每个任务对应一个事件处理函数。在这个程序中,一共有6个任务,有兴趣的同学可以自己查看void osalInitTasks( void )函数的代码。 接下来看一下zigbee协调器是怎么建立网络的
首先我们必须选择SimpleCollectorEB版本,在APP文件下看到sapi.c源文件。找到SAPI_Init(byte task_id)函数,此函数是进行sapi层的初始化,代码如下
void SAPI_Init( byte task_id ) {
sapi_TaskID = task_id;//将操作系统初始化任务时定义的任务id号传进来 sapi_bindInProgress = 0xffff;//设置不允许绑定
sapi_epDesc.task_id = &sapi_TaskID;//给端口描述符的任务ID号赋值,感觉也就是端口收到的数据或者消息就交给ID号指定的任务来处理。 sapi_epDesc.endPoint = 0;//端口描述符端口号初始化为0。
#if ( SAPI_CB_FUNC )//编译通过
sapi_epDesc.endPoint = zb_SimpleDesc.EndPoint;//端口号赋值
sapi_epDesc.task_id = &sapi_TaskID;//任务ID赋值,与上面的任务ID的值是相同的。
sapi_epDesc.simpleDesc = (SimpleDescriptionFormat_t
*)&zb_SimpleDesc;//简单描述符赋值,是描述一个端口最基本的信息
sapi_epDesc.latencyReq = noLatencyReqs;//这是一个枚举类型的,不清楚具体含义,不过大家都设成noLatencyReqs,除此之外还有两个值。
afRegister( &sapi_epDesc );//将定义的端点在AF层注册,一定要注册后端点才会生效 #endif
afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE);//设置描述符不能匹配
// Register callback evetns from the ZDApp
ZDO_RegisterForZDOMsg( sapi_TaskID, NWK_addr_rsp );//在sapi层注册网络地址事件,这个函数可以截取空中发来的消息,有兴趣的可以查查资料,第一个函数是截取的消息发到哪个任务中去,第二个参数,cluserID是消息的类型。 ZDO_RegisterForZDOMsg( sapi_TaskID, Match_Desc_rsp );//同理,在sapi层注册匹配描述符事件。
#if ( SAPI_CB_FUNC )
#if (defined HAL_KEY) && (HAL_KEY == TRUE) // Register for HAL events
RegisterForKeys( sapi_TaskID );//注册按键响应事件
if ( HalKeyRead () == HAL_KEY_SW_5)
{ uint8 startOptions = ZCD_STARTOPT_CLEAR_STATE | ZCD_STARTOPT_CLEAR_CONFIG;
zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
zb_SystemReset(); }
#endif // HAL_KEY
osal_set_event(task_id, ZB_ENTRY_EVENT);//在这里设置了一个进入事件,第一个参数是task_id是任务的ID号,因此我们可以在sapi层的事件处理函数中找到这个进入事件是怎么处理的。 #endif }
在UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )函数中,找到
if ( events & ZB_ENTRY_EVENT ) {
uint8 startOptions;
// Give indication to application of device startup #if ( SAPI_CB_FUNC )
zb_HandleOsalEvent( ZB_ENTRY_EVENT ); #endif
// LED off cancels HOLD_AUTO_START blink set in the stack HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);//为了方便观察实验现象,将第四个灯关闭。
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
if ( startOptions & ZCD_STARTOPT_AUTO_START ) {
zb_StartRequest(); } else {
// blink leds and wait for external input to config and restart HalLedBlink(HAL_LED_2, 0, 50, 500); }
return (events ^ ZB_ENTRY_EVENT ); }
这个时候,程序就停在这里,只能看到LED_2在闪烁。这个时候我们可以按下按键1,然后去找一下,事件处理函数中如何对此事件进行相应。找到UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )函数中的关于按键的处理: case KEY_CHANGE: #if ( SAPI_CB_FUNC )
zb_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys ); #endif
break;
进入到HandleKeys函数中,找到
if ( keys & HAL_KEY_SW_1 ) {
if ( myAppState == APP_INIT ) {
zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType );//读取flash中的设备类型
if ( logicalType != ZG_DEVICETYPE_ENDDEVICE ) {
logicalType = ZG_DEVICETYPE_COORDINATOR;//将设备类型改变为协调器类型
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);//将设备类型写入到flash中 }
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );//读取启动模式
startOptions = ZCD_STARTOPT_AUTO_START;//将启动模式赋值为自动启动模式
zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );//将启动模式存入到flash中
zb_SystemReset();//系统重启设置。这时候flash中,启动模式就变成了ZCD_STARTOPT_AUTO_START,系统重启以后,系统继续刚才讲过的过程,不同的是在进行到进入事件处理函数时,if ( startOptions &
ZCD_STARTOPT_AUTO_START )的值成立,调用if语句里面的zb_StartRequest()函数;即执行开始请求函数。 }
接下来进入到zb_StartRequest函数中看一看,原代码如下: void zb_StartRequest() {
uint8 logicalType;
zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType );//从flash中读出设备类型。
// Check for bad combinations of compile flag definitions and device type setting.
if ((logicalType > ZG_DEVICETYPE_ENDDEVICE) || //可以判断if里面语句为0,执行else
#if !ZG_BUILD_ENDDEVICE_TYPE // Only RTR or Coord possible. (logicalType == ZG_DEVICETYPE_ENDDEVICE) || #endif
#if !ZG_BUILD_RTR_TYPE // Only End Device possible. (logicalType == ZG_DEVICETYPE_ROUTER) || (logicalType == ZG_DEVICETYPE_COORDINATOR) || #elif ZG_BUILD_RTRONLY_TYPE // Only RTR possible. (logicalType == ZG_DEVICETYPE_COORDINATOR) || #elif !ZG_BUILD_JOINING_TYPE // Only Coord possible. (logicalType == ZG_DEVICETYPE_ROUTER) || #endif
(0)) {
logicalType = ZB_INVALID_PARAMETER;
SAPI_SendCback(SAPICB_START_CNF, logicalType, 0); } else {
logicalType = ZB_SUCCESS; //将设备类型改为ZB_SUCCESS ZDOInitDevice(zgStartDelay); // 执行初始化设备函数。 }
return; }
那么接下来去初始化设备函数中看一下,右键,go to definition
uint8 ZDOInitDevice( uint16 startDelay ) {
uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; //首先更改了网络状态为初始化新网络状态 uint16 extendedDelay = 0; if ( devState == DEV_HOLD ) {
zgInitItems( FALSE ); }
ZDConfig_InitDescriptors();
_NIB.CapabilityInfo = ZDO_Config_Node_Descriptor.CapabilityFlags;
devState = DEV_INIT; // 设备状态改为初始化 ZDApp_LeaveCtrlInit(); //离开控制初始化
ZDApp_LeaveCtrlStartup( &devState, &startDelay );//检查离开控制时的一些设置
if ( devState == DEV_HOLD ) {
zgWriteStartupOptions( ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE );
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
return ( ZDO_INITDEV_LEAVE_NOT_STARTED ); }
#if defined ( NV_RESTORE ) //NV_RESTORE编译不通过,NV_RESTORE主要是设置掉电再重新上电,参数是否保留
if ( HalKeyRead() == SW_BYPASS_NV )
networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; else {
networkStateNV = ZDApp_ReadNetworkRestoreState(); }
if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE ) {
networkStateNV = ZDApp_RestoreNetworkState(); } else {
NLME_InitNV();
NLME_SetDefaultNV(); } #endif
if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE ) {
ZDAppDetermineDeviceType(); //确定设备类型
extendedDelay = (uint16)((NWK_START_DELAY + startDelay) + (osal_rand() & EXTENDED_JOINING_RANDOM_MASK)); }
ZDApp_SecInit( networkStateNV );// 初始化设备对象的安全操作 ZDApp_NetworkInit( extendedDelay ); //进行网络的初始化
NLME_SetBroadcastFilter( ZDO_Config_Node_Descriptor.CapabilityFlags ); //网络层函数,代码看不到,看函数名字是设置广播地址掩码来支持广播过滤。不太明白这个。。。
return ( networkStateNV ); }
关于ZStack-CC2530-2.3.0-1.4.0中simpleApp例子的组网(二)
继续进入到网络初始化函数中看一下,代码如下:
void ZDApp_NetworkInit( uint16 delay ) {
if ( delay ) {
// Wait awhile before starting the device
osal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay );//这个函数是设置一个延时时间,延时时间到后,ZDO_NETWORK_INIT事件发生。 } else {
osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );//立即发生ZDO_NETWORK_INIT 事件 } }
好,继续去ZDO层 看一下,程序是怎么对ZDO_NETWORK_INIT 网络初始化事件处理的,在UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )函数中找到:
if ( events & ZDO_NETWORK_INIT ) {
// Initialize apps and start the network
devState = DEV_INIT; //将设备状态修改为初始化状态
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );//启动设备函数
// Return unprocessed events
return (events ^ ZDO_NETWORK_INIT); }
好,继续进入到 启动设备函数中 看一下源代码:
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder ) {
ZStatus_t ret;
#if defined ( ZIGBEE_FREQ_AGILITY ) static uint8 discRetries = 0; #endif
#if defined ( ZIGBEE_COMMISSIONING ) static uint8 scanCnt = 0; #endif
ret = ZUnsupportedMode;
if ( ZG_BUILD_COORDINATOR_TYPE && logicalType == NODETYPE_COORDINATOR )//是协调器,条件成立。 {
if ( startMode == MODE_HARD )//MODE_HARD意思是硬件启动方式,条件成立。 {
devState = DEV_COORD_STARTING;//将设备状态修改为协调器启动状态 ret = NLME_NetworkFormationRequest( zgConfigPANID, zgApsUseExtendedPANID, zgDefaultChannelList,
zgDefaultStartingScanDuration, beaconOrder,
superframeOrder, false ); }
else if ( startMode == MODE_RESUME ) {
// Just start the coordinator devState = DEV_COORD_STARTING;
ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false ); } else {
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( \#endif
} }
if ( ZG_BUILD_JOINING_TYPE && (logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE) ) {
if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) ) {
devState = DEV_NWK_DISC;
#if defined( MANAGED_SCAN ) ZDOManagedScan_Next();
ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC ); #else
ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );
#if defined ( ZIGBEE_FREQ_AGILITY )
if ( !( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE ) &&
( ret == ZSuccess ) && ( ++discRetries == 4 ) ) {
// For devices with RxOnWhenIdle equals to FALSE, any network channel
// change will not be recieved. On these devices or routers that have
// lost the network, an active scan shall be conducted on the Default
// Channel list using the extended PANID to find the network. If the
// extended PANID isn't found using the Default Channel list, an scan
// should be completed using all channels. zgDefaultChannelList = MAX_CHANNELS_24GHZ; }
#endif // ZIGBEE_FREQ_AGILITY
#if defined ( ZIGBEE_COMMISSIONING )
if (startMode == MODE_REJOIN && scanCnt++ >= 5 ) {
// When ApsUseExtendedPanID is commissioned to a non zero value via
// application specific means, the device shall conduct an active scan
// on the Default Channel list and join the PAN with the same
// ExtendedPanID. If the PAN is not found, an scan should be completed
// on all channels.
// When devices rejoin the network and the PAN is not found from zgDefaultChannelList = MAX_CHANNELS_24GHZ; }
#endif // ZIGBEE_COMMISSIONING #endif }
else if ( startMode == MODE_RESUME ) {
if ( logicalType == NODETYPE_ROUTER ) {
ZMacScanCnf_t scanCnf; devState = DEV_NWK_ORPHAN;
scanCnf.hdr.Status = ZSUCCESS;
scanCnf.ScanType = ZMAC_ORPHAN_SCAN; scanCnf.UnscannedChannels = 0; scanCnf.ResultListSize = 0;
nwk_ScanJoiningOrphan(&scanCnf);
ret = ZSuccess; } else {
devState = DEV_NWK_ORPHAN;
ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
zgDefaultStartingScanDuration ); } } else {
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( \#endif } }
if ( ret != ZSuccess )
osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY ); }
对于zigbee协调器而言,ZDO zigbee设备对象层向网络层发送一个组网请求,由于zigbee协议栈是个半开源,在网络层具体代码我们是看不到的,不过在网络层,网络组建完成以后会发送一个确认给ZDO层,即ZDO层使用
ZDO_NetworkFormationConfirmCB函数来接收发来的确认信息。下面看一下具体代码:
void ZDO_NetworkFormationConfirmCB( ZStatus_t Status ) nwkStatus = (byte)Status;
if ( Status == ZSUCCESS )//网络创建成功 {
HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );//点亮LED_3 HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);//关闭LED_4 #if defined ( ZBIT )
SIM_SetColor(0xd0ffd0); #endif
if ( devState == DEV_HOLD ) {
devState = DEV_COORD_STARTING; } }
#if defined(BLINK_LEDS) else
HalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH ); // Flash LED to show failure #endif
osal_set_event( ZDAppTaskID, ZDO_NETWORK_START );//设置网络启动事件 }
继续去ZDO层,看看是怎么处理网络启动事件的,在UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )中找到:
if ( events & ZDO_NETWORK_START ) {
ZDApp_NetworkStartEvt();//网络启动函数 return (events ^ ZDO_NETWORK_START); }
继续找到网络启动函数的代码如下:
void ZDApp_NetworkStartEvt( void ) {
if ( nwkStatus == ZSuccess )//if条件成立 {
if ( devState == DEV_COORD_STARTING )//这时设备状态确实是协调器启动状态 {
devState = DEV_ZB_COORD;//修改设备状态 }
osal_pwrmgr_device( PWRMGR_ALWAYS_ON );//进行电源管理,设置一直供电状态
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );//设置zigbee状态改变事件 } else {
// Try again with a higher energy threshold !!
if ( ( NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT ) < 0xff ) {
NLME_SetEnergyThreshold( (uint8)(NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT) );
osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT ); } else {
// Failed to start network. Enter a dormant state (until user intervenes)
devState = DEV_INIT;
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT ); }
} }
接下来看ZDO层怎么对状态改变时间进行处理,找到UINT16
ZDApp_event_loop( uint8 task_id, UINT16 events )函数中 如下代码:
if ( events & ZDO_STATE_CHANGE_EVT ) {
ZDO_UpdateNwkStatus( devState );//更新网络状态函数
// At start up, do one MTO route discovery if the device is a concentrator
if ( zgConcentratorEnable == TRUE ) //if不成立,先不用管 {
// Start next event
osal_start_timerEx( NWK_TaskID, NWK_MTO_RTG_REQ_EVT, 100 ); }
// Return unprocessed events
return (events ^ ZDO_STATE_CHANGE_EVT); }
接着看一下更新网络状态函数的代码:
void ZDO_UpdateNwkStatus(devStates_t state) {
epList_t *pItem = epList;//epList_t结构体中第一个数据成员便是端口描述符,最后一个成员是void类型的指针,突然想到了链表操作。。。刚查看了一下,端口描述符的数据确实使用链表的操作来存入的。
while (pItem != NULL) {
if (pItem->epDesc->endPoint != ZDO_EP) {
zdoSendStateChangeMsg(state, *(pItem->epDesc->task_id));//该函数的作用是将网络状态改变state这一消息通知给在AF层注册过的所有端点,除了端口0。 }
pItem = pItem->nextDesc; }
#if defined MT_ZDO_CB_FUNC
zdoSendStateChangeMsg(state, MT_TaskID); #endif
ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr(); //利用网络层的API函数得到网络地址
(void)NLME_GetExtAddr(); // 得到IEEE 64位长地址。 }
好,因为我们是在sapi层注册的端口描述符,那么信息肯定是发送到sapi层,那我们就去sapi层找一下处理这个消息的代码,在sapi.c中找到如下代码:
case ZDO_STATE_CHANGE:
// If the device has started up, notify the application if (pMsg->status == DEV_END_DEVICE || pMsg->status == DEV_ROUTER ||
pMsg->status == DEV_ZB_COORD )//成立 {
SAPI_StartConfirm( ZB_SUCCESS ); //sapi层利用这一函数对ZDO状态改变事件进行响应 }
else if (pMsg->status == DEV_HOLD || pMsg->status == DEV_INIT) {
SAPI_StartConfirm( ZB_INIT ); }
break;
好,进去看看SAPI_StartConfirm( ZB_SUCCESS )函数 void SAPI_StartConfirm( uint8 status ) {
#if defined ( MT_SAPI_CB_FUNC )
if ( SAPICB_CHECK( SPI_CB_SAPI_START_CNF ) ) {
zb_MTCallbackStartConfirm( status ); } else
#endif //MT_SAPI_CB_FUNC {
#if ( SAPI_CB_FUNC ) //条件成立,执行下面语句。 zb_StartConfirm( status ); #endif } }
再次进入到zb_StartConfirm(status)函数中 void zb_StartConfirm( uint8 status ) {
// If the device sucessfully started, change state to running if ( status == ZB_SUCCESS ) {
myAppState = APP_START; //在此函数中只做了一件事就是将应用状态改为启动状态。 } else {
osal_start_timerEx( sapi_TaskID, MY_START_EVT, myStartRetryDelay ); } }
到此为止,协调期建网操作已经全部完成了~ 中间出现很多参数,有时间的话可能会再去看一下这些参数的定义,我主要是先摸清zigbee协调器建网的主要过程,接下来我可能会研究一下终端节点是怎么加入网络以及怎么实现无线数据的收发。
关于ZStack-CC2530-2.3.0-1.4.0中simpleApp例子的 终端节点入网以及绑定操作(一)
今天来看一下终端节点是如何加入网络,以及建立绑定操作并发送数据了。首先我们选择工作空间为SimpleSensorEB ,好,我们先按一下S2键,因为在
SAPI_Init中,我们利用RegisterForKeys( sapi_TaskID );注册了键盘消息,因此我们看一下在sapi如何对按键消息进行响应的。在UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )中找到:
case KEY_CHANGE: #if ( SAPI_CB_FUNC )
zb_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys ); #endif
break;
好,接下来我们进入到zb_HandleKeys函数中看看源代码: void zb_HandleKeys( uint8 shift, uint8 keys ) {
uint8 startOptions; uint8 logicalType;
// Shift is used to make each button/switch dual purpose. if ( shift ) {
if ( keys & HAL_KEY_SW_1 ) { }
if ( keys & HAL_KEY_SW_2 ) { }
if ( keys & HAL_KEY_SW_3 ) { }
if ( keys & HAL_KEY_SW_4 ) { } } else {
if ( keys & HAL_KEY_SW_1 ) {
if ( myAppState == APP_INIT )
{
// In the init state, keys are used to indicate the logical mode. // The Sensor device is always an end-device logicalType = ZG_DEVICETYPE_ENDDEVICE;
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);
// Do more configuration if necessary and then restart device with auto-start bit set
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
startOptions = ZCD_STARTOPT_AUTO_START;
zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
zb_SystemReset();
} }
if ( keys & HAL_KEY_SW_2 )//成立 {
if ( myAppState == APP_INIT )//成立 {
logicalType = ZG_DEVICETYPE_ENDDEVICE;//修改本地设备类型为终端设备
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);//将设备类型写入到flash中
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
startOptions = ZCD_STARTOPT_AUTO_START;//修改启动选项
zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );//将启动选项写入到flash中 zb_SystemReset();//系统重启 } } }
好,系统重启后,程序从头开始执行,在进行SAPI_Init函数的最后时,出现 osal_set_event(task_id, ZB_ENTRY_EVENT);因此,进入到进入事件处理函数当中。我们在SAPI_ProcessEvent函数里找到如下代码:
if ( events & ZB_ENTRY_EVENT ) {
uint8 startOptions; #if ( SAPI_CB_FUNC )
zb_HandleOsalEvent( ZB_ENTRY_EVENT ); #endif
// LED off cancels HOLD_AUTO_START blink set in the stack HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);//首先关闭LED_4。 zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );//从flash中读出启动选项
if ( startOptions & ZCD_STARTOPT_AUTO_START )//条件成立 {
zb_StartRequest();//执行启动请求函数。 } else {
// blink leds and wait for external input to config and restart HalLedBlink(HAL_LED_2, 0, 50, 500); }
return (events ^ ZB_ENTRY_EVENT ); }
继续到启动请求函数中去看一下源代码:
void zb_StartRequest() {
uint8 logicalType;
zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType );
//以下代码主要是确认设备类型是否正确,可以判断if中的条件为0,执行else中的语句
if ((logicalType > ZG_DEVICETYPE_ENDDEVICE) ||
#if !ZG_BUILD_ENDDEVICE_TYPE // Only RTR or Coord possible.
(logicalType == ZG_DEVICETYPE_ENDDEVICE) || #endif
#if !ZG_BUILD_RTR_TYPE // Only End Device possible. (logicalType == ZG_DEVICETYPE_ROUTER) || (logicalType == ZG_DEVICETYPE_COORDINATOR) || #elif ZG_BUILD_RTRONLY_TYPE // Only RTR possible. (logicalType == ZG_DEVICETYPE_COORDINATOR) || #elif !ZG_BUILD_JOINING_TYPE // Only Coord possible. (logicalType == ZG_DEVICETYPE_ROUTER) || #endif
(0)) {
logicalType = ZB_INVALID_PARAMETER;
SAPI_SendCback(SAPICB_START_CNF, logicalType, 0); } else {
logicalType = ZB_SUCCESS;//设置本地设备类型为ZB_SUCCESS ZDOInitDevice(zgStartDelay);//执行ZDO初始化设备函数。 }
return; }
下面我们接着去初始化设备函数当中看一下源代码,可以找到:
uint8 ZDOInitDevice( uint16 startDelay ) {
uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; //设置网络状态为初始化新的网络
uint16 extendedDelay = 0;
if ( devState == DEV_HOLD ) { // Initialize the RAM items table, in case an NV item has been updated. zgInitItems( FALSE ); //初始化RAM item列表 }
ZDConfig_InitDescriptors();
//devtag.071807.todo - fix this temporary solution
_NIB.CapabilityInfo = ZDO_Config_Node_Descriptor.CapabilityFlags;
devState = DEV_INIT; // Remove the Hold state // Initialize leave control logic
ZDApp_LeaveCtrlInit(); //离开控制初始化
// Check leave control reset settings
ZDApp_LeaveCtrlStartup( &devState, &startDelay );//离开控制的一些设置
// Leave may make the hold state come back if ( devState == DEV_HOLD ) {
// Set the NV startup option to force a \ zgWriteStartupOptions( ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE );
// Notify the applications
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
return ( ZDO_INITDEV_LEAVE_NOT_STARTED ); // Don't join - (one time). }
#if defined ( NV_RESTORE )//编译不通过,并不是上电保留数据 模式 // Get Keypad directly to see if a reset nv is needed. // Hold down the SW_BYPASS_NV key (defined in OnBoard.h) // while booting to skip past NV Restore. if ( HalKeyRead() == SW_BYPASS_NV )
networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE; else {
// Determine if NV should be restored
networkStateNV = ZDApp_ReadNetworkRestoreState(); }
if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE ) {
networkStateNV = ZDApp_RestoreNetworkState(); } else {
// Wipe out the network state in NV NLME_InitNV();
NLME_SetDefaultNV();
} #endif
if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE )//成立 {
ZDAppDetermineDeviceType();
// Only delay if joining network - not restoring network state extendedDelay = (uint16)((NWK_START_DELAY + startDelay) + (osal_rand() & EXTENDED_JOINING_RANDOM_MASK)); }
// Initialize the security for type of device
ZDApp_SecInit( networkStateNV ); //初始化安全属性
// Trigger the network start
ZDApp_NetworkInit( extendedDelay );//开启网络初始化函数
// set broadcast address mask to support broadcast filtering
NLME_SetBroadcastFilter( ZDO_Config_Node_Descriptor.CapabilityFlags );
return ( networkStateNV ); }
好,我们进入到网络初始化函数当中,看看终端节点如何进行网络初始化,在此之前我们看过的函数与协调器执行的函数没有什么区别,在这以后区别开始出现了。
void ZDApp_NetworkInit( uint16 delay ) {
if ( delay ) {
// Wait awhile before starting the device
osal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay ); } else {
osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT ); //设置了一个网络初始化事件,在ZDO层的任务处理函数中处理。
} }
找到 UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )中的:
if ( events & ZDO_NETWORK_INIT ) {
devState = DEV_INIT;//改变设备状态为初始化状态。
ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
DEFAULT_BEACON_ORDER,
DEFAULT_SUPERFRAME_ORDER );//开启设备函数,参数分别为设备类型,启动模式,信标帧,超帧.第一个参数是在初始化设备时调用初始化节点描述符函数里赋值的。
return (events ^ ZDO_NETWORK_INIT); }
进入到启动设备函数中,看看源代码:
void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder ) {
ZStatus_t ret;
#if defined ( ZIGBEE_FREQ_AGILITY ) static uint8 discRetries = 0; #endif
#if defined ( ZIGBEE_COMMISSIONING ) static uint8 scanCnt = 0; #endif
ret = ZUnsupportedMode;
if ( ZG_BUILD_COORDINATOR_TYPE && logicalType == NODETYPE_COORDINATOR )//条件不通过,不是协调器 {
if ( startMode == MODE_HARD ) {
devState = DEV_COORD_STARTING;
ret = NLME_NetworkFormationRequest( zgConfigPANID,
zgApsUseExtendedPANID, zgDefaultChannelList,
zgDefaultStartingScanDuration, beaconOrder,
superframeOrder, false ); }
else if ( startMode == MODE_RESUME ) {
// Just start the coordinator devState = DEV_COORD_STARTING;
ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false ); } else {
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( \#endif } }
if ( ZG_BUILD_JOINING_TYPE && (logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE) )//条件通过 {
if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) )//条件通过 {
devState = DEV_NWK_DISC; //将设备状态修改为网络发现状态
#if defined( MANAGED_SCAN ) ZDOManagedScan_Next();
ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC ); #else
ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );//向网络层发送网络发现请求,交由网络层处理。怎么处理代码看不到。
#if defined ( ZIGBEE_FREQ_AGILITY )
if ( !( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE ) &&
( ret == ZSuccess ) && ( ++discRetries == 4 ) ) {
// For devices with RxOnWhenIdle equals to FALSE, any network channel
// change will not be recieved. On these devices or routers that have
// lost the network, an active scan shall be conducted on the Default
// Channel list using the extended PANID to find the network. If the
// extended PANID isn't found using the Default Channel list, an scan
// should be completed using all channels. zgDefaultChannelList = MAX_CHANNELS_24GHZ; }
#endif // ZIGBEE_FREQ_AGILITY
#if defined ( ZIGBEE_COMMISSIONING )
if (startMode == MODE_REJOIN && scanCnt++ >= 5 ) {
// When ApsUseExtendedPanID is commissioned to a non zero value via
// application specific means, the device shall conduct an active scan
// on the Default Channel list and join the PAN with the same // ExtendedPanID. If the PAN is not found, an scan should be completed
// on all channels.
// When devices rejoin the network and the PAN is not found from zgDefaultChannelList = MAX_CHANNELS_24GHZ; }
#endif // ZIGBEE_COMMISSIONING #endif }
else if ( startMode == MODE_RESUME ) {
if ( logicalType == NODETYPE_ROUTER ) {
ZMacScanCnf_t scanCnf; devState = DEV_NWK_ORPHAN;
scanCnf.hdr.Status = ZSUCCESS;
scanCnf.ScanType = ZMAC_ORPHAN_SCAN; scanCnf.UnscannedChannels = 0; scanCnf.ResultListSize = 0;
nwk_ScanJoiningOrphan(&scanCnf); ret = ZSuccess; } else
{
devState = DEV_NWK_ORPHAN;
ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
zgDefaultStartingScanDuration ); } } else {
#if defined( LCD_SUPPORTED )
HalLcdWriteScreen( \#endif } }
if ( ret != ZSuccess )
osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY ); }
关于ZStack-CC2530-2.3.0-1.4.0中simpleApp例子的 终端节点入网以及绑定操作(二)
当终端节点的ZDO向网络层发送完发现网络请求后,网络层操作完成后会向ZDO层发送发现网络确认,ZDO层利用ZDO_NetworkDiscoveryConfirmCB函数来对确认进行处理。下面看一下该函数的代码:
ZStatus_t ZDO_NetworkDiscoveryConfirmCB( uint8 ResultCount, networkDesc_t *NetworkList ) {
networkDesc_t *pNwkDesc = NetworkList; ZDO_NetworkDiscoveryCfm_t msg; uint8 i = ResultCount; uint8 stackProfile; uint8 stackProfilePro; uint8 selected;
#if defined ( ZDO_MGMT_NWKDISC_RESPONSE ) if ( zdappMgmtNwkDiscReqInProgress ) {
zdappMgmtNwkDiscReqInProgress = false;
ZDO_FinishProcessingMgmtNwkDiscReq( ResultCount, NetworkList ); return ( ZSuccess ); } #endif
// 以下为 检查网络描述符中的各项参数 stackProfilePro = FALSE; selected = FALSE;
for ( stackProfile = 0; stackProfile < STACK_PROFILE_MAX; stackProfile++ ) {
pNwkDesc = NetworkList;
for ( i = 0; i < ResultCount; i++, pNwkDesc = pNwkDesc->nextDesc ) {
if ( zgConfigPANID != 0xFFFF ) {
// PAN Id is preconfigured. check if it matches if ( pNwkDesc->panId != zgConfigPANID ) continue; }
if ( nwk_ExtPANIDValid( ZDO_UseExtendedPANID) == true ) {
// If the extended Pan ID is commissioned to a non zero value // Only join the Pan that has match EPID
if ( osal_ExtAddrEqual( ZDO_UseExtendedPANID, pNwkDesc->extendedPANID) == false ) continue; }
// check that network is allowing joining if ( ZSTACK_ROUTER_BUILD ) {
if ( stackProfilePro == FALSE ) {
if ( !pNwkDesc->routerCapacity ) {
continue; } } else {
if ( !pNwkDesc->deviceCapacity ) {
continue; } } }
else if ( ZSTACK_END_DEVICE_BUILD ) {
if ( !pNwkDesc->deviceCapacity ) {
continue; } }
// check version of zigbee protocol
if ( pNwkDesc->version != _NIB.nwkProtocolVersion ) continue;
// check version of stack profile
if ( pNwkDesc->stackProfile != zgStackProfile ) {
if ( ((zgStackProfile == HOME_CONTROLS) && (pNwkDesc->stackProfile == ZIGBEEPRO_PROFILE))
|| ((zgStackProfile == ZIGBEEPRO_PROFILE) && (pNwkDesc->stackProfile == HOME_CONTROLS)) )
stackProfilePro = TRUE;
if ( stackProfile == 0 ) {
continue; } } break; }
if (i < ResultCount) {
selected = TRUE; break; }
// break if selected or stack profile pro wasn't found if ( (selected == TRUE) || (stackProfilePro == FALSE) ) {
break; } }
if ( i == ResultCount ) {
msg.hdr.status = ZDO_FAIL; // couldn't find appropriate PAN to join ! } else {
msg.hdr.status = ZDO_SUCCESS;
msg.panIdLSB = LO_UINT16( pNwkDesc->panId );
msg.panIdMSB = HI_UINT16( pNwkDesc->panId ); msg.logicalChannel = pNwkDesc->logicalChannel; msg.version = pNwkDesc->version;
osal_cpyExtAddr( msg.extendedPANID, pNwkDesc->extendedPANID ); } ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_DISC_CNF, sizeof(ZDO_NetworkDiscoveryCfm_t), (uint8 *)&msg );//向ZDO层发送一个ZDO_NWK_DISC_CNF消息 return (ZSuccess);
} // ZDO_NetworkDiscoveryConfirmCB
那么接下来看看ZDApp是怎么处理ZDO_NWK_DISC_CNF消息的。在ZDO层找到处理消息的函数ZDApp_ProcessOSALMsg 源代码如下:
case ZDO_NWK_DISC_CNF:
if (devState != DEV_NWK_DISC) break;
if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE ) //条件成立 {
if ( (((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->hdr.status == ZDO_SUCCESS) && (zdoDiscCounter > NUM_DISC_ATTEMPTS) )//条件成立 {
if ( devStartMode == MODE_JOIN )//条件成立 {
devState = DEV_NWK_JOINING; //修改设备状态为网络加入状态
ZDApp_NodeProfileSync((ZDO_NetworkDiscoveryCfm_t *)msgPtr);//根据发现的网络信息进行一些节点的配置操作
if ( NLME_JoinRequest( ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->extendedPANID, BUILD_UINT16( ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->panIdLSB, ((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->panIdMSB ),
((ZDO_NetworkDiscoveryCfm_t *)msgPtr)->logicalChannel,
ZDO_Config_Node_Descriptor.CapabilityFlags ) != ZSuccess )//根据发现的网络信息向网络层发起加入网络请求 {
ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
+ ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) ); } }
网络层加入完网络后,会发送一个加入确认到ZDO层,利用ZDO_JoinConfirmCB函数,对确认信息进行处理,下面看下源代码:
void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
{
(void)PanId; // remove if this parameter is used.
nwkStatus = (byte)Status; //更新网络状态 if ( Status == ZSUCCESS ) {
// LED on shows device joined
HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON ); //打开LED_3 // LED off forgets HOLD_AUTO_START
HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF);//关闭LED_4 if ( (devState == DEV_HOLD) ) {
// Began with HOLD_AUTO_START devState = DEV_NWK_JOINING; }
if ( !ZG_SECURE_ENABLED ) {
// Notify to save info into NV ZDApp_NVUpdate(); } } else {
#if defined(BLINK_LEDS)
HalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH ); // Flash LED to show failure #endif }
// Notify ZDApp
ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_JOIN_IND, sizeof(osal_event_hdr_t), (byte*)NULL );//向ZDO层发送一个ZDO_NWK_JOIN_IND消息。 }
我们看是ZDO如何处理这个消息的,在 ZDApp_ProcessOSALMsg中看下源代码: case ZDO_NWK_JOIN_IND:
if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE ) {
ZDApp_ProcessNetworkJoin(); }
break;
进入此函数中看下主要进行什么操作:
void ZDApp_ProcessNetworkJoin( void ) {
if ( (devState == DEV_NWK_JOINING) ||
((devState == DEV_NWK_ORPHAN) &&
(ZDO_Config_Node_Descriptor.LogicalType == NODETYPE_ROUTER)) ) {
// Result of a Join attempt by this device. if ( nwkStatus == ZSuccess ) {
osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );//设置状态改变事件 #if defined ( POWER_SAVING )
osal_pwrmgr_device( PWRMGR_BATTERY ); #endif
if ( ZG_SECURE_ENABLED && ( ZDApp_RestoreNwkKey() == false ) ) {
// wait for auth from trust center!!
devState = DEV_END_DEVICE_UNAUTH; // Start the reset timer for MAX UNAUTH time
ZDApp_ResetTimerStart( 10000 );//MAX_DEVICE_UNAUTH_TIMEOUT ); } else {
if ( ZSTACK_ROUTER_BUILD ) {
if ( devState == DEV_NWK_ORPHAN
&& ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE ) {
// Change NIB state to router for restore _NIB.nwkState = NWK_ROUTER; } }
if ( devState == DEV_NWK_JOINING ) {
ZDApp_AnnounceNewAddress(); }
devState = DEV_END_DEVICE; if ( ZSTACK_ROUTER_BUILD ) {
// NOTE: first two parameters are not used, see NLMEDE.h for details
if ( ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE ) {
NLME_StartRouterRequest( 0, 0, false ); } } }
正在阅读:
Zstack中如何实现自己的任务04-30
谈沟槽管件连接技术的工程应用05-25
2011职前教育考试题目与答案(9套)01-31
小学思品五年级上册《认一认祖国政区》教学设计.doc05-18
2018年秋九年级语文上册人教版(江西专版)word版配套试卷:第四单05-03
关于灯塔作文800字06-24
关于不同意用临时牌加班及强烈要求宜昌交运集团有限公司返还被非法扣留车辆的反应材料10-20
《阿炳在1950》简案06-05
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 任务
- 实现
- 自己
- Zstack
- 如何