#include <msp430.h>
#include <msp430xgeneric.h>
#include <stdint.h>
#include <stdio.h>

#include "mw_main.h"
#include "mw_uart.h"

#include "mw_acc.h"

#include "oswald_main.h"

#define ACCEL_STATE_DISABLED		0x00
#define ACCEL_STATE_ENABLED		0x01

static uint8_t AccelState;
static uint8_t AccelerometerBusy;
static uint8_t LengthCount;
static uint8_t Index;
static uint8_t *pAccelerometerData;

/*
 * Accelerometer is a Kionix KXTF9-4100 connected to I2C
 * I2C is pretty slow so reading and writing should be done non blocking
 * using interrupts.
 */

#define ACCELEROMETER_NO_INTERRUPTS	0x00
#define ACCELEROMETER_ALIFG		0x02
#define ACCELEROMETER_NACKIFG		0x04
#define ACCELEROMETER_STTIFG		0x06
#define ACCELEROMETER_STPIFG		0x08
#define ACCELEROMETER_RXIFG		0x0a
#define ACCELEROMETER_TXIFG		0x0c

#pragma vector=USCI_B1_VECTOR
__interrupt void ACCERLEROMETER_I2C_ISR(void)
{
	// debug_uart_tx("ACC i2c irq\n");
	switch (USCI_ACCELEROMETER_IV) {
		case ACCELEROMETER_NO_INTERRUPTS: 
			break;
		case ACCELEROMETER_ALIFG: 
			break;
		case ACCELEROMETER_NACKIFG:
			nop();
			break; 
		case ACCELEROMETER_STTIFG:
			nop();
			break; 
		case ACCELEROMETER_STPIFG: 
			nop();
			break;
		case ACCELEROMETER_RXIFG:
			if (LengthCount > 0) {
				pAccelerometerData[Index++] = ACCELEROMETER_RXBUF;
				LengthCount--;
				if ( LengthCount == 1 ) {
					/* All but one byte received. Send stop */
					ACCELEROMETER_CTL1 |= UCTXSTP;
				} else if ( LengthCount == 0 ) {
					/* Last byte received; disable rx interrupt */
					ACCELEROMETER_IE &= ~UCRXIE;
					AccelerometerBusy = 0;
				}
			}
			break;
		case ACCELEROMETER_TXIFG:
			if ( LengthCount > 0 ) {
				ACCELEROMETER_TXBUF = pAccelerometerData[Index++];
				LengthCount--;
			} else {
				/* disable transmit interrupt and send stop */
				ACCELEROMETER_IE &= ~UCTXIE;
				ACCELEROMETER_CTL1 |= UCTXSTP;
				AccelerometerBusy = 0;
			}
			break;
		default:
			break;
	}

}

void mw_acc_init_i2c(void)
{
	/* enable reset before configuration */
	ACCELEROMETER_CTL1 |= UCSWRST;

	/* configure as master using smclk / 40 = 399.5 kHz */
	ACCELEROMETER_CTL0 = UCMST + UCMODE_3 + UCSYNC;
	ACCELEROMETER_CTL1 = UCSSEL__SMCLK + UCSWRST;
	ACCELEROMETER_BR0 = 42;

	ACCELEROMETER_BR1 = 0;
	ACCELEROMETER_I2CSA = KIONIX_DEVICE_ADDRESS;

	/* release reset */
	ACCELEROMETER_CTL1 &= ~UCSWRST;
}

void mw_acc_disable_i2c(void)
{
	/* enable reset to hold it */
	ACCELEROMETER_CTL1 |= UCSWRST;
}

