//----------------------------------------------------------------------------
//  One Way Audio Code
//
//  M J Burns
//  Texas Instruments, Rochester Design Center
//  December 2008
//                
//  For use with 'CC1150 Tx Rev D' and 'CC1101 Rx Rev B' cards
//
//----------------------------------------------------------------------------

// Interrupt Service Routines (ISRs)

#include "include.h"
#include "string.h"

// Subroutine declarations

void short2ascii(char, unsigned int);
void int2ascii(unsigned int);
int writeLCD(void);

extern unsigned int clktics;                      // increments every 125 usec
extern unsigned int adcvalue;
extern unsigned int adcbuf[4];                    // Used to hold 10 bit ADC samples 
extern unsigned char adctxbuf[2][ADCTXBUFRLEN];   // ADC TX Buffers
extern unsigned short adctxptr;                   // Pointer into adctxbuf
extern unsigned short adcsample;                  // adcsample (0 - 3)
extern unsigned short activebufr;                 // active TX buffer
extern unsigned short sendpacket;                 // Set non zero to initiate TX of data packet
extern char locate;                               // When non zero, the receiver should beep and flash
extern unsigned short agcsamples;
extern char pbstatus;                             // Push Button Status

unsigned short oddeven = 0;
unsigned int vpeak;                               // peak ADC voltage
unsigned int vpmax;                               // maximum peak voltage reached in ADCSAMPLES
unsigned short vgagain;                           // 0 <= VGAGAIN <= 3
unsigned short pvalue;
unsigned short rssiupdate = 0;                    // Update rssi display when this variable has a value of zero

#ifdef RECEIVER

extern unsigned short rcvpacket;                  // Set non zero when a data packet has been received
extern unsigned int pwmbufr[2][ADCSAMPLES];       // DAC buffers
extern unsigned short pwmbufrptr;                 // Pointer into DAC buffer (used in unpcaking code)
extern unsigned short pwmbfrptr;                  // Pointer into DAC buffer (used in interrupt code)
extern unsigned short rxactbufr;                  // 'active buffer' pointer used by the unpacking routine (following packet reception)
extern unsigned short pwmactbufr;                 // 'active buffer' pointer used by the PWM (Timer_A) intreeupt handler
extern unsigned short waitingforbeacon;           // Set until beacon is detected
extern unsigned short timeractive;
extern unsigned int timertics;
extern char tone;                                 // locate tone
extern int rssidbm, rssidbmn;
extern unsigned int lqiavg;
extern unsigned int pe1024;                       // number of packet errors in the last 1024 packets
extern unsigned int crc;                          // Packet received, but CRC failed
extern unsigned int packeterrors;                 // Packet Errors
extern unsigned int lostpackets;                  // Lost Packets

unsigned short peupdate = 0;                      // Update the 'Packet errors' display when this variable has a value of one
unsigned short volume;
unsigned short readadc = 0;                       // Read ADC and check battery voltage

#endif

// LCD 

extern char lcdtxt[26];
extern char txtstr[6];
extern unsigned short lcdbufrbusy;
extern unsigned short lcdbufrindx;
extern unsigned short lcdlen;

#ifdef RECEIVER

//==========================================================================================
// Port 2 Bit 1 (GDO0) Interrupt Service Routine
//==========================================================================================

// The ISR assumes the int came from the pin attached to GDO0 and therefore
// does not check the other seven inputs. Interprets this as a signal from
// CC1101 indicating that sync word has been detected.
  
#pragma vector=PORT2_VECTOR
__interrupt void port2_ISR (void)
{
  rcvpacket = 1;                          // Set packet available flag
  P2IFG &= ~TI_CC_GDO0_PIN;               // Clear GDO0 flag
}

#endif

//==========================================================================================
// Timer A_0 Interrupt Service Routine
//==========================================================================================

#ifdef MONITOR        // card is configured as a MONITOR

