第一次写博客,觉得应该把学习的心得体会记下来!长期更新。希望能帮助到新手少走弯路,也希望能提出问题,大家一起讨论,一起进步。转载请标明出处。谢 谢 http://blog.csdn.net/libin55/article/details/51203727
简要说明:
SimpliciTI 是TI针对简单低射频网络的低功耗射频协议。
支持两种网络拓扑结构,小型星型网络与点对点通信,星型网络最大支持30个节点,相对Zigbee协议栈而言,该协议栈资源小,仅需8KFlash左右,(本人大概占用10K)移植也很简单。可以很好的兼容
小数据低功耗应用场合。
本章主要讲解SimpliciTI星型网络应用部分。
一、协议栈组网原理
现科普一下星型组网的原理吧!
首先,射频模块之间能进行正常的数据收发。我们规定在网络中只允许存在一个中心节点,他可以与网络中任一子节点通信,这样便实现了对网络的监控与管理!
子节点与子节点是不能直接通信的,必须通过中心节点才能实现通信。下图为星型网络拓扑结构图,看图就比较好理解了
二、星型网络结构
星型网络由三部分组成。
中心节点(AP):星型网络的管理节点,是长供电的设备。单个网络仅可允许存在一个AP,协议栈中不同网络是通过宏定义JoinToken进行区分。
传感节点(ED):传感设备子节点,可进入睡眠模式(主要电池供电方式)。
范围扩展(RE):扩展网络的无线距离(路由功能),实际就是在转发数据帧,以扩展发送者的有效距离。目前网络最大支持4个RE,也属于长供电设备。
二、协议栈移植
协议栈源码可以到TI官网下载,可能官网下载比较慢,这里就我直接上传了。
安装后会在根目录生成Texas Instruments文件夹,里面有对应的例程源码与英文文档。里面对应的是IARfor8051与IARforMPS430的工程。这里我就以8051的工程进行解读把。
针对IAR上面的目录文件讲解一下吧
bsp:板级支持包,主要是板载的一些外围器件的初始化与MCU寄存器的配置。
mrfi:射频驱动的代码,CC2530是嵌入射频部分,内核是8051的。该文件内核心代码为mrfi_radio.c,关于射频的配置接口都在这里面。
nwk:网络层底层数据发送与接收的接口,主要为协议层部分。
nwk applications:网络层上层应用代码,实际开发中使用的接口。
我们如果需要移植其他平台的话,其实只需要修改bsp与mrfi里面的部分代码,而网络协议层是不需要进行修改的。
三、协议栈应用部分
好了,开始讲应用部分!直接进入main函数,这段针对代码讲解。
void main (void)
{
bspIState_t intState;
#ifdef FREQUENCY_AGILITY
memset(sSample, 0x0, sizeof(sSample));
#endif
BSP_Init();
/* If an on-the-fly device address is generated it must be done before the
* call to SMPL_Init(). If the address is set here the ROM value will not
* be used. If SMPL_Init() runs before this IOCTL is used the IOCTL call
* will not take effect. One shot only. The IOCTL call below is conformal.
*/
#ifdef I_WANT_TO_CHANGE_DEFAULT_ROM_DEVICE_ADDRESS_PSEUDO_CODE
{
addr_t lAddr;
createRandomAddress(&lAddr);
SMPL_Ioctl(IOCTL_OBJ_ADDR, IOCTL_ACT_SET, &lAddr);
}
#endif /* I_WANT_TO_CHANGE_DEFAULT_ROM_DEVICE_ADDRESS_PSEUDO_CODE */
SMPL_Init(sCB); //初始化通信系统和simpliciti的协议栈
/* green and red LEDs on solid to indicate waiting for a Join. */
if (!BSP_LED2_IS_ON())
{
toggleLED(2);
}
if (!BSP_LED1_IS_ON())
{
toggleLED(1);
}
/* main work loop */
while (1)
{
/* manage FHSS schedule if FHSS is active */
FHSS_ACTIVE( nwk_pllBackgrounder( false ) );
/* Wait for the Join semaphore to be set by the receipt of a Join frame from a
* device that supports an End Device.
*
* An external method could be used as well. A button press could be connected
* to an ISR and the ISR could set a semaphore that is checked by a function
* call here, or a command shell running in support of a serial connection
* could set a semaphore that is checked by a function call.
*/
if (sJoinSem && (sNumCurrentPeers < NUM_CONNECTIONS))
{
/* listen for a new connection */
while (1)
{
if (SMPL_SUCCESS == SMPL_LinkListen(&sLID[sNumCurrentPeers]))
{
break;
}
/* Implement fail-to-link policy here. otherwise, listen again. */
}
sNumCurrentPeers++;
BSP_ENTER_CRITICAL_SECTION(intState);
sJoinSem--;
BSP_EXIT_CRITICAL_SECTION(intState);
}
/* Have we received a frame on one of the ED connections?
* No critical section -- it doesn't really matter much if we miss a poll
*/
if (sPeerFrameSem)
{
uint8_t msg[MAX_APP_PAYLOAD], len, i;
/* process all frames waiting */
for (i=0; i<sNumCurrentPeers; ++i)
{
if (SMPL_SUCCESS == SMPL_Receive(sLID[i], msg, &len))
{
processMessage(sLID[i], msg, len);
BSP_ENTER_CRITICAL_SECTION(intState);
sPeerFrameSem--;
BSP_EXIT_CRITICAL_SECTION(intState);
}
}
}
if (BSP_BUTTON1())
{
SPIN_ABOUT_A_QUARTER_SECOND; /* debounce */
changeChannel();
}
else
{
checkChangeChannel();
}
BSP_ENTER_CRITICAL_SECTION(intState);
if (sBlinky)
{
if (++sBlinky >= 0xF)
{
sBlinky = 1;
toggleLED(1);
toggleLED(2);
}
}
BSP_EXIT_CRITICAL_SECTION(intState);
}
}
main函数最开始是初始化板级支持包Bsp_Init();
接着初始化协议栈SMPL_Init(sCB);
这里sCB对应的是回调函数,针对AP来说这个是必须加的,因为AP(协调器)是肯定要接收ED(节点)的数据的。而对于只上报的节点设备,sCB就可以忽略。
整个程序的核心在接收中断里,很庞大,简单解说就是通过数据帧剥离出端口,后面调用回调函数sCB,向应用层提示本帧数据是Join帧还是通用的数据帧。在main.c里面定义了两个全局变量。
static volatile uint8_t sPeerFrameSem = 0; /* 数据帧标识,每接收到一桢新数据就会通过回调加一,然后主循环查询进行数据的读取
static volatile uint8_t sJoinSem = 0; /* Join帧标识变量,ED请求加入时会通过回调加一,主循环查询后进行LINK的监听*/
static uint8_t sCB(linkID_t lid)
{
if (lid)
{
sPeerFrameSem++;
sBlinky = 0;
}
else
{
sJoinSem++;
}
/* leave frame to be read by application. */
return 0;
}
所以在mian函数里面,一直轮询sPeerFrameSem和sJoinSem两个变量。
smplStatus_t SMPL_Receive(linkID_t lid, uint8_t *msg, uint8_t *len)
读取数据帧接口函数,lid针对ED而已是唯一的,AP的话是根据ED申请LINK时按顺序分配的。*msg与*len就是接收数据的缓存与长度了
发送其实是一样的意思
smplStatus_t SMPL_Send(linkID_t lid, uint8_t *msg, uint8_t len)
从上面的代码来看,sCB函数内确实是这样做的!其实TI代码分层,程序模块化写的很好(很有借鉴意义),对于只关心应用的用户来说,不需要关心具体底层的实现方法。主函数代码可读性比较高!降低代码之间耦合性。
最后我们只需要根据变量标识去读写数据就可以了。
实际API接口比较多,下章我会细讲接收中断与协议栈本身的代码。先上传分中英文的API文档把~英文不好的小伙伴们可以下载看看http://download.csdn.net/detail/libin55/9500682