/*******************************************************************************
 *
 *  CapTouchBoosterPack_UserExperience.c
 *     - MSP430 firmware application to demonstrate the capacitive touch 
 *       capability of the MSP430G2452 interfacing with the LaunchPad CapTouch
 *       BoosterPack.
 *
 *  Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 
 * 
 *  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.
 *
 *
 ******************************************************************************/
 
/******************************************************************************
 *         MSP430G2-LaunchPad CapTouch BoosterPack User Experience
 * 
 * This application operates on the LaunchPad platform using the MSP430G2452
 * device and the CapTouch BoosterPack plugin board. The capacitive touch and 
 * proximity sensing are enabled by the pin oscillator feature new to the 
 * MSP430G2xx2 family devices. The User Experience application also utilizes 
 * the cap touch library to realize & measure the capacitive touch and proximity
 * sensors. The cap touch library also provides layers of abstractions to 
 * generate higher logical outputs such as logical touches, geometry (in this 
 * hardware, a four-button wheel), and even gestures. 
 * 
 * The User Experience application starts up and remains in 'sleep' mode, 
 * sampling the proximity sensor every ~8.3ms [VLO/100=12kHz/100=120Hz]. Upon 
 * registering a valid proximity event [hand/finger/object hovering ~3-5cm from
 * the BoosterPack], the application wakes up to operate in the 'active' mode.
 * 
 * In active mode, the application samples and registers individual finger touches 
 * on the 16-position wheel or the center button as well as simple gestures 
 * [Clockwise & Counter-clockwise] while the finger moves along and remains on 
 * the wheel.      
 * 
 * a 9600 baud UART link is also implemented using SW TimerA to provide 
 * application and cap touch data back to the PC via the UART-USB back channel.
 * The application sends UART data upon events such as wake up, sleep, touch,
 * or gesture.  
 *   
 * D. Dang
 * Texas Instruments, Inc.
 * Ver 0.90 Feb 2011
 ******************************************************************************/ 



#include "CTS_Layer.h"
#include "uart.h"


#define WAKE_UP_UART_CODE       0xBE
#define WAKE_UP_UART_CODE2      0xEF
#define SLEEP_MODE_UART_CODE    0xDE
#define SLEEP_MODE_UART_CODE2   0xAD
#define MIDDLE_BUTTON_CODE      0x80
#define INVALID_GESTURE         0xFD
#define GESTURE_START           0xFC
#define GESTURE_STOP            0xFB
#define COUNTER_CLOCKWISE       1
#define CLOCKWISE               2
#define GESTURE_POSITION_OFFSET 0x20
#define WHEEL_POSITION_OFFSET   0x30

#define WHEEL_TOUCH_DELAY		    12			    //Delay between re-sendings of touches
#define MAX_IDLE_TIME           200
#define PROXIMITY_THRESHOLD     60

unsigned int wheel_position=ILLEGAL_SLIDER_WHEEL_POSITION, last_wheel_position=ILLEGAL_SLIDER_WHEEL_POSITION;
unsigned int deltaCnts[1];
unsigned int prox_raw_Cnts;




/*----------------- LED definition---------------------------------------------
 * There are 8 LEDs to represent different positions around the wheel. They are 
 * controlled by 5 pins of Port 1 using a muxing scheme. The LEDs are divided 
 * vertically into two groups of 4, in which each LED is paired up [muxed] with
 * the LED mirrored on the other side of the imaginary center vertical line via 
 * the use of pin P1.3 and one specific port pin. 
 * Specifically, the pairs are LEDs [0,7], [1,6], [2,5], [3,4], as shown in the 
 * diagram below.
 *     LED                        Position (degrees, clockwise)
 * --RIGHT SIDE--
 *      0       BIT4,!BIT3                  45
 *      1       BIT5,!BIT3                  80
 *      2       BIT6,!BIT3                 100
 *      3       BIT7,!BIT3                 135 *
 * 
 * --LEFT SIDE--
 *      4       BIT3,(BIT4,5,6)             225
 *      5       BIT3,(BIT4,5,7)             260
 *      6       BIT3,(BIT4,6,7)             280
 *      7       BIT3,(BIT5,6,7)             315
 *----------------------------------------------------------------------------*/