void mw_acc_i2c_write(uint8_t RegisterAddress, uint8_t *pData, uint8_t Length)
{
	int tmo;

	if (Length == 0 || pData == 0)
		return;  
  
	while (UCB1STAT & UCBBUSY)
		nop();
  
	AccelerometerBusy = 1;
	LengthCount = Length;
	Index = 0;
	pAccelerometerData = pData;
  
	/* 
	 * enable transmit interrupt and 
	 * setup for write and send the start condition
	 */
	ACCELEROMETER_IFG = 0;
	ACCELEROMETER_CTL1 |= UCTR + UCTXSTT;
	while(!(ACCELEROMETER_IFG & UCTXIFG))
		nop();
  
	/* 
	 * clear transmit interrupt flag, enable interrupt, 
	 * send the register address
	 */
	ACCELEROMETER_IFG = 0;
	ACCELEROMETER_IE |= UCTXIE;
	ACCELEROMETER_TXBUF = RegisterAddress;

	tmo = 0;
	while (AccelerometerBusy) {
		while (tmo++ < 1000)
			__delay_cycles(16000);
		if (tmo >= 1000) {
			debug_uart_tx("ACC I2C tx tmo\n");
			return;
		}
	}

	while (ACCELEROMETER_CTL1 & UCTXSTP)
		nop();

	/* the rest of TX will be handled by the ISR */
}

void mw_acc_i2c_read_single(const uint8_t RegisterAddress, const uint8_t *pData)
{
	if ( pData == 0 )
		return;
  
	/* wait for bus to be free */
	while (UCB1STAT & UCBBUSY)
		nop();
  
	AccelerometerBusy = 1;
	LengthCount = 1;
	Index = 0;
	pAccelerometerData = (uint8_t *)pData;

	/* transmit address */
	ACCELEROMETER_IFG = 0;
	ACCELEROMETER_CTL1 |= UCTR + UCTXSTT;
	while (!(ACCELEROMETER_IFG & UCTXIFG))
		nop();
  
	/* write register address */
	ACCELEROMETER_IFG = 0;
	ACCELEROMETER_TXBUF = RegisterAddress;
	while (!(ACCELEROMETER_IFG & UCTXIFG))
		nop();

	/* send a repeated start (same slave address now it is a read command) 
	 * read possible extra character from rxbuffer
	 */
	ACCELEROMETER_RXBUF;
	ACCELEROMETER_IFG = 0;
	ACCELEROMETER_IE |= UCRXIE;
	ACCELEROMETER_CTL1 &= ~UCTR;
	/* for a read of a single byte the stop must be sent while the byte is being 
	 * received. If this is interrupted an extra byte may be read.
	 * however, it will be discarded during the next read
	 */
	if (LengthCount == 1) {
		/* errata usci30: prevent interruption of sending stop 
		 * so that only one byte is read
		 * this requires 62 us @ 320 kHz, 51 @ 400 kHz
		 */
		__disable_interrupt();

		ACCELEROMETER_CTL1 |= UCTXSTT;
  
		while(ACCELEROMETER_CTL1 & UCTXSTT)
			nop();

		ACCELEROMETER_CTL1 |= UCTXSTP;

		__enable_interrupt();
	} else {
		ACCELEROMETER_CTL1 |= UCTXSTT;
	}
  
	/* wait until all data has been received and the stop bit has been sent */
	while (AccelerometerBusy)
		nop();
	while (ACCELEROMETER_CTL1 & UCTXSTP)
		nop();
	Index = 0;
	pAccelerometerData = 0;
}

/* errata usci30: only perform single reads 
 * second solution: use DMA
 */
void mw_acc_i2c_read(const uint8_t RegisterAddress, uint8_t *pData, const uint8_t Length)
{
	int i;

	for ( i = 0; i < Length; i++ ) {
		mw_acc_i2c_read_single(RegisterAddress + i, (pData + i));
	}
}


