第三部分 进阶篇-第1章 CC2530 BasicRF协议剖析
1理论讲解
1.1 CC2530 BasicRF工程文件放置说明
当我们获取到CC2530BasicRF工程文件后,打开文件夹一看,比我们之前裸机实验的文件放置复杂得多,要找出light_switch.eww实属不易。下面就来探讨一下它的架构组成,图就是CC2530BasicRF工程文件的分布图。
按照图进行分析,我们来看看各个文件夹里究竟放了些什么东西。
docs文件夹
打开文件夹一看,里面就是这样一个文档:
CC2530_Software_Example.pdf。
而这个文档就是CC2530BasicRF例程的说明书。
ide文件夹
这个文件夹里面有3个子文件夹,具体如图所示。其中,
(1)setting文件夹里是IAR的相关设置,比如IAR的注释是什么颜色的等等;
(2)srf05_cc2530文件夹里面有CC2530没有加PA(CC2591)时的3个工程,分别是:
light_switch.eww、per_test.eww、spectrum_analyzer.eww。
(3)srf05_cc2530_91文件夹里面有CC2530加PA(CC2591)时的2个工程的hex文件,分
别是:light_switch.hex、per_test.hex。
(4)cc2530_sw_example.eww就是这3个工程(light_switch.eww、per_test.eww、spectrum_analyzer.eww)的总工程,也就是将它们包装在一起了。
Source文件夹
顾名思义,这个文件夹就是放置工程的源文件的。它里面有2个子文件夹,分别为:app和components,其中,
(1)app文件夹里是上面所说的3个工程的main函数所在的地方,另外,以后我们的外设底层驱动,基本上都是移植到app文件里面的,以后的实验会有详细的介绍。
(2)components文件夹是BasicRF的一些底层驱动文件,包括:BasicRF协议、hal层驱动文件、加密文件、系统驱动文件等等。
1.2 CC2530 BasicRF工程软件架构
当我们知道文件的放置后,我们就来探讨这个工程软件的架构,它就是CC2530BasicRF工程软件架构,如图所示,下面分别介绍各层。
Applicationlayer
用户应用层,它为用户使用Basic RF层和HAL,所提供的接口。
Basic RF
为双向无线传输提供一种简单的协议。
Hardware Abstraction Layer(hal)
为无线和板载资源,比如:LCD,UART,buttons,timers等,提供访问接口。
Hardware
其实,硬件已经不属于BasicRF工程软件的范畴了,它是我们使用BasicRF所需要的硬件平台而已,至于这个平台,已经做好,具体可以看原理图。
总结一下:往后实验的重点就在Application层,main函数就在这里。至于Basic RF层是实现无线通信的简单协议,而Hal层是一些硬件驱动,这两层我们下面会有讲解,但是读者如果没有兴趣研究的话,也没有关系,只需大致知道数据传输和函数的调用关系,在这三个层之间是怎么进行的,就可以了,重点还是Application层,至于这个层,我们以后的实验都会详细讲解的,当然下面我们也会作理论讲解。
1.3 Basic RF层介绍
Basic RF是双向无线传输提供一种简单的协议,它并不是协议栈。
1.3.1 BasicRF简介
Basic RF由TI公司提供,它包含了IEEE802.15.4标准的数据包的收发功能但并没有使用到协议栈,它仅仅是是让两个结点进行简单的通信,也就是说Basic RF仅仅是包含着IEEE802.15.4标准的一小部分而已。其主要特点有:
(1)不会自动加入协议、也不会自动扫描其他节点也没有组网指示灯(LED3);
(2)没有协议栈里所说的协调器、路由器或者终端的区分,节点的地位都是相等的;
(3)没有自动重发的功能。BasicRFlayer为双向无线通信提供了一个简单的协议。
通过这个协议能够进行数据的发送和接收。BasicRF还提供了安全通信所使用的CCM-64身份验证和数据加密,它的安全性大家可以通过在工程文件里面定义SECURITY_CCM在Project->Option里面就可以选择。
1.3.2 BasicRF工作原理
1.3.2.1启动
(1)板载外设、射频接口、系统时钟、中断等的初始化(halBoardInit(););
(2)创建BasicRF数据结构体(basicRfCfg_t,具体见下图5),并初始化其成员;若要加密的话,则更高的层(用户层)会分频一个16字节的**;
(3)BasicRF协议初始化(halRfInit(););
1.3.2.2发送
(1)将刚才配置的Basic RF结构体进行初始化,并为下一步的数据发送创建一个负载缓冲区,这个缓冲区最大为103个字节;
(2)如果要发送数据,就调用basicRfSendPacket(),将数据发送出去。
1.3.2.3接收
(1)通过调用basic RfPacketIs Ready()函数,以查询的方式检测用户层是否接收到数据;
(2)用户层首先创建一个足够大的缓冲区用于接收用户数据,并创建2个字节的缓冲区来存放RSSI值,然后调用basic RfReceive()函数接收数据。
1.3.3 Basic RF API说明
1.3.3.1加密功能
Basic RF提供安全通信所使用的CCM-64身份验证和数据加密。若要使用加密功能,可以通过在工程文件里面定义SECURITY_CCM来实现,具体操作是:在Project->Option里面就可以选择,本次实验并不是什么高度机密,所以在SECURITY_CCM前面带X了。
1.3.3.2数据结构体
下面是BasicRF数据结构体,翻译成中文如图所示。
typedef struct{
uint16myAddr;//16-bitshortaddress(Thisnode’saddress)
uint16panId;//PANID(IDofthePersonalAreaNetworkthisnodeisoperatingon)
uint8channel;//RFChannel(mustbesetbetween11and26)
uint8ackRequest;//Settruetorequestacknowledgementfromdestination
#ifdefSECURITY_CCM
uint8*securityKey;//Pointertothesecuritykeybufferallocatedbythecaller
uint8*securityNonce;//Pointertothesecuritynoncebuffer.Thisisnotusedbythecaller
#endif
}basicRfCfg_t;
1.3.3.3函数
void basicRfInit(basicRfCfg_t*pRfConfig)
初始化Basic RF的数据结构体,设置通道、短地址和节点PANID,以及配置数据接收中断。不过在调用此函数前,必须先调用halBoardInit()来初始化板载外设和射频接口。
uint8 basicRfSendPacket(uint16 destAddr,uint8* pPayload,uint8 length)
将数据发送到目的地址的节点,发送成功返回successfully,否则返回FAILED。返回successfully意味着:目标确认了,而且收到目的地址的节点发回来的应答信号。
uint8 basicRfPacketIsReady(void)
用于判断用户层是否准备好接收数据,准备好了则返回TRUE。
int8 basicRfGetRssi(void)
返回最后收到的数据包的RSSI(接收信号强度)值。
uint8 basicRfReceive(uint8* pRxData,uint8 len,int16* pRssi)
由用户层调用,用于将basicRF层接收到的数据和RSSI值,存入预先分频好的缓冲区。
void basicRfReceiveOn(void)
由用户层调用,用于打开数据接收器,直到调用basicRfReceiveOff把接收器关掉为止,接收器会一直接收数据。
void basicRfReceiveOff(void)
在不需要接收数据的时候,调用这个函数可以将接收器关闭。
void basicRfSecurityInit(basicRfCfg_t*pConfig)
初始化节点和安全**
1.4 硬件抽象层(hal)介绍
1.4.1 Hal层简介
Hal层为无线和板载资源,比如:LCD,UART,buttons,timers等,提供访问接口。Hal层是介于软件跟硬件之间,是驱动硬件资源最直接的层,所以,有关系统时钟、中断、板载资源等等,都由hal层管理,具体请看图。
1.4.2 Hal层API说明
1.4.2.1包括的头文件
hal_rf.h
hal_rf_security.h
1.4.2.2函数
uint8 halRfInit(void)
用于给无线电上电、通过相关寄存器的设置来配置无线电、使能自动应答和配置无线电IO,它是在调用halBoardInit()后才可以调用此函数。
uint8 halRfSetPower(uint8power)
设置无线电发送电压
uint8 halRfTransmit(void)
传输帧
void halRfSetGain(uint8gainMode)
集增益模式,只在使用外部LNA/PA时使用。
uint8 halRfGetChipId(void)
获取无线电芯片的ID。
uint8 halRfGetChipVer(void)
获取无线电芯片的版本。
uint8 halRfGetRandomByte(void)
获取随机字节。
uint8 halRfGetRssiOffset(void)
获取无线电RSSI偏移值。
void halRfWriteTxBuf(uint8*data,uint8length)
将存储器里的数据写到无线电的发送缓冲区里。
void halRfReadRxBuf(uint8*data,uint8length)
将无线电的接收缓冲区里的数据读到存储器里。
void halRfWaitTransceiverReady(void)
等待发送器准备完毕。
void halRfReceiveOn(void)
关闭无线电接收器。
void halRfReceiveOff(void)
打开无线电接收器。
void halRfDisableRxInterrupt(void)
关接收中断。
void halRfEnableRxInterrupt(void)
使能接收中断。
void halRfRxInterruptConfig(ISR_FUNC_PTRpf)
配置接收中断,以及设置本函数在中断时被调用
void halRfSetChannel(uint8channel)
设置视频通道,通道必须在11~26之间。
void halRfSetShortAddr(uint16shortAddr)
写16位短地址到无线电。
void halRfSetPanId(uint16PanId)
写16位PANID到无线电
1.5 用户层(Applicationlayer)
下面只会从理论上来讲解用户层,往后的实验会结合实际例子来讲解。我们以后的外设驱动,基本上都是放在用户层,不过也有例外,如LCD,我们就最好放在hal层,保持跟官方使用LCD的方法一致;而整个BasicRF工程的main函数就放在用户层,我们以后修改代码基本上都在用户层文件里进行,如light_switch工程,就在用户层文件夹里的light_switch.c里进行修改。下面我们就以light_switch.c为例子来讲解用户层。
下面是light_switch工程的light_switch.c中的main函数:
1 void main(void)
2 {
3 uint8 appMode=NONE;//不设置模块的模式
4
5 //创建BasicRF数据结构体(basicRfCfg_t),并初始化其成员
6 basicRfConfig.panId=PAN_ID; //用户的PAN_ID
7 basicRfConfig.channel=RF_CHANNEL; //RF通道必须在-26之间
8 basicRfConfig.ackRequest=TRUE; //目标确认设置
9
10 #ifdefSECURITY_CCM
11 basicRfConfig.securityKey=key;
12 //若要加密的话,则频一个16字节节的**;
13
14 #endif
15
16 //Initaliseboardperipherals
17 halBoardInit();
18 //板载外设、射频接口、系统时钟、中断的初始化---byCavani
19
20 halJoystickInit();
21
22 //Initalisehal_rf
23 if(halRfInit()==FAILED){//BasicRF协议初始化(halRfInit();)
24 HAL_ASSERT(FALSE);
25 }
26
27 //Indicate that device is powered
28 halLedSet(1);
29
30 //Print Logo and splash screen on LCD
31 utilPrintLogo("Light Switch");
32
33 //Wait for user to press S1 to enter menu
34 while(halButtonPushed()!=HAL_BUTTON_1);
35 halMcuWaitMs(350);
36 halLcdClear();
37
38 //Set application role
39 appMode=appSelectMode();
40 halLcdClear();
41
42 //Transmitter application
43 if(appMode==SWITCH){
44 //No return from here
45 appSwitch();
46 }
47 //Receiver application
48 else if(appMode==LIGHT){
49 //No return from here
50 appLight();
51 }
52 //Role is undefined .This code should not be reached
53 HAL_ASSERT(FALSE);
54 }
第6~14行:就是Basic RF工作原理里启动的第2步;
第17行:就是Basic RF工作原理里启动的第1步;
第23~25行:就是Basic RF工作原理里启动的第3步;
第45行:数据发送函数,就是Basic RF工作原理里发送的内容;
第50行:数据接收函数,就是Basic RF工作原理里接收的内容;
下面是light_switch工程的light_switch.c中的发送函数“appSwitch()函数”
1 static void appSwitch()
2 {
3 halLcdWriteLine(HAL_LCD_LINE_1,"Switch");
4 halLcdWriteLine(HAL_LCD_LINE_2,"JoystickPush");
5 halLcdWriteLine(HAL_LCD_LINE_3,"SendCommand");
6 #ifdefASSY_EXP4618_CC2420
7 halLcdClearLine(1);
8 halLcdWriteSymbol(HAL_LCD_SYMBOL_TX,1);
9 #endif
10
11 pTxData[0]=LIGHT_TOGGLE_CMD;
12
13 //Initialize BasicRF
14 basicRfConfig.myAddr=SWITCH_ADDR;
15 if(basicRfInit(&basicRfConfig)==FAILED){
16 //将刚才配置的Basic RF结构体进行初始化,并为下一步的数据发送创建一个负载
17 缓冲区,这个缓冲区最大为103个字节;
18
19 HAL_ASSERT(FALSE);
20}
21
22 //Keep Receiver off when not needed to save power
23 basicRfReceiveOff();
24
25 //Main loop
26 while(TRUE){
27 if(halJoystickPushed()){
28
29 basicRfSendPacket(LIGHT_ADDR,pTxData,APP_PAYLOAD_LENGTH);
30 //如果要发送数据,就调用basicR
31 fSendPacket(),将数据发送出去。
32
33
34 //PutMCUtosleep.Itwillwakeuponjoystickinterrupt
35 halIntOff();
36 halMcuSetLowPowerMode(HAL_MCU_LPM_3);//Willturnon global
37
38 //interruptenable
39 halIntOn();
40
41 }
42 }
43 }
第14~20行:就是BasicRF工作原理里发送的第1步;
第29~30行:就是BasicRF工作原理里发送的第2步;
下面是light_switch工程的light_switch.c中的接收函数“appLight()函数”:
1 static void appLight()
2 {
3 halLcdWriteLine(HAL_LCD_LINE_1,"Light");
4 halLcdWriteLine(HAL_LCD_LINE_2,"Ready");
5
6 #ifdefASSY_EXP4618_CC2420
7 halLcdClearLine(1);
8 halLcdWriteSymbol(HAL_LCD_SYMBOL_RX,1);
9 #endif
10
11 //InitializeBasicRF
12 basicRfConfig.myAddr=LIGHT_ADDR;
13 if(basicRfInit(&basicRfConfig)==FAILED){
14 HAL_ASSERT(FALSE);
15 }
16 basicRfReceiveOn();
17
18 //Mainloop
19 while(TRUE){
20 while(!basicRfPacketIsReady());
21 //通过调用basicRfPacketIsReady()函数,以查询的方式检测用户层是否接收数据;
22
23
24
25 if(basicRfReceive(pRxData,APP_PAYLOAD_LENGTH,NULL)>0){
26 //用户层首先创建一个足够大的缓冲区用于接收用户数据,并创建2个字节的缓冲
27 区来存放R28SSI值,然后调用basicRfReceive()函数接收数据。
29
30 if(pRxData[0]==LIGHT_TOGGLE_CMD){
31 halLedToggle(1);
32 }
33 }
34 }
35 }
第20行:就是Basic RF工作原理里接收的第1步;
第25行:就是Basic RF工作原理里接收的第2步;
其实,上面只是简单地讲解没有修改过的light_switch工程的light_switch.c,读者关键是要从中读懂BasicRF的工作原理。好啦,上面分别讲解用户层、BasicRF层、hal层,下面我们就来个总结,讲解数据传输和函数的调用关系,在这3个层之间是怎么进行的。
1.6 Basic RF操作总结
1.6.1初始化
下图描述了在初始化时,各层的函数的调用关系:
(1)首先,用户层调用halBoardInit()函数,初始化hal层的硬件外设和配置IO端口;
(2)然后,用户层初始化basicRfCfg_t结构体成员;
(3)接着,用户层调用basicRfInit()函数,而basicRfInit()函数就会调用halRFInit()函数,将刚才的basicRfCfg_t结构体写到BasicRF层,并使能中断;
(4)另外,halRFInit()函数也会同时将通道、短地址、个域网ID写到CC2530。
1.6.2数据发送
下图描述了在发送数据时,各层的函数的调用关系和数据传输之间的关系:
(1)首先,用户层调用basicRfSend()函数,将目的地址、数据缓冲区指针、数据缓冲区长度发给BasicRF层;
(2)然后,BasicRF层调用halRfWaitTransceiverReady()函数,检查SFD是否准备好;
(3)接着,BasicRF层调用basicRfBuildMpdu()函数,在BasicRF层建立一个缓冲区存放即将要发送出去的数据。
(4)接着,BasicRF层调用halRfWriteTxBuf()函数,将停留在BasicRF层缓冲区的数据传输到hal层的CC2530发送FIFO缓冲区里。
(5)接着,BasicRF层调用halRfTransmit()函数,通知hal层调用ISTXON()将数据发送出去。
(6)接着,halRfTransmit()函数会等待数据发送完毕,并从hal层获取发送结果Status给BasicRF层。
(7)接着,BasicRF层将等待由basicRfSend()函数返回的应答数据包;
(8)最后,如果BasicRF层从basicRfSend()函数里收到应答数据包,就往用户层发送SUCCESS,说明数据发送成功。
1.6.3数据接收
下图描述了在接收数据时,各层的函数的调用关系和数据传输之间的关系:
(1)首先,当hal层接收完一个数据包之后,就会自动申请中断,这时中断服务函basicRfRxFrmDoneIsr()(在BasicRF层里)就会停止BasicRF层去读取hal层接收到的数据;
(2)然后,BasicRF层就会调用halRfReadRxBuf(length,1)函数,从CC2530接收缓冲区里的第一个字节:接收帧;
(3)接着,BasicRF层调用halRfRecvFrame(rxMpdu,length)函数,将hal层接收缓冲区里所有数据读取到BasicRF层;
(4)接着,CC25320会自动发送应答信号ACK给发送端(如果使能了自动应答的话),同时,BasicRF层将会识别接收帧的地址,如果CRC正确则被接收。
(5)接着,BasicRF层就会对数据包的FCS和***进行检测,如果它们跟预期的rxiisReady标志一致的话,那么新的数据包就会被接收下来,并往用户层发送TRUE。
(6)接着,用户层将会通过basicRfPacketIsReady()函数,不断查询rxiisReady标志;
(7)接着,当basicRfPacketIsReady()返回TRUE,用户层就会调用basicRfReceive()函数,从BasicRF层获取新的数据和RSSI值;
(8)最后,basicRfReceive()函数,就会将数据和RSSI值存储到存储器内,并为这些数据提供一个名为pData的指针,方便用户使用。
附:相关名称解释
API-ApplicationProgrammingInterface
CBC-MAC-CipherBlockChainingMessageAuthenticationCode
CCM-CounterwithCBC-MAC(modeofoperation)
CCM*-ExtensionofCCM
FCS-FrameCheckSequence
HAL-HardwareAbstractionLayer
IO-Input/Output
MIC-MessageIntegrityCode
MPDU-MACProtocolDataUnit
PAN-PersonalAreaNetwork
PER-PacketErrorRate
RF-RadioFrequency
RSSI-ReceivedSignalStrengthIndicator
SFD-StartofFrameDelimiter
【注】本文分析的文件是TI的源文件–CC2530 BasicRF,有需要的请自行下载。
上一篇: CC2530之外部中断