#define MASK7                   BIT4
#define MASK6                   BIT5
#define MASK5                   BIT6
#define MASK4                   BIT7

#define MASK3                   (BIT3+BIT4+BIT5+BIT6)
#define MASK2                   (BIT3+BIT4+BIT5+BIT7)
#define MASK1                   (BIT3+BIT4+BIT6+BIT7)
#define MASK0                   (BIT3+BIT5+BIT6+BIT7)

const unsigned char LedWheelPosition[16] = 
                                {
                                  MASK0, MASK0, MASK0 & MASK1, MASK1,
                                  MASK1 & MASK2, MASK2, MASK2 & MASK3, MASK3,
                                  MASK4, MASK4, MASK4 | MASK5, MASK5, 
                                  MASK5 | MASK6,  MASK6, MASK6 | MASK7, MASK7 
                                };
const unsigned char startSequence[8] = 
                                {
								    MASK0,
								    MASK1,
								    MASK2,
								    MASK3,
								    MASK4,
								    MASK5,
								    MASK6,
								    MASK7
								   };
/*----------------- LED definition------------------------------*/
 

void InitLaunchPadCore(void)

{
  BCSCTL1 |= DIVA_0;                    // ACLK/(0:1,1:2,2:4,3:8)
  BCSCTL3 |= LFXT1S_2;                  // LFXT1 = VLO  
  
  // Port init
  P1OUT &= ~(BIT3+BIT4+BIT5+BIT6+BIT7+BIT0);
  P1DIR |= BIT3+BIT4+BIT5+BIT6+BIT7+BIT0;
  P2SEL = 0x00;                         // No XTAL
  P2DIR |= (BIT0+BIT4+BIT2+BIT3+BIT1+BIT5);
  P2OUT &= ~(BIT0+BIT4+BIT2+BIT3+BIT1+BIT5);
}

void SendByte(unsigned char touch)
{
  TimerA_UART_init();
  TimerA_UART_tx(touch);
  TimerA_UART_shutdown();  
}
 
/* ----------------CapTouchIdleMode-----------------------------------------
 * Device stays in LPM3 'sleep' mode, only Proximity Sensor is used to detect 
 * any movement triggering device wake up                                  
 * ------------------------------------------------------------------------*/ 
void CapTouchIdleMode(void)
{ 
  /* Send status via UART: 'sleep' = [0xDE, 0xAD] */  
  SendByte(SLEEP_MODE_UART_CODE);
  SendByte(SLEEP_MODE_UART_CODE2);
  
  /* Set DCO to 1MHz */
  /* Set SMCLK to 1MHz / 8 = 125kHz */
  BCSCTL1 = CALBC1_1MHZ;                
  DCOCTL = CALDCO_1MHZ;
  BCSCTL2 |= DIVS_3;  
  
  P1OUT |= BIT0;                            // Turn on center LED    
  deltaCnts[0] = 0;
  
  /* Sleeping in LPM3 with ACLK/100 = 12Khz/100 = 120Hz wake up interval */
  /* Measure proximity sensor count upon wake up */
  /* Wake up if proximity deltaCnts > THRESHOLD */  
  do
  {
    TACCR0 = 100;				                       							                                
    TACTL = TASSEL_1 + MC_1;                   
    TACCTL0 |= CCIE;    
    __bis_SR_register(LPM3_bits+GIE);
    TACCTL0 &= ~CCIE;                             
    TI_CAPT_Custom(&proximity_sensor,deltaCnts);
  }
  while (deltaCnts[0] <= PROXIMITY_THRESHOLD);
  
  P1OUT &= ~BIT0;                           // Turn off center LED
}
 
