A Atmel RF2xx Radio Library for Mbed

Dependents:   xBedRadio MxSniffer

MxRadio.cpp

Committer:
fredqian
Date:
2015-04-09
Revision:
0:5f1d66c85ae0

File content as of revision 0:5f1d66c85ae0:

/* Copyright (c) 2011 Frank Zhao
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

 * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
 * Neither the name of the authors nor the names of its contributors
     may be used to endorse or promote products derived from this software
     without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. */

#include "MxRadio.h"
#include "MxRadioEvents.h"
//#define radio_set_state radio_force_state

// the write function checks if beginTransmission was called prior to write
// in order to determine whether to use immediate or non-immediate transmission

const uint8_t HeadSize=9;

// a busy indicator so transmit functions can wait until the last transmission has finished
volatile uint8_t txIsBusy = 0;


cMxRadio::~cMxRadio()
{
}
// empty constructor, should not be called by user
cMxRadio::cMxRadio(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst, PinName slp, PinName irq)
:m_spi(mosi, miso, sclk), m_cs(cs), reset_pin(rst), sleep_pin(slp),irq_pin(irq)
{
	// default event handlers
	
	zrEventTxDone=0;
	zrEventReceiveFrame=0;
	temprssi=0;
	setautotx=false;
	setautorx=false;
	needack=false;
	usedBeginTransmission = 0;
	hasAttachedTxEvent = 0;
	hasAttachedRxEvent = 0;
	lastLqi = 0;
	lastRssi = 0;
	txTmpBufferLength = 0;
	rxRingBufferHead = 0;
	rxRingBufferTail = 0;
	/*
	user_radio_tx_done=0;
	user_radio_receive_frame=0;
	user_radio_irq=0;
	user_radio_error=0;
	*/
	zr_attach_receive_frame(&cMxRadio::onReceiveFrame);
	zr_attach_tx_done(&cMxRadio::onTxDone);

}

/**
 * @brief Radio Initialization
 *
 * The function initializes all IO ressources,
 * needed for the usage of the radio and performs
 * a reset to the radio.
 * It prepares the frame header.
 * Then it sets the channel number and defaults to RX state.
 *
 * @param chan the channel number for the radio to use, 11 to 26
 */
void cMxRadio::begin(channel_t chan)
{
	begin(chan, 0);
}

void cMxRadio::begin(channel_t chan,uint16_t panid,uint16_t localaddress,bool needackval,bool autotxval,bool autorxval,char autoretrycount)
{
	setautotx=autotxval;
	setautorx=autorxval;
	needack=needackval;
	radio_init(rxFrameBuffer, MAX_FRAME_SIZE);

	//////////////////////////////
#ifdef SR_MAX_FRAME_RETRES
	trx_bit_write(SR_MAX_FRAME_RETRES,autoretrycount & 0Xf);//for auto wake up
	//////////////////////////////
#endif


	if(!needack)
		txTmpBuffer[0] = 0x41;
	else
		txTmpBuffer[0] = 0x61; //ack required
	txTmpBuffer[1] = 0x88;
	txTmpBuffer[2] =  0;
	txTmpBuffer[3]=(uint8_t)(panid & 0xFF );
	txTmpBuffer[4]=(uint8_t)((panid>>8) & 0xFF );
	txTmpBuffer[5] = 0xFF; //dest address low byte
	txTmpBuffer[6] = 0xFF; //dest address hight byte
	txTmpBuffer[7] = (uint8_t)(localaddress & 0xFF );
	txTmpBuffer[8] = (uint8_t)((localaddress>>8) & 0xFF );
	setParam(phyPanId,(uint16_t)panid );
	setParam(phyShortAddr,(uint16_t)localaddress );

	radio_set_param(RP_CHANNEL(chan));

	// default to receiver
	if (setautorx)
		radio_set_state(STATE_RXAUTO);
	else
		radio_set_state(STATE_RX);
#ifdef ENABLE_DIG3_DIG4
	trx_bit_write(SR_PA_EXT_EN, 1);
#endif
}
void cMxRadio::begin(channel_t chan,uint16_t panid,uint16_t localaddress,bool needackval,bool autotxval,bool autorxval)
{
	cMxRadio::begin(chan,panid,localaddress,needackval,autotxval,autorxval,4);

}
void cMxRadio::sendFrame(uint16_t destaddress,bool needackval,uint8_t* frm, uint8_t len)
{
	uint8_t oldvalue=txTmpBuffer[0];
	if(!needackval)
		txTmpBuffer[0] = 0x41;
	else
		txTmpBuffer[0] = 0x61;
	beginTransmission(destaddress);
	write(frm,len);
	endTransmission();
	txTmpBuffer[0]=oldvalue;

}
/**
 * @brief Radio Initialization
 *
 * Overload for radio initalization that allows for the user to set a custom frame header
 *
 * @param chan channel number for the radio to use, 11 to 26
 * @param frameHeader HeadSize byte custom frame header
 */
