/**
 * @email support.ele@noritake.com
 * @website www.noritake-elec.com
 * @version v2.3
 * @ide Code Composer Studio
 * @brief I2C library for MSP430 (MSP-EXP430F5529LP) and Noritake GT-CP modules.
 *
 * @verbatim
 * Provide the I2C interface between MSP430 and Noritake GT-CP modules.
 * Compatible hosts:
 * - MSP430F5529
 * - MSP430FR6989
 * @endverbatim
 *
 *
 * Wiring Reference | MSP430F5529:
 * SBUSY(MBUSY)(4) -> P1.6
 * TRDY(7)         -> P2.7
 * SCL(2)          -> P4.2
 * SDA(3)          -> P4.1
 * RESET(1)        -> P1.5
 *
 * Wiring Reference | MSP430FR6989:
 * SBUSY(MBUSY)(4) -> P1.4
 * TRDY(7)         -> P1.5
 * SCL(2)          -> P4.1
 * SDA(3)          -> P4.0
 * RESET(1)        -> P2.2
 */

#include "GTCP_I2C.h"

/**
 * Initialize I2C communication on the MSP430.
 *
 * @return none
 */
void initI2C()
{
	WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

	PRxData = (unsigned char *)RXDataArray;
	TRDYFlag = 0;

	initRESET();
	configureI2CGPIO();

	resetUSCIStateMachine_I2C();
	configureI2C();
	initUSCIStateMachine_I2C();

}

/**
 * Put USCI in reset state.
 *
 * @return none
 */
void resetUSCIStateMachine_I2C()
{
#ifdef __MSP430F5529__
    UCB1CTL1 = UCSWRST;
#elif defined __MSP430FR6989
    UCB1CTLW0 |= UCSWRST;
#endif
}

/**
 * Release USCI reset state.
 * Can also be seen as USCI initialize.
 *
 * @return none
 */
void initUSCIStateMachine_I2C()
{
#ifdef __MSP430F5529__
    UCB1CTL1 &= ~UCSWRST;
#elif defined __MSP430FR6989
    UCB1CTLW0 &= ~UCSWRST;
#endif
}

/**
 * Configure registers for I2C interface.
 *
 * @return none
 */
void configureI2C()
{
#ifdef __MSP430F5529__
    UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC;
    UCB1CTL1 = UCSSEL_2 + UCSWRST;
    UCB1BR0 = 10; //Set baudrate lower byte
    UCB1BR1 = 0; //Set baudrate high byte
    UCB1I2CSA = GTCP_I2C_ADDRESS; //Set slave address
#elif defined __MSP430FR6989
    UCB1CTLW0 |= UCMST | UCMODE_3 | UCSYNC;
    UCB1BRW = 8;           // Set baudrate
    UCB1I2CSA = GTCP_I2C_ADDRESS;       // Set slave address
#endif
}

/**
 * Configure GPIO pins for the I2C interface.
 *
 * @return none
 */
void configureI2CGPIO()
{
#ifdef __MSP430F5529__
    P1DIR &= BIT6; //Set P1.6 for MBUSY input

    P2DIR &= BIT7; //Set P2.7 for TRDY input
    P2IES &= ~BIT7; //P2.7 Lo/Hi edge
    P2IFG &= ~BIT7; //P2.7 IFG cleared
    P2IE |= BIT7; // Enable P2.7 interrupt

    P4SEL |= 0x06; //Set I2C pins to UCB1 | P4.2(SCL) P4.1(SDA)
#elif defined __MSP430FR6989
    P1DIR &=  BIT4;         // Set P1.4 for MBUSY input      (Module Busy)

    P1DIR &=  BIT5;         // Set P1.5 for TRDY input       (Data Ready)
    P1IES &= ~BIT5;         // P1.5 Lo/Hi edge
    P1IFG &= ~BIT5;         // P1.5 IFG cleared
    P1IE  |=  BIT5;         // Enable P1.5 interrupt

    P4SEL1 |= BIT0 | BIT1; // Set I2C pins to UCB1 | P4.0 (SDA) P4.1 (SCL)
    P4SEL0 &= ~(BIT0 | BIT1);

    // Disable the GPIO power-on default high-impedance mode to activate
    // previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;
#endif
}

/**
 * Check if the stop condition was sent to the slave device.
 * Blocking function if a stop condition has not been sent.
 *
 * @return none
 */
void checkStop()
{
	while(UCB1CTL1 & UCTXSTP); //Check to see if stop condition was sent
}

/**
 * Check if the start condition was sent to the slave device.
 * Blocking function if the start condition has not been sent.
 *
 * @return none
 */
