/*******************************************************************************************************
 *                                                                                                     *
 *        **********                                                                                   *
 *       ************                                                                                  *
 *      ***        ***                                                                                 *
 *      ***   +++   ***     This file contains all functions related to the transmitting unit          *                                                                   *
 *      ***   + +   ***     (remote control).                                                          *
 *      ***   +                                                                                        *
 *      ***   + +   ***                                                                                *
 *      ***   +++   ***     TxWorWithAck.c                                                             *
 *      ***        ***                                                                                 *
 *       ************                                                                                  *
 *        **********                                                                                   *
 *                                                                                                     *
 *******************************************************************************************************
 * Compiler:                Keil C51 V7.50                                                             *
 * Target platform:         Chipcon CCxxx0 (Silabs F320)                                               *
 * Author:                  ESY                                                                        *
 *******************************************************************************************************
 * Revision history:        See end of file                                                            *
 *******************************************************************************************************/
#include <Chipcon\srf04\regssrf04.h>
#include <Chipcon\srf04\halsrf04.h>
#include <Chipcon\srf04\ebsrf04.h>
#include <WorWithAck.h>




//-------------------------------------------------------------------------------------------------------
//  Defines
#define MAX_PACKET_BURST_TIMEOUT    950     // Maximum number of packets to send without receiving an 
                                            // ACK. Used as burst timeout to avoid endless packet 
                                            // bursting without getting any response.
#define RX_BYTES                    0x7F
//-------------------------------------------------------------------------------------------------------




//-------------------------------------------------------------------------------------------------------
//  Global Variables

// Variables to control the outgoing packet burst
UINT32 xdata packetsSent = 0;
UINT16 xdata packetCount = 0;

UINT8 xdata txBuffer[1];            // Fixed packet length: 1B data payload

BOOL xdata burstDone = FALSE;
BOOL xdata timeout = FALSE;
//-------------------------------------------------------------------------------------------------------




//-------------------------------------------------------------------------------------------------------
// Function declarations
void initTransmitter();             // internal function
//-------------------------------------------------------------------------------------------------------




//-------------------------------------------------------------------------------------------------------
//  void initTransmitter(void)
//
//  DESCRIPTION:
//      This function is configuring some specific radio register settings and initiating interrupt 
//      timers for the transmitter unit.
//-------------------------------------------------------------------------------------------------------
void initTransmitter(void) {

    // Enable automatic switching after packet has been sent to listen for incoming ACK.
    // TXOFF_MODE = RX (TX->RX switch: 21.5 us), RXOFF_MODE = IDLE (RX->IDLE, no FS calib: 0.1 us) 
    halSpiWriteReg(CCxxx0_MCSM1, 0x33);

    // Set RX timeout (timeout for sync word search)) to 325 us.
    // WOR_RES = 0, (default value; Gives high time resolution/short periods)
    // Enable RC oscillator calibration. RCosc controls the WORTIME counter registers used as 
    // RX timeout counter.
    halSpiWriteReg(CCxxx0_WORCTRL, 0x78);
    halSpiWriteReg(CCxxx0_MCSM2, 0x05);                                 // RX_TIME = 101b
                

    // T_rxtime = T_event0 / 2^(8+WOR_RES) = 325 us
    // => T_event0 = 83.2 ms
    // T_event0 = 750 / f_xosc * EVENT0 * 2^(5*WOR_RES) = 87.04 ms     // Assuming f_xosc = 26 MHz
    // =>  EVENT0 = 2884 = 0x0B44                                                                       
    halSpiWriteReg(CCxxx0_WOREVT1, 0x0B);
    halSpiWriteReg(CCxxx0_WOREVT0, 0x44);

    // Setup TIMER1 to overflow every 1000 us (TX packet interval)
    // 1/6000000 Hz * x = 1000 us,                                      , SYSCLK_DIV_4 = 24 MHz/4 = 6 MHz
    // => x = 6000
    // 2^16 - 6000 = 59536 = 0xE890                                     , 16 bits timer register
    // => TH1 = 0xE8
    // => TL1 = 0x90
    TH1 = 0xE8;                                                         // Timer1 high byte
    TL1 = 0x90;                                                         // Timer1 low byte

    // Clock source: 6 MHz, Mode: 16-bits timer; Initiated with interrupts off.
    halSetupTimer01(TIMER_1, SYSCLK_DIV_4, MODE_1, INT_OFF);

    // Enable EXTERNAL0 interrupt on low edge of GDO2 pin (used for incoming packets)
    // CCxxx0_IOCFG1 == 0x06 => GDO0 asserts on sync word detection and de-asserts on packet end.
    ENABLE_GLOBAL_INT(INT_OFF);         // Got to globally disable interrupts because ...
    SETUP_GDO0_INT(EDGE, LOW);          // <-- this macro enables the new interrupt upon initialization ...
    INT_ENABLE(INUM_EXTERNAL0, INT_OFF);// and then got to disable it ...
    ENABLE_GLOBAL_INT(INT_ON);          // before interrupts can be globally re-enabled again
} // initTransmitter




