/*******************************************************************************************************
 *                                                                                                     *
 *        **********                                                                                   *
 *       ************                                                                                  *
 *      ***        ***                                                                                 *
 *      ***   +++   ***                                                                                *
 *      ***   + +   ***     Radio functions                                                            *
 *      ***   +                                                                                        *
 *      ***   + +   ***                                                                                *
 *      ***   +++   ***     tw_rf.c                                                                    *
 *      ***        ***                                                                                 *
 *       ************                                                                                  *
 *        **********                                                                                   *
 *                                                                                                     *
 *******************************************************************************************************
 * Compiler:                IAR C Compiler for 8051                                                    *
 * Target platform:         Chipcon CC2510                                                             *
 * Authors:                 TBR, KHO, ESY, MJB                                                         *
 *******************************************************************************************************
 * Revision history:
 * Last Update:             30 Jan 2010
 ******************************************************************************************************/

/***********************************************************************************
* INCLUDES
*/

#include "twoway.h"

/***********************************************************************************
* GLOBAL VARIABLES
*/

#define GDO0MSK 0x20        // GDO0 appears on bit 5 of P1

volatile UINT8 __xdata rxPacketStatus;
BOOL dmaDone;

#ifdef MASTER
TX_MASTER_STRUCT __xdata MAStxData;     // TX packet buffer for data from Master to Slave
DMA_DESC __xdata *dmaDescTx;            // DMA descriptor pointer for DMA channel associated with this TX buffer
RX_MASTER_STRUCT __xdata MASrxData;     // RX packet buffer for data from Slave
DMA_DESC __xdata *dmaDescRx;            // DMA descriptor pointer for DMA channel associated with this RX buffer
#endif

#ifdef SLAVE
TX_SLAVE_STRUCT __xdata SLVtxData;      // TX packet buffer for data from Slave to Master
DMA_DESC __xdata *dmaDescTx;            // DMA descriptor pointer for DMA channel associated with this TX buffer
RX_SLAVE_STRUCT __xdata SLVrxData;      // RX packet buffer for data from Master to Slave
DMA_DESC __xdata *dmaDescRx;            // DMA descriptor pointer for DMA channel associated with this RX buffer
#endif

/***********************************************************************************
* @fn		rfSetRadioFrequency
*
* @brief	Sets the radio's base frequency
*
* @param[in]    frequency
*               Radio frequency in kHz
*
* @return       void		
*/

void rfSetRadioFrequency(UINT32 frequency) {
  frequency = (frequency << 10);
  frequency /= 1000;
  frequency = (frequency << 6);
  frequency /= 26;
  FREQ0 = (BYTE) frequency;
  frequency >>= 8;
  FREQ1 = (BYTE) frequency;
  frequency >>= 8;
  FREQ2 = (BYTE) frequency;

  return;
}

/***********************************************************************************
* @fn		rfConfigRadio
*
* @brief	Configures the Radio Registers
*
* @param[in]    freq
*               Radio frequency in kHz
*
* @return       void	
*/

