#include <msp430x20x2.h>

/* type definitions for the IAR MSP430 compiler */

typedef unsigned char      uint8;
typedef signed char        int8;
typedef unsigned short     uint16;
typedef signed short       int16;
typedef unsigned long      uint32;
typedef signed long        int32;

void Init_USI_I2C_SLAVE(void);
void USI2_BUGFIX(void);

void sirc_transmit(unsigned short data);

void goertzel();
void sound_on();
void sound_off();

//define LED_ENABLE (P1DIR |= 0x01)
#define GREEN_LED_ON     (P1OUT |= 0x01)
#define GREEN_LED_OFF    (P1OUT &= ~0x01)
#define GREEN_LED_TOGGLE (P1OUT ^= 0x01)
#define RED_LED_ON       (P1OUT |= 0x02)
#define RED_LED_OFF      (P1OUT &= ~0x02)
#define RED_LED_TOGGLE   (P1OUT ^= 0x02)

volatile long dummy;

volatile uint16 sample;
volatile uint8  sample_ready = 0;

void main(void) {

  WDTCTL = WDTPW+WDTHOLD;                   // Stop watchdog timer

  DCOCTL = CALDCO_16MHZ;                     // Set DCO for 1MHz using
  BCSCTL1 = CALBC1_16MHZ;                    // calibration registers
  
  //BCSCTL3 |= LFXT1S1; // use VLOCLK for ACLK
  //BCSCTL3 |= XCAP_2; // 10pf

  P1SEL = 0x00; // All P1.x port function
  P1DIR = 0x03; // All P1.x direction: output on 0,1, input on rest 
  P1OUT = 0x00; // All P1.x reset
  
  P1REN = 0x08;  // enable pull up/down on P1.3
  P1OUT |= 0x08; // pull it up

  //P1DIR |= 0x04;
  //P1OUT |= 0x04; 

  //for (dummy = 0; dummy<1000000; dummy++);
  //GREEN_LED_ON;
  //RED_LED_ON;
  //GREEN_LED_OFF;
  //RED_LED_OFF;

  //IE1 |= WDTIE; // Enable WDT interrupt
  
  Init_USI_I2C_SLAVE(); // Initialize I2C as Slave

  __enable_interrupt();  
 
  while(1) LPM0;
}
  
/***********************************************************/
/* SIRC transmission using WDT and A2                      */
/* This code assumes that SMCLK runs at 16MHz              */
/***********************************************************/

#if 1

unsigned short sirc_periods;
unsigned short sirc_data;
signed   char  sirc_bits_sent;
signed   short sirc_pulse;
unsigned char  sirc_wdt_counter;

void sirc_transmit(unsigned short data)
{
  sirc_data = data;
  
  //TACCR0 = 26 - 1;                          // 1MHz clock, PWM Period ~ 38kHz
  //TACCR0  = 25 - 1;                          // 1MHz clock, PWM Period ~ 40kHz
  //TACCR0  = 25 - 1;                          // 1MHz clock, PWM Period ~ 40kHz
  TACCR0  = 400 - 1;
  TACTL   = TASSEL1+TACLR;                    // SMCLK, Clear TA
  TACCR1  = 133;                               // CCR1 PWM Duty Cycle
  TACCTL1 = OUTMOD_7;                       // CCR1 reset/set
  TACTL |= MC0;                             // Start TA in up mode
  P1SEL |= 0x04; // enable TA1 on P1.2
  P1DIR |= 0x04; // output direction
  //TACCTL0 = CCIE;                           // Enable CCR0 interrupt
  
  sirc_periods     = 0;
  sirc_pulse       = 4;  // start with 4 on periods
  sirc_wdt_counter = 0;
  sirc_bits_sent   = 0;

  WDTCTL = WDTPW+WDTTMSEL+WDTCNTCL+WDTIS0+WDTIS1; // interval timer, use SMCLCK, reset, every 64 clocks
  IE1 |= WDTIE; // Enable WDT interrupt
  
}


