SimpliciTI学习手记——从零开始实现SimpliciTI无线通信

SimpliciTI学习手记——从零开始实现SimpliciTI无线通信  2011-12-17 23:37

来自:http://www.eefocus.com/nightseas/blog/11-12/236340_1606e.html

加入收藏
  • 转发分享
    人人网
    开心网
    新浪微博
    搜狐博客
    百度收藏
    谷歌收藏
    qq书签
    豆瓣
    淘江湖
    Facebook
    Twitter
    Digg
    Yahoo!Bookmarks

 

这段时间折腾了一下SimpliciTI,有些个人理解,和大家讨论一下,希望有这方面经验的人不吝赐教,一个人闷头搞确实听没意思的。同时也在考虑从现有的MSP430向更强的M3平台移植(比如Stellaris),有兴趣的朋友可以一起讨论一下呀。

 

一、搭建SimpliciTI环境

一个MCU和一个RF模块,这就是搭建SimpliciTI环境的硬件需求(TISoC也是在芯片内部集成了RF模块)。当然了,这个协议栈是有版权的,当你用作商业用途时需要向TI缴纳一定的费用,而用于学习时,TI也会要求你至少用他的RF芯片。

这里使用的是TIeZ430-RF2500套件,板载MSP430F2274CC2500射频芯片,使用芯片天线,传输距离并不远,如果离开一段距离或者有遮蔽物就会出现丢包,这个我们会在后面进行测试。电路图如下(点击看大图),2274CC2500之间通过SPI和两个IO(中断方式)通信,一般网上卖的模块也会留出这些接口。如果对RF PCB设计没有信心,淘宝上有10块钱的CC2500模块,虽然是白菜价,但是PCB天线一般都是低于0dB的,通信距离可想而知。

 

TI官方的开发套件,$49

 

淘宝上可以找到的CC2500模块,¥10

 

原理图

 

同时,此平台也对外开放一个USB调试接口(包括UARTSBW)。

接口定义

 

二、SimpliciTI协议栈结构与移植

SimpliciTI协议的一大特点就是简单,所以在极大简化开发过程的同时也带了很多限制,比如缺少完备的路由协议,最多4RE(使通信距离受限),即使低功耗也是在ED关闭RF接收功能的前提下实现的(限制双向通信)。不过即使如此,也丝毫不影响它为无线网络通信带来的便利,你可以根据应用需求在ZigBeeSimpliciTI之间取舍。

想要使用它你需要下载协议栈代码,下面是及针对RF2500套件的演示代码:

 

http://www.ti.com/lit/zip/slac139

 

通用协议栈SimpliciTI-IAR1.1.1可以在TI官网上下载:

http://www.ti.com.cn/tool/cn/simpliciti

安装后在安装目录下可以找到代码和说明文档,建议看一下Developers NotesSample Application User's GuideAPI文档,如果需要涉及加密、调频等应用,这里还有相应的Application Note

SimpliciTI本身的软件分为3层,即BSPMRFINWK

 

BSP是一个轻量级板级支持包,包含了MCUSPI(这个必须有)、LEDButton,简单吧,TI的意思是尽可能的减少协议栈本身的硬件内容,因此如果想要用串口或者ADC,就要自行解决,我们暂且将这些额外添加的硬件控制代码称为Driver吧。

MRFI是最小射频接口,主要是与BSP中的SPI对接以及射频芯片的控制代码。如果要移植协议栈,就需要更改与MCU定义、中断、IOSPI相关的代码,不过只要你使用MSP430CC系列SoC作为MCU可以直接在代码中找到相近的Board从而减少工作量。当然啦,如果你是高手或者兼有求知和耐心的美德,甚至可以自己写底层而只保留NWKAPP层,不过这不是我们现在的目的,至少目前不是。

NWK网络层负责数据收发队列,同时包含很多网络层应用,注意区分NWK Applications与用户的应用代码(Customer Applications)的区别,这些网络层应用是以端口作为标识的(如Ping0x01Link0x02),相应的函数以nwk_作为前缀,而我们通常将用户代码称作应用层。SimpliciTI的端口(Port)与TCP/IP的端口神似(similar in spirit ^_^),每个端口对应一种应用,用户也可以定义自己的网络层应用。网络层为用户提供了API函数,以SMPL_为前缀,如SMPL_Link等,我们的工作就是利用这些API实现组网及无线通信。

 

三、建立第一个简单双向通信程序

1)配置工程

打开演示代码eZ430-RF2500 WirelessSensorMonitor中的Sensor_Demo_AP_as_Data_Hub工程,如果你使用的是eZ430-RF2500套件,直接修改其中的Application文件就好了,如果不是TI的官方套件,可以选择一个硬件组成相近的工程,修改MCU定义和按键、LED等配置,这里不再深究,以后可以专门开贴讨论。

 