void cMxRadio::begin(channel_t chan, uint8_t* frameHeader)
{
	radio_init(rxFrameBuffer, MAX_FRAME_SIZE);

	if (frameHeader)
	{
		// copy custom frame header
		int i;
		for (i = 0; i < HeadSize; i++)
			txTmpBuffer[i] = frameHeader[i];
	}
	else
	{
		txTmpBuffer[0] = 0x41;
		txTmpBuffer[1] = 0x88;
		txTmpBuffer[2] =  0;
		txTmpBuffer[3]=0xCD;
		txTmpBuffer[4]=0xAB;
		txTmpBuffer[5] = 0xFF; //dest address low byte
		txTmpBuffer[6] = 0xFF; //dest address hight byte
		txTmpBuffer[7] = 0x01;;
		txTmpBuffer[8] = 0x00;;
	}

	// set the channel
	radio_set_param(RP_CHANNEL(chan));

	// default to receiver
	if (setautorx)
		radio_set_state(STATE_RXAUTO);
	else
		radio_set_state(STATE_RX);

#ifdef ENABLE_DIG3_DIG4
	trx_bit_write(SR_PA_EXT_EN, 1);
#endif

}

/**
 * @brief Set Frame Header
 *
 * changes the custom frame header
 *
 * @param frameHeader HeadSize byte custom frame header
 */
void cMxRadio::setFrameHeader(uint8_t* frameHeader)
{
	// copy custom frame header
	int i;
	for (i = 0; i < HeadSize; i++)
		txTmpBuffer[i] = frameHeader[i];
}

/**
 * @brief Attach Error Event Handler
 *
 * Allows the user to set a error handling function
 * An empty one is used if none is set
 *
 * @param funct the function pointer to the event handler
 */
void cMxRadio::attachError(void (*funct)(radio_error_t))
{
	user_radio_error = funct;
}

/**
 * @brief Attach IRQ Event Handler
 *
 * Allows the user to set a IRQ handling function
 * An empty one is used if none is set
 *
 * @param funct the function pointer to the event handler
 */
void cMxRadio::attachIrq(void (*funct)(uint8_t))
{
	user_radio_irq = funct;
}

/**
 * @brief Attach RX Event Handler
 *
 * Allows the user to set a RX handling function
 * The default handler will read in the received frame and place it into the RX FIFO ring buffer
 * If the user chooses to use this attach function, then the default handler will not be used
 * This means read/peek/available/flush will stop working
 *
 * @param funct the function pointer to the event handler
 */
void cMxRadio::attachReceiveFrame(uint8_t* (*funct)(uint8_t, uint8_t*, uint8_t,int8_t, uint8_t))
{
	zrEventReceiveFrame = funct;
	hasAttachedRxEvent = (funct == 0) ? 0 : 1;
}

