📄 spp.c
字号:
/******************************************************************************
Filename: spp.c
Target: cc2430
Revised: 16/12-2005
Revision: 1.0
******************************************************************************/
#include <string.h>
#include "cul.h"
#include <stdio.h>
// protos
void rxCallBack(void);
void ackTimeout(void);
BOOL ackReceived(BYTE sourceAddress);
void sendAck(SPP_RX_STRUCT* receivedPacket);
void waitForAck(void);
static DMA_DESC* dmaTx; // pointer to the DMA descriptor for transmit.
static DMA_DESC* dmaRx; // pointer to the DMA descriptor for receive.
static BYTE dmaNumberTx = 0; // number indicating which DMA channel is used for transmit.
static BYTE dmaNumberRx = 0; // number indicating which DMA channel is used for receive.
static BYTE myAddress;
volatile BYTE sppRxStatus = 0;
volatile BYTE sppTxStatus = 0;
static BYTE pAckBuffer[7];
static SPP_TX_STRUCT* pAckData;
static volatile UINT8 retransmissionCounter;
static UINT8 ackTimerNumber;
static FUNCTION* rxCallBackFunction;
//-----------------------------------------------------------------------------
// See cul.h for a description of this function.
//-----------------------------------------------------------------------------
void sppSetRxCallBackFunction(FUNCTION* callBackFunction)
{
rxCallBackFunction = callBackFunction;
} // Ends sppSetRxCallBackFunction()
void sendAck(SPP_RX_STRUCT* receivedPacket)
{
RFD = SPP_HEADER_AND_FOOTER_LENGTH + SPP_ACK_LENGTH;
RFD = receivedPacket->srcAddress;
RFD = myAddress;
RFD = ACK;
RFD = 0;
RFIF &= ~IRQ_TXDONE;
ISTXON;
while(!(RFIF & IRQ_TXDONE));
return;
}
//------------------------------------------------------------------------------------------------------
// void rxCallBack(...)
//
// Description:
// This function is called by the interrupt routine when the Rx DMA channel
// finishes the data transfer. The received packet's destination address
// is checked. If not addressed to this node, or if the CRC value is not
// correct, the packet is erased. An ACK is sent if the packet
// tells to. A user defined callback function may is run if set (set
// with setRxCallBackFunction())
//
// Arguments:
// void
//
// Return value:
// void
//-----------------------------------------------------------------------------
void rxCallBack(void)
{
SPP_RX_STRUCT __xdata* receivedPacket;
BYTE res = FALSE;
if(RXFIFOCNT > 0)
{
ISFLUSHRX;
ISFLUSHRX;
}
// Investigating the received packet.
// Checking the destination address and that the CRC is OK.
// The message is ACK'ed if it tells to.
receivedPacket = (SPP_RX_STRUCT __xdata*) GET_DMA_DEST(dmaRx);
receivedPacket->payloadLength = receivedPacket->payloadLength-SPP_HEADER_AND_FOOTER_LENGTH;
if((receivedPacket->destAddress == myAddress) || (receivedPacket->destAddress == BROADCAST_ADDRESS))
{
if(receivedPacket->payload[receivedPacket->payloadLength+1] & 0x80)
{
if(receivedPacket->flags == ACK)
{
res = ackReceived(receivedPacket->srcAddress);
}
else
{
sppRxStatus = PACKET_RECEIVED;
res = TRUE;
if(receivedPacket->flags & DO_ACK)
{
sendAck(receivedPacket);
}
sppRxStatus = RX_COMPLETE;
if(rxCallBackFunction)
{
rxCallBackFunction();
}
}
}
}
if(res == FALSE)
{
ISFLUSHRX;
ISFLUSHRX;
// rearming DMA channel
DMA_ARM_CHANNEL(dmaNumberRx);
RFIM |= IRQ_SFD;
sppRxStatus = RX_WAIT;
}
return;
} // ends rxCallBack
//-----------------------------------------------------------------------------
// void ackTimeout(...)
//
// Description:
// This function resends a packet if it is not ACK'ed by the recipient
// within _ACK_TIMEOUT_ m-seconds. The message is resent _ACK_RETRIES_ times.
// If the message remains un-ACK'ed, transmission is aborted and spp TX
// status is set to DEST_UNREACHABLE.
//
// Arguments:
// void
//
// Return value:
// void
//-----------------------------------------------------------------------------
void ackTimeout(void){
culTimer4AdmClear(ackTimerNumber);
if(pAckData != NULL)
{
if(retransmissionCounter < ACK_RETRIES)
{
// Resending the message.
pAckData->flags |= RETRANSMISSION;
TIMER4_RUN(FALSE);
sppSend(pAckData);
T4CNT = 0;
TIMER4_RUN(TRUE);
retransmissionCounter++;
}
else
{
// The packet has been resent too many times. Assuming that the node is unreacheble.
pAckData = 0;
retransmissionCounter = 0;
sppTxStatus = DEST_UNREACHABLE;
RFIM &= ~IRQ_FIFOP;
}
}
return;
} // ends ackTimeout
//-----------------------------------------------------------------------------
// See cul.h for a description of this function.
//-----------------------------------------------------------------------------
BOOL sppInit(UINT32 frequency, BYTE address){
BYTE res = 0;
BOOL status = TRUE;
sppSetAddress(address);
// Clearing the states of the spp.
sppTxStatus = TX_IDLE;
sppRxStatus = RX_IDLE;
retransmissionCounter = 0;
ackTimerNumber = 0;
pAckData = 0;
// Clearing the RF interrupt flags and enable mask and enabling RF interrupts
RFIF = 0;
RFIM = 0;
INT_SETFLAG(INUM_RF,INT_CLR);
INT_ENABLE(INUM_RF,INT_ON);
// Setting the frequency and initialising the radio
res = halRfConfig(frequency);
if(res == FALSE){
status = FALSE;
}
// Setting the number of bytes to assert the FIFOP flag
IOCFG0 = 7;
INT_SETFLAG(INUM_RFERR, INT_CLR);
INT_ENABLE(INUM_RFERR, INT_ON);
// Flushing both Tx and Rx FiFo. The flush-Rx is issued twice to reset the SFD.
// Calibrating the radio and turning on Rx to evaluate the CCA.
SRXON;
SFLUSHTX;
SFLUSHRX;
SFLUSHRX;
STXCALN;
ISSTART;
// Using the timer 4 administrator to generate interrupt to check if a message is unacked...
culTimer4AdmInit();
// Initialising the DMA administrator
culDmaInit();
// Requesting a DMA channel for transmit data. No callback function is used. Instead the TX_DONE
// interrupt is used to determine when a transfer is finished. Configuring the DMA channel for
// transmit. The data address and length will be set prior to each specific transmission.
dmaTx = culDmaAllocChannel(&dmaNumberTx, 0);
if((dmaNumberTx == 0) || (dmaNumberTx > 4)){
status = FALSE;
}
culDmaToRadio(dmaTx, 0, 0, FALSE);
// Requesting a DMA channel for receiving data. Giving the address of the callback function.
// Configuring the DMA channel for receive. The data address will be set prior to each specific
// reception.
dmaRx = culDmaAllocChannel(&dmaNumberRx, &rxCallBack);
if((dmaNumberRx == 0) || (dmaNumberRx > 4)){
status = FALSE;
}
culDmaFromRadio(dmaRx, 0, TRUE);
// Making sure that none of the channels are armed.
DMA_ABORT_CHANNEL(dmaNumberRx);
DMA_ABORT_CHANNEL(dmaNumberTx);
INT_ENABLE(INUM_DMA, INT_ON);
return status;
} // ends sppInit
//-----------------------------------------------------------------------------
// See cul.h for a description of this function.
//-----------------------------------------------------------------------------
void sppSetAddress(BYTE address){
myAddress = address;
} // Ends sppSetAddress()
// Internal function which enables the timeout when waiting for an ACK.
void waitForAck(void)
{
ackTimerNumber = culTimer4AdmSet(ACK_TIMEOUT, &ackTimeout);
SET_DMA_DEST(dmaRx,pAckBuffer);
SET_DMA_LENGTH(dmaRx,7);
} // Ends waitForAck()
//-----------------------------------------------------------------------------
// See cul.h for a description of this function.
//-----------------------------------------------------------------------------
BYTE sppSend(SPP_TX_STRUCT* pPacketPointer){
BYTE res = TRUE;
// If data is to be transmitted, the DMA is set up.
if(pPacketPointer->payloadLength)
{
if (pPacketPointer->payloadLength > SPP_MAX_PAYLOAD_LENGTH)
{
res = TOO_LONG;
sppTxStatus = TX_IDLE;
}
else
{
// Setting up the DMA
DMA_ABORT_CHANNEL(dmaNumberTx);
SET_DMA_SOURCE(dmaTx,pPacketPointer->payload);
SET_DMA_LENGTH(dmaTx,pPacketPointer->payloadLength);
}
}
// Proceed if the packet length is OK.
if (res == TRUE)
{
// Flipping the sequence bit if the transfer is not a retransmission.
if(!(pPacketPointer->flags & RETRANSMISSION))
{
pPacketPointer->flags ^= SEQUENCE_BIT;
}
// Clearing RF interrupt flags and enabling RF interrupts.
if(FSMSTATE == 6 && RXFIFOCNT > 0)
{
ISFLUSHRX;
ISFLUSHRX;
}
RFIF &= ~IRQ_TXDONE;
RFIM &= ~IRQ_SFD;
INT_SETFLAG(INUM_RF, INT_CLR);
// Writing the total packet length, addresses and flags to Tx FiFo.
// Transferring the payload if any.
RFD = (pPacketPointer->payloadLength + SPP_HEADER_AND_FOOTER_LENGTH);
RFD = pPacketPointer->destAddress;
RFD = myAddress;
RFD = pPacketPointer->flags;
if(pPacketPointer->payloadLength)
{
DMA_ARM_CHANNEL(dmaNumberTx);
DMA_START_CHANNEL(dmaNumberTx);
}
// If the RSSI value is not valid, enable receiver
if(RSSIL == 0x80)
{
ISRXON;
// Turning on Rx and waiting 320u-sec to make the RSSI value become valid.
halWait(1);
}
//Transmitting
ISTXONCCA;
//if(TX_ACTIVE)
if(FSMSTATE > 30)
{
// Asserting the status flag and enabling ACK reception if expected.
sppTxStatus = TX_IN_PROGRESS;
if(pPacketPointer->flags & DO_ACK)
{
pAckData = pPacketPointer;
DMA_ABORT_CHANNEL(dmaNumberRx);
waitForAck();
}
else
{
pAckData = NULL;
}
RFIM |= IRQ_TXDONE;
}
else
{
ISFLUSHTX;
res = CHANNEL_BUSY;
RFIM &= ~IRQ_TXDONE;
// De-flipping the sequence bit.
if(!(pPacketPointer->flags & RETRANSMISSION))
{
pPacketPointer->flags ^= SEQUENCE_BIT;
}
}
}
return res;
} // ends sppSend
// Internal function which is called when an ack is received.
// If the ACK is from the expected node, the retransmission of the packet is cancelled.
BOOL ackReceived(BYTE sourceAddress)
{
BOOL res = FALSE;
if(sourceAddress == pAckData->destAddress)
{
res = TRUE;
culTimer4AdmClear(ackTimerNumber);
sppTxStatus = TX_SUCCESSFUL;
retransmissionCounter = 0;
pAckData = 0;
}
return res;
} //Ends ackReceived()
//-----------------------------------------------------------------------------
// See cul.h for a description of this function.
//-----------------------------------------------------------------------------
#pragma vector=RF_VECTOR
__interrupt void spp_rf_IRQ(void)
{
BYTE enabledAndActiveInterrupt;
INT_GLOBAL_ENABLE(INT_OFF);
enabledAndActiveInterrupt = RFIF;
RFIF = 0x00; // Clear all interrupt flags
INT_SETFLAG(INUM_RF, INT_CLR); // Clear MCU interrupt flag
enabledAndActiveInterrupt &= RFIM;
// Start of frame delimiter (SFD)
if(enabledAndActiveInterrupt & IRQ_SFD)
{
if(sppRxStatus == RX_WAIT)
{
sppRxStatus = RX_IN_PROGRESS;
RFIM &= ~IRQ_SFD;
}
}
// Transmission of a packet is finished. Enabling reception of ACK if required.
if(enabledAndActiveInterrupt & IRQ_TXDONE)
{
if(sppTxStatus == TX_IN_PROGRESS)
{
if(pAckData == NULL)
{
sppTxStatus = TX_SUCCESSFUL;
}
else
{
DMA_ARM_CHANNEL(dmaNumberRx);
}
}
// Clearing the tx done interrupt enable
RFIM &= ~IRQ_TXDONE;
}
INT_GLOBAL_ENABLE(INT_ON);
}
//-----------------------------------------------------------------------------
// See cul.h for a description of this function.
//-----------------------------------------------------------------------------
void sppReceive(SPP_RX_STRUCT* pReceiveData){
sppRxStatus = RX_WAIT;
DMA_ABORT_CHANNEL(dmaNumberRx);
// Setting the address to where the received data are to be written.
SET_DMA_DEST(dmaRx,pReceiveData);
SET_DMA_LENGTH(dmaRx,255);
// Arming the DMA channel. The receiver will initate the transfer when a packet is received.
DMA_ARM_CHANNEL(dmaNumberRx);
if(FSMSTATE == 6 && RXFIFOCNT > 0)
{
ISFLUSHRX;
ISFLUSHRX;
}
// Turning on the receiver
ISRXON;
return;
}
//-----------------------------------------------------------------------------
// See cul.h for a description of this function.
//-----------------------------------------------------------------------------
#pragma vector=RFERR_VECTOR
__interrupt static void rf_error_IRQ(void)
{
INT_GLOBAL_ENABLE(INT_OFF);
// If Rx overflow occurs, the Rx FiFo is reset.
// The Rx DMA is reset and reception is started over.
if(FSMSTATE == 17)
{
STOP_RADIO();
ISFLUSHRX;
ISFLUSHRX;
DMA_ABORT_CHANNEL(dmaNumberRx);
DMA_ARM_CHANNEL(dmaNumberRx);
ISRXON;
}
else if(FSMSTATE == 56)
{
ISFLUSHTX;
}
INT_SETFLAG(INUM_RFERR,INT_CLR);
INT_GLOBAL_ENABLE(INT_ON);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -