BasicRF 简析(四:appSwitch()简析)

appSwitch()在SI中的函数关系如图1所示,其中basicRfInit()和basicRfSendPacket()两个函数比较有内容的,本文主要针对这两个函数进行展开。

BasicRF 简析(四:appSwitch()简析) - 果子 - 果子的博客

图1

       开始之前先介绍三个比较重要的结构体: basicRfRxInfo_t、basicRfTxState_t和 basicRfPktHdr_t

//接收帧信息

typedef struct {
    uint8 seqNumber;     //帧序号;
    uint16 srcAddr;         //源地址;
    uint16 srcPanId;       //源节点的PANID;
    int8 length;               //帧长度;
    uint8* pPayload;       //该指针指向帧数据即:净载荷数据;
    uint8 ackRequest;     //帧控制域的应答位信息;
    int8 rssi;                     //接收信号强度指示;
    volatile uint8 isReady;  //通过CRC校验,数据接收完成,该标志位进行后续读取操作;
    uint8 status;              //待定????
} basicRfRxInfo_t;

//发送状态信息
typedef struct {
    uint8 txSeqNumber;     //帧序号;
    volatile uint8 ackReceived;   //ACK是否接收完成;
    uint8 receiveOn;          //是否处于接收状态;
    uint32 frameCounter;   //发送帧计数;
} basicRfTxState_t;


//BasicRf 帧头(IEEE 802.15.4)
typedef struct {
    uint8   packetLength;    //帧长度;
    uint8   fcf0;           // Frame control field LSB
    uint8   fcf1;           // Frame control field MSB
    uint8   seqNumber;        //帧序号;
    uint16  panId;                //PANID;
    uint16  destAddr;          //目的地址;
    uint16  srcAddr;             //源地址;
    #ifdef SECURITY_CCM  //安全选项;
    uint8   securityControl;
    uint8  frameCounter[4];
    #endif
} basicRfPktHdr_t;

(一)  关于basicRfInit()

/***********************************************************************************
* @fn          basicRfInit
*
* @brief       Initialise basic RF datastructures. Sets channel, short address and
*              PAN id in the chip and configures interrupt on packet reception

*              初始化BasicRF数据结构,如:通道选择、短地址、PANID及接收中断的配置;
* @param       pRfConfig - pointer to BASIC_RF_CONFIG struct.
*                          This struct must be allocated by higher layer
*              txState - file scope variable that keeps tx state info     //发送状态信息;

*              rxi - file scope variable info extracted from the last incoming
*                    frame                                                                      //最新的所接收帧信息;
*
* @return      none
*/
uint8 basicRfInit(basicRfCfg_t* pRfConfig)
{
    if (halRfInit()==FAILED)                       //Rf初始化,启用Rf的推荐简单配置,可选的PA模块配置,始终返回Success;
        return FAILED;

    halIntOff();                                          //关闭总中断;

    // Set the protocol configuration
    pConfig = pRfConfig;                         //指向相关配置信息;
    rxi.pPayload   = NULL;                       
//清空本节点的接收载荷数据;

    txState.receiveOn = TRUE;                //halRfInit()中开启接收;

    txState.frameCounter = 0;                  //发送帧计数值;
    txState.txSeqNumber = 0x88;             
//自行修改第一个发送帧序号初始值;

    // Set channel
    halRfSetChannel(pConfig->channel);    
//将定义的通道号写入相关寄存器;

    // Write the short address and the PAN ID to the CC2520 RAM
    halRfSetShortAddr(pConfig->myAddr);  //将定义的本节点地址写入相关寄存器;

                                                                   //#define SHORT_ADDR0     XREG( 0x6174 )

                                                                   //#define SHORT_ADDR1     XREG( 0x6175 )
    halRfSetPanId(pConfig->panId);            
//将定义的PANID写入相关寄存器;

                                                                   //#define PAN_ID0         XREG( 0x6172 )

                                                                   //#define PAN_ID1         XREG( 0x6173 )

    // if security is enabled, write key and nonce
    #ifdef SECURITY_CCM
    basicRfSecurityInit(pConfig);
    #endif

    // Set up receive interrupt (received data or acknowlegment)
    halRfRxInterruptConfig(basicRfRxFrmDoneIsr);  
//对函数指针进行赋值,关联相应的中断函数,即:声明中断程序;

    halIntOn();                                              //开启总中断;

                                                                   //为什么要开闭总中断一次????先启用发送节点后启用接收节点时,意外的接收中断?

    return SUCCESS;
}

       basicRfInit()如上代码所示,该函数仅对RF做简单初始化、通道选择、PANID、本节点地址进行配置,最后为RF接收中断 声明一个函数指针basicRfRxFrmDoneIsr