void checkStart()
{
	while(UCB1CTL1 & UCTXSTT); //Check to see if start condition was sent
}

/**
 * Set the host as a transmitter and send a start condition.
 *
 * @return none
 */
void sendWriteStart()
{
	UCB1CTL1 |= UCTR + UCTXSTT; //Set as transmitter and send start condition
}

/**
 * Make sure the receiver mode is set and send a start condition.
 *
 * @return none
 */
void sendReadStart()
{
#ifdef __MSP430F5529__
	UCB1CTL1 &= ~UCTR; //Make sure receiver mode is set
	UCB1CTL1 |= UCTXSTT; //Send start condition
#elif defined __MSP430FR6989
	UCB1CTLW0 &= ~UCTR; //Make sure receiver mode is set
	UCB1CTLW0 |= UCTXSTT; //Send start condition
#endif
}

/**
 * Send a stop condition to the slave device.
 *
 * @return none
 */
void sendStop()
{
	UCB1CTL1 |= UCTXSTP; //Send stop condition
}

/**
 * Enable or disable the receive interrupt.
 *
 * @param enable Enable/disable receive interrupt.
 * @return none
 */
void RX_Int(char enable)
{
#ifdef __MSP430F5529__
	if(enable)
	{
		UCB1IE |= UCRXIE; //Enable receive interrupt
	}else{
		UCB1IE &= ~UCRXIE; //Disable receive interrupt
	}
#elif defined __MSP430FR6989
	if(enable)
	{
	    UCB1IE |= UCRXIE; //Enable receive interrupt
	}else{
	    UCB1IE &= ~UCRXIE; //Disable receive interrupt
	}
#endif
}

/**
 * Enable or disable the transmit interrupt.
 *
 * Currently not used.
 *
 * @param enable Enable/disable transmit interrupt.
 * @return none
 */
void TX_Int(char enable)
{

#ifdef __MSP430F5529__
    if(enable)
    {
        __enable_interrupt();
        UCB1IE |= UCTXIE; //Enable transmit interrupt
    }else{
        UCB1IE &= ~UCTXIE; //Disable transmit interrupt
    }
#elif defined __MSP430FR6989
    if(enable)
    {
        //__enable_interrupt();
        UCB1IE |= UCTXIE; //Enable transmit interrupt
    }else{
        UCB1IE &= ~UCTXIE; //Disable transmit interrupt
    }
#endif
}

/**
 * Read data from the module using the RX interrupt.
 *
 * @param length Number of bytes to read.
 * @return none
 */
void readI2C(uint8_t length)
{

#ifdef __MSP430F5529__
	if((P2IN & BIT7) == BIT7) // Only read if TRDY is high
    {
        RXByteCtr = length;
        RX_Int(1);
        while(UCB1CTL1 & UCTXSTP); // Ensure stop condition was sent
        sendReadStart();

        while(UCB1CTL1 & UCTXSTT); //Check to see if start condition was sent
        __bis_SR_register(LPM0_bits + GIE);
    }
#elif defined __MSP430FR6989
	if((P1IN & BIT5) == BIT5) // Only read if TRDY is high
	{
		RXByteCtr = length;
		RX_Int(1);
		//__delay_cycles(2000);
		while(UCB1CTLW0 & UCTXSTP); // Ensure stop condition was sent
		sendReadStart();

		while(UCB1CTLW0 & UCTXSTT); //Check to see if start condition was sent
		__bis_SR_register(LPM0_bits + GIE);
		//while(UCB1STATW & UCBBUSY); // Wait until bus is not busy
	}
#endif
}

/**
 * Read data from the module without interrupts.
 * Read data in to RXDataArray global array.
 * Only works with MSP430F5529.
 *
 * @return The touch data read from the module.
 */
void readMultipleI2CNI()
{
	unsigned int pos = 0;
	int fallingFlag = 0;

	UCB1CTL1 &= ~UCTR; //Make sure receiver mode is set

	while((P2IN & BIT7) != BIT7); // Wait while TRDY is LOW
	UCB1CTL1 |= UCTXSTT; //Send start condition
	while(UCB1CTL1 & UCTXSTT); //Check to see if start condition was sent

	while(((P2IN & BIT7) == BIT7) && (fallingFlag == 0)) //While TRDY is HIGH
	{
		while(!(UCB1IFG & UCRXIFG)); //Check if a byte has been received
		RXDataArray[pos] = UCB1RXBUF; //Store read data

		if(RXDataArray[0] != 0xFF)
		{
			pos++;
		}
		if((P2IN & BIT7) == BIT7)
		{
			fallingFlag = 0;
		} else {
			fallingFlag = 1;
		}
	}
	while(!(UCB1IFG & UCRXIFG)); //Check if a byte has been received
	//When /TRDY goes high, the last byte is to be read
	RXDataArray[pos] = UCB1RXBUF; //Store read data

	UCB1CTL1 |= UCTXNACK; //Transmit a NACK since reading is complete.
	UCB1CTL1 |= UCTXSTP; //Stop condition
	UCB1IFG &= ~UCRXIFG; //Clear receive flag
	while(UCB1CTL1 & UCTXSTP); //Check for stop condition
}