/**
 * @brief Attach TX Complete Event Handler
 *
 * Allows the user to set a TX complete handling function
 * An empty one is used if none is set
 * The event will occur before the busy flag is reset and returning to RX state
 *
 * @param funct the function pointer to the event handler
 */
void cMxRadio::attachTxDone(void (*funct)(radio_tx_done_t))
{
	zrEventTxDone = funct;
	hasAttachedTxEvent = (funct == 0) ? 0 : 1;
}

/**
 * @brief Default RX Event Handler
 *
 * If the user has not attached a custom event handler, then the bytes are placed into the FIFO ring buffer
 * If the frame contains a header, then the frame header is ignored, only the payload is read
 * The above does not occur if a user handler is attached, which will be called instead
 * The LQI and RSSI is always remembered
 *
 * This should not be called by the user
 *
 * @param len length of the frame
 * @param frm array containing frame data
 * @param lqi link quality indicator
 * @param crc_fail boolean indicating whether the received frame failed FCS verification, not used
 */
uint8_t* cMxRadio::onReceiveFrame(uint8_t len, uint8_t* frm, uint8_t lqi, int8_t ed,uint8_t crc_fail)
{
	if (crc_fail)
		return rxRingBuffer;
	lastLqi = lqi;
	//lastRssi=ed;
	if (hasAttachedRxEvent == 0)
	{
		// no event handler, so write it into the FIFO

		if (len >= HeadSize-1)
		{
			// frame header exists
			// copy only payload

			for (uint8_t i = HeadSize; i < len - 2; i++)
			{
				uint16_t j = ((uint16_t)((uint16_t)rxRingBufferHead + 1)) % ZR_FIFO_SIZE;
				if (j != rxRingBufferTail)
				{
					// push into FIFO
					rxRingBuffer[rxRingBufferHead] = frm[i];
					rxRingBufferHead = j;
				}
				else
				{
					// FIFO full
					break;
				}
			}
		}
		else
		{
			// frame header does not exist
			// copy everything

			for (uint8_t i = 0; i < len; i++)
			{
				uint16_t j = ((uint16_t)((uint16_t)rxRingBufferHead + 1)) % ZR_FIFO_SIZE;
				if (j != rxRingBufferTail)
				{
					// push into FIFO
					rxRingBuffer[rxRingBufferHead] = frm[i];
					rxRingBufferHead = j;
				}
				else
				{
					// FIFO full
					break;
				}
			}
		}

		return rxRingBuffer;
	}
	else
	{
		// user event is attached so call it
		return zrEventReceiveFrame(len, frm, lqi, ed,crc_fail);
	}
}

/**
 * @brief RX Buffer Flush
 *
 * Flush the RX FIFO ring buffer
 */
void cMxRadio::flush()
{
	rxRingBufferHead = rxRingBufferTail;
}

/**
 * @brief RX Buffer Read
 *
 * pops and returns the next byte from the FIFO ring buffer
 *
 * @return the next byte, or -1 if buffer is empty
 */
int16_t cMxRadio::read()
{
	// if the head isn't ahead of the tail, we don't have any characters
	if (rxRingBufferHead == rxRingBufferTail)
	{
		return -1;
	}
	else
	{
		uint8_t c = rxRingBuffer[rxRingBufferTail];
		rxRingBufferTail = (rxRingBufferTail + 1) % ZR_FIFO_SIZE; // pop
		return c;
	}
}

/**
 * @brief RX Buffer Peek
 *
 * returns the next byte from the FIFO ring buffer without removing it
 *
 * @return the next byte, or -1 if buffer is empty
 */
int16_t cMxRadio::peek()
{
	// if the head isn't ahead of the tail, we don't have any characters
	if (rxRingBufferHead == rxRingBufferTail)
	{
		return -1;
	}
	else
	{
		uint8_t c = rxRingBuffer[rxRingBufferTail];
		return c;
	}
}

