TI15.4STACK协议栈解读



虽然TI的官方资料已经很全了,但是受限于资料体系庞大,还都是英文文档,所以想要很快的掌握TI15.4STACK协议栈还是比较困难的,所以笔者也是经过一段时间摸索,才渐渐入门了一些,这里把我理解的一些东西分享出来,希望能帮助大家更快的理解TI15.4STACK协议栈,这也是对自己这段时间学习的总结和知识梳理。

走进协议栈的学习之路


 授人以鱼不如授人以渔,我先分享一下自己学习过程中参考的资料和学习方法,以便读者后续自学巩固提高。
 后面代码解读需要读者已经拥有TI处理器的开发环境并且有一定的嵌入式开发经验;然后对无线通信技术有基本了解;对嵌入式操作系统有基本了解,像任务,信号量,消息队列等;对C语言能熟练掌握,特别是指针和结构体的理解要到位,因为TI的程序中大多使用结构体和指针进行配置。

官方SDK的获取

SDK 就是 Software Development Kit 的缩写,中文意思就是“软件开发工具包”。

1.TI官方链接: CC2652SDK获取
在这里插入图片描述
2.安装后TI的官方SDK后,在你的安装目录下就还有TI的很多辅助开发资料,有说明文档,API文档,例程,库文件等。
在这里插入图片描述
3.我们打开SDK文件下的docs\ti154stackti-15.4-stack-users-guide.html文件,这里推荐使用Google Chrome浏览器,可以一键翻译,对于阅读TI的英文文档很有帮助。
在这里插入图片描述

辅助软件

返回至目录
对于TI的PDF格式英文帮助文档,建议使用国产的WPS软件,可以划词翻译,也很方便。
在这里插入图片描述
对于代码中的英文,可以使用网易有道词典,划词翻译。
在这里插入图片描述

学习渠道

返回至目录
4.TI官方也有学习平台和技术论坛。
TI的中国培训视频: 可直接点击标题进入视频链接。

什么是TI15.4STACK协议栈

返回至目录

  首先我们要知道什么是TI15.4STACK协议栈?


  TI 15.4-Stack 是一种 IEEE 802.15.4e/g 射频通信堆栈。它是 SimpleLink CC13xx/CC26x2 软件开发套件 (SDK) 的主要部分,根据您选择的器件,为低于 1GHz 应用或 2.4GHz 应用的星形拓扑网络提供支持。
   协议栈已经实现了网络创建,无线设备的加网退网,数据的加密传输,无线发送的防碰撞检测,数据发送,确认和重传机制,设备地址分配,跳频算法等一系列的功能,可以非常方便的,让用户使用它快速开发1G以下产品
在这里插入图片描述

既然你看到这篇博客,估计你也是在做无线通讯项目,而无线通讯中两个节点之间或者网关与子节点之间通讯就要使用到通信协议,协议主要是双方约定以什么样的方式,什么样的时序,什么样的频率进行通信。
  就像我们最常用的串口通信协议一样,双方约定好波特率,约定好数据位等,简单的协议就能完成通信了。那么TI的15.4协议则是遵循IEEE 802.15.4e/g标准,实现无线通讯的。
  协议栈其实就是封装好的代码,按约定的协议写好的库文件,一个Library,然后很方便的供开发者使用API接口来开发,而不用去管底层驱动就能实现通讯功能。
  而TI15.4STACK协议栈就是由TI官方完成了PHY和MAC层的库文件,然后预留了很多API接口,可以让开发者很轻松的基于协议栈,继续开发网络层和应用层的代码。


TI15.4STACK协议栈用户指南——导读

返回至目录

  • 下面我们打开SDK文件下的docs\ti154stackti-15.4-stack-users-guide.html文件,详细解读一下TI15.4STACK协议栈。
    在这里插入图片描述
      对于上图可以理解为TI的15.4STACK支持对于单设备整体开发协同开发。对于想实现功能不多,数据处理量不大的无线小项目,可以直接将应用层代码和协议栈放在一起开发,直接一块MCU就能解决方案;而如果要实现比较复杂的功能,数据处理量庞大,可以使用协同开发方案,让装有TI15.4协议栈的MCU作为外设,然后与主机通过串口交互数据,这样也简化了无线产品的开发过程。
    在这里插入图片描述
    在这里插入图片描述

