一些关键字:

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

Ø Ide文件夹:

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

  • Ide\Settings文件夹:

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

  • Ide\srf05_CC2530文件夹:

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

Ø source文件夹:

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

  • Source\apps文件夹:

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

  • Source\components文件夹:

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

打开实验工程:

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

图2. BasicRF工程路径

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

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

图3. 软件设计框图

Ø Hardwarelayer——对应物理实体

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

Ø HardwareAbstraction layer——对应hal_rf.c

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

Ø Basic RF layer——对应basic_rf.c

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

Ø Application layer——对应light_switch.c

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

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

1.Basic RF layer简介

  BasicRF由TI公司提供,它包含了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_CCM在Project->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 Rf层basicRfInit()函数进行协议的初始化,在basic_rf.c代码中可以找到

uint8basicRfInit(basicRfCfg_t* pRfConfig)

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

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

HAL层实现:分别配制FREQCTRL,SHORT_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.c中HAL_ISR_FUNCTION( rfIsr, RF_VECTOR )即为中断服务函数。而其中下列代码中函数指针调用的函数即为在basicRfInit函数中配制的basicRfRxFrmDoneIsr函数。

if(pfISR){

            (*pfISR)();                 // Execute the custom ISR

        }

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

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

三、light_switch.c代码详解

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

1.main()函数

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

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

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

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

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

2.appSwitch()函数

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

.       static void appSwitch()
. {
. #ifdef ASSY_EXP4618_CC2420
. halLcdClearLine();
. halLcdWriteSymbol(HAL_LCD_SYMBOL_TX,);
. #endif
. // InitializeBasicRF
. basicRfConfig.myAddr =SWITCH_ADDR;
. if(basicRfInit(&basicRfConfig)==FAILED){
. HAL_ASSERT(FALSE);
. }
. pTxData[] = LIGHT_TOGGLE_CMD;
. // Keep Receiver off when not needed to savepower
. basicRfReceiveOff();
. // Main loop
. while (TRUE) //程序进入死循环
. {
. if(halButtonPushed()==HAL_BUTTON_1) //按键S1被按下
. {
. basicRfSendPacket(LIGHT_ADDR,pTxData,APP_PAYLOAD_LENGTH); . // Put MCU to sleep. It will wake up onjoystick interrupt
. halIntOff();
. halMcuSetLowPowerMode(HAL_MCU_LPM_3); // Will turn on global
. // interrupt enable
. halIntOn();
. }
. }
. }

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

Ø 第8~11行: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_ADDR、pTxData、APP_PAYLOAD_LENGTH的实参写出来就是basicRfSendPacket(0xBEEF ,pTxData[1] ,1 )把字节长度为1的命令,发送到地址0xBEEF

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

Ø 第25行:使能中断

3.appLight()函数

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

.       static void appLight()
. {
.
. #ifdef ASSY_EXP4618_CC2420
. halLcdClearLine();
. halLcdWriteSymbol(HAL_LCD_SYMBOL_RX,); . #endif
. // Initialize BasicRF
. basicRfConfig.myAddr = LIGHT_ADDR;
. if(basicRfInit(&basicRfConfig)==FAILED) {
. HAL_ASSERT(FALSE);
. }
. basicRfReceiveOn();
. // Main loop
. while (TRUE)
. {
. while(!basicRfPacketIsReady()); . if(basicRfReceive(pRxData,APP_PAYLOAD_LENGTH, NULL)>) { . if(pRxData[] == LIGHT_TOGGLE_CMD)
. {
. halLedToggle();
. }
. }
. }
. }

Ø 第7~10行:LCD内容暂时不用理它

Ø 第12~15行: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 BasicRF\ide文件夹下面的工程在light_switch.c里面找到main函数,找到下面内容,把appLight(); 注释掉,下载到发射模块。

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

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

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