void rfConfigRadio(UINT32 freq) {

  /** 300 kbps MSK setup **/

  rfSetRadioFrequency(freq);

  PKTCTRL1 = 0x04;          // No address check. Append status bytes.
  PKTCTRL0 = 0x45;          // Variable packet length. Use CRC. Enable whitening
  PKTLEN = 0x78;            // Maximum length = 120 bytes

  // IF frequency
  FSCTRL1 = 0x0A;
  FSCTRL0 = 0x00;

  // Filter BW, data rate.
  MDMCFG4 = 0x2D;           // Data Rate = 300 kbps
  MDMCFG3 = 0x7A;           // Rx filter bandwidth = 542 kHz

  // Modulation format, detection level
#ifdef MSK
  MDMCFG2 = 0x73;           // 30/32 bits sync word detection. MSK modulation.
#else  
  MDMCFG2 = 0x13;           // 30/32 bits sync word detection. GFSK modulation.
#endif
  MDMCFG1 = 0x23;           // 4 bytes preamble. No FEC.
  MDMCFG0 = 0x7A;           // Channel spacing: 300 kHz

  // Deviation setting
#ifdef MSK
  DEVIATN = 0x00;
#else  
  DEVIATN = 0x62;           // Deviation - 127 kHz
#endif  

  // Calibration synth
  MCSM0 = 0x04;             // Manual FS calibration

  // RXOFF and TXOFF modes
  MCSM1 &= 0x30;            // RXOFF_MODE: IDLE; TXOFF_MODE: IDLE
  
  // Frequency offset compensation configuration
  FOCCFG = 0x1D;

  // Bit synchronization
  BSCFG = 0x1C;

  // AGC settings
  AGCCTRL2 = 0xC7;
  AGCCTRL1 = 0x40;
  AGCCTRL0 = 0xB2;

  // Front end settings (from SmartRF Studio)
  FREND1 = 0xB6;
  FREND0 = 0x10;

  // Synth calibration
  FSCAL3 = 0xEA;
  FSCAL2 = 0x0A;
  FSCAL1 = 0x01;
  FSCAL0 = 0x11;
  
  // Test Mode (from SmartRF Studio)
  TEST2 = 0x88;
  TEST1 = 0x31;
  TEST0 = 0x09;
  
  // Output power
  PA_TABLE0 = 0xFE;         // Output power: 0 dBm

  // Calibrate the frequency synth.
  SIDLE();
  SCAL();
  while(MARCSTATE != MARCSTATE_IDLE);

  INT_SETFLAG(INUM_RFTXRX,INT_CLR);

  return;
}

/***********************************************************************************
* @fn           initRf
*
* @brief	This function initializes the resources necessary for sending
*               and receiving packets
*
* Two DMA channels are assigned to move data to and from the necessary RX or TX buffers.
* The radio is configured to transmit at a defined frequency and to
* automatically calculate and insert a CRC value when in transmit and to
* check the CRC value in receive.
*
* @param[in]  frequency
*             The radio base frequency (channel 0) in kHz
*
* @return
*             Unconditionally returns TRUE
*/
BOOL initRf(UINT32 frequency) {
  rfConfigRadio(frequency);               // Initialize the radio for the given frequency

  IOCFG0 = 0x06;                          // Set GDO0 to Sync Word Status

#ifdef MASTER
  dmaFromRadio(RX_PAYLOAD_LEN + 3, (WORD) &MASrxData);            // Configure the DMA channel for reception. Generate DMA interrupt (for the callback function).
                                                                  // Include the two bytes appended to the data for status (LQI/RSSI)
  DMA_ABORT_CHANNEL(DMA_RX);                                      // Make sure the DMA channel 1 is disarmed.

  dmaToRadio(TX_PAYLOAD_LEN + 1, (WORD) &MAStxData);              // Configure the DMA channel for transmission. Do not enable DMA interrupt.
  DMA_ABORT_CHANNEL(DMA_TX);                                      // Make sure the DMA channel is disarmed.
#endif

#ifdef SLAVE
  dmaToRadio(TX_PAYLOAD_LEN + 1, (WORD) &SLVtxData);              // Configure the DMA channel for transmission. Do not enable DMA interrupt.
  DMA_ABORT_CHANNEL(DMA_TX);                                      // Make sure the DMA channel is disarmed.

  dmaFromRadio(RX_PAYLOAD_LEN + 3, (WORD) &SLVrxData);            // Configure the DMA channel for reception. Generate DMA interrupt (for the callback function).
                                                                  // Include the two bytes appended to the data for status (LQI/RSSI)
  DMA_ABORT_CHANNEL(DMA_RX);                                      // Make sure the DMA channel is disarmed.
#endif

  return TRUE;
}

