BasicRF简析剖析.docx
《BasicRF简析剖析.docx》由会员分享,可在线阅读,更多相关《BasicRF简析剖析.docx(11页珍藏版)》请在冰豆网上搜索。
BasicRF简析剖析
BasicRF简析(四:
appSwitch()简析)
appSwitch()在SI中的函数关系如图1所示,其中basicRfInit()和basicRfSendPacket()两个函数比较有内容的,本文主要针对这两个函数进行展开。
图1
开始之前先介绍三个比较重要的结构体:
basicRfRxInfo_t、basicRfTxState_t和basicRfPktHdr_t
//接收帧信息
typedefstruct{
uint8seqNumber; //帧序号;
uint16srcAddr; //源地址;
uint16srcPanId; //源节点的PANID;
int8length; //帧长度;
uint8*pPayload; //该指针指向帧数据即:
净载荷数据;
uint8ackRequest; //帧控制域的应答位信息;
int8rssi; //接收信号强度指示;
volatileuint8isReady; //通过CRC校验,数据接收完成,该标志位进行后续读取操作;
uint8status; //待定?
?
?
?
}basicRfRxInfo_t;
//发送状态信息
typedefstruct{
uint8txSeqNumber; //帧序号;
volatileuint8ackReceived; //ACK是否接收完成;
uint8receiveOn; //是否处于接收状态;
uint32frameCounter; //发送帧计数;
}basicRfTxState_t;
//BasicRf帧头(IEEE802.15.4)
typedefstruct{
uint8 packetLength; //帧长度;
uint8 fcf0; //FramecontrolfieldLSB
uint8 fcf1; //FramecontrolfieldMSB
uint8 seqNumber; //帧序号;
uint16 panId; //PANID;
uint16 destAddr; //目的地址;
uint16 srcAddr; //源地址;
#ifdefSECURITY_CCM //安全选项;
uint8 securityControl;
uint8 frameCounter[4];
#endif
}basicRfPktHdr_t;
(一) 关于basicRfInit()
/***********************************************************************************
*@fn basicRfInit
*
*@brief InitialisebasicRFdatastructures.Setschannel,shortaddressand
* PANidinthechipandconfiguresinterruptonpacketreception
* 初始化BasicRF数据结构,如:
通道选择、短地址、PANID及接收中断的配置;
*@param pRfConfig-pointertoBASIC_RF_CONFIGstruct.
* Thisstructmustbeallocatedbyhigherlayer
* txState-filescopevariablethatkeepstxstateinfo //发送状态信息;
* rxi-filescopevariableinfoextractedfromthelastincoming
* frame //最新的所接收帧信息;
*
*@return none
*/
uint8basicRfInit(basicRfCfg_t*pRfConfig)
{
if(halRfInit()==FAILED) //Rf初始化,启用Rf的推荐简单配置,可选的PA模块配置,始终返回Success;
returnFAILED;
halIntOff(); //关闭总中断;
//Settheprotocolconfiguration
pConfig=pRfConfig; //指向相关配置信息;
rxi.pPayload =NULL; //清空本节点的接收载荷数据;
txState.receiveOn=TRUE; //halRfInit()中开启接收;
txState.frameCounter=0; //发送帧计数值;
txState.txSeqNumber=0x88; //自行修改第一个发送帧序号初始值;
//Setchannel
halRfSetChannel(pConfig->channel); //将定义的通道号写入相关寄存器;
//WritetheshortaddressandthePANIDtotheCC2520RAM
halRfSetShortAddr(pConfig->myAddr); //将定义的本节点地址写入相关寄存器;
//#defineSHORT_ADDR0 XREG(0x6174)
//#defineSHORT_ADDR1 XREG(0x6175)
halRfSetPanId(pConfig->panId); //将定义的PANID写入相关寄存器;
//#definePAN_ID0 XREG(0x6172)
//#definePAN_ID1 XREG(0x6173)
//ifsecurityisenabled,writekeyandnonce
#ifdefSECURITY_CCM
basicRfSecurityInit(pConfig);
#endif
//Setupreceiveinterrupt(receiveddataoracknowlegment)
halRfRxInterruptConfig(basicRfRxFrmDoneIsr); //对函数指针进行赋值,关联相应的中断函数,即:
声明中断程序;
halIntOn(); //开启总中断;
//为什么要开闭总中断一次?
?
?
?
先启用发送节点后启用接收节点时,意外的接收中断?
returnSUCCESS;
}
basicRfInit()如上代码所示,该函数仅对RF做简单初始化、通道选择、PANID、本节点地址进行配置,最后为RF接收中断声明一个函数指针basicRfRxFrmDoneIsr;
/***********************************************************************************
*@fn halRfRxInterruptConfig
*
*@brief ConfigureRXinterrupt.
* //配置接收中断,将RX中断指向一段可执行程序;
*@param none
*
*@return none
*/
voidhalRfRxInterruptConfig(ISR_FUNC_PTRpf)
{
uint8x;
HAL_INT_LOCK(x); //保存EA并将其清零;
pfISR=pf; //将函数指针赋值,而pfISR将在RX中断时被执行;
HAL_INT_UNLOCK(x); //恢复之前EA的值;
}
/************************************************************************************
*@fn rfIsr
*
*@brief InterruptserviceroutinethathandlesRFPKTDONEinterrupt.
* //RX中断服务程序;
*@param none
*
*@return none
*/
HAL_ISR_FUNCTION(rfIsr,RF_VECTOR)
{
uint8x;
HAL_INT_LOCK(x);
if(RFIRQF0&IRQ_RXPKTDONE)
{
if(pfISR){
(*pfISR)(); //ExecutethecustomISR
//如果pfISR不为空则将调用函数指针所指向的函数basicRfRxFrmDoneIsr();
}
S1CON=0; //CleargeneralRFinterruptflag
RFIRQF0&=~IRQ_RXPKTDONE; //ClearRXPKTDONEinterrupt
}
HAL_INT_UNLOCK(x);
}
RF中断采用了宏声明的方式(协议栈中多采用宏来声明中断,而非常规C语言函数),其声明语句如下:
#defineHAL_ISR_FUNC_DECLARATION(f,v) _PRAGMA(vector=v)__near_func__interruptvoidf(void) //中断函数声明的宏;
#defineHAL_ISR_FUNC_PROTOTYPE(f,v) _PRAGMA(vector=v)__near_func__interruptvoidf(void) //中断函数原型的宏;
#defineHAL_ISR_FUNCTION(f,v) HAL_ISR_FUNC_PROTOTYPE(f,v);HAL_ISR_FUNC_DECLARATION(f,v)//中断函数定义宏,包括
//原型和声明;
其中rfIsr对应于宏中的f(void),类似于指向其自身HAL_ISR_FUNCTION()。
(二)关于basicRfSendPacket()
/***********************************************************************************
*@fnbasicRfSendPacket
*
*@briefSendpacket
*
*@paramdestAddr-destinationshortaddress//目的地址;
*pPayload-pointertopayloadbuffer.Thisbuffermustbe
*allocatedbyhigherlayer.//需要MAC层以上产生要发送的数据(指针或数组);
*length-lengthofpayload//要发送数据的长度;
*txState-filescopevariablethatkeepstxstateinfo//发送状态信息;
*mpdu-filescopevariable.Bufferfortheframetosend//对数据进行封包为物理层协议数据单元;
*
*@returnbasicRFStatus_t-SUCCESSorFAILED
*/
uint8basicRfSendPacket(uint16destAddr,uint8*pPayload,uint8length)
{
uint8mpduLength;
uint8status;
//Turnonreceiverifitsnoton
//保证设备处于接收状态,其初始值在halRfInit()中开启接收并在basicRfInit()中被赋值为TRUE;
if(!
txState.receiveOn){
halRfReceiveOn();
}
//Checkpacketlength
//取最小的有效数据长度,类似与可变长度域,可变长度值是很有用的例如:
串口透传的数据长度;
//最大数据载荷为
//#defineBASIC_RF_MAX_PAYLOAD_SIZE(127-BASIC_RF_PACKET_OVERHEAD_SIZE-BASIC_RF_AUX_HDR_LENGTH-BASIC_RF_LEN_MIC)
//后面两项为安全选项的附加信息,可根据需要自行调整;
length=min(length,BASIC_RF_MAX_PAYLOAD_SIZE);
//Waituntilthetransceiverisidle
//根据SFD和TX_Active状态位来判定设备是否处于空闲状态;
//SFD状态位为0说明设备目前无发送无接收;
halRfWaitTransceiverReady();
//TurnoffRXframedoneinterrupttoavoidinterferenceontheSPIinterface
//防止2591冲突?
?
?
halRfDisableRxInterrupt();
mpduLength=basicRfBuildMpdu(destAddr,pPayload,length);//根据目的地址、载荷数据及长度信息进行封包;
#ifdefSECURITY_CCM
halRfWriteTxBufSecure(txMpdu,mpduLength,length,BASIC_RF_LEN_AUTH,BASIC_RF_SECURITY_M);
txState.frameCounter++;//Incrementframecounterfield
#else
halRfWriteTxBuf(txMpdu,mpduLength);//使用ISFLUSHTX()清空TXFIFO并清除IRQ_TXDONE中断溢出标志位,将MPDU一个字节一个字节的写入RFD;
#endif
//TurnonRXframedoneinterruptforACKreception
//仅仅是始能接收中断,为发送完成后自动进入接收模式接收ACK做准备性工作;
//仅作为发送节点且不启用ACK的话,这部分语句都可以省略去;
halRfEnableRxInterrupt();
//SendframewithCCA.returnFAILEDifnotsuccessful
//仅仅进行数据发送并没有进行CCA(比较坑爹的注释,事实是自己也没怎么仔细看--!
);
//若发送前进行CCA,需要ISSAMPLECCA再进行ISTXONCCA判断CCA标志位的值进行后续操作;
if(halRfTransmit()!
=SUCCESS){
status=FAILED;
}
//Waitfortheacknowledgetobereceived,ifany
//如果启用ACK,则在发送完成后进行进行等待580μs
//实际测试中发送7个字节的数据,两个节点先后发送A和B两个数据帧,两个数据帧的间隔大概需要不小于440μs+580μs+330μs(粗略估计^_^)的时间间隔Sniffer才能捕捉到A的应答帧(这些多出的时间由节点程序准备和结束时间?
)可以确定的是接受节点接收先后两个数据帧的时间间隔要大于580μs,时间间隔太短不能正确接收后一个数据帧,可以通过启用CCA解决这个冲突;
if(pConfig->ackRequest){
txState.ackReceived=FALSE;
//We'llenterRXautomatically,sojustwaituntilwecanbesurethattheackreceptionshouldhavefinished
//Thetimeoutconsistsofa12-symbolturnaroundtime,theackpacketduration,andasmallmargin
halMcuWaitUs((12*BASIC_RF_SYMBOL_DURATION)+(BASIC_RF_ACK_DURATION)+(2*BASIC_RF_SYMBOL_DURATION)+10);
//Ifanacknowledgmenthasbeenreceived(byRxFrmDoneIsr),theackReceivedflagshouldbeset
status=txState.ackReceived?
SUCCESS:
FAILED;
}else{
status=SUCCESS;
}
//Turnoffthereceiverifitshouldnotcontinuetobeenabled
//如果不需要继续接收则关闭接收
if(!
txState.receiveOn){
halRfReceiveOff();
}
if(status==SUCCESS){
txState.txSeqNumber++;
}
#ifdefSECURITY_CCM
halRfIncNonceTx();//Incrementnoncevalue
#endif
returnstatus;
}
根据以上basicRfSendPacket()基本流程:
确保设备处于接收状态→确认数据有效长度→等待设备处于发送空闲状态→构建LEN+MPDU→LEN+MPDU写入TXFIFO→始能接收中断→执行发送选通命令进行数据发送→如果要求有ACK应答,则延时等待ACK→关闭接收状态;另外从上述流程中可以看出,将数据写入TXFIFO并不进行数据的发送,需要通过相关的选通命令在启动发送!
!
其中basicRfSendPacket()调用basicRfBuildMpdu()对上层(通常为应用层)产生的数据进行封包操作,介绍basicRfBuildMpdu()之前简单介绍下802.15.4数据帧结构。
图2
以数据帧为例:
MAC的上层产生Payload作为MACPayload,即:
MSDU;
MPDU=MHR+MACPayload+MFR,即:
PSDU;
PPDU=SHR+ PHR+MPDU;//PHR+MHR+MACPayload需要写入TXFIFO,SHR和MFR(AUTOCRC=1时)硬件自动完成;
802.15.4数据帧结构简单介绍完毕,更具体的参见802.15.4协议文档,下面恢复正题;
/***********************************************************************************
*@fn basicRfBuildMpdu
*
*@brief Buildsmpdu(MACheader+payload)accordingtoIEEE802.15.4
* frameformat //根据802.15.4协议的帧结构构建MPDU(MAC帧头+净载荷数据,而由于AUTOCRC=1则FCS不必手动