/* ----------------MeasureCapBaseLine--------------------------------------
 * Re-measure the baseline capacitance of the wheel elements & the center  
 * button. To be called after each wake up event.                          
 * ------------------------------------------------------------------------*/
void MeasureCapBaseLine(void)
{
  P1OUT = BIT0;
  /* Set DCO to 8MHz */
  /* SMCLK = 8MHz/8 = 1MHz */
  BCSCTL1 = CALBC1_8MHZ;     
  DCOCTL = CALDCO_8MHZ;
  BCSCTL2 |= DIVS_3;
  
  TI_CAPT_Init_Baseline(&wheel);
  TI_CAPT_Update_Baseline(&wheel,2);
  TI_CAPT_Init_Baseline(&middle_button);
  TI_CAPT_Update_Baseline(&middle_button,2);  
}

/* ----------------LedStartUpSequence--------------------------------------
 * Display an LED lighting sequence to indicate the wake up event
 * ------------------------------------------------------------------------*/
void LedStartUpSequence(void)
{
  unsigned char i;
  TACCTL0 = CCIE;                           // CCR0 interrupt enabled
  TACTL |= TACLR;
  TACCR0 = TAR + 500;                       // 50ms                             
  TACTL = TASSEL_1 + MC_1;                  // ACLK, upmode
  
  /* Slow clockwise sequence */
  for(i=0; i<8; i++)
  {
      P1OUT = startSequence[i];
      __bis_SR_register(LPM3_bits+GIE);
     
      __delay_cycles(1000000);
      TACCR0 = TAR + 500;   // 50ms                             
  }

  P1OUT = BIT0;
  /* Fast counter-clockwise sequence */
  while(i)
  {
      i--;
      P1OUT = startSequence[i];
      __bis_SR_register(LPM3_bits+GIE);
      TACCR0 = TAR + 500;   // 50ms                             
  }
  TACCTL0 &= ~CCIE;                         // CCR0 interrupt disabled
  P1OUT = 0;                                // Turn off all LEDs
}

/* ----------------GetGesture----------------------------------------------
 * Determine immediate gesture based on current & previous wheel position
 * ------------------------------------------------------------------------*/
unsigned char GetGesture(unsigned char wheel_position)
{
 unsigned char gesture = INVALID_GESTURE, direction, ccw_check, cw_check; 
// ******************************************************************************
// gesturing
// determine if a direction/swipe is occuring
// the difference between the initial position and
// the current wheel position should not exceed 8
// 0-1-2-3-4-5-6-7-8-9-A-B-C-D-E-F-0...
//
// E-F-0-1-2:  cw, 4
// 2-1-0-F-E: ccw, 4
// A-B-C-D-E-F

  //if(initial_wheel_position == INVALID_WHEEL_POSITION)
  //{
    //gesture = 0;
    //initial_wheel_position = wheel_position;
  //}
  //else

  if(last_wheel_position != ILLEGAL_SLIDER_WHEEL_POSITION) 
  {
    if(last_wheel_position  > wheel_position)
    {
      // E-D-C-B-A:  ccw, 4
      // counter clockwise: 0 < (init_wheel_position - wheel_position) < 8
      //                    gesture = init_wheel_position - wheel_position
      //
      // E-F-0-1-2:  cw, 4
      // clockwise:        0 < (init_wheel_position+wheel_position)-16 <8
      //                    
      ccw_check = last_wheel_position  - wheel_position;
      if(ccw_check < 8)
      {
        gesture = ccw_check;
        direction = COUNTER_CLOCKWISE;
      }
      else
      {
        // E-F-0-1-2:  cw, 4
        // 16 - 14 + 2 = 4
        cw_check = 16 - last_wheel_position  + wheel_position ;
        if(cw_check < 8)
        {
            gesture = cw_check;
            direction = CLOCKWISE;
        }
      }
    }
    else 
    {
      // initial_wheel_position <= wheel_position
      //
      // 2-1-0-F-E: ccw, 4
      // counter clockwise: 
      //                    0 < (init_wheel_position+wheel_position)-16 <8
      //                    gesture = init_wheel_position - wheel_position
      //
      // 0-1-2-3-4:  cw, 4
      // clockwise:        0 < (wheel_position - init_wheel_position) < 8
      //    
      cw_check = wheel_position - last_wheel_position ;
      if(cw_check < 8)
      {
        gesture = cw_check;
        direction = CLOCKWISE;
      }
      else
      {
        // 2-1-0-F-E: ccw, 4
        // 16 + 2 - 14 = 4
        ccw_check = 16 + last_wheel_position  - wheel_position ;
        if(ccw_check < 8)
        {
          gesture = ccw_check;
          direction = COUNTER_CLOCKWISE;
        }
      }
    }
  } 
  if (gesture == INVALID_GESTURE)
  	return gesture;
  if (direction == COUNTER_CLOCKWISE)
    return (gesture + 16);
  else
    return gesture;
}