/***********************************************************************************
* @fn      halRfRxInterruptConfig
*            
* @brief   Configure RX interrupt.
*              //配置接收中断,将RX中断 指向 一段可执行程序; 
* @param   none
*
* @return  none
*/
void halRfRxInterruptConfig(ISR_FUNC_PTR pf)
{
    uint8 x;
    HAL_INT_LOCK(x);      //保存EA并将其清零;
    pfISR= pf;                     //将函数指针赋值,而 
pfISR将在RX中断时被执行;

    HAL_INT_UNLOCK(x);  //恢复之前EA的值;
}

 

/************************************************************************************
* @fn          rfIsr
*
* @brief       Interrupt service routine that handles RFPKTDONE interrupt.
*                   //RX中断服务程序;
* @param       none
*
* @return      none
*/
HAL_ISR_FUNCTION( rfIsr, RF_VECTOR )       

{
    uint8 x;

    HAL_INT_LOCK(x);

    if( RFIRQF0 & IRQ_RXPKTDONE )
    {
        if(pfISR){
            (*pfISR)();                 // Execute the custom ISR

                                             //如果pfISR不为空则将调用 函数指针所指向的函数basicRfRxFrmDoneIsr();
        }
        S1CON= 0;                   // Clear general RF interrupt flag
        RFIRQF0&= ~IRQ_RXPKTDONE;   // Clear RXPKTDONE interrupt
    }
    HAL_INT_UNLOCK(x);
}

         RF中断采用了宏声明的方式(协议栈中多采用宏来声明中断,而非常规C语言函数),其声明语句如下:

#define HAL_ISR_FUNC_DECLARATION(f,v)   _PRAGMA(vector=v) __near_func __interrupt void f(void)  //中断函数声明的宏;
#define HAL_ISR_FUNC_PROTOTYPE(f,v)     _PRAGMA(vector=v) __near_func __interrupt void f(void)   //中断函数原型的宏;
#define HAL_ISR_FUNCTION(f,v)         HAL_ISR_FUNC_PROTOTYPE(f,v); HAL_ISR_FUNC_DECLARATION(f,v) 
//中断函数定义宏,包括

                                                                                                                                                                                 //原型和声明;

其中rfIsr对应于宏中的 f(void),类似于指向其自身HAL_ISR_FUNCTION()。

 

(二)关于basicRfSendPacket()