关于2.4G信道的介绍,请参考这篇知乎博文链接 为什么2.4G信道是13个

上图介绍的关于无线通讯速率和PHY配置,这在程序中都可以根据具体需求在程序中修改配置。如下图所示在.syscfg文件中可通过RF DesignTI 15.4 STACK快速配置。
在这里插入图片描述
在这里插入图片描述
  关于配置完.syscfg文件后,代码中又是发生了什么变化,具体见我用 VISIO 2016 绘制的关系图。
从百度网盘获取链接: 提取码:irol

在这里插入图片描述


例程导读

返回至目录
  下面开始打开官方提供的示例应用程序,主要是collectorsensor两个例程,一个作为网关采集数据,一个作为节点发送传感器数据。
  

  1. 先演示一下用CCS导入SDK中的collector例程。(sensor的例程打开同理)
    在这里插入图片描述
  2. 然后我们来看应用架构。
    在这里插入图片描述
      在应用架构中,最底层的已经由TI15.4STACK协议栈完成,即MAC和PHY层,这层代码也是被封装起来了,我们看不见源码。但其开放了API接口供我们调用,并且也有API的用户手册可以查阅,所以对于我们用来二次开发,主要是读懂TI15.4STACK的API文档,会通过API函数初始化配置无线通讯,通过API函数发送和接收无线通讯的信息。
      由架构图还能看出在API接口之上,应用层还集成了逻辑链路层代码(实线框)(然后自己可以再继续编写网络层以及应用层代码)、可选的安全密钥功能和空中下载功能(虚线框)及SF程序(采集器或传感器应用例程的定义函数)和CUI程序(用户公用接口,如IIC,SPI,UART,GPIO)。然后整体通过TI的实时操作系统在管理和调度。
      
    在这里插入图片描述
  3. 然后我们展开Sensor 和 Collector 示例应用程序的基本应用程序源树。
    在这里插入图片描述
    更详细的描述见SDK文件下的docs\ti154stackti-15.4-stack-users-guide.html文件。

当然在例程下还有很多模板可以学习参考。
在这里插入图片描述

Main()函数解读

返回至目录
这里以sensor的main函数为例。

int main(void)
{
    Task_Params taskParams;
    macUser0Cfg[0].pAssertFP = assertHandler;
    /*
     Initialization for board related stuff such as LEDs
     following TI-RTOS convention
     */
    Board_init();
    /* Initialize CUI UART*/
    CUI_params_t cuiParams;
    CUI_paramsInit(&cuiParams);
    // One-time initialization of the CUI
    // All later CUI_* functions will be ignored if this isn't called
    CUI_init(&cuiParams);
    _macTaskId = macTaskInit(macUser0Cfg);
    /* Configure task. */
    Task_Params_init(&taskParams);
    taskParams.stack = appTaskStack;
    taskParams.stackSize = APP_TASK_STACK_SIZE;
    taskParams.priority = APP_TASK_PRIORITY;
    Task_construct(&appTask, appTaskFxn, &taskParams, NULL);
    BIOS_start(); /* enable interrupts and start SYS/BIOS */
    return (0);
}