/***********************************************************************************
* @fn           rfSendPacket

* @brief	Transmit the packet that is already in the txData TX buffer
*
* Arms the TX DMA channel, Starts a TX timeout timer, starts TX mode,
* waits for the timer to expire, forces the CC2510 to idle if it is still
* in TX mode when the timer expires.
*
* The packet to transmit must be in the TX buffer txData before calling this function.
*
* The timeout should be such that the packet is completely transmitted when
* the timer expires and TX operates normaly.
*
* How to calculate timeout:
* Timeout in milliseconds >= Margin + Total packet length in bytes  * 8 / RF baud rate in kilobits per second
*
* The total packet length must include preamble, syncword, header, user data and CRC.
*
* @param[in]
*               timeout       Must have a value between 1 and 255
*               multiplier    Must have a value between 1 and 255
*
*   Note: The timeout period can be calculated from the formula:
*   timeout period (usec) = multiplier*timeout*4.923076923
*
* @return
*               Returns TRUE is the transmission is successful and FALSE if the transceiver was in TX at timeout
*/

BOOL rfSendPacket(UINT8 timeout, UINT8 multiplier) {
  RFTXRXIF = 0;                                 // clear flag as this serves as a trigger for the DMA
  DMA_ARM_CHANNEL(DMA_TX);                      // ARM TX DMA Channel

  STX();
  macsetT3TimeoutAndWait(timeout, multiplier);  // wait for normal TX operation to complete
  if(MARCSTATE == MARCSTATE_TX) {
    SIDLE();
    DMAIRQ = 0x00;
    return FALSE;
  }
  if(MARCSTATE != MARCSTATE_IDLE) {
    SIDLE();
    DMAIRQ = 0x00;
    return FALSE;
  }
  DMAIRQ = 0x00;
  return TRUE;
}

/***********************************************************************************
* @fn         rfReceivePacket
*
* @brief      Attempts to receive a packet
*
* The timeout will always run out before function returns, so this function should not be used
* if timeout is longer than RX/2.
*
* @param[in]  rxData        pointer to the data structure into which the received is to be placed
*             timeout       Must have a value between 1 and 255
*             multiplier    Must have a value between 1 and 255
*             packetlen     packet length in bytes. Must have a value between 1 and 255.
*
*   Note: The timeout period can be calculated from the formula:
*   timeout period (usec) = multiplier*timeout*4.923076923
*
* @return     void
*
* Returns the following in rxPacketStatus:
*             TIMEOUT_ERROR:  If no packet is received within a given time
*             PKT_ERROR:      If either address or length of received packet was incorrect
*             CRC_ERROR:      CRC error in incomming audio data frame
*             PKT_OK:         Received packet is OK
*
*/

#ifdef MASTER
#define RX_STRUCT  RX_MASTER_STRUCT
#else
#define RX_STRUCT  RX_SLAVE_STRUCT
#endif

void rfReceivePacket(RX_STRUCT __xdata * rxData, UINT8 timeout, UINT8 t3_multiplier, UINT8 packetlen) {

  dmaDone = FALSE;
  INT_SETFLAG(INUM_DMA, INT_CLR);
  INT_ENABLE(INUM_DMA, INT_ON);                           // Enable DMA interrupt
  RFTXRXIF = 0x00;

  if ( DMAARM != 0x00 )
    DMA_ABORT_CHANNEL(DMA_RX);                            // Abort DMA channel

  DMA_ARM_CHANNEL(DMA_RX);                                // Arm DMA channel
  SRX();                                                  // Strobe RX
  macsetT3TimeoutAndWait(timeout, t3_multiplier);         // Start RX timeout

  if ((PKTSTATUS & SFD) == 0) {                           // If the Start of Frame Delimiter was not found ...
    SIDLE();                                              // Abort RX and return the the IDLE state
    DMA_ABORT_CHANNEL(DMA_RX);                            // Abort DMA channel
    rxPacketStatus = TIMEOUT_ERROR;                       // Return Timeout error
  }
  else {
    while ((PKTSTATUS & SFD) > 0);                        // Wait until RX is complete (Start of Frame Delimiter goes low)

    if (rxData->macPayloadLen != packetlen)
      rxPacketStatus = PKT_ERROR;                         // Return Packet error
    else if (!(rxData->append[CRC_LQI_VAL_IDX] & CRC_OK_MASK))
      rxPacketStatus = CRC_ERROR;                         // Return CRC error
    else
      rxPacketStatus = PKT_OK;
  }
}