/***********************************************************************************
* @fn basicRfSendPacket
*
* @brief Send packet
*
* @param destAddr - destination short address //目的地址;
* pPayload - pointer to payload buffer. This buffer must be
* allocated by higher layer. //需要MAC层以上产生要发送的数据(指针或数组);
* length - length of payload //要发送数据的长度;
* txState - file scope variable that keeps tx state info //发送状态信息;
* mpdu - file scope variable. Buffer for the frame to send //对数据进行封包为物理层协议数据单元;
*
* @return basicRFStatus_t - SUCCESS or FAILED
*/
uint8 basicRfSendPacket(uint16 destAddr, uint8* pPayload, uint8 length)
{
uint8 mpduLength;
uint8 status;

// Turn on receiver if its not on

//保证设备处于接收状态,其初始值在halRfInit()中开启接收并在basicRfInit()中被赋值为TRUE;

if(!txState.receiveOn) {
halRfReceiveOn();
}

 

// Check packet length

//取最小的有效数据长度,类似与可变长度域,可变长度值是很有用的例如:串口透传的数据长度;

//最大数据载荷为

//#define BASIC_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);

 

// Wait until the transceiver is idle

//根据SFD和TX_Active 状态位来判定设备是否处于空闲状态;

//SFD状态位为0说明设备目前无发送无接收;

halRfWaitTransceiverReady();

 

// Turn off RX frame done interrupt to avoid interference on the SPI interface

//防止2591冲突???
halRfDisableRxInterrupt();

mpduLength = basicRfBuildMpdu(destAddr, pPayload, length); //根据目的地址、载荷数据及长度信息进行封包;

 

#ifdef SECURITY_CCM
halRfWriteTxBufSecure(txMpdu, mpduLength, length, BASIC_RF_LEN_AUTH, BASIC_RF_SECURITY_M);
txState.frameCounter++; // Increment frame counter field
#else
halRfWriteTxBuf(txMpdu, mpduLength); //使用ISFLUSHTX()清空TXFIFO 并清除IRQ_TXDONE中断溢出标志位,将MPDU一个字节一个字节的写入RFD;
#endif

 

// Turn on RX frame done interrupt for ACK reception

//仅仅是始能接收中断,为发送完成后自动进入接收模式 接收 ACK做准备性工作;

//仅作为发送节点且不启用ACK的话,这部分语句都可以省略去;

halRfEnableRxInterrupt();

 

// Send frame with CCA. return FAILED if not successful

//仅仅进行数据发送并没有进行CCA(比较坑爹的注释,事实是自己也没怎么仔细看- -!);

//若发送前进行CCA,需要ISSAMPLECCA 再进行ISTXONCCA 判断CCA标志位的值进行后续操作;

if(halRfTransmit() != SUCCESS) {
status = FAILED;
}

 

// Wait for the acknowledge to be received, if any

//如果启用ACK,则在发送完成后进行进行等待580μs

//实际测试中发送7个字节的数据,两个节点先后发送A和B两个数据帧,两个数据帧的间隔大概需要不小于440μs+580μs+330μs (粗略估计^_^)的时间间隔Sniffer才能捕捉到A的应答帧(这些多出的时间 由节点程序准备和结束时间?可以确定的是接受节点接收先后两个数据帧的时间间隔要大于580μs,时间间隔太短不能正确接收后一个数据帧,可以通过启用CCA解决这个冲突;

if (pConfig->ackRequest) {
txState.ackReceived = FALSE;

// We'll enter RX automatically, so just wait until we can be sure that the ack reception should have finished
// The timeout consists of a 12-symbol turnaround time, the ack packet duration, and a small margin
halMcuWaitUs((12 * BASIC_RF_SYMBOL_DURATION) + (BASIC_RF_ACK_DURATION) + (2 * BASIC_RF_SYMBOL_DURATION) + 10);

// If an acknowledgment has been received (by RxFrmDoneIsr), the ackReceived flag should be set
status = txState.ackReceived ? SUCCESS : FAILED;

} else {
status = SUCCESS;
}

 

// Turn off the receiver if it should not continue to be enabled

//如果不需要继续接收则关闭接收
if (!txState.receiveOn) {
halRfReceiveOff();
}

if(status == SUCCESS) {
txState.txSeqNumber++;
}

#ifdef SECURITY_CCM
halRfIncNonceTx(); // Increment nonce value
#endif

return status;

}

       根据以上basicRfSendPacket()基本流程:确保设备处于接收状态→确认数据有效长度→等待设备处于发送空闲状态→构建LEN+MPDU→LEN+MPDU写入TXFIFO→始能接收中断→执行发送选通命令进行数据发送→如果要求有ACK应答,则延时等待ACK→关闭接收状态; 另外从上述流程中可以看出,将数据写入TXFIFO并不进行数据的发送,需要通过相关的选通命令在启动发送!!

       

       其中basicRfSendPacket()调用basicRfBuildMpdu()对上层(通常为应用层)产生的数据进行封包操作,介绍basicRfBuildMpdu()之前简单介绍下802.15.4数据帧结构。

BasicRF 简析(四:appSwitch()简析) - 果子 - 果子的博客

     图2
 

 以数据帧为例:

 MAC的上层产生Payload作为 MAC Payload,即:MSDU;

 MPDU = MHR + MAC Payload + MFR,即:PSDU;

 PPDU = SHR +  PHR + MPDU ;//PHR+MHR+MAC Payload 需要写入TXFIFO,SHR和MFR(AUTOCRC = 1时)硬件自动完成;

802.15.4数据帧结构简单介绍完毕,更具体的参见802.15.4协议文档,下面恢复正题;

