1.函数流程
从Main()函数开始,基本调用顺序为
①用户函数的初始化流程
②系统运行流程
下面对关键函数进行分析。
2.SampleApp_Init( )
这是用户任务的初始化函数。为了方便管理,可以将各种非网络必须的硬件软件初始化代码在此函数里面调用。
/*********************************************************************
* @fn SampleApp_Init
*
* @brief Initialization function for the Generic App Task.
* This is called during initialization and should contain
* any application specific initialization (ie. hardware
* initialization/setup, table initialization, power up
* notificaiton ... ).
*
* @param task_id - the ID assigned by OSAL. This ID should be
* used to send messages and set timers.
*
* @return none
*/
void SampleApp_Init( uint8 task_id )
{
SampleApp_TaskID = task_id; //osal分配的任务ID
SampleApp_NwkState = DEV_INIT;//设备状态设定为ZDO层中定义的初始化状态
SampleApp_TransID = 0; //消息发送ID(多消息时有顺序之分)
SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//广播
SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的网络地址为广播地址
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. 定义本设备用来通信的端点描述符
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
SampleApp_epDesc.task_id = &SampleApp_TaskID; //SampleApp 描述符的任务ID
SampleApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp简单描述符
SampleApp_epDesc.latencyReq = noLatencyReqs; //延时策略
// Register the endpoint description with the AF
afRegister( &SampleApp_epDesc ); //向AF层登记描述符
// Register for all key events - This app will handle all key events
RegisterForKeys( SampleApp_TaskID ); // 登记所有的按键事件
// By default, all devices start out in Group 1
SampleApp_Group.ID = 0x0001;//组号
osal_memcpy( SampleApp_Group.name, "Group 1", 7 );//设定组名
aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );//把该组登记添加到APS中
}
函数定义了一个广播地址变量与一个组播地址变量,这两个都是"目的地址"。而SampleApp_epDesc为本设备的端点描述符。
其中
SampleApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//
具体为
// This list should be filled with Application specific Cluster IDs.
const cId_t SampleApp_ClusterList[SAMPLEAPP_MAX_CLUSTERS] =
{
SAMPLEAPP_PERIODIC_CLUSTERID,
SAMPLEAPP_FLASH_CLUSTERID
};
const SimpleDescriptionFormat_t SampleApp_SimpleDesc =
{
SAMPLEAPP_ENDPOINT, // int Endpoint;
SAMPLEAPP_PROFID, // uint16 AppProfId[2];
SAMPLEAPP_DEVICEID, // uint16 AppDeviceId[2];
SAMPLEAPP_DEVICE_VERSION, // int AppDevVer:4;
SAMPLEAPP_FLAGS, // int AppFlags:4;
SAMPLEAPP_MAX_CLUSTERS, // uint8 AppNumInClusters;
(cId_t *)SampleApp_ClusterList, // uint8 *pAppInClusterList;
SAMPLEAPP_MAX_CLUSTERS, // uint8 AppNumInClusters;
(cId_t *)SampleApp_ClusterList // uint8 *pAppInClusterList;
};
Cluster就是所谓群集,簇。参考下图。
3.osal_run_system( )
/*********************************************************************
* @fn osal_run_system
*
* @brief
*
* This function will make one pass through the OSAL taskEvents table
* and call the task_event_processor() function for the first task that
* is found with at least one event pending. If there are no pending
* events (all tasks), this function puts the processor into Sleep.
*
* @param void
*
* @return none
*/
void osal_run_system( void )
{
uint8 idx = 0;
osalTimeUpdate();
Hal_ProcessPoll();
//获取当前最高优先级任务事件的位置。
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];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
activeTaskID = idx;
activeTaskID = TASK_NO_TASK;
events = (tasksArr[idx])( idx, events ); //调用任务回调函数,(1)
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
}
osal_start_system()实际上是一个死循环,不断调用osal_run_system();
tasksEvents[]为事件任务数组,“任务”标记位宽为16bit。当前需要运行的任务都标记在里面;idx从0开始递增直到tasksEvents[idx]不为零。可见idx越小优先级越高。
上面函数,通过idx获取最高优先级事件。(1)处调用了函数指针数组,可通过以下代码理解,这类似于C#中的委托与事件。
上面函数,通过idx获取最高优先级事件。(1)处调用了函数指针数组,可通过以下代码理解,这类似于C#中的委托与事件。
//函数指针
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
//定义函数指针数组
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_ProcessEvent,
#endif
ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_event_loop,
#endif
SampleApp_ProcessEvent
};
其中,SampleApp_ProcessEvent正是SampleApp.c中用户需要实现的进程函数。形式如下:
/*********************************************************************
* @fn SampleApp_ProcessEvent
*
* @brief Generic Application Task event processor. This function
* is called to process all events for the task. Events
* include timers, messages and any other user defined events.
*
* @param task_id - The OSAL assigned task ID.
* @param events - events to process. This is a bit map and can
* contain more than one event.
*
* @return none
*/
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
(void)task_id; // Intentionally unreferenced parameter
if ( 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 pressed
case KEY_CHANGE: //按键事件
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );//按键处理,用户实现
break;
// Received when a messages is received (OTA) for this endpoint
case AF_INCOMING_MSG_CMD: //无线接收数据事件
SampleApp_MessageMSGCB( MSGpkt );//调用回调函数对收到的数据进行处理,用户实现
break;
// Received whenever the device changes state in the network
case 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);
}
return 0;
}
函数主要对按键事件,网络接收事件,网络状态改变事件,周期性发送事件进行处理。
其中,SampleApp_MessageMSGCB()为无线接收函数,对接收的数据进行处理。SampleApp_SendPeriodicMessage()为无线发送函数。
下一篇专门分析一下发送与接收函数,以及如何添加用户代码实现功能。