/***********************************************************************************
* @fn         ListenforMaster
*
* @brief      Listen for Master
*
* Unlike 'rfReceivePacket', this routine will return immediately if a packet is received before the 'timeout' period
*
*
* @param[in]  rxData        pointer to the data structure into which the received is to be placed
*             timeout       Must have a value between 1 and 255
*             multiplier    Must have a value between 1 and 255
*             packetlen     packet length in bytes. Must have a value between 1 and 255.
*
*   Note: The timeout period can be calculated from the formula:
*   timeout period (usec) = multiplier*timeout*4.923076923
*
* @return     void
*
* Returns the following in rxPacketStatus:
*             TIMEOUT_ERROR:  If no packet is received within a given time
*             PKT_ERROR:      If either address or length of received packet was incorrect
*             CRC_ERROR:      CRC error in incomming audio data frame
*             PKT_OK:         Received packet is OK
*
*/

#ifdef SLAVE

void ListenforMaster(RX_SLAVE_STRUCT __xdata * rxData, UINT8 timeout, UINT8 t3_multiplier, UINT8 packetlen) {

  dmaDone = FALSE;
  INT_ENABLE(INUM_DMA, INT_ON);                           // Enable DMA interrupt
  RFTXRXIF = 0x00;

  if ( DMAARM != 0x00 )
    DMA_ABORT_CHANNEL(DMA_RX);                            // Abort DMA channel
  DMA_ARM_CHANNEL(DMA_RX);                                // Arm DMA channel

  TIMIF &= ~T3OVFIF;                                      // Clear the RX_TIMEOUT_TIMER_FLAG

  SRX();                                                  // Strobe RX

  for ( int i = 1; i <= t3_multiplier; i++) {
    T3CC0 = timeout;                      // Set timeout period
    TIMER3_RUN(TRUE);                     // Start timer

    while (!(TIMIF & T3OVFIF)) {          // loop until T3 reaches 0
      if ((PKTSTATUS & SFD) > 0) {        // If SYNC has been detected
        while ((PKTSTATUS & SFD) > 0);    // Wait until RX is complete

// Packet received

        if (rxData->macPayloadLen != packetlen)
          rxPacketStatus = PKT_ERROR;                         // Return Packet error
        else if (!(rxData->append[CRC_LQI_VAL_IDX] & CRC_OK_MASK))
          rxPacketStatus = CRC_ERROR;                         // Return CRC error
        else
          rxPacketStatus = PKT_OK;

        TIMIF &= ~T3OVFIF;                // Clear RX_TIMEOUT_TIMER_FLAG
        TIMER3_RUN(FALSE);                // Stop timer
        return;
      }         // end of 'sfdDetected' if
    }           // end of 'while' loop - if reached, T3CNT has reached 0
  }             // end of 'for' loop - if reached, SYNC was not detected within timeout period

  rxPacketStatus = TIMEOUT_ERROR;
  return;
}
#endif


/***********************************************************************************
  Copyright 2008 Texas Instruments Incorporated. All rights reserved.

  IMPORTANT: Your use of this Software is limited to those specific rights
  granted under the terms of a software license agreement between the user
  who downloaded the software, his/her employer (which must be your employer)
  and Texas Instruments Incorporated (the "License").  You may not use this
  Software unless you agree to abide by the terms of the License. The License
  limits your use, and you acknowledge, that the Software may not be modified,
  copied or distributed unless embedded on a Texas Instruments microcontroller
  or used solely and exclusively in conjunction with a Texas Instruments radio
  frequency transceiver, which is integrated into your product.  Other than for
  the foregoing purpose, you may not use, reproduce, copy, prepare derivative
  works of, modify, distribute, perform, display or sell this Software and/or
  its documentation for any purpose.

  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

  Should you have any questions regarding your right to use this Software,
  contact Texas Instruments Incorporated at www.TI.com.
***********************************************************************************/
