/* --COPYRIGHT--,BSD
 * Copyright (c) 2013, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/
 
//*****************************************************************************
//
//! main.c
//
//! 430BOOST-SHARP96 Capacitive Touch Slider Example Project
//! This project demonstrates the use of the two capacitive touch
//! slider sensors on the 430BOOST-SHARP96 booster pack.
//
//! Capacitive Touch Slider Implementation:
//!  - Two vertical sliders
//!  - Three elements each
//!  - End element is split between the top and the bottom on each slider,
//!    enabling four electrodes on the PCB to be driven with three pins
//!    and 3 software elements.  As such, each slider actually looks like
//!    a wheel sensor to software (since the ends are electrically connected,
//!    they appear as one element).  The MSP430 Capacitive Touch Library is
//!    used to measure the electrodes and calculate a wheel position, and the
//!    Sliders_update function in this file is used to derive a slider position
//!    from that.
//
//! Build Requirements:
//!  - GCC Extentions
//
//! \author Texas Instruments
//! \author MSP430 Strategic Applications
//
//! \version 1.0 - W. Schnoor, 1/7/14: Initial Release
//
//*****************************************************************************

//
// Include Standard Libraries
//
#include <msp430.h>
#include <stdbool.h>

//
// Include the MSP430 Capacitive Touch Library
//
#include "CTS_Layer.h"

//
// Include Touch Pro GUI Tools
//
#include "TouchProGUI.h"

//
//! \def TOGGLE_LEDR is a macro for toggling the MSP-EXP430G2
//! LaunchPad red LED (LED1)
//! \def TOGGLE_LEDG is a macro for toggling the MSP-EXP430G2
//! LaunchPad green LED (LED2)
//
#define LP_LEDPORT		(P1OUT)
#define LP_LEDPORTDIR 	(P1DIR)
#define LP_LEDR			(BIT0)
#define LP_LEDG			(BIT6)
#define TOGGLE_LEDR		(P1OUT ^= LP_LEDR)
#define TOGGLE_LEDG		(P1OUT ^= LP_LEDG)

//
//! \def RESPONSE_TIME_MILLISECONDS specifies the response time of the
//! panel in ms; i.e. how often the LCD image is re-drawn with the
//! updated slider position.
//
//! \def RESPONSE_TIME_CYCLES specifies the response time of the
//! panel in VLO clock cycles.
//
#define RESPONSE_TIME_MILLISECONDS	50
#define RESPONSE_TIME_CYCLES	(RESPONSE_TIME_MILLISECONDS * 12)

//
//! \def LEFT_SLIDER specifies the index of the left slider in the
//! slider pointer array and the position of the slider output in the
//! measurement array (g_ui16MeasurementArray).
//
//! \def RIGHT_SLIDER specifies the index of the right slider in the
//! slider pointer array and the position of the slider output in the
//! measurement array (g_ui16MeasurementArray).
//
//! \def NUM_OF_SLIDERS specifies the number of sliders to process.
//
#define LEFT_SLIDER		(0)
#define RIGHT_SLIDER	(1)
#define NUM_OF_SLIDERS	(2)

//
//! \var g_pSlider contains pointers to the sliders to process.
//
//! \var g_ui16LowerTrim contains the lower trim point for the two sliders.
//
//! \var g_ui16UpperTrim contains the upper trim point for the two sliders.
//
const struct Sensor* g_pSlider[NUM_OF_SLIDERS] = { &leftSlider, &rightSlider };
const uint16_t g_ui16LowerTrim[NUM_OF_SLIDERS] = { 2, 2 };
const uint16_t g_ui16UpperTrim[NUM_OF_SLIDERS] = { 62, 62 };

//
// \var g_bUpdatePanel indicates to the background loop that the
// panel should be scanned and the g_ui16MeasurementArray updated.
// TouchProGUI is also sent an update at this time.
//
bool g_bUpdatePanel = false;

//
//! \var g_ui16MeasurementArray contains the outputs of the capacitive touch
//! algorithms.  There are two sliders with three elements each for a total of
//! 6 raw channels and 2 position channels (8 total channels).
//
//! The array is used as follows:
//!  -[0] = Left Slider Position
//!  -[1] = Right Slider Position
//!  -[2] = Left Slider Element 0 Delta
//!  -[3] = Left Slider Element 1 Delta
//!  -[4] = Left Slider Element 2 Delta
//!  -[5] = Right Slider Element 0 Delta
//!  -[6] = Right Slider Element 1 Delta
//!  -[7] = Right Slider Element 2 Delta
//
uint16_t g_ui16MeasurementArray[8] = {0};

//
// Local Function Prototypes
//
bool Sliders_update(void);
void errorTrap(void);

//*****************************************************************************
//
//! main()
//! This is the point of entry into the program,
//! and houses the background loop.
//
//*****************************************************************************
void
main(void)
{
	bool bPanelTouched;

	//
	// Hold the watchdog timer.
	// NOTE: The watchdog timer is utilized in this program by
	// the capacitive touch library (in interval mode).
	// As such, it is unavailable to the application to be used in
	// watchdog mode (though it can be shared with the touch library
	// via time multiplexing.
	//
	WDTCTL = WDTPW | WDTHOLD;

	//
	// Configure Port 1 Pins 0 and 6 to be Output Low.
	// These pins are the LED pins on the MSP-EXP430G2 LaunchPad.
	//
	LP_LEDPORT = 0x00;
	LP_LEDPORTDIR = LP_LEDR | LP_LEDG;

	//
	// Configure the G2xx Basic Clock Module (BCM+)
	//  - MCLK  = 12MHz
	//  - SMCLK = 6MHz
	//  - ACLK  = ~12kHz
	//
	if ((CALBC1_1MHZ == 0xFF) || (CALBC1_8MHZ==0xFF))
	{
		//
		// If DCO calibrations are erased, trap CPU
		//
		errorTrap();
	}
	DCOCTL = 0x00;
	BCSCTL1 = CALBC1_12MHZ;
	DCOCTL =  CALDCO_12MHZ;
	BCSCTL2 = DIVS_1;
	BCSCTL3 |= LFXT1S_2;

	//
	// Initialize the TouchPro GUI USCI UART interface.
	//
	TouchProGUI_init();

	//
	// Initialize the baseline measurement for each element
	// in the left slider and the right slider.
	//
	TI_CAPT_Update_Tracking_Rate(TRIDOI_MED | TRADOI_SLOW);
	TI_CAPT_Init_Baseline(&leftSlider);
	TI_CAPT_Init_Baseline(&rightSlider);

	//
	// Perform baseline averaging to ensure the starting
	// baseline value is accurate.
	//
	TI_CAPT_Update_Baseline(&leftSlider, 5);
	TI_CAPT_Update_Baseline(&rightSlider, 5);

	//
	// Start the periodic interval wakeup timer (Timer A1 is used in this example).
	//
	TA1CCTL0 = CCIE;
	TA1CCR0 = RESPONSE_TIME_CYCLES;
	TA1CTL = TASSEL_1 | MC_1;

	//
	// Enable CPU interrupts
	//
	__enable_interrupt();

	//
	// Background Loop
	//
	while (1)
	{
		if (g_bUpdatePanel == true)
		{
			g_bUpdatePanel = false;
			TOGGLE_LEDR;
			bPanelTouched = Sliders_update();
			TouchProGUI_sendData(g_ui16MeasurementArray, 8);
			if (bPanelTouched == true)
			{
				LP_LEDPORT |= LP_LEDG;
			}
			else
			{
				LP_LEDPORT &= ~LP_LEDG;
			}

		}
		LPM0;
	}
}

//*****************************************************************************
//
//! Sliders_update()
//! Measure the left and right slider sensors, storing new measurement info in
//! the global array g_ui16MeasurementArray.  This function calls into the
//! capacitive touch library to get measurents back, than handles the wheel
//! unwrapping, basic edge trim, and a single-level averaging filter.
//
//! /param none.
//! /return true if either of the two sliders are being touched, else false.
//
//*****************************************************************************
bool
Sliders_update(void)
{
	uint16_t ui16newPosition[NUM_OF_SLIDERS];
	uint8_t ui8currSlider;
	uint16_t ui16ZeroPoint;

	//
	// Get Delta Measurement.  This data will be used to display in TouchPro GUI.
	//
	TI_CAPT_Custom(&leftSlider, &g_ui16MeasurementArray[2]);
	TI_CAPT_Custom(&rightSlider, &g_ui16MeasurementArray[5]);

	//
	// Get Current Slider Positions.
	// The Capacitive Touch Library wheel abstraction is used to handle
	// the electrode geometry, and is unwrapped into a slider in the
	// processing code below.
	//
	for (ui8currSlider=0; ui8currSlider<NUM_OF_SLIDERS; ui8currSlider++)
	{
		ui16newPosition[ui8currSlider] = TI_CAPT_Wheel(g_pSlider[ui8currSlider]);

		//
		// If new position is valid, unwrap the wheel value back to a slider value
		//
		if (ui16newPosition[ui8currSlider] != ILLEGAL_SLIDER_WHEEL_POSITION)
		{
			//
			// Calculate Zero Point (bottom/top division point)
			//
			ui16ZeroPoint = (g_pSlider[ui8currSlider]->points / g_pSlider[ui8currSlider]->numElements) >> 1;

			//
			// If below zero point, the user is at the top of the slider
			//
			if(ui16newPosition[ui8currSlider] < ui16ZeroPoint)
            {
            	ui16newPosition[ui8currSlider] += g_pSlider[ui8currSlider]->points - ui16ZeroPoint;
            }

			//
			// Else if above the zero point, the user is at the bottom or middle of the slider
			//
            else
            {
            	ui16newPosition[ui8currSlider] -= ui16ZeroPoint;
            }

			//
			// Trim edges to prevent un-intentional wraparound
			//
			if (ui16newPosition[ui8currSlider] > g_ui16LowerTrim[ui8currSlider])
			{
				ui16newPosition[ui8currSlider] -= g_ui16LowerTrim[ui8currSlider];
			}
			else
			{
				ui16newPosition[ui8currSlider] = 0;
			}

			ui16newPosition[ui8currSlider] = (ui16newPosition[ui8currSlider] * g_pSlider[ui8currSlider]->points);
			ui16newPosition[ui8currSlider] /= g_ui16UpperTrim[ui8currSlider] - g_ui16LowerTrim[ui8currSlider];

			if (ui16newPosition[ui8currSlider] > g_pSlider[ui8currSlider]->points)
			{
				ui16newPosition[ui8currSlider] = g_pSlider[ui8currSlider]->points;
			}

			//
			// Average in new positions to the measurement array ([0] = Left Slider Position, [1] = Right Slider Position)
			//
			g_ui16MeasurementArray[ui8currSlider] += ui16newPosition[ui8currSlider];
			g_ui16MeasurementArray[ui8currSlider] >>= 1;

			//
			// Panel is being touched, return true
			//
			return true;
		}
	}

	//
	// Panel not touched, return false
	//
	return false;
}

//*****************************************************************************
//
//! errorTrap() will hold the CPU and light both LEDs if called.
//
//! \param none.
//! \return none.
//
//*****************************************************************************
void
errorTrap(void)
{
	LP_LEDPORT |= LP_LEDR | LP_LEDG;
	while(1)
	{
		__no_operation();
	};
}

//*****************************************************************************
//
//! IntervalWakeup provides periodic wake up from LPM3 to measure the
//! capacitive touch sliders.
//
//*****************************************************************************
#pragma vector=TIMER1_A0_VECTOR
__interrupt void IntervalWakeupISR (void)
{
	//
	// Set flag to update panel, then wake from LPM3 sleep
	//
	g_bUpdatePanel = true;
	LPM3_EXIT;
}

//*****************************************************************************
//
//! ISR Trap
//
//*****************************************************************************
#pragma vector= PORT2_VECTOR,PORT1_VECTOR,ADC10_VECTOR,NMI_VECTOR,          \
                USCIAB0RX_VECTOR,TIMER0_A1_VECTOR, COMPARATORA_VECTOR,      \
                TIMER1_A1_VECTOR,TIMER0_A0_VECTOR
__interrupt void ISR_trap(void)
{
	//
	// Trap the CPU and flag an error
	//
	errorTrap();
}
