Testing out the PPD42 Air Quality Sensor, with an MSP430 Launchpad and graphing the data with GNUplot.
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.

437 lines
14 KiB

5 years ago
  1. /*
  2. * main.c
  3. *
  4. * MSP-EXP430G2-LaunchPad User Experience Application
  5. *
  6. * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
  7. *
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. *
  13. * Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. *
  16. * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the
  19. * distribution.
  20. *
  21. * Neither the name of Texas Instruments Incorporated nor the names of
  22. * its contributors may be used to endorse or promote products derived
  23. * from this software without specific prior written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  28. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  31. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  35. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. */
  38. /******************************************************************************
  39. * MSP-EXP430G2-LaunchPad User Experience Application
  40. *
  41. * 1. Device starts up in LPM3 + blinking LED to indicate device is alive
  42. * + Upon first button press, device transitions to application mode
  43. * 2. Application Mode
  44. * + Continuously sample ADC Temp Sensor channel, compare result against
  45. * initial value
  46. * + Set PWM based on measured ADC offset: Red LED for positive offset, Green
  47. * LED for negative offset
  48. * + Transmit temperature value via TimerA UART to PC
  49. * + Button Press --> Calibrate using current temperature
  50. * Send character '' via UART, notifying PC
  51. *
  52. * Changes:
  53. *
  54. * 1.2 + Updated register naming conventions to reflect latest standard by TI
  55. * e.g.: CCR0 --> TACCR0, CCTL0 --> TACCTL0
  56. * + Changed method to capture TAR value into TACCR0 by using capture a
  57. * SW-triggered event. [Changing TACCR input from GND to VCC]
  58. * 1.1 + LED1 & LED2 labels changed so that Green LED(LED2) indicates sampled
  59. * temperature colder than calibrated temperature and vice versa
  60. * with Red LED (LED1).
  61. * + Turn off peripheral function of TXD after transmitting byte to
  62. * eliminate the extra glitch at the end of UART transmission
  63. * 1.0 Initial Release Version
  64. *
  65. * Texas Instruments, Inc.
  66. ******************************************************************************/
  67. //#include "msp430g2553.h"
  68. //No longer needed, as the header file is specified by msp430-gcc build parameter
  69. #include "signal.h"
  70. #define LED1 BIT0
  71. #define LED2 BIT6
  72. #define LED_DIR P1DIR
  73. #define LED_OUT P1OUT
  74. #define BUTTON BIT3
  75. #define BUTTON_OUT P1OUT
  76. #define BUTTON_DIR P1DIR
  77. #define BUTTON_IN P1IN
  78. #define BUTTON_IE P1IE
  79. #define BUTTON_IES P1IES
  80. #define BUTTON_IFG P1IFG
  81. #define BUTTON_REN P1REN
  82. #define TXD BIT1 // TXD on P1.1
  83. #define RXD BIT2 // RXD on P1.2
  84. #define APP_STANDBY_MODE 0
  85. #define APP_APPLICATION_MODE 1
  86. #define TIMER_PWM_MODE 0
  87. #define TIMER_UART_MODE 1
  88. #define TIMER_PWM_PERIOD 2000
  89. #define TIMER_PWM_OFFSET 20
  90. #define TEMP_SAME 0
  91. #define TEMP_HOT 1
  92. #define TEMP_COLD 2
  93. #define TEMP_THRESHOLD 5
  94. // Conditions for 9600/4=2400 Baud SW UART, SMCLK = 1MHz
  95. #define Bitime_5 0x05*4 // ~ 0.5 bit length + small adjustment
  96. #define Bitime 13*4//0x0D
  97. #define UART_UPDATE_INTERVAL 1000
  98. unsigned char BitCnt;
  99. unsigned char applicationMode = APP_STANDBY_MODE;
  100. unsigned char timerMode = TIMER_PWM_MODE;
  101. unsigned char tempMode;
  102. unsigned char calibrateUpdate = 0;
  103. unsigned char tempPolarity = TEMP_SAME;
  104. unsigned int TXByte;
  105. /* Using an 8-value moving average filter on sampled ADC values */
  106. long tempMeasured[8];
  107. unsigned char tempMeasuredPosition=0;
  108. long tempAverage;
  109. long tempCalibrated, tempDifference;
  110. void InitializeLeds(void);
  111. void InitializeButton(void);
  112. void PreApplicationMode(void); // Blinks LED, waits for button press
  113. void ConfigureAdcTempSensor(void);
  114. void ConfigureTimerPwm(void);
  115. void ConfigureTimerUart(void);
  116. void Transmit(void);
  117. void InitializeClocks(void);
  118. void main(void)
  119. {
  120. unsigned int uartUpdateTimer = UART_UPDATE_INTERVAL;
  121. unsigned char i;
  122. WDTCTL = WDTPW + WDTHOLD; // Stop WDT
  123. InitializeClocks();
  124. InitializeButton();
  125. InitializeLeds();
  126. PreApplicationMode(); // Blinks LEDs, waits for button press
  127. /* Application Mode begins */
  128. applicationMode = APP_APPLICATION_MODE;
  129. ConfigureAdcTempSensor();
  130. ConfigureTimerPwm();
  131. __enable_interrupt(); // Enable interrupts.
  132. /* Main Application Loop */
  133. while(1)
  134. {
  135. ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
  136. __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled
  137. /* Moving average filter out of 8 values to somewhat stabilize sampled ADC */
  138. tempMeasured[tempMeasuredPosition++] = ADC10MEM;
  139. if (tempMeasuredPosition == 8)
  140. tempMeasuredPosition = 0;
  141. tempAverage = 0;
  142. for (i = 0; i < 8; i++)
  143. tempAverage += tempMeasured[i];
  144. tempAverage >>= 3; // Divide by 8 to get average
  145. if ((--uartUpdateTimer == 0) || calibrateUpdate )
  146. {
  147. ConfigureTimerUart();
  148. if (calibrateUpdate)
  149. {
  150. TXByte = 248; // A character with high value, outside of temp range
  151. Transmit();
  152. calibrateUpdate = 0;
  153. }
  154. TXByte = (unsigned char)( ((tempAverage - 630) * 761) / 1024 );
  155. Transmit();
  156. uartUpdateTimer = UART_UPDATE_INTERVAL;
  157. ConfigureTimerPwm();
  158. }
  159. tempDifference = tempAverage - tempCalibrated;
  160. if (tempDifference < -TEMP_THRESHOLD)
  161. {
  162. tempDifference = -tempDifference;
  163. tempPolarity = TEMP_COLD;
  164. LED_OUT &= ~ LED1;
  165. }
  166. else
  167. if (tempDifference > TEMP_THRESHOLD)
  168. {
  169. tempPolarity = TEMP_HOT;
  170. LED_OUT &= ~ LED2;
  171. }
  172. else
  173. {
  174. tempPolarity = TEMP_SAME;
  175. TACCTL0 &= ~CCIE;
  176. TACCTL1 &= ~CCIE;
  177. LED_OUT &= ~(LED1 + LED2);
  178. }
  179. if (tempPolarity != TEMP_SAME)
  180. {
  181. tempDifference <<= 3;
  182. tempDifference += TIMER_PWM_OFFSET;
  183. TACCR1 = ( (tempDifference) < (TIMER_PWM_PERIOD-1) ? (tempDifference) : (TIMER_PWM_PERIOD-1) );
  184. TACCTL0 |= CCIE;
  185. TACCTL1 |= CCIE;
  186. }
  187. }
  188. }
  189. void PreApplicationMode(void)
  190. {
  191. LED_DIR |= LED1 + LED2;
  192. LED_OUT |= LED1; // To enable the LED toggling effect
  193. LED_OUT &= ~LED2;
  194. BCSCTL1 |= DIVA_1; // ACLK/2
  195. BCSCTL3 |= LFXT1S_2; // ACLK = VLO
  196. TACCR0 = 1200; //
  197. TACTL = TASSEL_1 | MC_1; // TACLK = SMCLK, Up mode.
  198. TACCTL1 = CCIE + OUTMOD_3; // TACCTL1 Capture Compare
  199. TACCR1 = 600;
  200. __bis_SR_register(LPM3_bits + GIE); // LPM0 with interrupts enabled
  201. }
  202. void ConfigureAdcTempSensor(void)
  203. {
  204. unsigned char i;
  205. /* Configure ADC Temp Sensor Channel */
  206. ADC10CTL1 = INCH_10 + ADC10DIV_3; // Temp Sensor ADC10CLK/4
  207. ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;
  208. __delay_cycles(1000); // Wait for ADC Ref to settle
  209. ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
  210. __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled
  211. tempCalibrated = ADC10MEM;
  212. for (i=0; i < 8; i++)
  213. tempMeasured[i] = tempCalibrated;
  214. tempAverage = tempCalibrated;
  215. }
  216. void ConfigureTimerPwm(void)
  217. {
  218. timerMode = TIMER_PWM_MODE;
  219. TACCR0 = TIMER_PWM_PERIOD; //
  220. TACTL = TASSEL_2 | MC_1; // TACLK = SMCLK, Up mode.
  221. TACCTL0 = CCIE;
  222. TACCTL1 = CCIE + OUTMOD_3; // TACCTL1 Capture Compare
  223. TACCR1 = 1;
  224. }
  225. void ConfigureTimerUart(void)
  226. {
  227. timerMode = TIMER_UART_MODE; // Configure TimerA0 UART TX
  228. TACCTL0 = OUT; // TXD Idle as Mark
  229. TACTL = TASSEL_2 + MC_2 + ID_3; // SMCLK/8, continuous mode
  230. P1SEL |= TXD + RXD; //
  231. P1DIR |= TXD; //
  232. }
  233. // Function Transmits Character from TXByte
  234. void Transmit()
  235. {
  236. BitCnt = 0xA; // Load Bit counter, 8data + ST/SP
  237. /* Simulate a timer capture event to obtain the value of TAR into the TACCR0 register */
  238. TACCTL0 = CM_1 + CCIS_2 + SCS + CAP + OUTMOD0; //capture on rising edge, initially set to GND as input // clear CCIFG flag
  239. TACCTL0 |= CCIS_3; //change input to Vcc, effectively rising the edge, triggering the capture action
  240. while (!(TACCTL0 & CCIFG)); //allowing for the capturing//updating TACCR0.
  241. TACCR0 += Bitime ; // Some time till first bit
  242. TXByte |= 0x100; // Add mark stop bit to TXByte
  243. TXByte = TXByte << 1; // Add space start bit
  244. TACCTL0 = CCIS0 + OUTMOD0 + CCIE; // TXD = mark = idle
  245. while ( TACCTL0 & CCIE ); // Wait for TX completion
  246. }
  247. // Timer A0 interrupt service routine
  248. //-------- IAR & CCS Convention
  249. //#pragma vector=TIMER0_A0_VECTOR
  250. //__interrupt void Timer_A (void)
  251. //-------- GCC Convention
  252. interrupt (TIMER0_A0_VECTOR) Timer_A(void)
  253. {
  254. if (timerMode == TIMER_UART_MODE)
  255. {
  256. TACCR0 += Bitime; // Add Offset to TACCR0
  257. if (TACCTL0 & CCIS0) // TX on CCI0B?
  258. {
  259. if ( BitCnt == 0)
  260. {
  261. P1SEL &= ~(TXD+RXD);
  262. TACCTL0 &= ~ CCIE ; // All bits TXed, disable interrupt
  263. }
  264. else
  265. {
  266. TACCTL0 |= OUTMOD2; // TX Space
  267. if (TXByte & 0x01)
  268. TACCTL0 &= ~ OUTMOD2; // TX Mark
  269. TXByte = TXByte >> 1;
  270. BitCnt --;
  271. }
  272. }
  273. }
  274. else
  275. {
  276. if (tempPolarity == TEMP_HOT)
  277. LED_OUT |= LED1;
  278. if (tempPolarity == TEMP_COLD)
  279. LED_OUT |= LED2;
  280. TACCTL0 &= ~CCIFG;
  281. }
  282. }
  283. // ----------- CCS & IAR convention
  284. //#pragma vector=TIMER0_A1_VECTOR
  285. //__interrupt void ta1_isr(void)
  286. interrupt (TIMER0_A1_VECTOR) ta1_isr(void)
  287. {
  288. TACCTL1 &= ~CCIFG;
  289. if (applicationMode == APP_APPLICATION_MODE)
  290. LED_OUT &= ~(LED1 + LED2);
  291. else
  292. LED_OUT ^= (LED1 + LED2);
  293. }
  294. void InitializeClocks(void)
  295. {
  296. BCSCTL1 = CALBC1_1MHZ; // Set range
  297. DCOCTL = CALDCO_1MHZ;
  298. BCSCTL2 &= ~(DIVS_3); // SMCLK = DCO = 1MHz
  299. }
  300. void InitializeButton(void) // Configure Push Button
  301. {
  302. BUTTON_DIR &= ~BUTTON;
  303. BUTTON_OUT |= BUTTON;
  304. BUTTON_REN |= BUTTON;
  305. BUTTON_IES |= BUTTON;
  306. BUTTON_IFG &= ~BUTTON;
  307. BUTTON_IE |= BUTTON;
  308. }
  309. void InitializeLeds(void)
  310. {
  311. LED_DIR |= LED1 + LED2;
  312. LED_OUT &= ~(LED1 + LED2);
  313. }
  314. /* *************************************************************
  315. * Port Interrupt for Button Press
  316. * 1. During standby mode: to exit and enter application mode
  317. * 2. During application mode: to recalibrate temp sensor
  318. * *********************************************************** */
  319. interrupt (PORT1_VECTOR) PORT1_ISR(void)
  320. {
  321. BUTTON_IFG = 0;
  322. BUTTON_IE &= ~BUTTON; /* Debounce */
  323. WDTCTL = WDT_ADLY_250;
  324. IFG1 &= ~WDTIFG; /* clear interrupt flag */
  325. IE1 |= WDTIE;
  326. if (applicationMode == APP_APPLICATION_MODE)
  327. {
  328. tempCalibrated = tempAverage;
  329. calibrateUpdate = 1;
  330. }
  331. else
  332. {
  333. applicationMode = APP_APPLICATION_MODE; // Switch from STANDBY to APPLICATION MODE
  334. TACCTL1 &= ~CCIE;
  335. __bic_SR_register_on_exit(LPM3_bits );
  336. }
  337. }
  338. // WDT Interrupt Service Routine used to debounce button press
  339. interrupt (WDT_VECTOR) WDT_SIR(void)
  340. {
  341. IE1 &= ~WDTIE; /* disable interrupt */
  342. IFG1 &= ~WDTIFG; /* clear interrupt flag */
  343. WDTCTL = WDTPW + WDTHOLD; /* put WDT back in hold state */
  344. BUTTON_IE |= BUTTON; /* Debouncing complete */
  345. }
  346. // ADC10 interrupt service routine
  347. // -------- CCS & IAR convention
  348. //#pragma vector=ADC10_VECTOR
  349. //__interrupt void ADC10_ISR (void)
  350. interrupt (ADC10_VECTOR) ADC10_ISR(void)
  351. {
  352. __bic_SR_register_on_exit(CPUOFF); // Return to active mode
  353. }