整个协议栈是以一个OS贯穿的,要加入自己的应用,就要添加一个任务,在任务中执行,与协议栈实现无缝连接。
1、添加一个任务。在协议栈中的OSAL.c文件中,byteosal_init_system( void )函数的功能是初始化OS、添加任务到OS任务表中。在这个函数中通过调用osalAddTasks()函数来定制项目所需要应用的任务,该函数属于应用层和OS之间的接口函数,一般项目的建立需要根据系统的需要自己编写改函数,并将函数放到应用层。osalAddTasks()函数是通过osalTaskAdd()函数完成任务添加。
首先,将支持协议栈功能需要的任务加载到该函数中,
void osalAddTasks( void )
{
osalTaskAdd (Hal_Init, Hal_ProcessEvent,OSAL_TASK_PRIORITY_LOW);
#if defined( ZMAC_F8W )
osalTaskAdd( macTaskInit, macEventLoop,OSAL_TASK_PRIORITY_HIGH );
#endif
#if defined( MT_TASK )
osalTaskAdd( MT_TaskInit, MT_ProcessEvent,OSAL_TASK_PRIORITY_LOW );
#endif
osalTaskAdd( nwk_init, nwk_event_loop,OSAL_TASK_PRIORITY_MED );
osalTaskAdd( APS_Init, APS_event_loop,OSAL_TASK_PRIORITY_LOW );
osalTaskAdd( ZDApp_Init, ZDApp_event_loop,OSAL_TASK_PRIORITY_LOW );
}
这些任务是协议栈运行的先决条件,为了更好的使用协议栈,建议将这些任务都添加到任务列表中。这些函数的参数条件在协议栈中已经定义好,可以直接使用。
从上面加载的函数中可以发现,要建立一个单独的任务,必须先将osalTaskAdd()函数所需要的参数条件定义好,这些参数分别是初始化函数example_Init,任务处理函数example_event_loop和任务优先级。
2、任务初始化函数。任务初始化函数的功能是将该任务需要完成的功能的功能部件初始化,在每一个任务的初始化函数中,必须完成的功能是要得到设置任务的任务ID。
void SampleApp _Init ( uint8 task_id )
{
SampleApp _Init = task_id;
}
由于在这个任务中还有其他的功能,所以,对其他功能也需要做一定的初始化,包括对发送数据的设置,按键的设置等。实现的函数为:
void SampleApp_Init ( uint8 task_id )
{
SampleApp_TaskID = task_id; //任务ID
SampleApp_NwkState = DEV_INIT; //网络类型
SampleApp_TransID = 0; // 设置发送数据的方式和目的地址,
SampleApp_All_DstAddr.addrMode =(afAddrMode_t)AddrBroadcast;//广播到所有的设备
SampleApp_All_DstAddr.endPoint =SAMPLEAPP_ENDPOINT;
SampleApp_All_DstAddr.addr.shortAddr = 0xFFFF;
// 单播到一个设备
SampleApp_Single_DstAddr.addrMode =(afAddrMode_t)afAddrGroup;
SampleApp_Single_DstAddr.endPoint =SAMPLEAPP_ENDPOINT;
// 设置 endpoint description.
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_epDesc.task_id =&SampleApp_TaskID;
SampleApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t*)&SampleApp_SimpleDesc;
SampleApp_epDesc.latencyReq = noLatencyReqs;
// 登记endpoint description 到 AF
afRegister( &SampleApp_epDesc );
// 登记所有的按键事件
RegisterForKeys( SampleApp_TaskID );
}
3、任务处理函数。任务处理函数是对任务发生后的事件进行处理,在这个项目中主要完成的功能是通过协调器上的按键发送一个数据,控制路由器的小灯。所以里面就应该设计到按键的事件处理,网络状态的判断(判断设备的类型,是协调器还是路由器或者是终端设备)和接收到信息后的处理。处理函数为:
uint16 SampleApp_ProcessEvent( uint8 task_id,uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
if ( events & SYS_EVENT_MSG ) //系统信息,
{ MSGpkt= (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); //OS发送过来的信息
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{ // 按键事件
case KEY_CHANGE: //按键处理函数
SampleApp_HandleKeys( ((keyChange_t*)MSGpkt)->keys );
break; // 接收数据事件
case AF_INCOMING_MSG_CMD: //接收数据的处理函数
SampleApp_MessageMSGCB( MSGpkt );
break; // 网络状态发生变化时间
case ZDO_STATE_CHANGE:
SampleApp_NwkState =(devStates_t)(MSGpkt->hdr.status); //获取网络状态
if ( (SampleApp_NwkState == DEV_ZB_COORD)//判断网络类型
|| (SampleApp_NwkState == DEV_ROUTER)
|| (SampleApp_NwkState == DEV_END_DEVICE) )
{
}
else
{ // 设备不属于这个网络
}
break;
default:
break;
} // 释放存储器
osal_msg_deallocate( (uint8 *)MSGpkt ); // Next- 如果有一个空闲的任务
MSGpkt = (afIncomingMSGPacket_t*)osal_msg_receive( SampleApp_TaskID );
} // 返回未处理的任务
return (events ^ SYS_EVENT_MSG);
}
return 0;
}
4、按键子函数。按键子函数的功能是处理所有的按键事件,按键的底层驱动函数在Hal_key.c中,在这里按键需要完成的任务是,当协调器按键1被按下后,以广播的方式发送数据去让路由器小灯闪烁。
void SampleApp_HandleKeys(uint8 keys )
{
if ( keys & HAL_KEY_SW_1 )
{
if(SampleApp_NwkState == DEV_ZB_COORD) //如果是协调器
SampleApp_SendFlashMessage(SAMPLEAPP_FLASH_DURATION ); //发送数据
else
{
}
}
}
5、接收处理函数。接收处理函数的功能有两部分,一是路由器的接收函数,二是协调器的接收处理函数。在这个项目里面,我们将这两种设备的处理函数都固化在了一个函数里面,用串ID来判断他们的设备类型。当路由器接收到数据后,先判断该信息的串ID,然后判断命令,如果命令正确,则小灯闪烁,然后单播发送确认信号给协调器,协调器收到信号后,同样先判断串ID,然后确认命令后小灯闪烁示意。
void SampleApp_MessageMSGCB(afIncomingMSGPacket_t *pkt )
{
unsigned char Rx_Buf[4];
switch ( pkt->clusterId )
{
case SAMPLEAPP_CLUSTERID1:
memcpy(Rx_Buf,pkt->cmd.Data,3);
if((Rx_Buf[0] == 'Y') && (Rx_Buf[1] =='E') && (Rx_Buf[2] == 'S'))
{
HalLedBlink( HAL_LED_4, 4, 50, 250); //小灯闪烁四次
}
break;
case SAMPLEAPP_CLUSTERID2:
memcpy(Rx_Buf,pkt->cmd.Data,4);
if((Rx_Buf[0] == 'O') && (Rx_Buf[1] =='P') && (Rx_Buf[2] == 'E') && (Rx_Buf[3] == 'N'))
{
HalLedBlink( HAL_LED_4, 4, 50, 250); //小灯闪烁四次
SendData("YES",pkt->srcAddr.addr.shortAddr,3);//以单播的方式回复信号
}
break;
}
}
6、发送函数:广播发送一段数据
void SampleApp_SendFlashMessage( uint8 *buffer )
{
if ( AF_DataRequest( &SampleApp_All_DstAddr, &SampleApp_epDesc,
SAMPLEAPP_CLUSTERID2,
4,
buffer,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
//********************************************************************
void SampleApp_SendData(uint8 *buf, uint16 addr, uint8 Leng)
{
SampleApp_Single_DstAddr.addr.shortAddr = addr;
if ( AF_DataRequest( &SampleApp_Single_DstAddr, //发送的地址和模式
&SampleApp_epDesc, //终端(比如操作系统中任务ID等)
SAMPLEAPP_CLUSTERID1,//发送串ID
Leng,
buf,
&SampleApp_TransID,
AF_DISCV_ROUTE,
// AF_ACK_REQUEST,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) {
}
else
{
}
}
发送数据只是调用一个函数,在这里不多做解释。
在完成以上的步骤之后就可以完成任务的添加。用户就可以实现自己的程序功能,但是由于Z_Stack已经把通信协议写好(如图4-4),所以只需要调用函数就可以完成无线传感器网络,这样只能使用却不能了解关于具体底层的信息。所以对于无线传感器网络开发来说的话,Tiny OS则是完全开放源代码的专用于无线传感器网络的操作系统。