/**
 * @brief RX Buffer Size
 *
 * Shows how many bytes are in the RX FIFO ring buffer
 *
 * @return how many bytes are in the RX FIFO ring buffer
 */
int8_t cMxRadio::available()
{
	return ((int16_t)((int16_t)ZR_FIFO_SIZE + (int16_t)rxRingBufferHead - (int16_t)rxRingBufferTail)) % ZR_FIFO_SIZE;
}

/**
 * @brief Raw Frame Transmit
 *
 * Transmits a frame
 * Warning: no frame header or FCS is added
 *
 * @param frm array containing frame data
 * @param len length of the frame
 */
void cMxRadio::txFrame(uint8_t* frm, uint8_t len)
{
#ifdef ZR_TXWAIT_BEFORE
	waitTxDone(0xFFFF);
#endif
	txIsBusy = 1;
	if (setautotx)
		radio_set_state(STATE_TXAUTO);
	else
		radio_set_state(STATE_TX);
	ZR_RFTX_LED_ON();
	radio_send_frame(len, frm, 0);
#ifdef ZR_TXWAIT_AFTER
	waitTxDone(0xFFFF);
	if (setautorx)
		radio_set_state(STATE_RXAUTO);
	else
		radio_set_state(STATE_RX);
	txIsBusy = 0;
#endif
}

/**
 * @brief Prepare for Trasmission
 *
 * Goes into non-immediate transmit mode, resets the transmit payload
 * Non-immediate mode sends multiple bytes per frame
 *
 */
void cMxRadio::beginTransmission()
{
	usedBeginTransmission = 1;
	txTmpBuffer[5]= 0xFF;
	txTmpBuffer[6]= 0XFF;
	// add frame header
	txTmpBufferLength = HeadSize;
}

void cMxRadio::beginTransmission(uint16_t destaddress)
{
	txTmpBuffer[5]=(uint8_t)(destaddress & 0xFF );
	txTmpBuffer[6]=(uint8_t)((destaddress>>8) & 0xFF );
	usedBeginTransmission = 1;

	// add frame header
	txTmpBufferLength = HeadSize;
}

/**
 * @brief Finalize Trasmission
 *
 * Finalize the payload and transmits it when ready
 *
 */
void cMxRadio::endTransmission()
{
	usedBeginTransmission = 0;

	// empty FCS field
	txTmpBufferLength += 2;

#ifdef ZR_TXWAIT_BEFORE
	waitTxDone(0xFFFF);
#endif
	txIsBusy = 1;
	if (setautotx)
		radio_set_state(STATE_TXAUTO);
	else
		radio_set_state(STATE_TX);
	ZR_RFTX_LED_ON();
	//if broadcase ,cant need ack
	if(txTmpBuffer[5]==0xff && txTmpBuffer[5]==0xff)
		txTmpBuffer[0]=txTmpBuffer[0]&0xdf;
	else
	{
		if(!needack)
			txTmpBuffer[0] = 0x41;
		else
			txTmpBuffer[0] = 0x61; //ack required
	}
	radio_send_frame(txTmpBufferLength, txTmpBuffer, 0);
#ifdef ZR_TXWAIT_AFTER
	waitTxDone(0xFFFF);
	if (setautorx)
		radio_set_state(STATE_RXAUTO);
	else
		radio_set_state(STATE_RX);
	txIsBusy = 0;
#endif
}

/**
 * @brief Cancel Trasmission
 *
 * Clears payload buffer
 *
 * Warning: does not actually cancel a transmission in progress
 *
 */
void cMxRadio::cancelTransmission()
{
	usedBeginTransmission = 0;

	// add frame header
	txTmpBufferLength = HeadSize;
}
/**
 * @brief TX a Byte
 *
 * Wrapper for "write", since the "Wire" library uses "send"
 *
 */
void cMxRadio::send(uint8_t c)
{
	write(c);
}

