一、端点(Endpoint)
1、端点基础知识
(1)、它是一个字节编号的(端点编号是0-255),数据接收和发送的基本单元,在模块通信的时候,发送模块必须指定收发双方模块的网络地址和端点。
(2)、端点要使用必须要和模块里的某个任务挂钩定义。
首先每一个端点可以看成是一个1给字节数字编号的开有一扇门的房间,数据的最终目标是进入到无线数据包指定的接收方的房间(端点);而获取无线数据的这个相关代码在任务事件处理函数中,而TI协议栈每一个层都有各自的一个任务事件处理函数,所以必须要指定在哪个任务事件处理函数来取这个无线数据包中有用的数据。
(3)、一个端点只能挂钩在一个任务上,而一个任务可以挂钩多个端点,并且端点对所有的任务是公用的,定义一个少一个。
理解:一个端点假如可以挂钩在多个任务上,那么接收模块接收到无线数据的时候,同一个端点有多个任务事件处理函数去处理,不合理;一个任务上挂钩多个端点(如6和7挂在应用层任务),发送给协调器模块的6、7端点的数据都会进入到应用层任务事件处理函数里来,仅仅做个判断到底是投递到6号房间还是7号房间就可以了。
举例:假如我们接收模块定义两个两个端点6和7,并且都挂钩在应用层,那么端点6和7就不能再被其它层定义和挂钩。如果发送方发送一个无线数据到接收模块的6号端点或者7号端点,那么数据都会进入到应用层任务的任务事件处理函数里来,最后再判断是进入到6号端点房间还是7号端点房间。
2、注册端点
(1)、在目录APP下的smartHomeApp.c文件文件中,有一个smartHomeApp_Init( byte task_id )函数,定义端点和任务的挂钩的代码就在这里。
代码的分析:
void smartHomeApp_Init( byte task_id ) //定义10号端点,并与这个模块的应用层任务挂钩
{
...
...
// Fill out the endpoint description.
//指定端点10
smartHomeApp_epDesc.endPoint = 10;
//smartHomeApp_TaskID是应用层任务ID,指定挂在哪个任务上
smartHomeApp_epDesc.task_id = &smartHomeApp_TaskID;
//添加端点描述信息
smartHomeApp_epDesc.simpleDesc
= ( SimpleDescriptionFormat_t* )&smartHomeApp_SimpleDesc;
//添加端点描述信息
smartHomeApp_epDesc.latencyReq = noLatencyReqs;
// Register the endpoint description with the AF
//调用此函数完成最终的端点注册与挂载
afRegister( &smartHomeApp_epDesc );
...
...
}
/*
smartHomeApp_epDesc是一个端点变量,是一个如下结构体类型
typedef struct
{
byte endPoint;//端点编号
byte *task_id; //挂再的任务ID的地址 Pointer to location of the Application task ID.
SimpleDescriptionFormat_t *simpleDesc;//更加详细的描述这个端点的一些情况
afNetworkLatencyReq_t latencyReq;//更加详细的描述这个端点的一些情况
} endPointDesc_t;
*/
(2) 如果同时有两个端点与应用层任务挂钩,那么一旦接收到数据,最后都会到smartHomeApp_MessageMSGCB 函数中(工程名被修改为smartHomeApp),因此我们可以针对不同端点进行区分,将外部来数据处理函数smartHome_MessageMSGCB修改如下:
static void StarryApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
if(pkt->endPoint==10)//处理由目标端点10进入的数据
{
switch ( pkt->clusterId )//簇判断,具体由哪一个簇来接收数据
{
case StarryApp_CLUSTERID:
... ...
break;
}
}
if(应该接收的目标端点B)
{
switch ( pkt->clusterId )
{
case StarryApp_CLUSTERID:
... ...
break;
}
}
... ...
}
二、簇(clusterId)
如果把端点可以看成是一个1给字节数字编号的开有一扇门的房间,那么簇就是相对于端点房间里的人,是数据的最终接收者,可以有很多个。它为2个字节编号。在射频发送的时候,必须要指定接收模块的簇,发送模块不需要指定。
结合上一篇(zigbee笔记:十二)实验中的发送代码来看:
(1)发送模块的代码和接收模块在定义注册的端点是一模一样的。
(2) 在发送模块里,我们用的数据发送源端点编号也是10,所以我们定义这个端点10也和应用层任务挂钩。原则上,外部一旦给我们的此实验的发送模块(终端模块)的10号端点发来数据,也会进入应用层任务事件处理函数中。虽然我们这个端点仅仅作为发送模块,但是我们要使用10号端点就必须要挂钩定义。(这里定义的端点10既可以当本模块作为发送方时的源端点,也可以当本模块作为接收方时的目标端点。在本实验中作为发送源端点)
在smartHomeApp_ProcessEvent的射频数据发送函数配置中
char theMessageData[] = "Hello World";
smartHomeApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
smartHomeApp_DstAddr.addr.shortAddr = 0x000;//接收模块的网络地址
smartHomeApp_DstAddr.endPoint = smartHomeApp_ENDPOINT;//接收模块的接收端点号
//smartHomeApp_epDesc 结构体 是一个端点结构体描述符,描述了发送源的端点的信息
AF_DataRequest( &smartHomeApp_DstAddr, &smartHomeApp_epDesc,
smartHomeApp_CLUSTERID,//指定接收模块的端点的簇;两个字节描述
(byte)osal_strlen( theMessageData ) + 1, //表示发送字符串的长度
// 1,
(byte *)&theMessageData,//发送的字符数组的首地址
&smartHomeApp_TransID,//记录我们应用层任务从开始到当前发送的数据包个数(也能表示当前数据包是发送的第几个)
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
三、抓包信息分析
1、两个信号帧
(1)数据信号帧
(2)应答帧
2、 信号帧各项标签含义
(1)其中P.nbr.和Time(us)都是抓包工具自动生成的(和实际数据帧没有关系),P.nbr.表示从捕获开始空气中的第几个数据帧,Time(us)表示两帧之间的间隔时间以及从抓包开启到当前的时间长度
(2)从Length开始才是数据包携带的信息。其中Length表示当前这个帧一共有多少个字节。
(3)Dest Address为目标地址,Src Address为源地址(网络地址)。
(4)Dest Endpoint为目标端点:10;
(5)Src Endpoint为源端点:10;
(6)帧末尾的LQI表示当前帧的信号强度是多少,最大为255;FCS表示硬件校验,校验成功为OK,失败为ERR。
(7)APS Payload域里面的内容就是我们发送模块发送的有效信息,都为16进制表示。
(8)Cluster Id是簇
(9)APS Counter表示当前发送模块发送的第几个数据包(数据帧)
(10)APS Profile Id在端点描述符里有提到。
四、手动实验验证理论
1、 实验内容
终端发送模块上使用端点11,挂载在应用层任务上。终端发送模块两个按钮KEY1和KEY2,实现以下功能:
(1)终端发送模块按下KEY1给协调器接收模块发送字符 '1' ,目标端点为7,簇点0x0001,协调器的LED1灯状态翻转;再次按下KEY1给协调器接收模块发送字符 '2' ,目标端点为7,簇点0x0002,协调器的LED2灯状态翻转。
(2)终端发送模块按下KEY2给协调器接收模块发送字符 '3' ,目标端点为8,簇点0x0001,协调器的LED3灯状态翻转;
(3)终端发送模块按下KEY1和KEY1,自身的LED1状态翻转一次,以示指示。
2 、终端发送模块配置
(1)开发板原理图
(2)定义11号端点并与应用层任务挂钩注册。
在工程目录APP下的smartHome.c文件中的smartHomeApp_Init函数对应位置,修改端点注册的代码,如下图。
void smartHomeApp_Init( byte task_id )
{
...
// Fill out the endpoint description.
smartHomeApp_epDesc.endPoint = 11;//指定端点号
smartHomeApp_epDesc.task_id = &smartHomeApp_TaskID;//指定绑定的应用层任务
smartHomeApp_epDesc.simpleDesc
= ( SimpleDescriptionFormat_t* )&smartHomeApp_SimpleDesc;//采用默认值
smartHomeApp_epDesc.latencyReq = noLatencyReqs;//采用默认值
// Register the endpoint description with the AF
afRegister( &smartHomeApp_epDesc );
...
}
(3)添加按键KEY1和KEY2 初始化函数和中断函数(编写自己的外部中断函数,注意统一要注释自带的中断函数定义声明)
#pragma vector = P0INT_VECTOR
__interrupt void P0_ISR( void )
{
if( P0IFG & 0x02 ) //按键中断
{
osal_start_timerEx( smartHomeApp_TaskID, smartHomeApp_ZFM_EVT, 20 );
//smartHomeApp_ZFM_EVT 为自行定义的一个应用任务的事件
}
P0IFG = 0; //清除中断标志
P0IF = 0; //清除中断标志
}
#pragma vector = P2INT_VECTOR
__interrupt void P2_ISR( void )
{
if( P2IFG & 0x01 ) //按键中断
{
osal_start_timerEx( smartHomeApp_TaskID, smartHomeApp_ZFM_EVT, 20 );
}
P2IFG = 0; //清除中断标志
P2IF = 0; //清除中断标志
}
(3)在应用层处理函数中的自定义事件添加处理内容(发送内容)
...
if( events & smartHomeApp_ZFM_EVT )
{
static unsigned char isTwice = 1;
if( Btn1 == 0 )
{
if( isTwice )
{
char theMessageData[] = {'1'};
smartHomeApp_DstAddr.addrMode = ( afAddrMode_t )Addr16Bit;
smartHomeApp_DstAddr.addr.shortAddr = 0x000;
smartHomeApp_DstAddr.endPoint = 7;
AF_DataRequest( &smartHomeApp_DstAddr, &smartHomeApp_epDesc,
0x0001,
// (byte)osal_strlen( theMessageData ) + 1, //表示发送字节个数
1,
( byte* )&theMessageData, //发送的数组的首地址
&smartHomeApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
LED1 = ~LED1;
}
else
{
char theMessageData[] = {'2'};
smartHomeApp_DstAddr.addrMode = ( afAddrMode_t )Addr16Bit;
smartHomeApp_DstAddr.addr.shortAddr = 0x000;
smartHomeApp_DstAddr.endPoint = 7;
AF_DataRequest( &smartHomeApp_DstAddr, &smartHomeApp_epDesc,
0x0002,
// (byte)osal_strlen( theMessageData ) + 1, //表示发送字节个数
1,
( byte* )&theMessageData, //发送的数组的首地址
&smartHomeApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
LED1 = ~LED1;
}
if( isTwice == 1 )
{
isTwice = 0;
}
else
{
isTwice = 1;
}
}
if( Btn2 == 0 )
{
char theMessageData[] = {'3'};
smartHomeApp_DstAddr.addrMode = ( afAddrMode_t )Addr16Bit;
smartHomeApp_DstAddr.addr.shortAddr = 0x000;
smartHomeApp_DstAddr.endPoint = 8;
AF_DataRequest( &smartHomeApp_DstAddr, &smartHomeApp_epDesc,
0x0001,
// (byte)osal_strlen( theMessageData ) + 1, //表示发送字节个数
1,
( byte* )&theMessageData, //发送的数组的首地址
&smartHomeApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
LED1 = ~LED1;
}
return ( events ^ smartHomeApp_ZFM_EVT );
}
...
3 、协调器接收模块配置
(1)定义7、8号端点并与应用层任务挂钩注册。
在工程目录APP下的smartHome.c文件中的smartHomeApp_Init函数对应位置,修改端点注册的代码,如下图。
(2)添加LED1~3初始化函数、串口0 初始化函数
LED1~3初始化函数
串口0 初始化函数
在 ZMain目录下的ZMain.c文件中的main函数中,对应位置添加初始化函数
(3)在工程目录APP下的smartHome.c文件中的smartHomeApp_MessageMSGCB函数中,修改添加数据接收函数。
void smartHomeApp_MessageMSGCB( afIncomingMSGPacket_t* pkt )
{
//外来数据包投入到7号端点
if(pkt->endPoint==7){
//判断接收簇
switch( pkt->clusterId ){
case 0x0001:
InformationSend(pkt->cmd.Data,pkt->cmd.DataLength);
LED1=~LED1;
break;
case 0x0002:
InformationSend(pkt->cmd.Data,pkt->cmd.DataLength);//串口0发送函数,自行编写
LED2=~LED2;
break;
default:
break;
}
}
//外来数据包投入到8号端点
if(pkt->endPoint==8){
//判断接收簇
switch( pkt->clusterId ){
case 0x0001:
InformationSend(pkt->cmd.Data,pkt->cmd.DataLength);//串口0发送函数,自行编写
LED3=~LED3;
break;
default:
break;
}
}
// switch( pkt->clusterId )
// {
// case smartHomeApp_CLUSTERID:
// // "the" message
//#if defined( LCD_SUPPORTED )
// HalLcdWriteScreen( ( char* )pkt->cmd.Data, "rcvd" );
//#elif defined( WIN32 )
// WPRINTSTR( pkt->cmd.Data );
// #endif
// InformationSend(pkt->cmd.Data,pkt->cmd.DataLength);
// LED2 = ~LED2;
// break;
// }
}
参考链接
(1)lesson 8下 Zigbee单播通信理论相关概念原理(端点、簇)_zigbee 簇-CSDN博客
(2)胜达zigbee lesson8中协议栈单播通信理论相关概念原理_哔哩哔哩_bilibili
(3)lesson8下自动动手全面实验验证理论_转_哔哩哔哩_bilibili