以上相比于源代码,我删去了宏编译部分精简化后,看看主函数中都进行了哪些配置。

  1. 首先定义了任务参数的结构体变量Task_Params taskParams
    在这里插入图片描述
    结构体里的成员我们暂时不用全部弄清楚,在后面配置中只需要配置堆栈的参数即可。
  2. 是一个结构体数组[0]的一个成员赋值macUser0Cfg[0].pAssertFP = assertHandler;

    该结构体声明是一个全局变量,并给了初值。
    具体见我用 VISIO 2016 绘制的关系图。
    从百度网盘获取链接: 提取码:d72l
    在这里插入图片描述
  3. Board_init();初始化板卡。
  4. CUI初始化(the Combined User Interface)
    在这里插入图片描述
  5. _macTaskId = macTaskInit(macUser0Cfg);初始化MAC收发任务,并传入实参macUser0Cfg即(2)的结构体数组,并返回MAC任务ID。
    在这里插入图片描述
    macTaskFxn任务的优先级和堆栈配置如下:
    在这里插入图片描述
  6. appTaskFxn用户程序的任务初始化。
    在这里插入图片描述
    在这里插入图片描述
  7. BIOS_start();使能中断,开始操作系统任务调度


  那么以上就将Main主函数的内容解读完毕,主要是建立了两个任务 appTaskFxnmacTaskFxn,即用户程序任务和mac驱动任务,优先级分别是1和3,数字越小,优先级越高。
  而至于两个任务主要处理了什么内容,用户怎么通过API发送和获取无线通信的报文呢?接着往下看SDK文件下的docs\ti154stackti-15.4-stack-users-guide.html文件,来了解OSALPort服务是怎么起到桥梁作用,将两个任务 appTaskFxnmacTaskFxn串联起来。

OSALPort 协议栈服务

返回至目录

OSAL为Operating System Abstraction Layer,即操作系统抽象层,支持多任务运行,其中协议栈、配置文件以及所有的应用程序(app)都在其上运行,它并不是一个传统意义上的操作系统,但是实现了部分类似操作系统的功能

在这里插入图片描述
在这里插入图片描述
  OSALPort 协议栈服务可以理解为生存在TIRTOS和TI15.4协议栈之间的调度员,它能结合操作系统的特性很好的将MAC层接收的信息传递给应用层,也能很好的将应用层的消息传递给MAC层,然后无线发送出去。

OSALPort的功能

返回至目录  
在这里插入图片描述
  下面这段我没有用谷歌的翻译,因为它翻译的错误较多,不好说清。这段主要解释,OSALPort作为桥梁或作为调度者,除了初始化还要让那些需要他来管理的任务们(Task)在它那注册一下,我们知道sensor这个例程目前主要有两个任务,分别是 appTaskFxnmacTaskFxn,即用户程序任务和mac驱动任务,所以这两个任务在初始化时都执行了OsalPort_registerTask();这个函数,就是在OSALPort服务中注册一下自身,并绑定了各自的信号量和记录事件。

OSALPort的初始化和注册

返回至目录
在这里插入图片描述
在这里插入图片描述

那么OSALPort服务在程序中具体怎么注册的,因为截图太小,看不清。
见我用 VISIO 2016 绘制的关系图。
从百度网盘获取链接: 提取码:w7ln
在这里插入图片描述

OSALPort线程同步

返回至目录
OSALPort是通过信号量来同步不同任务之间的信号。
在这里插入图片描述

示例 OSALPort 用法

返回至目录
在这里插入图片描述
   上面一段意思是说在APP层即应用层(appTaskFxn任务)想发送命令给协议栈,就可以通过cllc.c(collecter例程的)或jdllc.c(sensor例程的)中调用的ApiMac_mlmeSetReqArray()函数,直接给协议栈设置一个属性值并等待协议栈配置完属性后的信号量。然后协议栈执行完post一个信号量,通过OSALPort服务返回给APP层。
在这里插入图片描述
在这里插入图片描述

应用层收发数据

返回至目录

  经过前面的学习,我们最终目的是想通过TI15.4STACK协议栈编写自己的应用,那么就涉及到怎么去修改collectersensor例程,应用层通过哪个API接口函数来收发数据,那么最后让我们结合例程,学习一下例程中数据收发的操作。
这一章学前须知:

  1. SDK文件下的docs\ti154stackti-15.4-stack-users-guide.html文件后面的还有些内容,读者可以接着往下看,继续研究,不过我觉得重点不多了。
  2. 后面例程主要以collectersensor为主,读者可以先用CCS开发平台先导入这两个例程。导入例程前面有演示过程
  3. 导入例程后,可以在工程目录下看到README.md文件,这也可以用Google Chrome浏览器打开并翻译,看看例程要实现的功能是什么,这样修改起例程才更得心应手。


  首先我们看一下sensor例程。
  在前面我们分析过main函数中的操作,主要是建立了两个任务 appTaskFxnmacTaskFxn,到现在还没看到两个任务的庐山真面目。