/**
 * @brief TX a Byte
 *
 * If "beginTrasmission" was used, then it writes into the transmit buffer for non-immediate mode
 * If "beginTrasmission" was not used, then it transmits the single byte immediately (slower for multiple bytes)
 *
 * @param c character to be sent
 */
void cMxRadio::write(uint8_t c)
{
	if (usedBeginTransmission)
	{
		if (txTmpBufferLength < ZR_TXTMPBUFF_SIZE - 2)
		{
			txTmpBuffer[txTmpBufferLength] = c;
			txTmpBufferLength++;

			if (txTmpBufferLength >= ZR_TXTMPBUFF_SIZE - 2)
			{
				// buffer is now full
				// just send it all out so we have more room
				endTransmission();
				beginTransmission();
			}
		}
	}
	else
	{
		txTmpBuffer[HeadSize] = c; // set payload
		txTmpBuffer[HeadSize+1] = 0; // empty FCS
		txTmpBuffer[HeadSize+2] = 0; // empty FCS

#ifdef ZR_TXWAIT_BEFORE
		waitTxDone(0xFFFF);
#endif
		txIsBusy = 1;
		if (setautotx)
			radio_set_state(STATE_TXAUTO);
		else
			radio_set_state(STATE_TX);
		ZR_RFTX_LED_ON();
		radio_send_frame(10, txTmpBuffer, 0);
#ifdef ZR_TXWAIT_AFTER
		waitTxDone(0xFFFF);
		if (setautorx)
			radio_set_state(STATE_RXAUTO);
		else
			radio_set_state(STATE_RX);
		txIsBusy = 0;
#endif
	}
}
/**
 * @brief TX a String
 *
 * A overload for "write" that sends a null-terminated string
 *
 * @param str null-terminated string to be sent
 */
void cMxRadio::write(char* str)
{
	while (*str)
		write(*str++);
}

/**
 * @brief TX an Array
 *
 * A overload for "write" that sends an array
 *
 * @param arr data array to be sent
 * @param len length of data array
 */
void cMxRadio::write(uint8_t* arr, uint8_t len)
{
	uint8_t i;
	for (i = 0; i < len; i++)
		write(arr[i]);
}

/**
 * @brief Default TX Complete Event Handler
 *
 * Calls the user event function if one is attached
 * Clear the TX busy status flag
 * Defaults back to RX state
 *
 * this should not be called by the user
 *
 * @param x one of the radio_tx_done_t enumerations indicating transmission success
 */
void cMxRadio::onTxDone(radio_tx_done_t x)
{
	if (hasAttachedTxEvent)
	{
		zrEventTxDone(x);
	}

	txIsBusy = 0;
}

/**
 * @brief radio_set_param Wrapper
 *
 * set a radio parameter
 *
 * see radio.h for more info
 */
void cMxRadio::setParam(radio_attribute_t attr, radio_param_t parm)
{
	radio_set_param(attr, parm);
}

/**
 * @brief radio_do_cca Wrapper
 *
 * perform CCA measure
 *
 * see radio.h for more info
 */
radio_cca_t cMxRadio::doCca()
{
	return radio_do_cca();
}

/**
 * @brief Set Radio State Wrapper
 *
 * sets or forces the radio into a state
 *
 * see radio.h for more info
 *
 * @param state requested radio state
 * @param force boolean indicating to force the state
 */
void cMxRadio::setState(radio_state_t state, uint8_t force)
{
	if (force)
		radio_force_state(state);
	else
		radio_set_state(state);
}

/**
 * @brief radio_set_state Wrapper
 *
 * bring the the radio in the requested state
 *
 * see radio.h for more info
 */
void cMxRadio::setState(radio_state_t state)
{
	radio_set_state(state);
}

/**
 * @brief radio_set_state to STATE_RX
 *
 * bring the the radio in the requested state of STATE_RX
 *
 * the radio state does not return to STATE_RX automatically if ZR_TXWAIT_AFTER is not used
 * thus why this is provided
 *
 * see radio.h for more info
 */