CC2530 light_switch分析的更多相关文章

  1. cc2530 makefile简略分析 <contiki学习之三>

    前面将contiki的makefile框架都理了下,这篇就以cc2530为收篇吧,也即makefile分析就该到此为止了. contiki/examples/cc2530dk 打开Makefile如下 ...

  2. cc2530操作任务系统初始化分析

    操作系统任务初始化void osalInitTasks( void ){ uint8 taskID = 0; // 分配内存,返回指向缓冲区的指针 tasksEvents = (uint16 *)os ...

  3. CC2530调试过程中遇到的问题们

    应用场景描述: 多个发送端在不同的信道上发送信息(11~26)信道,接收端轮询所有信道(11~26),若有信号,则接收,若无信号则继续轮询.形成多个点对点的收发系统. 一.问题1 Ø 问题现象描述: ...

  4. [ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)

    说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c程序入 ...

  5. 【CC2530入门教程-01】IAR集成开发环境的建立与项目开发流程

    [引言] 本系列教程就有关CC2530单片机应用入门基础的实训案例进行分析,主要包括以下6部分的内容:1.CC2530单片机开发入门.2.通用I/O端口的输入和输出.3.外部中断初步应用.4.定时/计 ...

  6. CC2530入门教程-02】CC2530的通用I/O端口输入和输出控制

    第2课  CC2530的通用I/O端口输入和输出控制 广东职业技术学院  欧浩源 一.CC2530的引脚概述 CC2530微控制器采用QFN40封装,有40 个引脚.其中,有21个数字I/O端口,其中 ...

  7. 【CC2530入门教程-增强版】基础技能综合实训案例(基础版)-上位机源码

    [CC2530入门教程-增强版]基础技能综合实训案例(基础版)-上位机源码 广东职业技术学院  欧浩源 一.需求分析 按照指定参数打开串口,与测控终端建立数据传输通道,并根据应用要求实现程序逻辑,具体 ...

  8. 基于CC2530/CC2430 的温度采集系统--DS18B20

    DS18B20是常用的温度传感器.CC2530 采集DS18B20 可以实现温度采集系统等等. 模块链接:https://item.taobao.com/item.htm?id=54130861732 ...

  9. OSAL工作机制分析

    协议栈代码main()函数分析 ZMain文件->ZMain.c->main()  在这里我们重点了解osal_start_system()函数 int main( void ) { // ...

随机推荐

  1. 浅谈BST(二叉查找树)

    目录 BST的性质 BST的建立 BST的检索 BST的插入 BST求前驱/后继 BST的节点删除 复杂度 平衡树 BST的性质 树上每个节点上有个值,这个值叫关键码 每个节点的关键码大于其任意左侧子 ...

  2. DOM操作 三大家族

    clientHeight     获取对象的高度,不计算任何边距.边框.滚动条,但包括该对象的补白.   clientLeft     获取    offsetLeft     属性和客户区域的实际左 ...

  3. 阿里云 API 签名机制的 Python 实现

    在调用阿里云 API 的时候,最让人头疼的就是 API 的签名(Signature)机制,阿里云在通用文档中也有专项说明,但是仅仅有基于 Java 的实现代码示例.所以这里基于 Python 来分析下 ...

  4. 用简单的JS代码制作计算器

    代码+注释一共不到200行,是练习交流的必备良药 主界面如下: 操作示意图: 以下是代码部分 HTML: <div> <table class="window"& ...

  5. Maven 教程(13)— Maven插件解析运行机制

    原文地址:https://blog.csdn.net/liupeifeng3514/article/details/79551210 这里给大家详细说一下Maven的运行机制,让大家不仅知其然,更知其 ...

  6. 在GitHub中创建目录

    需求:假定我们需要在 Answer 目录下创建一个目录 [注]GitHub无法单独创建一个空目录,但是可以在创建一个文件的同时创建它的所属目录 1.点击进入所需的目录 Answer 2.点击“Crea ...

  7. 你不知道的js——数组 join

    你可能对使用数组的 join 方法已经轻车熟路,但你也许不知道: 10.If element0 is undefined or null, let R be the empty String; oth ...

  8. 2014百度之星 Party

    Party Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  9. Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call错误

    我这边新增的接口之后编译,启动debug后提示这个问题, 在网上找了一段时间,感觉各大神说的都好有道理,但是没有作用 so,尝试对整个工程重新编译(理论上只要重新编译修改的文件影响到的地方)

  10. 微软官方关于 Windows To Go 的常见问题

    Windows To Go:常见问题 2016/04/01 本文内容 什么是 Windows To Go? Windows To Go 是否依赖虚拟化? 哪些人员应该使用 Windows To Go? ...