#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A(void)
{
  ADC10CTL0 |= ADC10SC;                   // Start ADC Conversion
  while ((ADC10CTL0 & ADC10SC) > 0) {     // Wait for conversion to complete
  }  

  adcvalue = ADC10MEM;      // retain the ADC sample for use later ...
  
  if (adcsample < 3)    
    adcbuf[adcsample++] = adcvalue;
  else {                    // every 4th ADC sample, pack 4 ADC samples into 5 data bytes
    adctxbuf[activebufr][adctxptr] = adcbuf[0] & 0x00FF;               // A7...A0
    adctxbuf[activebufr][adctxptr + 1] = (adcbuf[0] & 0x0300) >> 8;    // A9A8
    adctxbuf[activebufr][adctxptr + 1] |= (adcbuf[1] & 0x003F) << 2;   // B5...B0
    adctxbuf[activebufr][adctxptr + 2] = (adcbuf[1] & 0x03C0) >> 6;    // B9...B6
    adctxbuf[activebufr][adctxptr + 2] |= (adcbuf[2] & 0x000F) << 4;   // C3...C0
    adctxbuf[activebufr][adctxptr + 3] = (adcbuf[2] & 0x03F0) >> 4;    // C9...C4
    adctxbuf[activebufr][adctxptr + 3] |= (adcvalue & 0x0003) << 6;    // D1D0
    adctxbuf[activebufr][adctxptr + 4] = (adcvalue & 0x03FC) >> 2;     // D9...D2
    adctxptr += 5;
    adcsample = 0;
  }
     
  if (adctxptr == ADCTXBUFRLEN) {
    if (activebufr == 0)          // Alternate TX buffers
      activebufr = 1;
    else
      activebufr = 0;
    adctxptr = 0;                 // Reset the ADC buffer pointer
    sendpacket = 1;               // Send Packet
  }
  
// AGC code
  
 
  if (adcvalue > 512)           // calculate the peak value
    adcvalue -= 512;
  else
    adcvalue = 512 - adcvalue;
  if (adcvalue > vpmax)
    vpmax = adcvalue;
  agcsamples++;
  if (agcsamples == AGCSAMPLES)  {
    agcsamples = 0;
    if ((vpmax > VPMAX) && (vgagain > 0))   // if Vp exceeds upper limit ...
      vgagain--;                            // reduce the VGA gain (if posible)
    if ((vpmax < VPMIN) && (vgagain < 3))   // if Vp is below the lower limit ...
      vgagain++;                            // increase the VGA gain (if posible)
    vpmax = 0;
    pvalue = P3OUT;                         // set the VGA gain, 
    pvalue &= ~0xC0;                        // being careful not to 'glitch' the VGA Gain lines                         
    pvalue |= vgagain << 6;
    P3OUT = pvalue;
  }
   
// end of AGC code
  
  clktics++; 
 
  if (clktics == 2000) {   
    clktics = 0;
    P1OUT ^= LED_HEARTBEAT;         // Toggle the Heartbeat LED every 250 msec
  }  
  
  if ((clktics & 0x000F) == 0) {    // Check Push Button Status every 16 tics (2 msec) 
    if (((P1IN & PB1) == 0) && ((pbstatus & 0x01) == 0)) {      // PB1 is low true
      pbstatus |= 0x01;                                         // PB1 has changed state from False to True
      locate |= 0x01;
      P1OUT |= LED_RED;
    }
    if (((P1IN & PB1) > 0) && ((pbstatus & 0x01) > 0))         
      pbstatus &= ~0x01;                                        // PB1 has changed state from True to False
    
    if (((P1IN & PB2) == 0) && ((pbstatus & 0x02) == 0)) {      // PB2 is low true
      pbstatus |= 0x02;                                         // PB2 has changed state from False to True
      locate &= ~0x01;
      P1OUT &= ~LED_RED;
    }  
    if (((P1IN & PB2) > 0) && ((pbstatus & 0x02) > 0))         
      pbstatus &= ~0x02;                                       // PB2 has changed state from True to False
   }
}

#else           // card is configured as a RECEIVER