void mw_acc_init(void)
{
	uint8_t WriteRegisterData;
	uint8_t pReadRegisterData[4];
#if defined MW_DEVBOARD_V2
	char tstr[16];
#endif

	// it takes at least 20ms to power up
	ENABLE_ACCELEROMETER_POWER();
	__delay_cycles(320000);

	mw_acc_init_i2c();

	/*
	 * make sure part is in standby mode because some registers can only
	 * be changed when the part is not active.
	 */
	WriteRegisterData = PC1_STANDBY_MODE;
	mw_acc_i2c_write(KIONIX_CTRL_REG1, &WriteRegisterData, 1);

	/* enable face-up and face-down detection */
	WriteRegisterData = TILT_FDM | TILT_FUM;
	mw_acc_i2c_write(KIONIX_CTRL_REG2, &WriteRegisterData, 1);
    
	/* 
	 * the interrupt from the accelerometer can be used to get periodic data
	 * the real time clock can also be used
	 */
  
	/* change to output data rate to 25 Hz */
	WriteRegisterData = WUF_ODR_25HZ | TAP_ODR_400HZ;
	mw_acc_i2c_write(KIONIX_CTRL_REG3, &WriteRegisterData, 1);
  
	/* enable interrupt and make it active high */
	WriteRegisterData = IEN | IEA;
	mw_acc_i2c_write(KIONIX_INT_CTRL_REG1, &WriteRegisterData, 1);
  
	/* enable motion detection interrupt for all three axis */
	WriteRegisterData = XBW | YBW | ZBW;
	mw_acc_i2c_write(KIONIX_INT_CTRL_REG2, &WriteRegisterData, 1);

	/* enable tap interrupt for Z-axis */
	WriteRegisterData = TFDM;
	mw_acc_i2c_write(KIONIX_INT_CTRL_REG3, &WriteRegisterData, 1);
  
	/* set TDT_TIMER to 0.2 secs*/
	WriteRegisterData = 0x50;
	mw_acc_i2c_write(KIONIX_TDT_TIMER, &WriteRegisterData, 1);
  
	/* set tap low and high thresholds (default: 26 and 182) */
	WriteRegisterData = 40; //78;
	mw_acc_i2c_write(KIONIX_TDT_L_THRESH, &WriteRegisterData, 1);
	WriteRegisterData = 128;
	mw_acc_i2c_write(KIONIX_TDT_H_THRESH, &WriteRegisterData, 1);
    
	/* set WUF_TIMER counter */
	WriteRegisterData = 10;
	mw_acc_i2c_write(KIONIX_WUF_TIMER, &WriteRegisterData, 1);
    
	/* this causes data to always be sent */
	// WriteRegisterData = 0x00;
	WriteRegisterData = 0x01 /*0x08*/;
	mw_acc_i2c_write(KIONIX_WUF_THRESH, &WriteRegisterData, 1);
     
	/* single byte read test */
	mw_acc_i2c_read(KIONIX_DCST_RESP, pReadRegisterData, 1);
#if defined MW_DEVBOARD_V2
	debug_uart_tx("acc DCST 0x");
	hexnum2str(pReadRegisterData[0],tstr,2);
	debug_uart_tx(tstr);
	debug_uart_tx("\n");
#endif
  
	/* multiple byte read test */
	mw_acc_i2c_read(KIONIX_WHO_AM_I, pReadRegisterData, 2);
#if defined MW_DEVBOARD_V2
	debug_uart_tx("acc is 0x");
	hexnum2str(pReadRegisterData[0],tstr,2);
	debug_uart_tx(tstr);
	debug_uart_tx(" 0x");
	hexnum2str(pReadRegisterData[1],tstr,2);
	debug_uart_tx(tstr);
	debug_uart_tx("\n");
#endif

	/* 
	 * KIONIX_CTRL_REG3 and DATA_CTRL_REG can remain at their default values 
	 *
	 * 50 Hz
	 */
#if 0  
	/* KTXF9 300 uA; KTXI9 165 uA */
	WriteRegisterData = PC1_OPERATING_MODE | TAP_ENABLE_TDTE;
  
	/* 180 uA; KTXI9 115 uA */
	WriteRegisterData = PC1_OPERATING_MODE | RESOLUTION_8BIT | WUF_ENABLE;

	/* 180 uA; KTXI9 8.7 uA */
	WriteRegisterData = PC1_OPERATING_MODE | TILT_ENABLE_TPE;

	/* 720 uA; KTXI9 330 uA */  
	WriteRegisterData = PC1_OPERATING_MODE | RESOLUTION_12BIT | WUF_ENABLE;
#endif
  
	/* setup the default for the AccelerometerEnable command */
#if 0
	OperatingModeRegister = PC1_OPERATING_MODE | RESOLUTION_12BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE; // | WUF_ENABLE;
	InterruptControl = INTERRUPT_CONTROL_DISABLE_INTERRUPT;
	SidControl = SID_CONTROL_SEND_DATA;
	SidAddr = KIONIX_XOUT_L;
	SidLength = XYZ_DATA_LENGTH;  

	AccelState = ACCEL_STATE_INIT;
#endif
}

