所有的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个任务,有兴趣的同学可以自己查看voidosalInitTasks( void )函数的代码。
接下来看一下zigbee协调器是怎么建立网络的
首先我们必须选择SimpleCollectorEB版本,在APP文件下看到sapi.c源文件。找到SAPI_Init(bytetask_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 theZDApp
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)
{ uint8startOptions = 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 )
{
uint8startOptions;
// Giveindication to application of device startup
#if ( SAPI_CB_FUNC )
zb_HandleOsalEvent( ZB_ENTRY_EVENT );
#endif
// LEDoff 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 andrestart
HalLedBlink(HAL_LED_2, 0, 50, 500);
}
return(events ^ ZB_ENTRY_EVENT );
}
这个时候,程序就停在这里,只能看到LED_2在闪烁。这个时候我们可以按下按键1,然后去找一下,事件处理函数中如何对此事件进行相应。找到UINT16SAPI_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 flagdefinitions 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) ||
#elifZG_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 );
}
继续进入到网络初始化函数中看一下,代码如下:
void ZDApp_NetworkInit( uint16 delay )
{
if ( delay )
{
// Waitawhile 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 );//启动设备函数
// Returnunprocessed events
return(events ^ ZDO_NETWORK_INIT);
}
好,继续进入到 启动设备函数中 看一下源代码:
void ZDO_StartDevice( byte logicalType, devStartModes_tstartMode, 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( "StartDevice ERR", "MODE unknown" );
#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 networkchannel
// change will not be recieved. On these devices or routers thathave
// lost the network, an active scan shall be conducted on theDefault
// Channel list using the extended PANID to find the network. Ifthe
// extended PANID isn't found using the Default Channel list, anscan
// 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 valuevia
// application specific means, the device shall conduct an activescan
// on the Default Channel list and join the PAN with the same
// ExtendedPanID. If the PAN is not found, an scan should becompleted
// on all channels.
// When devices rejoin the network and the PAN is not foundfrom
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( "StartDevice ERR", "MODE unknown" );
#endif
}
}
if ( ret != ZSuccess )
osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY);
}
对于zigbee协调器而言,ZDOzigbee设备对象层向网络层发送一个组网请求,由于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 toshow 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 againwith 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 userintervenes)
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 startup, do one MTO route discovery if the device is aconcentrator
if (zgConcentratorEnable == TRUE ) //if不成立,先不用管
{
// Start next event
osal_start_timerEx( NWK_TaskID, NWK_MTO_RTG_REQ_EVT, 100 );
}
// Returnunprocessed 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, changestate to running
if ( status == ZB_SUCCESS )
{
myAppState =APP_START; //在此函数中只做了一件事就是将应用状态改为启动状态。
}
else
{
osal_start_timerEx( sapi_TaskID, MY_START_EVT, myStartRetryDelay);
}
}
到此为止,协调期建网操作已经全部完成了~ 中间出现很多参数,有时间的话可能会再去看一下这些参数的定义,我主要是先摸清zigbee协调器建网的主要过程,接下来我可能会研究一下终端节点是怎么加入网络以及怎么实现无线数据的收发。加油把~
转载请注明出处- 作者:郑通通