#pragma vector=WDT_VECTOR
__interrupt 
void watchdog_timer (void) {
  if (sirc_wdt_counter++ == 150) {
    sirc_periods++;
    sirc_wdt_counter = 0;
    sirc_pulse--;
    if (sirc_pulse > 0) return;
    if (sirc_pulse == 0) {
      P1SEL &= ~0x04; // enable P1.2 instead of TA1
      P1OUT &= ~0x04; // turn it off for one period
      return;
    }
    if (sirc_bits_sent < 12) {
      P1SEL |= 0x04; // enable TA1 on P1.2
      if ((sirc_data >> sirc_bits_sent) & 1) sirc_pulse = 2;
      else                                   sirc_pulse = 1;
      sirc_bits_sent++;
    }
    if (sirc_periods == 75) { // 45ms, start another transmission
      sirc_periods   = 0;
      sirc_bits_sent = 0;
      sirc_pulse     = 4;
      P1SEL |= 0x04; // enable TA1 on P1.2
    }
  }
  //_BIC_SR_IRQ(LPM3_bits); // Clear LPM3 bits from 0(SR)
}

#endif

/***********************************************************/
/* i2c code                                                */
/***********************************************************/

#if 1

static short USI_OA; // Storage for Own Address Compare Value
static short USI_SA; // Storage for Slave Address Received
static unsigned char USIData; // Storage for Received Data

static signed char i2c_byte_count;

#define I2C_OA (0x44)
//#define I2C_OA (0x48)

static unsigned int ProgramMode; // Current program mode
enum { 
  PM_IDLE, // Program Mode - IDLE (Slave Idle Mode)
  PM_SA_RECD, // Program Mode - SA_RECD (Slave Address Receive)
  PM_RCVR_DATA_PREP, // Program Mode - RCVR_DATA_PREP (Receiver Data Control Setup)
  PM_XMIT_DATA_PREP, // Program Mode - XMIT_DATA_PREP (Transmitter Data Control Setup)
  PM_DATA_RECD, // Program Mode - DATA_REC (Data Receive)
  PM_DATA_SENT // Program Mode - DATA_SEND (Data Send)
};

void Init_USI_I2C_SLAVE(void) {
  USI_OA = I2C_OA*2;
  USIData = 0x00;
    
  ProgramMode = PM_IDLE;
  USISRL = 0xFF;
  USICKCTL = USICKPL; // USICLK=SCL (Inverted)
  USICTL1 |= USII2C+USISTTIE; // USI in I2C Mode, Enable USISTTIFG Interrupts
  USICTL0 = USIPE7+USIPE6+USISWRST; // USI Port Control for P1.7 and P1.6

  P1REN |= 0xC0; // Enable Pull-up/down Option
  P1OUT |= 0xC0; // Select Pull-up for P1.7 and P1.6
  //P1DIR = 0xFF; // P1.0, P1.1, and P1.4 As output
  //P1SEL |= 0xC0; // 

  USICNT = 0x08; // USICNT 8-bits
  USICTL0 &= ~USISWRST; // Clear Reset of USI
}