/***********************************************************************************

* @fn          basicRfBuildMpdu

*

* @brief       Builds mpdu (MAC header + payload) according to IEEE 802.15.4

*              frame format  //根据802.15.4协议的帧结构 构建 MPDUMAC帧头+净载荷数据,而由于AUTOCRC = 1FCS不必手动写入TXFIFO,可以忽略但空间长度需要保留);

*

* @param      destAddr - Destination short address      //目的地址;

*              pPayload - pointer to buffer with payload   //净载荷数据buffer

*              payloadLength - length of payload buffer   //净载荷数据长度;

*

* @return      uint8 - length of mpdu   //MPDU的长度= MAC Hdr + MAC Payload

*/

static uint8 basicRfBuildMpdu(uint16 destAddr, uint8* pPayload, uint8 payloadLength)

{

    uint8 hdrLength, n;

 

    hdrLength = basicRfBuildHeader(txMpdu, destAddr, payloadLength);  //构建lEN+MHR,见下面代码

 

//将净载荷数据放置于 MHR  组成MPDU

    for(n=0;n<payloadLength;n++)

    {

        txMpdu[hdrLength+n] = pPayload[n];

    }

    return hdrLength + payloadLength; // total mpdu length

}

 

 

/***********************************************************************************

* @fn          basicRfBuildHeader

*

* @brief       Builds packet header according to IEEE 802.15.4 frame format

*              //根据802.15.4 协议构建 帧头;

* @param       buffer - Pointer to buffer to write the header   //MPDUbuffer

*              destAddr - destination short address   //目的短地址;

*              payloadLength - length of higher layer payload  //载荷数据长度;

*

* @return      uint8 - length of header   //帧头长度;

*/

static uint8 basicRfBuildHeader(uint8* buffer, uint16 destAddr, uint8 payloadLength)

{

    basicRfPktHdr_t *pHdr;   //帧头指针;

    uint16 fcf;     //存储帧控制域相关信息 变量;

 

    pHdr= (basicRfPktHdr_t*)buffer;   //帧头buffer 指向 MPDU buffer

 

// Populate packet header

//将相关信息 存至 帧头buffer

//帧头长度定义为

//#define BASIC_RF_PACKET_OVERHEAD_SIZE       ((2 + 1 + 2 + 2 + 2) + (2))

//其对应关系为((FCF+SeqNum+PANID+DestAddr+SrcAddr+FCS))

//然后将帧头信息 赋值给帧头 buffer

    pHdr->packetLength = payloadLength + BASIC_RF_PACKET_OVERHEAD_SIZE;

    //pHdr->frameControlField = pConfig->ackRequest ? BASIC_RF_FCF_ACK : BASIC_RF_FCF_NOACK;

    fcf= pConfig->ackRequest ? BASIC_RF_FCF_ACK : BASIC_RF_FCF_NOACK;

    pHdr->fcf0 = LO_UINT16(fcf);  

    pHdr->fcf1 = HI_UINT16(fcf);

    pHdr->seqNumber= txState.txSeqNumber;

    pHdr->panId= pConfig->panId;

    pHdr->destAddr= destAddr;

    pHdr->srcAddr= pConfig->myAddr;

 

    #ifdef SECURITY_CCM

 

    // Add security to FCF, length and security header

    pHdr->fcf0 |= BASIC_RF_SEC_ENABLED_FCF_BM_L;

    pHdr->packetLength += PKT_LEN_MIC;

    pHdr->packetLength += BASIC_RF_AUX_HDR_LENGTH;

 

    pHdr->securityControl= SECURITY_CONTROL;

    pHdr->frameCounter[0]=   LO_UINT16(LO_UINT32(txState.frameCounter));

    pHdr->frameCounter[1]=   HI_UINT16(LO_UINT32(txState.frameCounter));

    pHdr->frameCounter[2]=   LO_UINT16(HI_UINT32(txState.frameCounter));

    pHdr->frameCounter[3]=   HI_UINT16(HI_UINT32(txState.frameCounter));

 

    #endif

 

// Make sure bytefields are network byte order

//高低位变换;无线电数据传输为先低位后高位,这样做是在为无线传输做准备??

    UINT16_HTON(pHdr->panId);

    UINT16_HTON(pHdr->destAddr);

    UINT16_HTON(pHdr->srcAddr);

 

    return BASIC_RF_HDR_SIZE;

}

 

相关链接:http://blog.sina.com.cn/s/blog_750abb970101076q.html

猜你喜欢

转载自blog.csdn.net/z_hualin/article/details/79010282