//-------------------------------------------------------------------------------------------------------
//  void runTransmitter(void)
//
//  DESCRIPTION:
//      This function loops forever and transmits packet bursts upon joystick movements: 
//      Identical packet will be transmitted until an ACK packet has been received.
//-------------------------------------------------------------------------------------------------------
void runTransmitter(void) {

    // Perform initalization specific for transmitting unit.
    initTransmitter();

    SET_YLED(LED_ON);                   // Turn on yellow LED to indicate unit is running
    
    // Loops forever, sending packets upon joystick movement. Press S1 to abort.
    while(!ebButtonPushed()) {

        // Get current joystick position
        joystickPosition = ebGetJoystickPosition();

        //If joystick is moved, start burst of packets
        if (joystickPosition != prevJoystickPosition) {

            // Save joystick position for next iteration
            prevJoystickPosition = joystickPosition;

            // Temporary disable interrupts while updating LCD + calibrating
            INT_ENABLE(INUM_TIMER1, INT_OFF);

            // Calibrates the RC osc manually (721 us) in case of long time since last 
            // calibration/joystick movement
            halSpiStrobe(CCxxx0_SCAL);

            // Update LCD and reset packet counter
            ebLcdUpdate("Bursting...", NULL);
            packetCount = 0;            // Reset counter before stating a new burst

            halWait(800);               // Wait for calibration to finish before packet burst can start
    
            // Update payload with current joystick position
            txBuffer[0] = joystickPosition;

            // Start packet transmit interval timer
            INT_ENABLE(INUM_TIMER1, INT_ON);
            SET_BLED(LED_ON);           // Blue LED on; Start burst
        }

        // NOTE! If the receiver receives one of the first packets in the burst and hence the burtDone
        // flag is set not long after the ebLcdUpdate("Bursting...", NULL); function was called, the LCD
        // will not be updated with the message below as the previous message is still being written to 
        // the display.
        if (burstDone) {
            burstDone = FALSE;
            intToAscii(packetCount);
            ebLcdUpdate("# pkts in burst:", asciiString);
        }

        if (timeout) {
            timeout = FALSE;

            //Update LCD with notice
            ebLcdUpdate("No ACK->timeout.", "Burst stopped!");
        }
    }
    SET_YLED(LED_OFF);                  // Turn off yellow LED to indicate unit has stopped.
}// runTransmitter




//-------------------------------------------------------------------------------------------------------
//  void TIMER1_ISR(void)
//
//  DESCRIPTION:
//      This interrupt occurs every 1000 us, when timer1 overflows. The timer registers are reloaded and
//      a packet is sent. This is a HIGH priority interrupt.
//-------------------------------------------------------------------------------------------------------
void TIMER1_ISR(void) interrupt INUM_TIMER1 {

    // Reload timer1 registers (see  initTransmitter() for calculation)
    TH1 = 0xE8;
    TL1 = 0x90; // this gives interval of 1000 us

    // Check if more packets should be sent 
    if (packetCount < MAX_PACKET_BURST_TIMEOUT) {

        // Send packet
        halRfSendPacket(txBuffer, sizeof(txBuffer));

        // The radio should now automatically switch to RX waiting for an ACK (TXOFF_MODE = RX).
        // Increase the number of packets sent in the burst
        packetCount++;

        // Enable interrupts upon incoming ACK when radio is in RX in between TX periods
        INT_SETFLAG(INUM_EXTERNAL0, INT_CLR);   // Clear the interrupt flag
        INT_ENABLE(INUM_EXTERNAL0, INT_ON);     // Enable the interrupt

    //Stop packet transmission (timeout) since no one is answering (No ACK is being received)
    } else {

        INT_ENABLE(INUM_TIMER1, INT_OFF);
        SET_BLED(LED_OFF);                      // Blue LED off; Burst is over

        timeout = TRUE;
    }
}// TIMER1_ISR




//-------------------------------------------------------------------------------------------------------
//  void EXTERNAL0_ISR(void)
//
//  DESCRIPTION:
//      This ISR runs whenever CC2500 has received a packet.
//-------------------------------------------------------------------------------------------------------
void EXTERNAL0_ISR(void) interrupt INUM_EXTERNAL0 {
    BYTE packetLength;

    // Disable new interrupts while running ISR
    ENABLE_GLOBAL_INT(INT_OFF);

    // Check size of packet content in RX FIFO.
    // Because of fixed length packet filtering the RX FIFO should contain only 1 byte 
    // (other lengths => flushed). If the packet had CRC error, it should also get flushed 
    // (CRC_AUTOFLUSH enabled) and the RX FIFO should be empty.
    packetLength = halSpiReadStatus(CCxxx0_RXBYTES) & RX_BYTES;

    if (packetLength != 0) { // The RX FIFO got some packet content

        // Read out of RX FIFO into rxBuffer array
        rxBuffer[0] = halSpiReadReg(CCxxx0_RXFIFO);

        // Check ACK
        if ((rxBuffer[0] ^ 0xFF) == joystickPosition) { // ACK received? 

            // Stop packet burst
            INT_ENABLE(INUM_TIMER1, INT_OFF);
            SET_BLED(LED_OFF);                          // Blue LED off; burst is over

            burstDone = TRUE;
  
            P_LED1 = ~P_LED1;                           // Toggle green LED for ACK received
        }
    } else {

        // Detected sync word, but got no packet in RX FIFO
        // Possible reasons: packet had wrong length or contained CRC errors.
        P_LED2 = ~P_LED2;                               // Toggle red LED for packet error
    }

    ENABLE_GLOBAL_INT(INT_ON);                          // Re-enable interrupts globally

    // The radio is now in IDLE state (RXOFF_MODE = IDLE)
    // The state transition happens automatically after end of received packet.
    // RX->IDLE (no FS calibration) : 0.1 us

}// EXTERNAL0_ISR




/******************************************************************************************************
 * Revision history:                                                                                  *
 *
 * $Log: TxWorWithAck.c,v $
 * Revision 1.1  2005/10/06 12:13:45  sna
 * Initial version in CVS.
 *
 *
 *
 ******************************************************************************************************/


