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_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分析的更多相关文章
- cc2530 makefile简略分析 <contiki学习之三>
前面将contiki的makefile框架都理了下,这篇就以cc2530为收篇吧,也即makefile分析就该到此为止了. contiki/examples/cc2530dk 打开Makefile如下 ...
- cc2530操作任务系统初始化分析
操作系统任务初始化void osalInitTasks( void ){ uint8 taskID = 0; // 分配内存,返回指向缓冲区的指针 tasksEvents = (uint16 *)os ...
- CC2530调试过程中遇到的问题们
应用场景描述: 多个发送端在不同的信道上发送信息(11~26)信道,接收端轮询所有信道(11~26),若有信号,则接收,若无信号则继续轮询.形成多个点对点的收发系统. 一.问题1 Ø 问题现象描述: ...
- [ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)
说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c程序入 ...
- 【CC2530入门教程-01】IAR集成开发环境的建立与项目开发流程
[引言] 本系列教程就有关CC2530单片机应用入门基础的实训案例进行分析,主要包括以下6部分的内容:1.CC2530单片机开发入门.2.通用I/O端口的输入和输出.3.外部中断初步应用.4.定时/计 ...
- CC2530入门教程-02】CC2530的通用I/O端口输入和输出控制
第2课 CC2530的通用I/O端口输入和输出控制 广东职业技术学院 欧浩源 一.CC2530的引脚概述 CC2530微控制器采用QFN40封装,有40 个引脚.其中,有21个数字I/O端口,其中 ...
- 【CC2530入门教程-增强版】基础技能综合实训案例(基础版)-上位机源码
[CC2530入门教程-增强版]基础技能综合实训案例(基础版)-上位机源码 广东职业技术学院 欧浩源 一.需求分析 按照指定参数打开串口,与测控终端建立数据传输通道,并根据应用要求实现程序逻辑,具体 ...
- 基于CC2530/CC2430 的温度采集系统--DS18B20
DS18B20是常用的温度传感器.CC2530 采集DS18B20 可以实现温度采集系统等等. 模块链接:https://item.taobao.com/item.htm?id=54130861732 ...
- OSAL工作机制分析
协议栈代码main()函数分析 ZMain文件->ZMain.c->main() 在这里我们重点了解osal_start_system()函数 int main( void ) { // ...
随机推荐
- 浅谈BST(二叉查找树)
目录 BST的性质 BST的建立 BST的检索 BST的插入 BST求前驱/后继 BST的节点删除 复杂度 平衡树 BST的性质 树上每个节点上有个值,这个值叫关键码 每个节点的关键码大于其任意左侧子 ...
- DOM操作 三大家族
clientHeight 获取对象的高度,不计算任何边距.边框.滚动条,但包括该对象的补白. clientLeft 获取 offsetLeft 属性和客户区域的实际左 ...
- 阿里云 API 签名机制的 Python 实现
在调用阿里云 API 的时候,最让人头疼的就是 API 的签名(Signature)机制,阿里云在通用文档中也有专项说明,但是仅仅有基于 Java 的实现代码示例.所以这里基于 Python 来分析下 ...
- 用简单的JS代码制作计算器
代码+注释一共不到200行,是练习交流的必备良药 主界面如下: 操作示意图: 以下是代码部分 HTML: <div> <table class="window"& ...
- Maven 教程(13)— Maven插件解析运行机制
原文地址:https://blog.csdn.net/liupeifeng3514/article/details/79551210 这里给大家详细说一下Maven的运行机制,让大家不仅知其然,更知其 ...
- 在GitHub中创建目录
需求:假定我们需要在 Answer 目录下创建一个目录 [注]GitHub无法单独创建一个空目录,但是可以在创建一个文件的同时创建它的所属目录 1.点击进入所需的目录 Answer 2.点击“Crea ...
- 你不知道的js——数组 join
你可能对使用数组的 join 方法已经轻车熟路,但你也许不知道: 10.If element0 is undefined or null, let R be the empty String; oth ...
- 2014百度之星 Party
Party Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
- Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call错误
我这边新增的接口之后编译,启动debug后提示这个问题, 在网上找了一段时间,感觉各大神说的都好有道理,但是没有作用 so,尝试对整个工程重新编译(理论上只要重新编译修改的文件影响到的地方)
- 微软官方关于 Windows To Go 的常见问题
Windows To Go:常见问题 2016/04/01 本文内容 什么是 Windows To Go? Windows To Go 是否依赖虚拟化? 哪些人员应该使用 Windows To Go? ...