Tips在下载程序时可能出现这个错误,具体描述为:

Fatal error: The Object file contains features not suported by the driver.   Session aborted!

Failed to load debugee: …

这是因为在新版本的IAR中同时使用了选项配置和命令行,Debugger的版本较旧不识别,只需要取消命令行即可。

 

打开工程后可以看到两种器件,APAccess Point)和EDEnd Device)。AP是组网的关键,负责网络的建立和维护,同时负责对外通信,有点类似于ZigBee中的协调器。网络中最多只能有一个AP,在某些模式下(如P2P)可以不需要AP,但是这样的网络需要固定参数而缺少灵活性。ED是终端设备,在TI的演示中负责入网后通过片上温度传感器采集自身的温度,并发送给AP,由AP将接收到的数据转换成字符串后通过串给上位机。

在左侧的WorkSpace栏选择AP或者ED进行选择编译。

 

和网络相关的设置可以在几个配置文件中修改,其中smpl_config_AP.datsmpl_config_ED.dat文件定义了设备类型、最大连接数、发送和接收帧队列长度等内容,smpl_nwk_config.dat文件则定义了基本的网络参数如网络和应用层负载长度、最大跳数等,这些配置可以在不同的应用中灵活选择,这里我们采用默认配置。

 

2AP节点

首先,通过BSP_Init函数初始化BSP,然后初始化需要的外围设备如串口、定时器、AD等,这里的串口函数并不包含在SimpliciTI中,而是以virtual_com_cmds.c文件的形式添加到工程中,也可以像我那样写自己的串口函数(参见我的SED430-RF2500一贴),甚至加入printfscanf

SED430-RF2500传送门:http://www.eefocus.com/nightseas/blog/11-12/236080_f6d24.html

在设备初始化完成后,需要初始化SimpliciTI网络,AP中对应的函数为:

SMPL_Init(sCB);

这里的sCB是一个回调函数,本例中用来在接收到帧时区分是入网还是数据,并分别处理,它会在CC2500 RX接收中断后调用。想要研究协议结构的童鞋可以一路Go to definition找到函数调用的地方,现在我们只需定义该函数而无须理会调用机制。

回调函数的原型如下,当lid0时,表示有设备入网,此时sJoinSem++,在主函数的sJoinSem子过程中进行处理,建立连接并为其分配LinkID。如果lid不为零则表示该设备已经入网并分配了LinkID,这个数据帧被作为通信数据在主函数的sPeerFrameSem子过程中通过SMPL_Recieve函数接收。

static uint8_t sCB(linkID_t lid)

{

  if (lid)

  {

    sPeerFrameSem ;

 

  }

  else

  {

    sJoinSem ;

  }

  return 0;

}

至此整个初始化工作完成,然后AP进入while(1)循环,这个循环中包含几个子过程,这里我们只需要sJoinSemsPeerFrameSem两个过程。

sJoinSem过程内容如下:

    if (sJoinSem && (sNumCurrentPeers < NUM_CONNECTIONS))

    {

      while (1)

      {

        if (SMPL_SUCCESS == SMPL_LinkListen(&sLID[sNumCurrentPeers]))

        {

          break;

        }

      }

      sNumCurrentPeers ;

      BSP_ENTER_CRITICAL_SECTION(intState);

      sJoinSem--;

      BSP_EXIT_CRITICAL_SECTION(intState);

    }

一旦进入这个过程(有设备入网),软件将持续通过SMPL_LinkListen函数监听ED发起的链接,在链接建立后会为该链接分配一个LinkID,以后数据收发就考这个LinkID作为标识。该函数的实参就是一个用来存放LinkID的数组。sNumCurrentPeers是当前连接设备计数器,这里要注意的是在处理完入网帧后,需要减少sJoinSem的值,而sJoinSem是一个公共变量,同时受回调函数的控制。sCB是一个在中断中执行的函数,所以要通过BSP_ENTER_CRITICAL_SECTION进行临界保护,参数intState其实是一个unsigned short型变量,在主函数中定义,用于保存相关寄存器,在退出临界状态时恢复。