void cMxRadio::setStateRx()
{
	if (setautorx)
		radio_set_state(STATE_RXAUTO);
	else
		radio_set_state(STATE_RX);
}

/**
 * @brief radio_force_state Wrapper
 *
 * force the radio to the requested state
 *
 * see radio.h for more info
 */
void cMxRadio::forceState(radio_state_t state)
{
	radio_force_state(state);
}

/**
 * @brief Sets Radio Channel
 *
 * changes the radio channel by setting the radio channel state
 *
 * @param chan channel number, 11 to 26
 */
void cMxRadio::setChannel(channel_t chan)
{
	radio_set_param(RP_CHANNEL(chan));
}
uint8_t cMxRadio::getChannel()
{
	trx_param_t p;
	trx_parms_get(&p);
	channel_t chan= p.chan;
	return (uint8_t)chan;
}


/**
 * @brief Read Receiver Signal Strength Indicator Now
 *
 * returns the current RSSI
 *
 * range is between -91 and -9 dBm
 * where -9 is the best
 *
 * @return RSSI of the last transmission received
 */
int8_t cMxRadio::getRssiNow()
{
	int16_t rssi = ((int16_t)(trx_reg_read(RG_PHY_RSSI) & 0x1F)); // mask only important bits
	rssi = (rssi == 0) ? (RSSI_BASE_VAL - 1) : ((rssi < 28) ? ((rssi - 1) * 3 + RSSI_BASE_VAL) : -9);
	// if 0, then rssi < RSSI_BASE_VAL, if 28, then >= -10

	return rssi;
}



/**
 * @brief Read Last Receiver Signal Strength Indicator
 *
 * returns the RSSI of the last transmission
 *
 * range is between -91 and -9 dBm
 * where -9 is the best
 *
 * @return RSSI of the last transmission received
 */
int8_t cMxRadio::getLastRssi()
{
	int16_t rssi = ((int16_t)(temprssi & 0x1F)); // mask only important bits
	rssi = (rssi == 0) ? (RSSI_BASE_VAL - 1) : ((rssi < 28) ? ((rssi - 1) * 3 + RSSI_BASE_VAL) : -9);
	// if 0, then rssi < RSSI_BASE_VAL, if 28, then >= -10

	return rssi;
}


/**
 * @brief Read Link Quality Indicator
 *
 * returns the LQI of the last transmission received
 *
 * range is from 0 to 255
 * 255 is the best
 *
 * @return LQI of the last transmission received
 */
uint8_t cMxRadio::getLqi()
{
	return lastLqi;
}

/**
 * @brief Read Last Energy Detection Level
 *
 * returns the ED level of the last transmission received
 *
 * range is between -90 and -6 dBm
 * where -6 is the best
 *
 * @return ED level of the last transmission received
 */
int8_t cMxRadio::getLastEd()
{
	int8_t ed = trx_reg_read(RG_PHY_ED_LEVEL);

	return ed == 0xFF ? 0 : (ed + RSSI_BASE_VAL);
	//return lastRssi == 0xFF ? 0 : (lastRssi + RSSI_BASE_VAL);

}

/**
 * @brief Read Energy Detection Level Now
 *
 * forces a reading of the ED level
 *
 * range is between -90 and -6 dBm
 * where -6 is the best
 *
 * @return ED level
 */
int8_t cMxRadio::getEdNow()
{
	trx_reg_write(RG_PHY_ED_LEVEL, 0); // forces a reading

	return getLastEd();
}

/**
 * @brief Wait for TX to Complete
 *
 * waits until the last transmission is complete, or timed out
 *
 * @param timeout iterations to countdown before timeout
 */
void cMxRadio::waitTxDone(uint16_t timeout)
{
	volatile uint16_t cnt = timeout;
	while (txIsBusy && cnt--);
}