/******************************************************************************* * * 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(); } }