You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

553 lines
19 KiB

5 years ago
  1. /*******************************************************************************
  2. *
  3. * CapTouchBoosterPack_UserExperience.c
  4. * - MSP430 firmware application to demonstrate the capacitive touch
  5. * capability of the MSP430G2452 interfacing with the LaunchPad CapTouch
  6. * BoosterPack.
  7. *
  8. * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. *
  17. * Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in the
  19. * documentation and/or other materials provided with the
  20. * distribution.
  21. *
  22. * Neither the name of Texas Instruments Incorporated nor the names of
  23. * its contributors may be used to endorse or promote products derived
  24. * from this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  27. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  28. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  29. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  30. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  31. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  32. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  33. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  34. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  35. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  36. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  37. *
  38. *
  39. ******************************************************************************/
  40. /******************************************************************************
  41. * MSP430G2-LaunchPad CapTouch BoosterPack User Experience
  42. *
  43. * This application operates on the LaunchPad platform using the MSP430G2452
  44. * device and the CapTouch BoosterPack plugin board. The capacitive touch and
  45. * proximity sensing are enabled by the pin oscillator feature new to the
  46. * MSP430G2xx2 family devices. The User Experience application also utilizes
  47. * the cap touch library to realize & measure the capacitive touch and proximity
  48. * sensors. The cap touch library also provides layers of abstractions to
  49. * generate higher logical outputs such as logical touches, geometry (in this
  50. * hardware, a four-button wheel), and even gestures.
  51. *
  52. * The User Experience application starts up and remains in 'sleep' mode,
  53. * sampling the proximity sensor every ~8.3ms [VLO/100=12kHz/100=120Hz]. Upon
  54. * registering a valid proximity event [hand/finger/object hovering ~3-5cm from
  55. * the BoosterPack], the application wakes up to operate in the 'active' mode.
  56. *
  57. * In active mode, the application samples and registers individual finger touches
  58. * on the 16-position wheel or the center button as well as simple gestures
  59. * [Clockwise & Counter-clockwise] while the finger moves along and remains on
  60. * the wheel.
  61. *
  62. * a 9600 baud UART link is also implemented using SW TimerA to provide
  63. * application and cap touch data back to the PC via the UART-USB back channel.
  64. * The application sends UART data upon events such as wake up, sleep, touch,
  65. * or gesture.
  66. *
  67. * D. Dang
  68. * Texas Instruments, Inc.
  69. * Ver 0.90 Feb 2011
  70. ******************************************************************************/
  71. #include "CTS_Layer.h"
  72. #include "uart.h"
  73. #define WAKE_UP_UART_CODE 0xBE
  74. #define WAKE_UP_UART_CODE2 0xEF
  75. #define SLEEP_MODE_UART_CODE 0xDE
  76. #define SLEEP_MODE_UART_CODE2 0xAD
  77. #define MIDDLE_BUTTON_CODE 0x80
  78. #define INVALID_GESTURE 0xFD
  79. #define GESTURE_START 0xFC
  80. #define GESTURE_STOP 0xFB
  81. #define COUNTER_CLOCKWISE 1
  82. #define CLOCKWISE 2
  83. #define GESTURE_POSITION_OFFSET 0x20
  84. #define WHEEL_POSITION_OFFSET 0x30
  85. #define WHEEL_TOUCH_DELAY 12 //Delay between re-sendings of touches
  86. #define MAX_IDLE_TIME 200
  87. #define PROXIMITY_THRESHOLD 60
  88. unsigned int wheel_position=ILLEGAL_SLIDER_WHEEL_POSITION, last_wheel_position=ILLEGAL_SLIDER_WHEEL_POSITION;
  89. unsigned int deltaCnts[1];
  90. unsigned int prox_raw_Cnts;
  91. /*----------------- LED definition---------------------------------------------
  92. * There are 8 LEDs to represent different positions around the wheel. They are
  93. * controlled by 5 pins of Port 1 using a muxing scheme. The LEDs are divided
  94. * vertically into two groups of 4, in which each LED is paired up [muxed] with
  95. * the LED mirrored on the other side of the imaginary center vertical line via
  96. * the use of pin P1.3 and one specific port pin.
  97. * Specifically, the pairs are LEDs [0,7], [1,6], [2,5], [3,4], as shown in the
  98. * diagram below.
  99. * LED Position (degrees, clockwise)
  100. * --RIGHT SIDE--
  101. * 0 BIT4,!BIT3 45
  102. * 1 BIT5,!BIT3 80
  103. * 2 BIT6,!BIT3 100
  104. * 3 BIT7,!BIT3 135 *
  105. *
  106. * --LEFT SIDE--
  107. * 4 BIT3,(BIT4,5,6) 225
  108. * 5 BIT3,(BIT4,5,7) 260
  109. * 6 BIT3,(BIT4,6,7) 280
  110. * 7 BIT3,(BIT5,6,7) 315
  111. *----------------------------------------------------------------------------*/
  112. #define MASK7 BIT4
  113. #define MASK6 BIT5
  114. #define MASK5 BIT6
  115. #define MASK4 BIT7
  116. #define MASK3 (BIT3+BIT4+BIT5+BIT6)
  117. #define MASK2 (BIT3+BIT4+BIT5+BIT7)
  118. #define MASK1 (BIT3+BIT4+BIT6+BIT7)
  119. #define MASK0 (BIT3+BIT5+BIT6+BIT7)
  120. const unsigned char LedWheelPosition[16] =
  121. {
  122. MASK0, MASK0, MASK0 & MASK1, MASK1,
  123. MASK1 & MASK2, MASK2, MASK2 & MASK3, MASK3,
  124. MASK4, MASK4, MASK4 | MASK5, MASK5,
  125. MASK5 | MASK6, MASK6, MASK6 | MASK7, MASK7
  126. };
  127. const unsigned char startSequence[8] =
  128. {
  129. MASK0,
  130. MASK1,
  131. MASK2,
  132. MASK3,
  133. MASK4,
  134. MASK5,
  135. MASK6,
  136. MASK7
  137. };
  138. /*----------------- LED definition------------------------------*/
  139. void InitLaunchPadCore(void)
  140. {
  141. BCSCTL1 |= DIVA_0; // ACLK/(0:1,1:2,2:4,3:8)
  142. BCSCTL3 |= LFXT1S_2; // LFXT1 = VLO
  143. // Port init
  144. P1OUT &= ~(BIT3+BIT4+BIT5+BIT6+BIT7+BIT0);
  145. P1DIR |= BIT3+BIT4+BIT5+BIT6+BIT7+BIT0;
  146. P2SEL = 0x00; // No XTAL
  147. P2DIR |= (BIT0+BIT4+BIT2+BIT3+BIT1+BIT5);
  148. P2OUT &= ~(BIT0+BIT4+BIT2+BIT3+BIT1+BIT5);
  149. }
  150. void SendByte(unsigned char touch)
  151. {
  152. TimerA_UART_init();
  153. TimerA_UART_tx(touch);
  154. TimerA_UART_shutdown();
  155. }
  156. /* ----------------CapTouchIdleMode-----------------------------------------
  157. * Device stays in LPM3 'sleep' mode, only Proximity Sensor is used to detect
  158. * any movement triggering device wake up
  159. * ------------------------------------------------------------------------*/
  160. void CapTouchIdleMode(void)
  161. {
  162. /* Send status via UART: 'sleep' = [0xDE, 0xAD] */
  163. SendByte(SLEEP_MODE_UART_CODE);
  164. SendByte(SLEEP_MODE_UART_CODE2);
  165. /* Set DCO to 1MHz */
  166. /* Set SMCLK to 1MHz / 8 = 125kHz */
  167. BCSCTL1 = CALBC1_1MHZ;
  168. DCOCTL = CALDCO_1MHZ;
  169. BCSCTL2 |= DIVS_3;
  170. P1OUT |= BIT0; // Turn on center LED
  171. deltaCnts[0] = 0;
  172. /* Sleeping in LPM3 with ACLK/100 = 12Khz/100 = 120Hz wake up interval */
  173. /* Measure proximity sensor count upon wake up */
  174. /* Wake up if proximity deltaCnts > THRESHOLD */
  175. do
  176. {
  177. TACCR0 = 100;
  178. TACTL = TASSEL_1 + MC_1;
  179. TACCTL0 |= CCIE;
  180. __bis_SR_register(LPM3_bits+GIE);
  181. TACCTL0 &= ~CCIE;
  182. TI_CAPT_Custom(&proximity_sensor,deltaCnts);
  183. }
  184. while (deltaCnts[0] <= PROXIMITY_THRESHOLD);
  185. P1OUT &= ~BIT0; // Turn off center LED
  186. }
  187. /* ----------------MeasureCapBaseLine--------------------------------------
  188. * Re-measure the baseline capacitance of the wheel elements & the center
  189. * button. To be called after each wake up event.
  190. * ------------------------------------------------------------------------*/
  191. void MeasureCapBaseLine(void)
  192. {
  193. P1OUT = BIT0;
  194. /* Set DCO to 8MHz */
  195. /* SMCLK = 8MHz/8 = 1MHz */
  196. BCSCTL1 = CALBC1_8MHZ;
  197. DCOCTL = CALDCO_8MHZ;
  198. BCSCTL2 |= DIVS_3;
  199. TI_CAPT_Init_Baseline(&wheel);
  200. TI_CAPT_Update_Baseline(&wheel,2);
  201. TI_CAPT_Init_Baseline(&middle_button);
  202. TI_CAPT_Update_Baseline(&middle_button,2);
  203. }
  204. /* ----------------LedStartUpSequence--------------------------------------
  205. * Display an LED lighting sequence to indicate the wake up event
  206. * ------------------------------------------------------------------------*/
  207. void LedStartUpSequence(void)
  208. {
  209. unsigned char i;
  210. TACCTL0 = CCIE; // CCR0 interrupt enabled
  211. TACTL |= TACLR;
  212. TACCR0 = TAR + 500; // 50ms
  213. TACTL = TASSEL_1 + MC_1; // ACLK, upmode
  214. /* Slow clockwise sequence */
  215. for(i=0; i<8; i++)
  216. {
  217. P1OUT = startSequence[i];
  218. __bis_SR_register(LPM3_bits+GIE);
  219. __delay_cycles(1000000);
  220. TACCR0 = TAR + 500; // 50ms
  221. }
  222. P1OUT = BIT0;
  223. /* Fast counter-clockwise sequence */
  224. while(i)
  225. {
  226. i--;
  227. P1OUT = startSequence[i];
  228. __bis_SR_register(LPM3_bits+GIE);
  229. TACCR0 = TAR + 500; // 50ms
  230. }
  231. TACCTL0 &= ~CCIE; // CCR0 interrupt disabled
  232. P1OUT = 0; // Turn off all LEDs
  233. }
  234. /* ----------------GetGesture----------------------------------------------
  235. * Determine immediate gesture based on current & previous wheel position
  236. * ------------------------------------------------------------------------*/
  237. unsigned char GetGesture(unsigned char wheel_position)
  238. {
  239. unsigned char gesture = INVALID_GESTURE, direction, ccw_check, cw_check;
  240. // ******************************************************************************
  241. // gesturing
  242. // determine if a direction/swipe is occuring
  243. // the difference between the initial position and
  244. // the current wheel position should not exceed 8
  245. // 0-1-2-3-4-5-6-7-8-9-A-B-C-D-E-F-0...
  246. //
  247. // E-F-0-1-2: cw, 4
  248. // 2-1-0-F-E: ccw, 4
  249. // A-B-C-D-E-F
  250. //if(initial_wheel_position == INVALID_WHEEL_POSITION)
  251. //{
  252. //gesture = 0;
  253. //initial_wheel_position = wheel_position;
  254. //}
  255. //else
  256. if(last_wheel_position != ILLEGAL_SLIDER_WHEEL_POSITION)
  257. {
  258. if(last_wheel_position > wheel_position)
  259. {
  260. // E-D-C-B-A: ccw, 4
  261. // counter clockwise: 0 < (init_wheel_position - wheel_position) < 8
  262. // gesture = init_wheel_position - wheel_position
  263. //
  264. // E-F-0-1-2: cw, 4
  265. // clockwise: 0 < (init_wheel_position+wheel_position)-16 <8
  266. //
  267. ccw_check = last_wheel_position - wheel_position;
  268. if(ccw_check < 8)
  269. {
  270. gesture = ccw_check;
  271. direction = COUNTER_CLOCKWISE;
  272. }
  273. else
  274. {
  275. // E-F-0-1-2: cw, 4
  276. // 16 - 14 + 2 = 4
  277. cw_check = 16 - last_wheel_position + wheel_position ;
  278. if(cw_check < 8)
  279. {
  280. gesture = cw_check;
  281. direction = CLOCKWISE;
  282. }
  283. }
  284. }
  285. else
  286. {
  287. // initial_wheel_position <= wheel_position
  288. //
  289. // 2-1-0-F-E: ccw, 4
  290. // counter clockwise:
  291. // 0 < (init_wheel_position+wheel_position)-16 <8
  292. // gesture = init_wheel_position - wheel_position
  293. //
  294. // 0-1-2-3-4: cw, 4
  295. // clockwise: 0 < (wheel_position - init_wheel_position) < 8
  296. //
  297. cw_check = wheel_position - last_wheel_position ;
  298. if(cw_check < 8)
  299. {
  300. gesture = cw_check;
  301. direction = CLOCKWISE;
  302. }
  303. else
  304. {
  305. // 2-1-0-F-E: ccw, 4
  306. // 16 + 2 - 14 = 4
  307. ccw_check = 16 + last_wheel_position - wheel_position ;
  308. if(ccw_check < 8)
  309. {
  310. gesture = ccw_check;
  311. direction = COUNTER_CLOCKWISE;
  312. }
  313. }
  314. }
  315. }
  316. if (gesture == INVALID_GESTURE)
  317. return gesture;
  318. if (direction == COUNTER_CLOCKWISE)
  319. return (gesture + 16);
  320. else
  321. return gesture;
  322. }
  323. /* ----------------CapTouchActiveMode----------------------------------------------
  324. * Determine immediate gesture based on current & previous wheel position
  325. *
  326. *
  327. *
  328. *
  329. *
  330. *
  331. *
  332. * -------------------------------------------------------------------------------*/
  333. void CapTouchActiveMode()
  334. {
  335. unsigned char idleCounter, activeCounter;
  336. unsigned char gesture, gestureDetected;
  337. unsigned char centerButtonTouched = 0;
  338. unsigned int wheelTouchCounter = WHEEL_TOUCH_DELAY - 1;
  339. gesture = INVALID_GESTURE; // Wipes out gesture history
  340. /* Send status via UART: 'wake up' = [0xBE, 0xEF] */
  341. SendByte(WAKE_UP_UART_CODE);
  342. SendByte(WAKE_UP_UART_CODE2);
  343. idleCounter = 0;
  344. activeCounter = 0;
  345. gestureDetected = 0;
  346. while (idleCounter++ < MAX_IDLE_TIME)
  347. {
  348. /* Set DCO to 8MHz */
  349. /* SMCLK = 8MHz/8 = 1MHz */
  350. BCSCTL1 = CALBC1_8MHZ;
  351. DCOCTL = CALDCO_8MHZ;
  352. BCSCTL2 |= DIVS_3;
  353. TACCTL0 &= ~CCIE;
  354. wheel_position = ILLEGAL_SLIDER_WHEEL_POSITION;
  355. wheel_position = TI_CAPT_Wheel(&wheel);
  356. /* Process wheel touch/position/gesture if a wheel touch is registered*/
  357. /* Wheel processing has higher priority than center button*/
  358. if(wheel_position != ILLEGAL_SLIDER_WHEEL_POSITION)
  359. {
  360. centerButtonTouched = 0;
  361. /* Adjust wheel position based: rotate CCW by 2 positions */
  362. if (wheel_position < 0x08)
  363. {
  364. wheel_position += 0x40 - 0x08;
  365. }
  366. else
  367. {
  368. wheel_position -= 0x08;
  369. /* Adjust wheel position based: rotate CCW by 2 positions */
  370. }
  371. wheel_position = wheel_position >>2; // divide by four
  372. gesture = GetGesture(wheel_position);
  373. /* Add hysteresis to reduce toggling between wheel positions if no gesture
  374. * has been TRULY detected. */
  375. if ( (gestureDetected==0) && ((gesture<=1) || (gesture==0x11) || (gesture==0x10)))
  376. {
  377. if (last_wheel_position != ILLEGAL_SLIDER_WHEEL_POSITION)
  378. wheel_position = last_wheel_position;
  379. gesture = 0;
  380. }
  381. /* Turn on corresponding LED(s) */
  382. P1OUT = (P1OUT & BIT0) | LedWheelPosition[wheel_position];
  383. if ((gesture != 0) && (gesture != 16) && (gesture != INVALID_GESTURE))
  384. { /* A gesture has been detected */
  385. if (gestureDetected ==0)
  386. { /* Starting of a new gesture sequence */
  387. gestureDetected = 1;
  388. /* Transmit gesture start status update & position via UART to PC */
  389. SendByte(GESTURE_START);
  390. SendByte(last_wheel_position + GESTURE_POSITION_OFFSET);
  391. }
  392. /* Transmit gesture & position via UART to PC */
  393. SendByte(gesture);
  394. SendByte(wheel_position + GESTURE_POSITION_OFFSET);
  395. }
  396. else
  397. if (gestureDetected==0)
  398. { /* If no gesture was detected, this is constituted as a touch/tap */
  399. if (++wheelTouchCounter >= WHEEL_TOUCH_DELAY)
  400. {
  401. /* Transmit wheel position [twice] via UART to PC */
  402. wheelTouchCounter = 0;
  403. SendByte(wheel_position + WHEEL_POSITION_OFFSET );
  404. SendByte(wheel_position + WHEEL_POSITION_OFFSET );
  405. }
  406. }
  407. else
  408. wheelTouchCounter = WHEEL_TOUCH_DELAY - 1;
  409. idleCounter = 0; // Reset idle counter
  410. activeCounter++;
  411. last_wheel_position = wheel_position;
  412. }
  413. else
  414. { /* no wheel position was detected */
  415. if(TI_CAPT_Button(&middle_button))
  416. { /* Middle button was touched */
  417. if (centerButtonTouched==0)
  418. {
  419. /* Transmit center button code [twice] via UART to PC */
  420. SendByte(MIDDLE_BUTTON_CODE);
  421. SendByte(MIDDLE_BUTTON_CODE);
  422. centerButtonTouched = 1;
  423. P1OUT = (P1OUT&BIT0) ^ BIT0; // Toggle Center LED
  424. }
  425. idleCounter = 0;
  426. }
  427. else
  428. { /* No touch was registered at all [Not wheel or center button */
  429. centerButtonTouched = 0;
  430. P1OUT &= BIT0;
  431. if ( (gesture == INVALID_GESTURE) || (gestureDetected ==0))
  432. { /* No gesture was registered previously */
  433. if (last_wheel_position != ILLEGAL_SLIDER_WHEEL_POSITION)
  434. {
  435. /* Transmit last wheel position [twice] via UART to PC */
  436. SendByte(last_wheel_position + WHEEL_POSITION_OFFSET );
  437. SendByte(last_wheel_position + WHEEL_POSITION_OFFSET );
  438. wheelTouchCounter = WHEEL_TOUCH_DELAY - 1;
  439. }
  440. }
  441. if (gestureDetected == 1)
  442. { /* A gesture was registered previously */
  443. /* Transmit status update: stop gesture tracking [twice] via UART to PC */
  444. SendByte(GESTURE_STOP);
  445. SendByte(GESTURE_STOP);
  446. }
  447. }
  448. // Reset all touch conditions, turn off LEDs,
  449. last_wheel_position= ILLEGAL_SLIDER_WHEEL_POSITION;
  450. gesture = INVALID_GESTURE;
  451. gestureDetected = 0;
  452. }
  453. /* ------------------------------------------------------------------------
  454. * Option:
  455. * Add delay/sleep cycle here to reduce active duty cycle. This lowers power
  456. * consumption but sacrifices wheel responsiveness. Additional timing
  457. * refinement must be taken into consideration when interfacing with PC
  458. * applications GUI to retain proper communication protocol.
  459. * -----------------------------------------------------------------------*/
  460. }
  461. }
  462. void main(void)
  463. {
  464. WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
  465. InitLaunchPadCore();
  466. /* Set DCO to 1MHz */
  467. /* Set SMCLK to 1MHz / 8 = 125kHz */
  468. BCSCTL1 = CALBC1_1MHZ;
  469. DCOCTL = CALDCO_1MHZ;
  470. BCSCTL2 |= DIVS_3;
  471. /* Establish baseline for the proximity sensor */
  472. TI_CAPT_Init_Baseline(&proximity_sensor);
  473. TI_CAPT_Update_Baseline(&proximity_sensor,5);
  474. while (1)
  475. {
  476. CapTouchIdleMode();
  477. MeasureCapBaseLine();
  478. LedStartUpSequence();
  479. CapTouchActiveMode();
  480. }
  481. }