/* ----------------CapTouchActiveMode----------------------------------------------
 * Determine immediate gesture based on current & previous wheel position
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * -------------------------------------------------------------------------------*/
void CapTouchActiveMode()
{
  unsigned char idleCounter, activeCounter;
  unsigned char gesture, gestureDetected; 
  unsigned char centerButtonTouched = 0;
  unsigned int wheelTouchCounter = WHEEL_TOUCH_DELAY - 1;
  
     
  gesture = INVALID_GESTURE;            // Wipes out gesture history
  
  /* Send status via UART: 'wake up' = [0xBE, 0xEF] */  
  SendByte(WAKE_UP_UART_CODE);
  SendByte(WAKE_UP_UART_CODE2);

  idleCounter = 0;      
  activeCounter = 0;
  gestureDetected = 0;
  
  while (idleCounter++ < MAX_IDLE_TIME)
  {  
	  /* Set DCO to 8MHz */
	  /* SMCLK = 8MHz/8 = 1MHz */
    BCSCTL1 = CALBC1_8MHZ;     
    DCOCTL = CALDCO_8MHZ;
    BCSCTL2 |= DIVS_3;  
    TACCTL0 &= ~CCIE;   
    
    wheel_position = ILLEGAL_SLIDER_WHEEL_POSITION;       
    wheel_position = TI_CAPT_Wheel(&wheel);
    
    /* Process wheel touch/position/gesture  if a wheel touch is registered*/
    /* Wheel processing has higher priority than center button*/
    
    if(wheel_position != ILLEGAL_SLIDER_WHEEL_POSITION)
    {
      centerButtonTouched = 0;
      
      /* Adjust wheel position based: rotate CCW by 2 positions */
      if (wheel_position < 0x08)
      {
         wheel_position += 0x40 - 0x08;
      }
      else
      {
         wheel_position -= 0x08;
            /* Adjust wheel position based: rotate CCW by 2 positions */
      }
      
      wheel_position = wheel_position >>2;  // divide by four
               
      gesture = GetGesture(wheel_position);            
      
      /* Add hysteresis to reduce toggling between wheel positions if no gesture 
       * has been TRULY detected. */  
                   
      if ( (gestureDetected==0) && ((gesture<=1) || (gesture==0x11) || (gesture==0x10)))
      {
        if (last_wheel_position != ILLEGAL_SLIDER_WHEEL_POSITION)
            wheel_position = last_wheel_position;
        gesture = 0;
      }
      
      /* Turn on corresponding LED(s) */
      P1OUT = (P1OUT & BIT0) | LedWheelPosition[wheel_position];
      
     
      if ((gesture != 0) && (gesture != 16) && (gesture != INVALID_GESTURE))           
      { /* A gesture has been detected */ 
        
        if (gestureDetected ==0)
        { /* Starting of a new gesture sequence */
          gestureDetected = 1;
          
          /* Transmit gesture start status update & position via UART to PC */
          SendByte(GESTURE_START);
          SendByte(last_wheel_position + GESTURE_POSITION_OFFSET);
        } 
                 
        /* Transmit gesture & position via UART to PC */
        SendByte(gesture);
        SendByte(wheel_position + GESTURE_POSITION_OFFSET);
      }      
      else
        if (gestureDetected==0)
        { /* If no gesture was detected, this is constituted as a touch/tap */
          if (++wheelTouchCounter >= WHEEL_TOUCH_DELAY)
          {
          	/* Transmit wheel position [twice] via UART to PC */
          	wheelTouchCounter = 0;
          	SendByte(wheel_position + WHEEL_POSITION_OFFSET );
          	SendByte(wheel_position + WHEEL_POSITION_OFFSET );
          }
        }
       	else
          wheelTouchCounter = WHEEL_TOUCH_DELAY - 1;      	
        
      idleCounter = 0;                      // Reset idle counter
      activeCounter++;
      last_wheel_position = wheel_position;
    } 
    else   
    { /* no wheel position was detected */
       
      if(TI_CAPT_Button(&middle_button))
      { /* Middle button was touched */   
        if (centerButtonTouched==0)
        {
          /* Transmit center button code [twice] via UART to PC */
          SendByte(MIDDLE_BUTTON_CODE);          
          SendByte(MIDDLE_BUTTON_CODE);
                    
          centerButtonTouched = 1;
          
          P1OUT = (P1OUT&BIT0) ^ BIT0;      // Toggle Center LED
        }
        idleCounter = 0;
      }
      else    
      { /* No touch was registered at all [Not wheel or center button */
        centerButtonTouched = 0;
        P1OUT &= BIT0;       
        if ( (gesture == INVALID_GESTURE) || (gestureDetected ==0))
        { /* No gesture was registered previously */
          if (last_wheel_position  != ILLEGAL_SLIDER_WHEEL_POSITION)
            {
              /* Transmit last wheel position [twice] via UART to PC */
              SendByte(last_wheel_position  + WHEEL_POSITION_OFFSET );
              SendByte(last_wheel_position  + WHEEL_POSITION_OFFSET );
              wheelTouchCounter = WHEEL_TOUCH_DELAY - 1;
            }
        }
          
          if (gestureDetected == 1)
          { /* A gesture was registered previously */
            
            /* Transmit status update: stop gesture tracking [twice] via UART to PC */
            SendByte(GESTURE_STOP);
            SendByte(GESTURE_STOP);
          }
          
      }
      
      // Reset all touch conditions, turn off LEDs, 
      last_wheel_position= ILLEGAL_SLIDER_WHEEL_POSITION;      
      gesture = INVALID_GESTURE;
      gestureDetected = 0;

    } 
    
  /* ------------------------------------------------------------------------
   * Option:
   * Add delay/sleep cycle here to reduce active duty cycle. This lowers power
   * consumption but sacrifices wheel responsiveness. Additional timing 
   * refinement must be taken into consideration when interfacing with PC
   * applications GUI to retain proper communication protocol.
   * -----------------------------------------------------------------------*/
     
  } 
}


void main(void)
{   

  WDTCTL = WDTPW + WDTHOLD;             // Stop watchdog timer
  
  InitLaunchPadCore();
    
  
  /* Set DCO to 1MHz */
  /* Set SMCLK to 1MHz / 8 = 125kHz */
  BCSCTL1 = CALBC1_1MHZ;                
  DCOCTL = CALDCO_1MHZ;
  BCSCTL2 |= DIVS_3;
  /* Establish baseline for the proximity sensor */  
  TI_CAPT_Init_Baseline(&proximity_sensor);
  TI_CAPT_Update_Baseline(&proximity_sensor,5);
  
  while (1)
  {
    CapTouchIdleMode();  
  
    MeasureCapBaseLine();
    LedStartUpSequence();
    CapTouchActiveMode();
  }
}