/**
 * Send a byte of data to the slave device.
 *
 * @param data Data byte to write.
 * @return none
 */
void writeI2C(unsigned char data)
{
	PTxData = &data;
	TXByteCtr += 1;
	TX_Int(1);

#ifdef __MSP430F5529__
	UCB1CTL1 |= UCTR + UCTXSTT; //Set as transmitter and send start condition
	__bis_SR_register(LPM0_bits | GIE);
	while(UCB1STAT & UCBBUSY); // Wait until bus is not busy

#elif defined __MSP430FR6989
	UCB1CTLW0 |= UCTR + UCTXSTT; //Set as transmitter and send start condition
	__bis_SR_register(LPM0_bits | GIE);
	while(UCB1STATW & UCBBUSY); // Wait until bus is not busy

#endif
}

/**
 * Send multiple bytes of data to the slave device.
 * Strings work well with this function.
 * Does not work well with commands.
 *
 * @param data String data to send.
 * @return none
 */
void writeMultipleI2C(unsigned char* data)
{
	PTxData = data;
	TXByteCtr += arraySize(data);
	TX_Int(1);

#ifdef __MSP430F5529__
    UCB1CTL1 |= UCTR + UCTXSTT; //Set as transmitter and send start condition
    __bis_SR_register(LPM0_bits | GIE);
    while(UCB1STAT & UCBBUSY); // Wait until bus is not busy
#elif defined __MSP430FR6989
    while (UCB1CTLW0 & UCTXSTP);             // Ensure stop condition got sent
    UCB1CTLW0 |= UCTR + UCTXSTT; //Set as transmitter and send start condition
    __bis_SR_register(LPM0_bits | GIE);
    while(UCB1STATW & UCBBUSY); // Wait until bus is not busy
#endif
}

/**
 * Send an array of data to the slave device.
 *
 * @param data Data to send.
 * @param size Size of data array.
 * @return none
 */
void writeArrayI2C(unsigned char* data, int size)
{
    PTxData = data;
    TXByteCtr += size;
    TX_Int(1);

#ifdef __MSP430F5529__
    UCB1CTL1 |= UCTR + UCTXSTT; //Set as transmitter and send start condition
    __bis_SR_register(LPM0_bits | GIE);
    while(UCB1STAT & UCBBUSY); // Wait until bus is not busy
#elif defined __MSP430FR6989
    while (UCB1CTLW0 & UCTXSTP);             // Ensure stop condition got sent
    UCB1CTLW0 |= UCTR + UCTXSTT; //Set as transmitter and send start condition
    while(UCB1CTLW0 & UCTXSTT); //Check to see if start condition was sent
    __bis_SR_register(LPM0_bits | GIE);
    while(UCB1STATW & UCBBUSY); // Wait until bus is not busy

#endif
}


#ifdef __MSP430F5529__
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B1_VECTOR))) USCI_B1_ISR (void)
#else
#error Compiler not supported!
#endif
{
	switch(__even_in_range(UCB1IV, 12))
	{
	case  0:  // Vector  0: No interrupts
		break;
	case  2:  // Vector  2: ALIFG
		break;
	case  4:  // Vector  4: NACKIFG
		break;
	case  6:  // Vector  6: STTIFG
		break;
	case  8:  // Vector  8: STPIFG
		break;
	case 10:  // Vector 10: RXIFG
	    RXByteCtr--;
		if(RXByteCtr)
		{
			*PRxData++ = UCB1RXBUF;
			if(RXByteCtr == 1)
			{
			    UCB1CTL1 |= UCTXSTP; //Send stop condition
			}
		} else{
			*PRxData = UCB1RXBUF;
			PRxData = (unsigned char *)RXDataArray; // Go back to start of RXDataArray
			UCB1IFG &= ~UCRXIFG; //Clear receive flag
			UCB1IE &= ~UCRXIE; // Disable receive interrupt
			__bic_SR_register_on_exit(LPM0_bits);
		}

		break;
	case 12:  // Vector 12: TXIFG
		if(TXByteCtr){
			UCB1TXBUF = *PTxData++;
			while((P1IN & BIT6) == BIT6); //Wait for module busy to go LOW
			TXByteCtr--;
		}else {
			UCB1CTL1 |= UCTXSTP; //Stop condition
			UCB1IFG &= ~UCTXIFG; //Clear transmit flag
			UCB1IE &= ~UCTXIE; //Disable transmit interrupt
			__bic_SR_register_on_exit(LPM0_bits);
		}

		break;
	default:
		break;
	}
}

