• CC2530 light_switch分析


    一些关键字:

    CCM  - Counter with CBC-MAC (mode ofoperation)

    HAL   - HardwareAbstraction Layer      (硬件抽象层)

    PAN   - PersonalArea Network          (个人局域网)

    RF    - RadioFrequency                (射频)

    RSSI  - Received SignalStrength Indicator  (接收信号强度指示)

     

    本实现讲解的主要内容有分三部分:

    1、工程文件介绍

    2、Basic RF layer介绍及其工作过程

    3、light_switch.c代码详解

     

    一、工程文件介绍

      文件夹结构大至如下,仅列出CC2530 BasicRF目录一些相关的的文件夹:每个文件夹里面放着什么东西,如果缺少其中某些,我们的灯还是否可以点亮呢?我们来一一探讨:

     

    图1. 文件夹结构图

    Ø docs文件夹:

      打开文件夹里面仅有一个名为CC2530_Software_ExamplesPDF文档,文档的主要内容是介绍BasicRF的特点、结构及使用,如果读者有TI的开发板的话阅读这个文档就可以做Basic RF里面的实验了,从中我们可以知道,里面Basic RF包含三个实验例程:无线点灯、传输质量检测、谱分析应用。下面讲解的内容中也有部分内容是从这个文档中翻译所得,是一份相当有价值的参考资料。

    Ø Ide文件夹:

      打开文件夹后会有三个文件夹,及一个cc2530_sw_examples.eww工程,其中这个工程是上面提及的三个实验例程工程的集合,当然也包含了我们无线点灯的实验工程!在IAR环境中打开,在workspace看到

    • IdeSettings文件夹:

    是在每个基础实验的文件夹里面都会有的,它主要保存有读者自己的IAR环境里面的设置。

    •  Idesrf05_CC2530文件夹:

    里面放有三个工程,light_switch.ewwper_test.ewwspectrum_analyzer.eww  如果读者不习惯几个工程集合在一起看,也可以在这里直接打开你想要用的实验工程。

    Ø source文件夹:

    打开文件夹里面有apps文件夹和components文件夹

    • Sourceapps文件夹:

    存放BasicRF三个实验的应用实现的源代码

    • Sourcecomponents文件夹:

    包含着BasicRF的应用程序使用不同组件的源代码

     

    打开实验工程:

      打开文件夹Zigbee CC2530BasicRFidesrf05_cc2530iar路径里面的工程light_switch.eww(无线点灯)。我们的实验就是对它进行修改的。并点击applicationlight_switch.c用户的应用程序就是在里面的了 

     

     

    图2. BasicRF工程路径

    二、Basic RF layer介绍及其工作过程

    在介绍Basic RF之前,来看看这个实验例程设计的大体结构,如3所示Basic RF例程的软件设计框图就如一座建筑物。

    图3. 软件设计框图

    Ø Hardwarelayer——对应物理实体

      放在最底,肯定是你实现数据传输的基础了。

    Ø HardwareAbstraction layer——对应hal_rf.c

      它提供了一种接口来访问TIMERGPIOUARTADC等。这些接口都通过相应的函数进行实现。

    Ø Basic RF layer——对应basic_rf.c

      为双向无线传输提供一种简单的协议

    Ø Application layer——对应light_switch.c

      是用户应用层,它相当于用户使用Basic RF层和HAL的接口,也就是说我们通过在Application layer就可以使用到封装好的Basic RFHAL的函数。

      本例程的要求就是读者理解掌握Basic RF

    1.Basic RF layer简介

      BasicRFTI公司提供,它包含了IEEE 802.15.4标准的数据包的收发功能但并没有使用到协议栈,它仅仅是是让两个结点进行简单的通信,也就是说Basic RF仅仅是包含着IEEE 802.15.4标准的一小部分而已。其主要特点有:

    a)不提供多跳设备扫描Beacon

    b)不提供不同种的网络设备,如协调器、路由器等。所有节点同级,只实现点对点传输。

    c)传输时会等待信道空闲,但不按802.15.4 CSMA-CA要求进行两次CCA检测。

    d)不重传数据

     

    BasicRF layer为双向无线通信提供了一个简单的协议,通过这个协议能够进行数据的发送和接收。Basic RF还提供了安全通信所使用的CCM-64身份验证和数据加密,它的安全性读者可以通过在工程文件里面定义SECURITY_CCMProject->Option里面就可以选择

    本次实验并不是什么高度机密,所以在SECURITY_CCM前面带X了。

     

     

    图4. 注释SECURITY_CCM

    2.Basic RF的工作过程

    Basic RF的工作过程:启动、发射、接收 (请大家按照代码走)

    Ø 启动

    a)确保外围器件没有问题

    b)创建一个basicRfCfg_t的数据结构,并初始化其中的成员,在basic_rf.h代码中可以找到

       typedefstruct {
              uint16 myAddr;          //16位的短地址(就是节点的地址)
              uint16 panId;            //节点的PAN ID
              uint8 channel;          //RF通道(必须在11-26之间)
              uint8 ackRequest;       //目标确认就置true
              #ifdef SECURITY_CCM //是否加密,预定义里取消了加密
                uint8*securityKey;   
                uint8*securityNonce;
                #endif
    } basicRfCfg_t;

    c)在Application层调用Basic RfbasicRfInit()函数进行协议的初始化,在basic_rf.c代码中可以找到

    uint8basicRfInit(basicRfCfg_t* pRfConfig)

    函数功能:对Basic RF的数据结构初始化,设置模块的传输通道,短地址,PAD ID

    Basic Rf层实现:调用HAL层函数设置模块的传输通道,短地址,PAD ID。并且调用halRfRxInterruptConfig(basicRfRxFrmDoneIsr)语句动态配制接收中断的中断服务函数。

    HAL层实现:分别配制FREQCTRLSHORT_ADDR,和PAN_ID寄存器来设置模块的传输通道,短地址,PAD ID。在hal_rf.c中可以找到

     

    Ø 发送

    a)创建一个buffer,把payload放入其中。Payload最大为103个字节

    b)调用basicRfSendPacket()函数发送,并查看其返回值。在basic_rf.c中可以找到

    uint8basicRfSendPacket(uint16 destAddr, uint8* pPayload, uint8 length)

    destAddr 目的短地址

    pPayload 指向发送缓冲区的指针

    length 发送数据长度

    函数功能:给目的短地址发送指定长度的数据,发送成功刚返回SUCCESS,失败则返回FAILED

    Basic Rf层实现:使能模块的接收功能(为了接收ACK),发送数据,等待ACK,关接收功能。

    HAL层实现:发送数据的HAL层实现,将要发送的数据写入RFD寄存器,该寄存器自动将数据写入负责装发送数据的TXFIFO寄存器。

     

    Ø 接收

    a)上层通过basicRfPacketIsReady()函数来检查是否收到一个新数据包

    basic_rf.c中可以找到

    uint8basicRfPacketIsReady(void)

    函数功能:检查模块是否已经可以接收下一个数据,如果准备好刚返回TRUE

    Basic Rf层实现:读取rxi.isReady的值并返回。

    HAL层实现:在接收中断中,检测收到的帧的帧头,如果是正确的,则把rxi.isReady赋值成TRUE.

    b)调用basicRfReceive()函数,把收到的数据复制到buffer中。

    代码可以在basic_rf.c中可以找到

    uint8basicRfReceive(uint8* pRxData, uint8 len, int16* pRssi)

    函数功能:接收来自Basic RF层的数据包,并为所接收的数据和RSSI值配缓冲区

    Basic Rf层实现:把rxi.pPayload中的值赋给pRxData并传递给上层函数。从rxi.rssi中得到pRssi。并且把rxi.isReady的值重新设置成FALSE.

    HAL层实现:rxi.pPayload中的值是在接收中断服务函数中,读取RFD寄存器得到的,读后,RXFIFO会自动把值写入RFD寄存器。

    接收中断:在hal_types.h中可以看到如下代码将中断重命名。

    #define HAL_ISR_FUNC_DECLARATION(f,v)   
    
        _PRAGMA(vector=v##_VECTOR) __interrupt void f(void)
    
    #define HAL_ISR_FUNC_PROTOTYPE(f,v)     
    
        _PRAGMA(vector=v##_VECTOR) __interrupt void f(void)
    
    #define HAL_ISR_FUNCTION(f,v)           
    
        HAL_ISR_FUNC_PROTOTYPE(f,v); HAL_ISR_FUNC_DECLARATION(f,v)

    找HAL_ISR_FUNCTION(f,v)函数,可以看到在hal.rf.cHAL_ISR_FUNCTION( rfIsr, RF_VECTOR )即为中断服务函数。而其中下列代码中函数指针调用的函数即为在basicRfInit函数中配制的basicRfRxFrmDoneIsr函数。

    if(pfISR){
    
                (*pfISR)();                 // Execute the custom ISR
    
            }

    如果能看懂启动、发射、接收就可以说你基本上能使用这个无线模块了。

    使用Basic RF实现无线传输只要学会使用这些函数就可以了,但是具体的实现过程远没有那么简单的,大家可以到….CC2530 BasicRFdocs里面查看CC2530_Software_Examples中的5.2.4 Basic RF operation这个章节的内容,里面详细介绍了Basic RF的初始化过程、Basic RF的发射过程、Basic RF的接收过程,具体到每个层的功能函数。Zigbee本来想将这部分的内容也详细的和读者们讲解清楚,但后来再仔细考虑还是不放上来了。因为它的具体实现过程大家看文档的那个章节就可以大概明白的了,另一方面,实验例程的模块化编程做得很好,大家只需要明白函数的作用,学会使用它就行了,至于它内部是怎么样一层一层的实现,我们也不用太过关心。

    三、light_switch.c代码详解

    无论你看哪个实验的代码,首先要找的就是main函数。

    1.main()函数

    main函数开始:(部分已经屏蔽的代码并未贴出,详细的代码请看打开工程)

    1.       void main(void)
    2.       {
    3.         uint8 appMode = NONE;             //不设置模块的模式
    4.       // Config basicRF
         basicRfConfig.panId= PAN_ID;            //上面讲的Basic RF的启动中的
    5.         basicRfConfig.channel =RF_CHANNEL;  第2步初始化basicRfCfg_t
    6.         basicRfConfig.ackRequest =TRUE;       结构体的成员。
    7.      
    8.         #ifdef SECURITY_CCM             //密钥安全通信,本例程不加密
    9.         basicRfConfig.securityKey =key;
    10.     #endif
    11.   
    12.     // Initalise boardperipherals    初始化外围设备
    13.     halBoardInit();
    14.     halJoystickInit();
    15.   
    16.     // Initalise hal_rf  硬件抽象层的rf进行初始化
    17.     if(halRfInit()= =FAILED)
    18.     {
    19.       HAL_ASSERT(FALSE);
    20.     }
    21.     
    22.     halLedSet(2);            // 关 LED2(P1_1=1)
    23.     halLedClear(1);          // 开LED1(P1_0=0)
    24.   
    25.   
    26.    appSwitch();          //节点为按键S1        P0_4
    27.    appLight();           //节点为指示灯LED1    P1_0
    28.     // Role is undefined. This code should not bereached
    29.     HAL_ASSERT(FALSE);
    30.   }

    Ø 第2223行:关闭Zigbee底板的LED2,开LED1。由于Zigbee设计的LED电路是低电平点亮的,与TI不同,更符合以前大家学习单片机的习惯,所以halLedSet()1是使灯熄灭,不过这个没关系,关键是掌握怎么使用就可以了。

    Ø 第2627行:选择其中的一行,并把另外一行屏蔽掉;这两行重要啦,一个是实现发射按键信息的功能,另一个是接收按键信息并改变LED状态的功能。分别为Basic RF发射和接收。不同模块在烧写程序时选择不同功能。

    注意:程序会在appSwitch();  或者appLight();里面循环或者等待,不会执行到第29行。

    2.appSwitch()函数

    接下来看看appSwitch()函数,它是如何实现数据发送的呢?

    1.       static void appSwitch()
    2.       {
    3.         #ifdef ASSY_EXP4618_CC2420
    4.         halLcdClearLine(1);
    5.         halLcdWriteSymbol(HAL_LCD_SYMBOL_TX,1);
    6.         #endif
    7.         // InitializeBasicRF
    8.         basicRfConfig.myAddr =SWITCH_ADDR;
    9.         if(basicRfInit(&basicRfConfig)==FAILED){
    10.     HAL_ASSERT(FALSE);
    11.     }
    12.     pTxData[0] = LIGHT_TOGGLE_CMD;
    13.     // Keep Receiver off when not needed to savepower
    14.     basicRfReceiveOff();
    15.     // Main loop
    16.     while (TRUE)  //程序进入死循环
    17.     {
    18.       if(halButtonPushed()==HAL_BUTTON_1)    //按键S1被按下
    19.       {
    20.       basicRfSendPacket(LIGHT_ADDR,pTxData,APP_PAYLOAD_LENGTH);
    
    
    21.       // Put MCU to sleep. It will wake up onjoystick interrupt
    22.       halIntOff();
    23.       halMcuSetLowPowerMode(HAL_MCU_LPM_3); // Will turn on global
    24.       // interrupt enable
    25.       halIntOn();
    26.       }
    27.      }
    28.   }

    Ø 第36行:TI学习板上的液晶模块的定义,我们不用管他

    Ø 第811行:Basic RF启动中的初始化,就是上面所讲的Basic RF启动的第3

    Ø 第12行:Basic RF发射第1步,把要发射的数据或者命令放入一个数据buffer,此处把灯状态改变的命令LIGHT_TOGGLE_CMD放到pTxData中。

    Ø 第14行:由于模块只需要发射,所以把接收屏蔽掉以降低功耗。

    Ø 第18行:if(halButtonPushed()==HAL_BUTTON_1)判断是否S1按下,函数halButtonPushed()halButton.c里面的,它的功能是:按键S1有被按动时,就回返回true,则进入basicRfSendPacket(LIGHT_ADDR,pTxData, APP_PAYLOAD_LENGTH);

    Ø 第20行:Basic RF发射第2步,也是发送数据最关键的一步,函数功能在前面已经讲述。basicRfSendPacket(LIGHT_ADDR, pTxData,APP_PAYLOAD_LENGTH)就是说:将LIGHT_ADDRpTxDataAPP_PAYLOAD_LENGTH的实参写出来就是basicRfSendPacket(0xBEEF ,pTxData[1] ,1 )把字节长度为1的命令,发送到地址0xBEEF

    Ø 第2223行:Zigbee开发板暂时还没有joystick(多方向按键),不用理它先。

    Ø 第25行:使能中断

    3.appLight()函数

    发送的appSwitch()讲解完毕,接下来就到我们的接收appLight()函数了

    1.       static void appLight()
    2.       {
    3.         
    7.         #ifdef ASSY_EXP4618_CC2420
    8.         halLcdClearLine(1);
    9.         halLcdWriteSymbol(HAL_LCD_SYMBOL_RX,1);
    
    10.      #endif
    11.    // Initialize BasicRF
    12.      basicRfConfig.myAddr = LIGHT_ADDR;
    13.      if(basicRfInit(&basicRfConfig)==FAILED) {
    14.      HAL_ASSERT(FALSE);
    15.      }
    16.      basicRfReceiveOn();
    17.      // Main loop
    18.      while (TRUE)
    19.       {
    20.         while(!basicRfPacketIsReady());
    
    
    21.         if(basicRfReceive(pRxData,APP_PAYLOAD_LENGTH, NULL)>0) {
    
    22.         if(pRxData[0] == LIGHT_TOGGLE_CMD)
    23.         {
    24.          halLedToggle(1);
    25.          }
    26.        }
    27.      }
    28.    }

    Ø 第710行:LCD内容暂时不用理它

    Ø 第1215行:Basic RF启动中的初始化,上面Basic RF启动的第3

    Ø 第16行:函数basicRfReceiveOn(),开启无线接收功能,调用这个函数后模块一直会接收,除非再调用basicRfReceiveOff()使它关闭接收。

    Ø 第18行:程序开始进行不断扫描的循环

    Ø 第19行:Basic RF接收的第1步,while(!basicRfPacketIsReady()) 检查是否接收上层数据,

    Ø 第20行:Basic RF接收的第2步,if(basicRfReceive(pRxData,APP_PAYLOAD_LENGTH, NULL)>0)判断否接收到有数据

    Ø 第21行:if(pRxData[0] == LIGHT_TOGGLE_CMD)判断接收到的数据是否就是发送函数里面的LIGHT_TOGGLE_CMD 如果是,执行第22

    Ø 第22行:halLedToggle(1),改变Led1的状态。

     

    实验操作:

    第一步:打开….CC2530 BasicRFide文件夹下面的工程在light_switch.c里面找到main函数,找到下面内容,把appLight(); 注释掉,下载到发射模块。

           appSwitch();        //节点为按键S1       P0_4
           //appLight();        //节点为指示灯LED1    P1_0

    第二步:找到相同位置,这次把appSwitch();注释掉,下载到接收模块。     

            //appSwitch();        //节点为按键S1       P0_4
            appLight();         //节点为指示灯LED1   P1_0
  • 相关阅读:
    LeetCode-165 Compare Version Numbers
    shop--6.店铺注册--Thumbnailator图片处理和封装Util
    shop--6.店铺注册
    shop--5.使用Junit进行验证
    shop--4.SSM的各项配置
    shop--3.配置Maven
    shop--2.项目设计和框架搭建
    shop--1.创建maven项目
    AJAX 使用FormData 传送数据 DATA 为空 现象的处理
    Daemon Thread [http-nio-8080-exec-5] (Suspended (exception UnsatisfiedDependency))
  • 原文地址:https://www.cnblogs.com/ben-ben/p/3191742.html
Copyright © 2020-2023  润新知