#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A(void)
{
  oddeven++;
  if (oddeven == 1)
    return;
  
  oddeven = 0;
  
  if (waitingforbeacon == 0) {                      // do nothing until the first data packet arrives
    if (pwmactbufr == 0) {                          // Using the 'A' buffer
      if ((P4OUT & 0x40) > 0)
        TACCR1 = pwmbufr[0][pwmbfrptr++];           // A buffer is valid
      else {
        TACCR1 = 512;                               // A buffer is invalid - mute
        P1OUT |= 0x08;                              // mark the error
      }
    }
  
    if (pwmactbufr == 1) {                          // Using the 'B' buffer
      if ((P4OUT & 0x80) > 0)
        TACCR1 = pwmbufr[1][pwmbfrptr++];           // B buffer is valid
      else {
        TACCR1 = 512;                               // B buffer is invalid - mute
        P1OUT |= 0x08;                              // mark the error
      }
    }

    if (pwmbfrptr >= ADCSAMPLES) {           
      pwmbfrptr = 0;                      // reset the buffer pointer
      pwmactbufr ^= 0x01;                 // switch to other buffer
      if ((P1OUT & 0x08) > 0)
        pwmbfrptr += 18;                  // if a buffer error occurred, skip ahead 18 samples
      P1OUT &= ~0x08;                     // clear the buffer error flag
    }
  }
  
  if ((timeractive > 0) && (timertics < 0xFFFF))
    timertics++;
  
  clktics++; 
  if (clktics == 2000) {   
    clktics = 0;
    P1OUT ^= LED_HEARTBEAT;               // Toggle the Hearbeat LED every 250 msec
    if ((tone & 0x01) > 0) {              // if in 'locate' mode
      P4OUT ^= 0x1F;                      // Toggle the Volume Bar Graph LEDs
      tone ^= 0x04;                       // Mute tone when bit 2 is set
      tone |= 0x02;                       // Signal to load buffer
    } 

    // LCD  
    
    rssiupdate++;
    rssiupdate = rssiupdate & 0x01;       // mod 2 (every 500 msec)
    
    if ((lcdbufrbusy == 0) && (rssiupdate == 0)) {     
      strcpy(lcdtxt, "L4RSSI: ");
   
      if ((rssidbm & 0x007F) > 0) {       // if rssidbm is a neagtive number
        rssidbmn = rssidbm & 0x007F;      // take absolute value
        rssidbmn ^= 0x007F;
        rssidbmn += 1;
        short2ascii('-', rssidbmn & 0x7FF);
      }
      else
        short2ascii(' ', rssidbm);
      
      strncat(lcdtxt, txtstr, 4);
      strcat(lcdtxt, " LQI:");
      short2ascii(' ', lqiavg);
      strncat(lcdtxt, txtstr, 4);
      writeLCD();
    }

    peupdate++;
    peupdate = peupdate & 0x0F;   // mod 16 (every 4 seconds)
    
    if ((lcdbufrbusy == 0) && (peupdate == 1)) {
      strcpy(lcdtxt, "L3CRC:");
      short2ascii(' ', crc);
      strncat(lcdtxt, txtstr, 4);
      strcat(lcdtxt, "  LP:");
      short2ascii(' ', lostpackets);
      strncat(lcdtxt, txtstr, 4);
      writeLCD();
    }
        
    if ((lcdbufrbusy == 0) && (peupdate == 15)) {
      strcpy(lcdtxt, "L2PE:");
      short2ascii(' ', packeterrors);
      strncat(lcdtxt, txtstr, 4);
      strncat(lcdtxt, "  PEAVG:", 8);
      int2ascii(pe1024);
      strncat(lcdtxt, txtstr, 5);
      writeLCD();
    }

    readadc++;
    readadc = readadc & 0x3F;               // mod 64 (every 16 seconds)
    
    ADC10CTL0 |= ADC10SC;                   // Start ADC Conversion
    while ((ADC10CTL0 & ADC10SC) > 0) {     // Wait for conversion to complete
    }  

    adcvalue = ADC10MEM;
    if (adcvalue < BVLOWLIMIT)
      P1OUT |= LED_RED;
    else
      P1OUT &= ~LED_RED;
   
  }   // end of 'clocktics == 2000' if

  if ((clktics & 0x000F) == 0) {                                // Check Push Button Status every 16 tics (2 msec) 
    if (((P2IN & PB1) == 0) && ((pbstatus & 0x01) == 0)) {      // PB1 is low true
      pbstatus |= 0x01;                                         // PB1 has changed state from False to True
      if (volume < 3) {
        volume++;                                               // increase volume
        P3OUT &= ~0xC0;
        P3OUT |= (volume << 6);
      }
    }
    if (((P2IN & PB1) > 0) && ((pbstatus & 0x01) > 0))         
      pbstatus &= ~0x01;                                        // PB1 has changed state from True to False
    
    if (((P2IN & PB2) == 0) && ((pbstatus & 0x02) == 0)) {      // PB2 is low true
      pbstatus |= 0x02;                                         // PB2 has changed state from False to True
      if (volume > 0) {
        volume--;                                               // decrease volume 
        P3OUT &= ~0xC0;
        P3OUT |= (volume << 6);
      }
    }  
    if (((P2IN & PB2) > 0) && ((pbstatus & 0x02) > 0))         
      pbstatus &= ~0x02;                                        // PB2 has changed state from True to False
   }
}

#endif

//==========================================================================================
// UCA0 Tx Interrupt Service Routine
//==========================================================================================

#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void)
{
  UCA0TXBUF = lcdtxt[lcdbufrindx++];       // TX next character

  if (lcdbufrindx == lcdlen) {             // End of buffer reached?
    IE2 &= ~UCA0TXIE;                      // Disable USCI_A0 TX interrupt
    lcdbufrbusy = 0;                       // clear the "buffer in use" flag  
  }
}