#elif defined __MSP430FR6989
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B1_VECTOR))) USCI_B1_ISR (void)
#else
#error Compiler not supported!
#endif
{
  switch(__even_in_range(UCB1IV, USCI_I2C_UCBIT9IFG))
  {
    case USCI_NONE:          break;         // Vector 0: No interrupts
    case USCI_I2C_UCALIFG:   break;         // Vector 2: ALIFG
    case USCI_I2C_UCNACKIFG: break;         // Vector 4: NACKIFG
    case USCI_I2C_UCSTTIFG:  break;         // Vector 6: STTIFG
    case USCI_I2C_UCSTPIFG:  break;         // Vector 8: STPIFG
    case USCI_I2C_UCRXIFG3:  break;         // Vector 10: RXIFG3
    case USCI_I2C_UCTXIFG3:  break;         // Vector 12: TXIFG3
    case USCI_I2C_UCRXIFG2:  break;         // Vector 14: RXIFG2
    case USCI_I2C_UCTXIFG2:  break;         // Vector 16: TXIFG2
    case USCI_I2C_UCRXIFG1:  break;         // Vector 18: RXIFG1
    case USCI_I2C_UCTXIFG1:  break;         // Vector 20: TXIFG1
    case USCI_I2C_UCRXIFG0:                 // Vector 22: RXIFG0
        RXByteCtr--;
        if(RXByteCtr)
        {
            *PRxData++ = UCB1RXBUF;
            if(RXByteCtr == 1)
            {
                UCB1CTLW0 |= UCTXSTP; //Send stop condition
            }
        } else{
            *PRxData = UCB1RXBUF;
            PRxData = (unsigned char *)RXDataArray; // Go back to start of RXDataArray
            UCB1IFG &= ~UCRXIFG; //Clear receive flag
            UCB1IE &= ~UCRXIE; // Disable receive interrupt
            __bic_SR_register_on_exit(LPM0_bits);
        }

        break;
    case USCI_I2C_UCTXIFG0:                 // Vector 24: TXIFG0
        if(TXByteCtr){
            UCB1TXBUF = *PTxData++;
            while((P1IN & BIT4) == BIT4); //Wait for module busy to go LOW
            TXByteCtr--;
        } else {
            UCB1CTLW0 |= UCTXSTP; //Stop condition
            UCB1IFG &= ~UCTXIFG; //Clear transmit flag
            UCB1IE &= ~UCTXIE; //Disable transmit interrupt
            __bic_SR_register_on_exit(LPM0_bits);
        }

        break;
    case USCI_I2C_UCBCNTIFG: break;         // Vector 26: BCNTIFG
    case USCI_I2C_UCCLTOIFG: break;         // Vector 28: clock low timeout
    case USCI_I2C_UCBIT9IFG: break;         // Vector 30: 9th bit
    default: break;
  }
}
#endif

#ifdef __MSP430F5529__
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = PORT2_VECTOR
__interrupt void Port_2(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(Port2_VECTOR))) PORT_2 (void)
#else
#error Compiler not supported!
#endif
{
	if((P2IES & BIT7) != BIT7 || (P2IN & BIT7))
	{
		TRDYFlag = 1;
		P2IES |= BIT7; //P2.7 HI to LOW edge trigger
	} else if((P2IES & BIT7) == BIT7 || !(P2IN & BIT7)){
		TRDYFlag = 0;
		P2IES &= ~BIT7; //P2.7 LOW to HI edge trigger
	}

	P2IFG &= ~BIT7;

	__bic_SR_register_on_exit(LPM0_bits);
}

#elif defined __MSP430FR6989
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = PORT1_VECTOR
__interrupt void Port_1(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(Port1_VECTOR))) PORT_1 (void)
#else
#error Compiler not supported!
#endif
{
    if((P1IES & BIT5) != BIT5 || (P1IN & BIT5))
    {
        TRDYFlag = 1;
        P1IES |= BIT5; //P1.5 HI to LOW edge trigger
    } else if((P1IES & BIT5) == BIT5 || !(P1IN & BIT5)){
        TRDYFlag = 0;
        P1IES &= ~BIT5; //P1.5 LOW to HI edge trigger
    }

    P1IFG &= ~BIT5;

    __bic_SR_register_on_exit(LPM0_bits);
}
#endif
