/*******************************************************************************************************
 *                                                                                                     *
 *        **********                                                                                   *
 *       ************                                                                                  *
 *      ***        ***                                                                                 *
 *      ***   +++   ***                                                                                *
 *      ***   + +   ***     This example shows how to use the Wake-on-Radio function of the CC2500     *
 *      ***   +             in a simple burst mode Wake-on-Radio application.                          *
 *      ***   + +   ***                                                                                *
 *      ***   +++   ***     Wor.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 <RegSettings_Wor.h>




//-------------------------------------------------------------------------------------------------------
//  DESCRIPTION:
//      This example is a simple implementation of a control system for a radio-controlled object.
//      The system is modeled as a simplex system with a transmitting remote 
//      control sending packets with control commands to the remotely controlled receiving object. 
//      WOR is used to reduce power consumption at the receiver side.
//      One evaluation board (EB) is set up as the remote control (TX unit), and the other as 
//      the remotely controlled object (RX/WOR unit). Data regarding movements of the joystick on 
//      the remote control are transmitted to the receiver and displayed on the LCD of this EB.
//      Please see App. Note 38 for more details.
//      
//      RX UNIT:
//          * Move joystick right to choose RX
//          * Push the S1 button to confirm your choice. 
//          After the unit has been configured, the yellow LED will be on.
//
//          GLED: Toggles when a packet has been received successfully.
//          RLED: Toggles when an invalid packet has been received. 
//          BLED: Toggles when a packet is received and an interrupt is given to the MCU.
//
//      TX UNIT:
//          * Move joystick left to choose TX
//          * Push the S1 button to confirm your choice. 
//          After the unit has been configured, the yellow LED will turn on.
//
//          BLED: Toggles when a packet has been sent.
//
//      If the TX unit is selected, you are now able to push the joystick button or move it up, 
//      down, left or right. This will start the packet burst. When all the packets (305) are transmitted
//      "Done" will be displayed at the LCD. One should not move the joystick to a new position before
//      the previous burst transmission has completed. 
//
//      If the RX unit is selected, it will automatically enter the WOR mode, waiting for packets. 
//
//      The LCD will continuously display how many valid packets have been received together with 
//      the position of the joystick on the remote control.
//      
//      The program can be terminated by pressing the S1 button after the setup has been done. 
//      This will turn off the yellow LED.
//
//      Current config. specs
//
//          Common Settings:
//              Packet format = 4B preamble + 4B sync word + 1B payload + 2B CRC = 11 Bytes
//
//          TX Unit Settings:
//              Packet interval:    1.0 ms
//              Packet send time:   ~ 11 B * 8 bit/B / 250 kbps = 352 us
//              Tx duty cycle:      ~ 352 us / 1.0 ms = 35.2 %
//
//          RX/WOR Unit Settings:
//              Event0 timeout:     300 ms
//              Event1 timeout:     ~ 1.4 ms (f_xosc = 26 MHz)
//              Rx timeout:         ~ 1.172 ms
//              Rx duty cycle:      ~ 1.172 ms / 300 ms = 0.391 %
//-------------------------------------------------------------------------------------------------------




//-------------------------------------------------------------------------------------------------------
// Defines

// Defines used as mode values for the setup menu in main loop
#define MODE_NOT_SET    0
#define TX              1
#define RX              2

#define BURST_LENGTH    305     // Number of packets to send per packet burst (some margin added 
                                // because of possible oscillator variations)
                                // 305 packets * 1.0 ms interval = 305 ms burst length 
                                // (> 300 ms RX polling interval (WOR))
#define RX_BYTES        0x7F
//-------------------------------------------------------------------------------------------------------




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

RF_SETTINGS code rfSettings;

BYTE code paTable;

UINT8 xdata joystickPosition = JOYSTICK_CENTER;
UINT8 xdata prevJoystickPosition = JOYSTICK_CENTER;

UINT8 xdata mode = MODE_NOT_SET;    // Variable used to hold the mode when setting up the unit in the menu

UINT32 xdata packetsSent = 0;
UINT32 xdata packetsReceived = 0;

// TX & RX FIFO buffer arrays
BYTE xdata txBuffer[1];             // Fixed packet length: 1B payload
BYTE xdata rxBuffer[1];             // Max received packet length = 1B (All packets longer then 1 byte 
                                    // will not be put in the RX FIFO due to packet length filtering 

BYTE xdata asciiString[11];         // Byte array used by the intToAscii(UINT32 value) function
BYTE xdata asciiJoystickPosition[] = "Joystick:  ";
BYTE xdata asciiPackets[] =          "Pkts rcvd:      ";

UINT8 xdata updateLcd = FALSE;

UINT16 xdata burstCount = 0;        // Number of packets sent of current burst

BOOL burstDone = FALSE;             // Flag set on the transmitter when all 305 packets are transmitted
//-------------------------------------------------------------------------------------------------------




//-------------------------------------------------------------------------------------------------------
// Function declarations
void initTransmitter(void);
void runTransmitter(void);

void initReceiver(void);
void runReceiver(void);

void intToAscii(UINT32 value);
void showMenu(void);                    // Show the initial setup menu
void parseMenu(UINT8 joystickPosition); // Parses the input for menu navigation
//-------------------------------------------------------------------------------------------------------




//-------------------------------------------------------------------------------------------------------
//  void main(void)
//
//  DESCRIPTION:
//      This function takes care of all the MCU initialization and the radio settings which is common
//      for both the receiver and the transmitter.
//-------------------------------------------------------------------------------------------------------
void main (void) {

    UINT8 xdata length = 0;

    #ifdef STAND_ALONE

        // Select the Internal Oscillator as Multiplier input source and disable the watchdog timer
        // SYSCLK = 4X Clock Multiplier / 2
        CLOCK_INIT();
    #endif

    // Set up the crossbar and I/O ports to communicate with the SmartRF04EB peripherals
    IO_PORT_INIT();

    // Initialize the LCD display. The SMBus uses timer 0 to generate SCL
    ebLcdInit();

    // Initialize the ADC converter
    ebAdcInit(ADC_JOY);

    SPI_INIT(SCLK_6_MHZ);

    // Reset CC2500 and write RF settings to config registers
    POWER_UP_RESET_CCxxx0();

    halRfWriteRfSettings(&rfSettings);

    halSpiWriteReg(CCxxx0_PATABLE, paTable);

    // Calibrating frequency synthesizer (FS) at startup
    halSpiStrobe(CCxxx0_SCAL);

    // Starts menu for setting up EB unit to either "TX" or "RX/WOR" unit.
    showMenu();

    // Remote control
    if (mode == TX) {                                                                           
        ebLcdUpdate("Tx unit...", "(Move joystick)");
        runTransmitter();

    // Remote-controlled object
    } else {                                                                                            
        ebLcdUpdate("Rx/WOR unit...", NULL);
        runReceiver();
    }
}// Main




//-------------------------------------------------------------------------------------------------------
//  void runTransmitter(void)
//
//  DESCRIPTION:
//      This function is used to monitor the joystick position on the remote control. Every time the
//      position changes, a packet burst is started (i.e. rapid repetition of a fixed number of 
//      identical packets containing information on the joystick position). These packets are used to 
//      wake up the WOR receiver. The packets are sent with a 1.0 ms interval, and 305 packets are 
//      transmitted in each burst.
//-------------------------------------------------------------------------------------------------------
void runTransmitter(void) {

    // Initialize transmitter
    initTransmitter();

    SET_YLED(LED_ON);       // Turn on yellow LED to indicate unit is running

    // Loops forever. Press S1 for abortion.
    while(!ebButtonPushed()) {
        joystickPosition = ebGetJoystickPosition();

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

            prevJoystickPosition = joystickPosition;

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

            // Calibrate
            halSpiStrobe(CCxxx0_SCAL);

            // Setting correct payload into packet corresponding to joystick position
            txBuffer[0] = joystickPosition;

            // Starting burst of packets
            burstCount = 0;
            ebLcdUpdate("Bursting...", NULL);
            INT_ENABLE(INUM_TIMER1, INT_ON);    // Enable timer for packet burst interval
            SET_BLED(LED_ON);                   // Set blue LED to indicate burst in progress
        }
        if (burstDone) {
            ebLcdUpdate("Done", NULL);
            burstDone = FALSE;
        }
    }

    // Button S1 pressed for abortion
    INT_ENABLE(INUM_TIMER1, INT_OFF);           // Disable timer1 interrupts
    SET_YLED(LED_OFF);
}// runTransmitter




//-------------------------------------------------------------------------------------------------------
//  void initTransmitter(void)
//
//  DESCRIPTION:
//      This function is configuring some transmitter specific radio register settings and initializes
//      interrupt timers for the transmitter unit.
//      Timer1 is set up to overflow every 1000 us (Tx packet interval)
//      
//      1/6000000 Hz * x = 1000 us => x = 6000
//      2^16 - 6000 = 59536 = 0xE890
//      => TH1 = 0xE8
//      => TL1 = 0x90
//-------------------------------------------------------------------------------------------------------
void initTransmitter(void) {

    // Setup and initialize Timer1 to overflow every 1000 us (Tx packet interval)
    TH1 = 0xE8; // Timer1 high byte
    TL1 = 0x90; // Timer1 low byte

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




//-------------------------------------------------------------------------------------------------------
//  void runReceiver(void)
//
//  DESCRIPTION:
//      This function is used to put the radio in SLEEP mode using WOR. If the MCU detects that a packet
//      is received, this function is used to update the LCD display in accordance with the packet
//      data received.
//-------------------------------------------------------------------------------------------------------
void runReceiver(void) {
    UINT8 i;

    // Initialize receiver
    initReceiver();

    SET_YLED(LED_ON);                               // Turn on yellow LED to indicate unit is running

    // Put the radio to SLEEP by starting Wake-on-Radio.
    halSpiStrobe(CCxxx0_SWORRST);                   // Resets the real time clock
    halSpiStrobe(CCxxx0_SWOR);                      // Starts Wake-on-Radio

    // The MCU could here enter some power-down state to reduce power consumption while waiting for a 
    // WOR interrupt from the CC2500 radio. (this is not implemented here in this example 
    // - instead there is a while loop updating the LCD.)

    // Loops forever updating LCD with the received data. Button S1 for abortion.
    while(!ebButtonPushed()) {
        if (updateLcd) { // This flag is set in EXTERNAL1_ISR if the LCD needs to be updated 
            updateLcd = FALSE;
            intToAscii(packetsReceived);

            // Copying chars for better LCD presentation
            for (i = 0; asciiString[i] >= '0' && asciiString[i] <= '9'; i++)
                asciiPackets[11 + i] = asciiString[i];
            asciiPackets[11 + i] = NULL;
            ebLcdUpdate(asciiPackets, asciiJoystickPosition);
        }
    }

    //Button S1 has been pressed for abortion
    INT_ENABLE(INUM_EXTERNAL1, INT_OFF);    // Disable external1 interrupts
    SET_YLED(LED_OFF);                      // Turn off yellow LED to indicate unit has stopped.
}// runReceiver





//-------------------------------------------------------------------------------------------------------
//  void initReceiver(void)
//
//  DESCRIPTION:
//      This function configures the Wake-on-Radio specific radio register settings and initializes
//      the necessary MCU interrupt given from the radio when a packet has been detected with WOR.
//-------------------------------------------------------------------------------------------------------
void initReceiver(void) {

    // Configuring all the WOR related settings
    // Enable automatic initial calibration of RCosc.
    // Set T_event1 ~ 1.4 ms, enough for XOSC stabilize and FS calibration before RX.
    // Enable RC oscillator before starting with WOR (or else it will not wake up).
    halSpiWriteReg(CCxxx0_WORCTRL, 0x78);           // Not using AUTO_SYNC function.

    // Set Event0 timeout = 300 ms (RX polling interval)
    // WOR_RES = 0
    // T_event0 = 750 / f_xosc * EVENT0 * 2**(5*WOR_RES) = 300 ms   // Assuming f_xosc = 26 MHz
    // =>  EVENT0 = 10400 = 0x28A0
    halSpiWriteReg(CCxxx0_WOREVT1, 0x28);                           // High byte Event0 timeout
    halSpiWriteReg(CCxxx0_WOREVT0, 0xA0);                           // Low byte Event0 timeout.

    // Setting Rx_timeout > 1.0 ms.
    // MCSM2.RX_TIME = 101b
    // => Rx_timeout = T_event0 / (2**(8+WOR_RES)) = 300 ms / 256 = 1.17 ms, i.e. 0.391% RX duty cycle
    halSpiWriteReg(CCxxx0_MCSM2, 0x05);

    // Enable automatic FS calibration when going from IDLE to RX/TX/FSTXON (in between EVENT0 and EVENT1)
    halSpiWriteReg(CCxxx0_MCSM0, 0x18);

    // Enable external interrupt when packet is received
    // IOCFG2 register = 0x06 => GDO2 pin is asserted when sync word detected/sent, de-asserted at 
    // end of packet.
    // MCU is interrupted by radio on low edge of GDO2, i.e. whenever a packet is received.
    ENABLE_GLOBAL_INT(INT_OFF);
    SETUP_GDO2_INT(EDGE, LOW);                  // Enables external interrupt on low edge
    INT_SETFLAG(INUM_EXTERNAL1, INT_CLR);       // Clears the interrupt flag
    ENABLE_GLOBAL_INT(INT_ON);
}// initReceiver





//-------------------------------------------------------------------------------------------------------
//  void TIMER1_ISR(void)
//
//  DESCRIPTION:
//      This interrupt occurs every 1000 us on the transmitter unit. The timer
//      registers are reloaded and a packet is sent.
//-------------------------------------------------------------------------------------------------------
void TIMER1_ISR(void) interrupt INUM_TIMER1 {
    ENABLE_GLOBAL_INT(INT_OFF);

    // Reload timer1 registers to overflow again after same time interval
    TH1 = 0xE8;
    TL1 = 0x90;     // overflows every 1000 us (see initialization of the timer for calculation)

    // Is the burst transmission done?
    if (burstCount < BURST_LENGTH) {        // Continue to send packets

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

        burstCount++;
    } else {                                // End of burst
        INT_ENABLE(INUM_TIMER1, INT_OFF);   // Disable the timer
        SET_BLED(LED_OFF);                  // Turn off blue LED. Unit is no longer transmitting.
        burstDone = TRUE;
    }
    ENABLE_GLOBAL_INT(INT_ON);
}// TIMER1_ISR




//-------------------------------------------------------------------------------------------------------
//  void EXTERNAL1_ISR(void)
//
//  DESCRIPTION:
//      This interrupt service routine occurs whenever CC2500 detects a packet while in Wake-on-Radio
//-------------------------------------------------------------------------------------------------------
void EXTERNAL_ISR(void) interrupt INUM_EXTERNAL1 {
    BYTE packetLength;

    P_LED4 = ~P_LED4;   // Toggle blue LED to indicate MCU is interrupted by Wake-on-Radio.

    // The contents of TX FIFO and RX FIFO are cleared when entering SLEEP state (WOR),
    // so the RX FIFO does only contain the new packet.
    // The CRC_AUTOFLUSH function is enabled: automatically flushing the RX FIFO if received packet 
    // contains CRC error
    // Fixed packet length enabled (1 byte): packets longer than 1 byte is automatically filtered out

    // Check if we got any packets in the RX FIFO
    packetLength = halSpiReadStatus(CCxxx0_RXBYTES) & RX_BYTES; // Only 7 LSB

    if (packetLength != 0) {             // Packet received OK.

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

        // Parsing received data byte into correct char for proper LCD presentation
        joystickPosition = rxBuffer[0];

        switch(joystickPosition) {
            
            default:    // Other received byte values are parsed as 'C' for ease
            
            case JOYSTICK_CENTER:
                asciiJoystickPosition[10] = 'C';
                break;
            
            case JOYSTICK_LEFT:
                asciiJoystickPosition[10] = 'L';
                break;
            
            case JOYSTICK_RIGHT:
                asciiJoystickPosition[10] = 'R';
                break;
            
            case JOYSTICK_UP:
                asciiJoystickPosition[10] = 'U';
                break;
            
            case JOYSTICK_DOWN:
                asciiJoystickPosition[10] = 'D';
                break;
            
            case JOYSTICK_PUSHED:
                asciiJoystickPosition[10] = 'P';
                break;
        }

        packetsReceived++;
        updateLcd = TRUE;           // Set flag for update of LCD.
        P_LED1 = ~P_LED1;           // Toggle green LED for packet received.

    } else                          // No packet in RX FIFO. Invalid packet detection.
        P_LED2 = ~P_LED2;           // Toggle red LED for error.

    // After a wake-up, the radio should be in IDLE.
    // Have to manually put the radio back to sleep/WOR.
    halSpiStrobe(CCxxx0_SWOR);
}// EXTERNAL_ISR




//-------------------------------------------------------------------------------------------------------
//  void showMenu(void) 
//
//  DESCRIPTION:
//      Function to show menu. Select mode by moving joystick left/right. 
//      Press S1 button to confirm the choice.
//-------------------------------------------------------------------------------------------------------
void showMenu(void) {
    mode = MODE_NOT_SET;
    ebLcdUpdate("Wor.c", "<-  Set Mode  ->");
    
    // Select Tx or Rx mode by moving the joystick left or right
    do {
        halWait(250);
        
        // Get current position of joystick
        joystickPosition = ebGetJoystickPosition();

        if (prevJoystickPosition != joystickPosition) {
            prevJoystickPosition = joystickPosition;
            parseMenu(joystickPosition);
        }
    } while (!ebButtonPushed() || (mode == MODE_NOT_SET));
}// showMenu




//-------------------------------------------------------------------------------------------------------
//  void parseMenu(UINT8 joystickPosition) 
//
//  DESCRIPTION: 
//      This function selects mode and updates the LCD display based on the joystick position
//
//  ARGUMENTS:
//      UINT8 joystickPosition
//          The position of the joystick
//-------------------------------------------------------------------------------------------------------
void parseMenu(UINT8 joystickPosition) {
    switch (joystickPosition) {

        case JOYSTICK_LEFT:
            if (mode == RX || mode == MODE_NOT_SET) {
                mode = TX;
                ebLcdUpdate("Mode:", "Tx/Burst");
            }
            break;

        case JOYSTICK_RIGHT:
            if (mode == TX || mode == MODE_NOT_SET) {
                mode = RX;
                ebLcdUpdate("Mode:", "Rx/Wake-on-Radio");
            }
            break;

        default:    // Other joystick movements: do nothing
            break;
    }
}// parseMenu




//-------------------------------------------------------------------------------------------------------
//  void intToAscii(UINT32 value)
//
//  DESCRIPTION:
//      Takes a 32 bits integer as input and converts it to ascii. Puts the result in the global
//      variable asciiString[]
//
//  ARGUMENTS:
//      UINT32 value
//          The value to be converted
//-------------------------------------------------------------------------------------------------------
void intToAscii(UINT32 value) {
    UINT8 i;
    UINT8 j = 0;
    UINT8 digit_start = 0;
    UINT16 digit = 0;
    UINT32 denom = 1000000000;

    if (value == 0) {
        asciiString[0] = '0';
        asciiString[1] = NULL;
    } else {
         for(i = 10; i > 0; i--) {
            digit = value / denom;
            if((digit_start == 1) || (digit != 0)) {
                digit_start = 1;
                value %= denom;
                asciiString[j++] = (digit + '0');
            }
            denom /= 10;
        }
        asciiString[j++] = NULL;
    }
}// intToAscii




/******************************************************************************************************
 * Revision history:                                                                                  *
 *
 * $Log: Wor.c,v $
 * Revision 1.3  2005/11/09 10:01:47  sna
 * no message
 *
 * Revision 1.2  2005/10/25 12:23:20  sna
 * Register settings moved to separate *.h file
 *
 * Revision 1.1  2005/10/06 12:13:52  sna
 * Initial version in CVS.
 *
 *
 *
 ******************************************************************************************************/