void mw_acc_enable(void)
{
	uint8_t sdata;

	mw_acc_init();

	sdata = PC1_OPERATING_MODE | RESOLUTION_12BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE | WUF_ENABLE;
	//sdata = PC1_OPERATING_MODE | RESOLUTION_8BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE; // | WUF_ENABLE;
	mw_acc_i2c_write(KIONIX_CTRL_REG1, &sdata, 1);
  
	ACCELEROMETER_INT_ENABLE();
	mw_acc_i2c_read(KIONIX_INT_REL, &sdata, 1);
	AccelState = ACCEL_STATE_ENABLED;
}

void mw_acc_disable(void)
{
	uint8_t sdata;

	if (AccelState == ACCEL_STATE_ENABLED) {
		sdata = PC1_STANDBY_MODE;
		mw_acc_i2c_write(KIONIX_CTRL_REG1, &sdata, 1);

		ACCELEROMETER_INT_DISABLE();
		mw_acc_disable_i2c();
		/// DISABLE_ACCELEROMETER_POWER();
		AccelState = ACCEL_STATE_DISABLED;
	}
}

void mw_acc_read(int16_t *x, int16_t *y, int16_t *z)
{
	uint8_t rdata[6];

	if (AccelState == ACCEL_STATE_ENABLED) {
		mw_acc_i2c_read(KIONIX_XOUT_L, rdata, 6);

		*x = rdata[0] | (rdata[1] << 8);
		*y = rdata[2] | (rdata[3] << 8);
		*z = rdata[4] | (rdata[5] << 8);
	} else {
		*x = 0;
		*y = 0;
		*z = 0;
	}
}

void mw_acc_handle_irq(void)
{
	uint8_t sdata, srcreg1, srcreg2;
#if defined MW_DEVBOARD_V2
	char tstr[16];
#endif

	if (AccelState != ACCEL_STATE_ENABLED)
		return;

	mw_acc_i2c_read(KIONIX_INT_SRC_REG1, &srcreg1, 1);
#if defined MW_DEVBOARD_V2
	debug_uart_tx("accsrc1: 0x");
	hexnum2str(srcreg1,tstr,2);
	debug_uart_tx(tstr);
	debug_uart_tx("\n");
#endif
	mw_acc_i2c_read(KIONIX_INT_SRC_REG2, &srcreg2, 1);
#if defined MW_DEVBOARD_V2
	debug_uart_tx("accsrc2: 0x");
	hexnum2str(srcreg2,tstr,2);
	debug_uart_tx(tstr);
	debug_uart_tx("\n");
	debug_uart_tx(tstr);
#endif
	if (srcreg1 & INT_TAP_SINGLE) {
	};
	if (srcreg1 & INT_TAP_DOUBLE) {
	};
	if (srcreg2 & INT_WUFS) {
		int16_t x, y, z;
		mw_acc_read(&x, &y, &z);
		oswald_handle_accel_event((int8_t)(x / (32768 / 255)), (int8_t)(y / (32768 / 255)), (int8_t)(z / (32768 / 255)));
	}

	mw_acc_i2c_read(KIONIX_INT_REL, &sdata, 1);
}