/* * main.c * * MSP-EXP430G2-LaunchPad User Experience Application * * 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. * */ /****************************************************************************** * MSP-EXP430G2-LaunchPad User Experience Application * * 1. Device starts up in LPM3 + blinking LED to indicate device is alive * + Upon first button press, device transitions to application mode * 2. Application Mode * + Continuously sample ADC Temp Sensor channel, compare result against * initial value * + Set PWM based on measured ADC offset: Red LED for positive offset, Green * LED for negative offset * + Transmit temperature value via TimerA UART to PC * + Button Press --> Calibrate using current temperature * Send character '°' via UART, notifying PC * * Changes: * * 1.2 + Updated register naming conventions to reflect latest standard by TI * e.g.: CCR0 --> TACCR0, CCTL0 --> TACCTL0 * + Changed method to capture TAR value into TACCR0 by using capture a * SW-triggered event. [Changing TACCR input from GND to VCC] * 1.1 + LED1 & LED2 labels changed so that Green LED(LED2) indicates sampled * temperature colder than calibrated temperature and vice versa * with Red LED (LED1). * + Turn off peripheral function of TXD after transmitting byte to * eliminate the extra glitch at the end of UART transmission * 1.0 Initial Release Version * * Texas Instruments, Inc. ******************************************************************************/ //#include "msp430g2553.h" //No longer needed, as the header file is specified by msp430-gcc build parameter #include "signal.h" #define LED1 BIT0 #define LED2 BIT6 #define LED_DIR P1DIR #define LED_OUT P1OUT #define BUTTON BIT3 #define BUTTON_OUT P1OUT #define BUTTON_DIR P1DIR #define BUTTON_IN P1IN #define BUTTON_IE P1IE #define BUTTON_IES P1IES #define BUTTON_IFG P1IFG #define BUTTON_REN P1REN #define TXD BIT1 // TXD on P1.1 #define RXD BIT2 // RXD on P1.2 #define APP_STANDBY_MODE 0 #define APP_APPLICATION_MODE 1 #define TIMER_PWM_MODE 0 #define TIMER_UART_MODE 1 #define TIMER_PWM_PERIOD 2000 #define TIMER_PWM_OFFSET 20 #define TEMP_SAME 0 #define TEMP_HOT 1 #define TEMP_COLD 2 #define TEMP_THRESHOLD 5 // Conditions for 9600/4=2400 Baud SW UART, SMCLK = 1MHz #define Bitime_5 0x05*4 // ~ 0.5 bit length + small adjustment #define Bitime 13*4//0x0D #define UART_UPDATE_INTERVAL 1000 unsigned char BitCnt; unsigned char applicationMode = APP_STANDBY_MODE; unsigned char timerMode = TIMER_PWM_MODE; unsigned char tempMode; unsigned char calibrateUpdate = 0; unsigned char tempPolarity = TEMP_SAME; unsigned int TXByte; /* Using an 8-value moving average filter on sampled ADC values */ long tempMeasured[8]; unsigned char tempMeasuredPosition=0; long tempAverage; long tempCalibrated, tempDifference; void InitializeLeds(void); void InitializeButton(void); void PreApplicationMode(void); // Blinks LED, waits for button press void ConfigureAdcTempSensor(void); void ConfigureTimerPwm(void); void ConfigureTimerUart(void); void Transmit(void); void InitializeClocks(void); void main(void) { unsigned int uartUpdateTimer = UART_UPDATE_INTERVAL; unsigned char i; WDTCTL = WDTPW + WDTHOLD; // Stop WDT InitializeClocks(); InitializeButton(); InitializeLeds(); PreApplicationMode(); // Blinks LEDs, waits for button press /* Application Mode begins */ applicationMode = APP_APPLICATION_MODE; ConfigureAdcTempSensor(); ConfigureTimerPwm(); __enable_interrupt(); // Enable interrupts. /* Main Application Loop */ while(1) { ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled /* Moving average filter out of 8 values to somewhat stabilize sampled ADC */ tempMeasured[tempMeasuredPosition++] = ADC10MEM; if (tempMeasuredPosition == 8) tempMeasuredPosition = 0; tempAverage = 0; for (i = 0; i < 8; i++) tempAverage += tempMeasured[i]; tempAverage >>= 3; // Divide by 8 to get average if ((--uartUpdateTimer == 0) || calibrateUpdate ) { ConfigureTimerUart(); if (calibrateUpdate) { TXByte = 248; // A character with high value, outside of temp range Transmit(); calibrateUpdate = 0; } TXByte = (unsigned char)( ((tempAverage - 630) * 761) / 1024 ); Transmit(); uartUpdateTimer = UART_UPDATE_INTERVAL; ConfigureTimerPwm(); } tempDifference = tempAverage - tempCalibrated; if (tempDifference < -TEMP_THRESHOLD) { tempDifference = -tempDifference; tempPolarity = TEMP_COLD; LED_OUT &= ~ LED1; } else if (tempDifference > TEMP_THRESHOLD) { tempPolarity = TEMP_HOT; LED_OUT &= ~ LED2; } else { tempPolarity = TEMP_SAME; TACCTL0 &= ~CCIE; TACCTL1 &= ~CCIE; LED_OUT &= ~(LED1 + LED2); } if (tempPolarity != TEMP_SAME) { tempDifference <<= 3; tempDifference += TIMER_PWM_OFFSET; TACCR1 = ( (tempDifference) < (TIMER_PWM_PERIOD-1) ? (tempDifference) : (TIMER_PWM_PERIOD-1) ); TACCTL0 |= CCIE; TACCTL1 |= CCIE; } } } void PreApplicationMode(void) { LED_DIR |= LED1 + LED2; LED_OUT |= LED1; // To enable the LED toggling effect LED_OUT &= ~LED2; BCSCTL1 |= DIVA_1; // ACLK/2 BCSCTL3 |= LFXT1S_2; // ACLK = VLO TACCR0 = 1200; // TACTL = TASSEL_1 | MC_1; // TACLK = SMCLK, Up mode. TACCTL1 = CCIE + OUTMOD_3; // TACCTL1 Capture Compare TACCR1 = 600; __bis_SR_register(LPM3_bits + GIE); // LPM0 with interrupts enabled } void ConfigureAdcTempSensor(void) { unsigned char i; /* Configure ADC Temp Sensor Channel */ ADC10CTL1 = INCH_10 + ADC10DIV_3; // Temp Sensor ADC10CLK/4 ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE; __delay_cycles(1000); // Wait for ADC Ref to settle ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled tempCalibrated = ADC10MEM; for (i=0; i < 8; i++) tempMeasured[i] = tempCalibrated; tempAverage = tempCalibrated; } void ConfigureTimerPwm(void) { timerMode = TIMER_PWM_MODE; TACCR0 = TIMER_PWM_PERIOD; // TACTL = TASSEL_2 | MC_1; // TACLK = SMCLK, Up mode. TACCTL0 = CCIE; TACCTL1 = CCIE + OUTMOD_3; // TACCTL1 Capture Compare TACCR1 = 1; } void ConfigureTimerUart(void) { timerMode = TIMER_UART_MODE; // Configure TimerA0 UART TX TACCTL0 = OUT; // TXD Idle as Mark TACTL = TASSEL_2 + MC_2 + ID_3; // SMCLK/8, continuous mode P1SEL |= TXD + RXD; // P1DIR |= TXD; // } // Function Transmits Character from TXByte void Transmit() { BitCnt = 0xA; // Load Bit counter, 8data + ST/SP /* Simulate a timer capture event to obtain the value of TAR into the TACCR0 register */ TACCTL0 = CM_1 + CCIS_2 + SCS + CAP + OUTMOD0; //capture on rising edge, initially set to GND as input // clear CCIFG flag TACCTL0 |= CCIS_3; //change input to Vcc, effectively rising the edge, triggering the capture action while (!(TACCTL0 & CCIFG)); //allowing for the capturing//updating TACCR0. TACCR0 += Bitime ; // Some time till first bit TXByte |= 0x100; // Add mark stop bit to TXByte TXByte = TXByte << 1; // Add space start bit TACCTL0 = CCIS0 + OUTMOD0 + CCIE; // TXD = mark = idle while ( TACCTL0 & CCIE ); // Wait for TX completion } // Timer A0 interrupt service routine //-------- IAR & CCS Convention //#pragma vector=TIMER0_A0_VECTOR //__interrupt void Timer_A (void) //-------- GCC Convention interrupt (TIMER0_A0_VECTOR) Timer_A(void) { if (timerMode == TIMER_UART_MODE) { TACCR0 += Bitime; // Add Offset to TACCR0 if (TACCTL0 & CCIS0) // TX on CCI0B? { if ( BitCnt == 0) { P1SEL &= ~(TXD+RXD); TACCTL0 &= ~ CCIE ; // All bits TXed, disable interrupt } else { TACCTL0 |= OUTMOD2; // TX Space if (TXByte & 0x01) TACCTL0 &= ~ OUTMOD2; // TX Mark TXByte = TXByte >> 1; BitCnt --; } } } else { if (tempPolarity == TEMP_HOT) LED_OUT |= LED1; if (tempPolarity == TEMP_COLD) LED_OUT |= LED2; TACCTL0 &= ~CCIFG; } } // ----------- CCS & IAR convention //#pragma vector=TIMER0_A1_VECTOR //__interrupt void ta1_isr(void) interrupt (TIMER0_A1_VECTOR) ta1_isr(void) { TACCTL1 &= ~CCIFG; if (applicationMode == APP_APPLICATION_MODE) LED_OUT &= ~(LED1 + LED2); else LED_OUT ^= (LED1 + LED2); } void InitializeClocks(void) { BCSCTL1 = CALBC1_1MHZ; // Set range DCOCTL = CALDCO_1MHZ; BCSCTL2 &= ~(DIVS_3); // SMCLK = DCO = 1MHz } void InitializeButton(void) // Configure Push Button { BUTTON_DIR &= ~BUTTON; BUTTON_OUT |= BUTTON; BUTTON_REN |= BUTTON; BUTTON_IES |= BUTTON; BUTTON_IFG &= ~BUTTON; BUTTON_IE |= BUTTON; } void InitializeLeds(void) { LED_DIR |= LED1 + LED2; LED_OUT &= ~(LED1 + LED2); } /* ************************************************************* * Port Interrupt for Button Press * 1. During standby mode: to exit and enter application mode * 2. During application mode: to recalibrate temp sensor * *********************************************************** */ interrupt (PORT1_VECTOR) PORT1_ISR(void) { BUTTON_IFG = 0; BUTTON_IE &= ~BUTTON; /* Debounce */ WDTCTL = WDT_ADLY_250; IFG1 &= ~WDTIFG; /* clear interrupt flag */ IE1 |= WDTIE; if (applicationMode == APP_APPLICATION_MODE) { tempCalibrated = tempAverage; calibrateUpdate = 1; } else { applicationMode = APP_APPLICATION_MODE; // Switch from STANDBY to APPLICATION MODE TACCTL1 &= ~CCIE; __bic_SR_register_on_exit(LPM3_bits ); } } // WDT Interrupt Service Routine used to debounce button press interrupt (WDT_VECTOR) WDT_SIR(void) { IE1 &= ~WDTIE; /* disable interrupt */ IFG1 &= ~WDTIFG; /* clear interrupt flag */ WDTCTL = WDTPW + WDTHOLD; /* put WDT back in hold state */ BUTTON_IE |= BUTTON; /* Debouncing complete */ } // ADC10 interrupt service routine // -------- CCS & IAR convention //#pragma vector=ADC10_VECTOR //__interrupt void ADC10_ISR (void) interrupt (ADC10_VECTOR) ADC10_ISR(void) { __bic_SR_register_on_exit(CPUOFF); // Return to active mode }