//pragma vector=USI_VECTOR
__interrupt 
void usi_isr (void) {
  if (USICTL1 & USISTTIFG) { // Was Start Condition Detected?
    USICTL0 &= ~USIOE; // Clear Output Enable
    USICNT = 0x08; // Load USICNT for 8-bits Slave Address receive
    USICTL1 &= ~USISTTIFG; // Clear USISTTIFG Start Interrupt Flag
    USICTL1 |= USIIE; // Enable USIIFG Interrupt
    ProgramMode = PM_SA_RECD; // Receive Slave Address
  } else {
    switch (ProgramMode) {
      case PM_IDLE : // Program Mode - IDLE (Slave Idle Mode)
        USICNT = 0x08; // Keep Loading USICNT While IDLE
        break;
      case PM_SA_RECD : // Program Mode - SA_RECD (Slave Address Received, If Match then Acknowledge, else Idle)
        USI_SA = USISRL;
        USI_SA &= 0xFE; // Remove Read/Write Bit
        if (USI_SA == 0x44) { // Slave Address Match --> Send Acknowledge
          i2c_byte_count = 0;
          if (USISRL & 0x01) {
            ProgramMode = PM_XMIT_DATA_PREP; // Generate Slave Address Ack -> Transmitter Data Control Setup
          } else {
            //USIData = 0; // clear data storage
            ProgramMode = PM_RCVR_DATA_PREP; // Generate Slave Address Ack -> Receiver Data Control Setup
          }
          USICTL0 |= USIOE; // Set Output Enable
          USISRL = 0x00; // Load USISRL for Acknowledge
          // USICNT = 0x01; // Load USICNT 1-bit transmit ACK (Expected Operation)
          USI2_BUGFIX(); // Bug Fix for Issue with USI Ack Generation
          break;
        } else { // Slave Address Not Matched --> Do not Acknowledge and return to IDLE
          ProgramMode = PM_IDLE; // Idle State - Wait For USISTTIFG
          USICTL0 &= ~USIOE; // Clear Output Enable
          USISRL = 0xFF; // Load USISRL for No Acknowledge
          // USICNT = USISCLREL+0x01; // Load USICNT 1-bit transmit NAck (Expected Operation)
          // // Disable SCL Holding (Start Condition Must Reset)
          USI2_BUGFIX(); // Bug Fix for Issue with USI NAck Generation
          USICNT = USISCLREL; // Disable SCL Holding (Start Condition Must Reset)
          USICTL1 &= ~USIIE; // Clear USIIFG Interrupt Enable
          break;
        }
      case PM_RCVR_DATA_PREP : // Program Mode - RCVR_DATA_PREP (Receiver Data Setup).
        USICTL0 &= ~USIOE; // Clear Output Enable
        USICNT = 0x08; // Load USICNT for 8-bits Data to Receive
        ProgramMode = PM_DATA_RECD; // Data Received - I2C Slave received Data from Master
        break;
      case PM_XMIT_DATA_PREP : // Program Mode - XMIT_ACK_SENT (Transmitter Acknowledge Sent) --> Transmit Data Setup.
        USICTL0 |= USIOE; // Set Output Enable
        USISRL = USIData; // Fixed Data Load
        USIData++; // sivan, increment
        USICNT = 0x08; // Load USICNT for 8-bits Data to Send
        ProgramMode = PM_DATA_SENT; // Data Sent - I2C Slave sent Data to Master
        break;
     case PM_DATA_RECD : // Program Mode - DATA_RECD (Data Received) --> Send Acknowledge
        //i2c_byte_count++;
        //USIData <<= 8; //shift the previous bytes to the left (low-order byte first)
        USIData = USISRL; // Store USISRL Data
        if (USIData == 1) sound_on();
        if (USIData == 0) sound_off();
        //if (i2c_byte_count == 2) {
          //P1OUT |= 0x01; // Set P1.0 LED on
          //sirc_transmit(USIData);
          //sound();
        //}
        
        USICTL0 |= USIOE; // Set Output Enable
        USISRL = 0x00; // Load USISRL for Acknowledge
        //USICNT = 0x01; // Load USICNT 1-bit transmit ACK (Expected Operation)
        USI2_BUGFIX(); // Bug Fix for Issue with USI Ack Generation
        ProgramMode = PM_RCVR_DATA_PREP; // Receiver Acknowledge Sent
        break;
     case PM_DATA_SENT : // Program Mode - DATA_SENT (Data Sent) --> Check Acknowledge
        USICTL0 &= ~USIOE; // Clear Output Enable 
        // USICNT = 0x01; // Load USICNT 1-bit transmit ACK (Expected Operation)
        USI2_BUGFIX(); // Bug Fix for Issue with USI Ack Generation 
        if (USISRL & 0x01) {
          USICNT = 0x02; // Master NAck, Load USICNT 2-bit To accept Stop Condition or Handle Re-start
          USICTL1 &= ~USIIE; // Clear USIIFG Interrupt Enable
          ProgramMode = PM_IDLE; // Idle State - Wait For USISTTIFG
        } else 
          ProgramMode = PM_XMIT_DATA_PREP; // Transmitter Acknowledge Sent
        break;
     }
  }
}
USI_ISR(usi_isr)

//################################################################################
// USI2 Bug fix routines
//
//################################################################################
void USI2_BUGFIX(void) {
  P1OUT ^= 0x02;
  while (P1IN & 0x40){} // Test SCL P1(6) LOW
  USICNT = USISCLREL+0x01;
  while ((P1IN & 0x40) == 0){} // Test SCL P1(6) HIGH
  while (P1IN & 0x40){} // Test SCL P1(6) LOW
  USICNT = 0x00; // Clear USISCLREL
  P1OUT &= ~0x02;
}

#endif