对接收到的通信数据的处理则要靠sPeerFrameSem

    if (sPeerFrameSem)

    {

      uint8_t     msg[MAX_APP_PAYLOAD], len, i;

      for (i=0; i

      {

        if (SMPL_SUCCESS == SMPL_Receive(sLID[i], msg, &len))

        {

          USCI0_SendDataString(msg, len);

          BSP_ENTER_CRITICAL_SECTION(intState);

          sPeerFrameSem--;

          BSP_EXIT_CRITICAL_SECTION(intState);

        }

      }

    }

SMPL_Receive函数读取接收到的数据帧,将APP_PAYLOAD分离出来保存在msg数组中,len作为实参保存msg的长度,最后通过USCI0_SendDataString发送给上位机。

void USCI0_SendDataString(uint8_t *msg, uint8_t len)

{

  int i;

  for(i=0;i

    USCI0_PutChar(msg[i]);

}

其中USCI0_PutChar这个函数需要自己写,如果是带有USCI模块的430可以使用我的SED430-RF2500中的UART.c,若是USART模块的430则参见SED430中的串口文件。传送门上面已经给出。

那么如果要给ED发数据怎么办呢,依然只要一个函数即可:

定义一个msg数组,写入要发送的数据,然后:

SMPL_Send(lid, msg, len);

lid是要目的设备的LinkIDlen是要发送数据的长度,这里len和接收函数不同是形参,注意不要超过最大负载长度哦。

这就是AP的部分,可以完成入网,数据接收发送功能,另外还有一些跳频和信号强度检测的功能,可以自己研究一下。

 

3ED节点

修改代码之前,需要给ED设置一个32位地址,这个地址相当于TCP/IP中的IP地址,由于SimpliciTI没有MAC层,所以设备就要靠这个地址识别数据包是不是发给自己的。网络中每一个设备的地址都应当是唯一的,否则会发生冲突。可以在对应设备的.dat配置文件中修改这条语句:

-DTHIS_DEVICE_ADDRESS="{0x7A, 0x56, 0x34, 0x12}"

若不想一个一个设置,可以参考范例共给出的产生随机地址的方法,通过ADC或者VLO采集一个真随机数(虽然是真随机数不过不知道是什么分布的),换算成地址并写入Flash

EDAP的设备初始化过程一样,初始化完成后,开始检测是否存在网络并,直到检测到AP建立的网络并入网。__bis_SR_register函数的作用是休眠并等待TA中断,如果不想采用这种方式,也可以通过__delay_cycles函数或自己编写Delay函数实现延时。

  //搜寻并加入网络

  while (SMPL_SUCCESS != SMPL_Init(sCB))

  {

    BSP_TOGGLE_LED1();  //LED闪烁

    BSP_TOGGLE_LED2();

    __bis_SR_register(LPM0_bits GIE);

  }

如果ED不需要接收数据,SMPL_Init时可以不要回调函数:SMPL_Init(0)。如果需要接收数据,则应当开启RX

SMPL_Ioctl( IOCTL_OBJ_RADIO, IOCTL_ACT_RADIO_RXON, 0);

ED的回调函数中只处理数据帧而没有入网帧,其形式如下:

static uint8_t sCB(linkID_t lid)

{

  if (lid)

  {

    sPeerFrameSem ;

  }

  return 0;

}

ED在加入网络后还不能直接和AP通信,首先需要建立一个连接(Link):

  //AP发起Link

  BSP_TOGGLE_LED1(); 

  while (SMPL_SUCCESS != SMPL_Link(&LID_AP))

  {

    BSP_TOGGLE_LED1();

    BSP_TOGGLE_LED2();

    __bis_SR_register(LPM0_bits GIE); //替换成你自己的延时函数

  }

连接建立好以后,APLinkID被保存在LID_AP变量中,因为我们只需要和一个固定的AP通信,所以不需要数组来保存多个LinkID

至此,ED已经可以和AP自由的交换数据了,在sPeerFrameSem子过程中,我们将接收到的数据发还给AP并翻转一个LED

    //接收到RF数据

    if(sPeerFrameSem)

    {

      uint8_t msg[MAX_APP_PAYLOAD], len;

      if (SMPL_SUCCESS == SMPL_Receive(LID_AP, msg, &len))

      {

        SMPL_Send(LID_AP, msg, len); //发回接收到的数据

        BSP_TOGGLE_LED2(); //翻转LED状态

        BSP_ENTER_CRITICAL_SECTION(intState);

        sPeerFrameSem--;

        BSP_EXIT_CRITICAL_SECTION(intState);

      }

    }

搞定,可以编译运行了。

设置Show build messages显示全部消息,编译……

 

 

可以看到软件占用资源情况,想要运行这个协议栈,还要跑一些自己的程序,至少要有10KBFlash1KBRAM,不过比起ZigBee那样的重量级协议,已经非常节省了。

 

四、运行结果

这是本例的工程文件(IAR5.30),分别将APED的程序下载到模块中。

RF2500_Example_01.rar
 

装好各模块:

 

AP模块通过USB Debugger连接PC

 

ED模块,这里外接了电源

 

打开你的串口调试工具,发送数据。接收到的数据与发送的一致,表明整个通信没有问题。同时,每发送一帧数据,ED模块的LED的状态会改变一次。这里设置了定时发送:

 

 

你可能感兴趣的:(SimpliciTI学习手记——从零开始实现SimpliciTI无线通信)