appTaskFxn

Void appTaskFxn(UArg a0, UArg a1)
{
    /* Copy the extended address from the CCFG area */
    CCFGRead_IEEE_MAC(ApiMac_extAddr);
    /* Check to see if the CCFG IEEE is valid */
    if(memcmp(ApiMac_extAddr, dummyExtAddr, APIMAC_SADDR_EXT_LEN) == 0)
    {
        /* No, it isn't valid.  Get the Primary IEEE Address */
        memcpy(ApiMac_extAddr, (uint8_t *)(FCFG1_BASE + EXTADDR_OFFSET),
               (APIMAC_SADDR_EXT_LEN));
    }
    /* Setup the NV driver */
    NVOCMP_loadApiPtrs(&Main_user1Cfg.nvFps);
    if(Main_user1Cfg.nvFps.initNV)
    {
        Main_user1Cfg.nvFps.initNV( NULL);
    }
    /* Initialize the application */
    Sensor_init(_macTaskId);
    /* Kick off application - Forever loop */
    while(1)
    {
        Sensor_process();
    }
}

以上相比于源代码,我删去了宏编译部分精简化后,看看appTaskFxn中都进行了哪些配置。

  1. CCFGRead_IEEE_MAC(ApiMac_extAddr);首先是从CCFG区域读取IEEE地址
  2. Main_user1Cfg.nvFps.initNV初始化内部FALSH
  3. Sensor_init(_macTaskId);初始化传感器,注意这里传入的实参是macTaskId

Sensor_init(_macTaskId);

void Sensor_init(uint8_t macTaskId)
{
    ApiMac_deviceDescriptor_t devInfo;
    Llc_netInfo_t parentInfo;
    uint32_t frameCounter = 0;
    /* Initialize the sensor's structures */
    memset(&configSettings, 0, sizeof(Smsgs_configReqMsg_t));
    configSettings.frameControl |= Smsgs_dataFields_tempSensor;
    configSettings.frameControl |= Smsgs_dataFields_msgStats;
    configSettings.frameControl |= Smsgs_dataFields_configSettings;
    if(!CERTIFICATION_TEST_MODE)
    {
        configSettings.reportingInterval = CONFIG_REPORTING_INTERVAL;
    }
    else
    {
        /* start back to back data transmission at the earliest */
        configSettings.reportingInterval = 100;
    }
    configSettings.pollingInterval = CONFIG_POLLING_INTERVAL;

    /* Initialize the MAC */
    sem = ApiMac_init(macTaskId, CONFIG_FH_ENABLE);

    /* Initialize the Joining Device Logical Link Controller */
    Jdllc_init(&Sensor_macCallbacks, &jdllcCallbacks);

    /* Register the MAC Callbacks */
    ApiMac_registerCallbacks(&Sensor_macCallbacks);

    /* Initialize the platform specific functions */
    Ssf_init(sem);

    ApiMac_mlmeSetReqUint8(ApiMac_attribute_phyCurrentDescriptorId,
                           (uint8_t)CONFIG_PHY_ID);

    ApiMac_mlmeSetReqUint8(ApiMac_attribute_channelPage,
                           (uint8_t)CONFIG_CHANNEL_PAGE);

    Ssf_getFrameCounter(NULL, &frameCounter);

    /* Initialize the MAC Security */
    Jdllc_securityInit(frameCounter, NULL);
    
    /* Set the transmit power */
    ApiMac_mlmeSetReqUint8(ApiMac_attribute_phyTransmitPowerSigned,
                           (uint8_t)CONFIG_TRANSMIT_POWER);
    /* Set Min BE */
    ApiMac_mlmeSetReqUint8(ApiMac_attribute_backoffExponent,
                              (uint8_t)CONFIG_MIN_BE);
    /* Set Max BE */
    ApiMac_mlmeSetReqUint8(ApiMac_attribute_maxBackoffExponent,
                              (uint8_t)CONFIG_MAX_BE);
    /* Set MAC MAX CSMA Backoffs */
    ApiMac_mlmeSetReqUint8(ApiMac_attribute_maxCsmaBackoffs,
                              (uint8_t)CONFIG_MAC_MAX_CSMA_BACKOFFS);
    /* Set MAC MAX Frame Retries */
    ApiMac_mlmeSetReqUint8(ApiMac_attribute_maxFrameRetries,
                              (uint8_t)CONFIG_MAX_RETRIES);
    /* Initialize the app clocks */
    initializeClocks();
    
    if(Ssf_getNetworkInfo(&devInfo, &parentInfo) == true)
    {
        /* Update Channel Mask to show the previous network channel */
        if (!CONFIG_FH_ENABLE)
        {
            uint8_t channelMask[APIMAC_154G_CHANNEL_BITMAP_SIZ] = {0};
            uint8_t idx = parentInfo.channel / 8;
            uint8_t shift = (parentInfo.channel % 8);
            uint8_t chan = (0x01) << shift;
            channelMask[idx] = chan;
            Jdllc_setChanMask(channelMask);
        }
        /* Start the device */
        Util_setEvent(&Sensor_events, SENSOR_START_EVT);
    }
    else if (CONFIG_AUTO_START)
    {
        /* Start the device */
        Util_setEvent(&Sensor_events, SENSOR_START_EVT);
    }
}

以上相比于源代码,我删去了宏编译部分精简化后,看看Sensor_init中都进行了哪些配置。
其实每行都有英文注释,主要是和传感器相关的数据初始化和外设初始化。我们注意这几行:

 /* Initialize the MAC */
   sem = ApiMac_init(macTaskId, CONFIG_FH_ENABLE);

   /* Initialize the Joining Device Logical Link Controller */
 Jdllc_init(&Sensor_macCallbacks, &jdllcCallbacks);

 /* Register the MAC Callbacks */
ApiMac_registerCallbacks(&Sensor_macCallbacks);

 /* Initialize the platform specific functions */
Ssf_init(sem);

分别初始化了与MAC任务的联系,逻辑链路层的控制,MAC的回调函数配置和外设接口配置(按钮,LED,串口)。
然后看看sem = ApiMac_init(macTaskId, CONFIG_FH_ENABLE);中执行了哪些内容。
在这里插入图片描述
4. Sensor_process();传感器函数的主要进程。在这里主要通过事件掩码MASK来检索发生的事件,也是在该进程里可以看到无线数据的收发过程。所以比较重要,也是比较难理解的地方,了解了这块后面便可以尝试自己修改程序,完成自己想要的功能。

Sensor_process()

返回至目录
在这里插入图片描述
  首先,在Sensor_process()通过各类事件掩码,触发对应程序,其中列举了三个事件SENSOR_START_EVT SENSOR_READING_TIMEOUT_EVT0
  在SENSOR_READING_TIMEOUT_EVT条件中,有个发送传感器温度的函数,看它怎么发送数据的。
在这里插入图片描述
  所以最终是通过api_mac.h中官方封装的API函数ApiMac_mcpsDataReq()把传感器的数据发送出去的。
  然后发送完数据,传感器还要接收网关的回复数据,那么看传感器是怎么接收数据的。
在这里插入图片描述
  所以接收数据,是通过系统监测信号量appSemHandle,然后通过回调函数dataIndCB()处理数据,而数据存放在msdu.p这个指针下。
  而在collector例程中,主要不同就是接收处理的函数dataIndCB()里面处理的数据内容更多。
在这里插入图片描述
在这里插入图片描述

更多数据收发的细节见我用 VISIO 2016 绘制的关系图。
从百度网盘获取链接: 提取码:87r8
在这里插入图片描述

以上就是我学习整理的一些内容,希望能给正遇到此类问题的你带来一些帮助,也欢迎大家在评论区留言,我也会继续和大家交流分享。

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值