This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Output ports stopped working for this program.

Other Parts Discussed in Thread: MSP430G2231

Hello,

I've only done 5 or 6 MSP430 projects, so I'm pretty new, but still have never seen this before. I'm following my program in debug, and even though it's properly setting the P1OUT and P1DIR bits, I'm not getting any output at the specified pins. Does anyone know what might cause this and what can effect pin output other than the P1 registers? My code is below, as is a picture of the debugger window once when it is working and once when it's not. I saw that the only difference in the P1 registers was the interrupt flag, so I tried clearing it, but that didn't help.

Here's my code:

//***************************************************************************************
// Project:
// Chip:
// Description:
// Date:
// Created by Michael Willems
//***************************************************************************************
#include "msp430g2231.h"

// Define functional aliases.
#define MAX(a, b) a > b ? a : b                // Returns the greater of a and b.
#define USE_VLO (BCSCTL3 |= LFXT1S_2)        // Uses VLO for ACLK.

// Define variable aliases.
#define     RED             BIT0    // Pin 1.0, used for red LED and time input.
#define     GREEN           BIT6    // Pin 1.6, used for green LED and T-critical input.
#define        BUTTON            BIT3    // Pin 1.3, connected to pushbutton (which grounds).
#define     SOURCE_TEMP        BIT4    // Pin 1.4, will be connected through POT to the pin
                                    // where Tcrit user input will be measured (TEMP_IN).
#define        SOURCE_TIME        BIT7    // Pin 1.7, will be connected through POT to provide
                                    // controllable divider voltage at TIME_IN.
#define        RESET_HOLD_TIME    10        // Defines how long button must be held for reset.
#define        TEMP_IN            RED        // Choose red LED output pin for measuring T_CRIT.
#define        TIME_IN            GREEN    // Choose green LED output pin for measuring TIME.
#define        TEMP_CHANNEL    10        // Analog input channel 10.
#define        T_BLINK            10000    // Blink period of 1000 cycles.
#define        T_SAMPLE        12000    // Sampling period of roughly 12k cycles at 12kHz.

// Declare global variables.
unsigned int TEMP;             // Temperature measurements stored here.
unsigned int TIME;            // Time threshold (acceptable overage) stored here.
unsigned int T_CRIT;        // Critical temperature stored here.
unsigned int OVERAGE;        // Stores the total time*temp over the limit.
int WAKEUP_NEXT;            // Flag used by the button ISR to either wake or sleep.
int T_CRIT_SET;            // Flag ensures ADC only sets critical temp once.
int TIME_SET;                // Flag ensures ADC only sets time threshold once.

//***************************************************************************************
// FUNCTION DEFINITIONS
//***************************************************************************************

/*
 * This function configures the ADC to read off of 2 P1 inputs, takes voltage readings,
 * and stores the results in the TIME and T_CRIT fields for further use.
 */
void readThresholds(void)
{
    /******************** READING T_CRIT ********************/

    // Configure measurement pin as inputs with internal pull up resistors off.
        P1REN &= ~(TEMP_IN);    // Pull-up resistors off for measurement pins.
        P1OUT &= ~(TEMP_IN);    // Turn off output before switching to input.
        P1DIR &= ~(TEMP_IN);     // Can be input or output, ADC doesn't care.
        // Thus, set direction to input with resistors off so it won't get set hi or lo.

    // Configure ADC channels to read POT-divided voltage at T_CRIT input first.
    ADC10AE0 |= TEMP_IN;    // Make T_CRIT input pin analog to make available to ADC.
    ADC10CTL1 = TEMP_IN;    // Set ADC to read from T_CRIT input pin.

    // Enable ADC10 interrupt, set reference voltages, and turn ADC on.
    ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON + ADC10IE + REFON + REF2_5V;

    // Turn on supply voltage for T_CRIT voltage divider.
    P1DIR |= SOURCE_TEMP;            // Set T_CRIT divider source to output.
    P1OUT |= SOURCE_TEMP;            // Turn divider source on.

    // Start read.
    ADC10CTL0 |= ENC;                 // Enable conversion bit set.
    ADC10CTL0 |= ADC10SC;            // Start conversion bit set.

    //Wait in LPM3 with everything off except ACLK (VLO) while ADC writes data.
    _BIS_SR(CPUOFF + SCG1 + SCG0 + GIE);    // Enter LPM3 w/ interrupt.

    // Read ADC10 in ISR. ISR will return here.

    /******************** READING TIME ********************/

    // Turn off supply to Temperature voltage divider and reset to output.
    P1OUT &= ~SOURCE_TEMP;
    P1DIR += TEMP_IN;
    P1OUT |= TEMP_IN;

    // Configure measurement pin as inputs with internal pull up resistors off.
    P1REN &= ~(TIME_IN);    // Pull-up resistors off for measurement pins.
    P1OUT &= ~(TIME_IN);    // Turn off output before switching to input.
    P1DIR &= ~(TIME_IN);     // Can be input or output, ADC doesn't care.
    // Thus, set direction to input with resistors off so it won't get set hi or lo.

    // Now configure ADC channels to read POT-divided voltage at Time input.
    ADC10AE0 |= TIME_IN;    // Enable Time input pin as analog to make available to ADC.
    ADC10CTL1 = TIME_IN;    // Set ADC to read from Time input pin.

    // Enable ADC10 interrupt, set reference voltages, and turn ADC on.
    ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON + ADC10IE + REFON + REF2_5V;

    // Turn on supply voltage for Time voltage divider.
    P1DIR |= SOURCE_TIME;            // Set Time divider source to output.
    P1OUT |= SOURCE_TIME;            // Turn Time divider source on.

    // Start read.
    ADC10CTL0 |= ENC;                 // Enable conversion bit set.
    ADC10CTL0 |= ADC10SC;            // Start conversion bit set.

    //Wait in LPM3 with everything off until ADC complete interrupt wakes up.
    _BIS_SR(CPUOFF + SCG1 + SCG0 + GIE);    // ACLK runs ADC while it converts result.

    // Read ADC10 in ISR. ISR will return to here but awakened.

    // Disable ADC to save power and so control bits can be changed next time.
    ADC10CTL0 &= ~ENC;

    // Turn off supply to Temperature voltage divider and reset to output.
    P1OUT &= ~SOURCE_TIME;
    P1DIR += TEMP_IN;
    P1OUT |= TEMP_IN;

    // Calculate the overage limit as a function of the TIME raw input data, and store
    // that result as the variable to compare to:
    TIME = TIME >> 6;

    return;

} //*************************************************************************************
//***************************************************************************************

/*
 * This function blinks the green LED once for roughly T_BLINK cycles.
 */
void blinkGreenOnce(void) {

    P1DIR = GREEN;
    P1OUT = GREEN;
    long i;
    for(i = T_BLINK; i>0; i--);
    P1OUT &= ~(GREEN);

} //*************************************************************************************
//***************************************************************************************

/*
 * This function blinks the red LED once for roughly T_BLINK cycles.
 */
void blinkRedOnce(void) {

    P1DIR = RED;
    P1OUT = RED;
    long i;
    for(i = T_BLINK; i>0; i--);
    P1OUT &= ~(RED);

} //*************************************************************************************
//***************************************************************************************

/*
 * This function blinks both LEDs once for roughly T_BLINK cycles.
 */
void blinkBothOnce(void) {

    P1IFG = 0;
    P1DIR = RED + GREEN;
    P1OUT = RED + GREEN;
    long i;
    for(i = T_BLINK; i>0; i--);
    P1OUT &= ~(RED + GREEN);

} //*************************************************************************************
//***************************************************************************************

/*
 * This function takes one integer parameter, period, and sets up the ADC and a VLO clock
 * to take readings roughly every [period] seconds.
 */
void setTemp(void) {
    //Disable ADC so control bits can be changed.
    ADC10CTL0 &= ~ENC;

    // Configure ADC channels to read internal temperature sensor.
    ADC10CTL1 = TEMP_CHANNEL;                // Set ADC to read from Time input pin.

    // Enable ADC10 interrupt, set reference voltages, and turn ADC on.
    ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON + ADC10IE + REFON + REF2_5V;

    // Start read.
    ADC10CTL0 |= ENC + ADC10SC;                // Enable and start conversion bits set.

    //Wait in LPM3 with everything off until ADC complete interrupt wakes up.
    _BIS_SR(CPUOFF + SCG1 + SCG0 + GIE);    // ACLK runs ADC while it converts result.

    // Read ADC10 in ISR. ISR will return to here but awakened.

    // Upon exit, disable ADC to save power and so control bits can be changed next time.
    ADC10CTL0 &= ~ENC;

} //*************************************************************************************
//***************************************************************************************

/*
 * This function starts the main method again - rereading threshold settings.
 */
void reset(void) {
    // Writing to the p/w protected WDT control without p/w causes reset.
    WDTCTL = WDTHOLD;
} //*************************************************************************************
//***************************************************************************************
// MAIN FUNCTION
//***************************************************************************************

/*
 * This is the main function.
 */
int main(void)
{
    // Basic setup:                // Initialize global variables.
    TEMP = TIME = T_CRIT = WAKEUP_NEXT = OVERAGE = T_CRIT_SET = TIME_SET = 0;
    WDTCTL = WDTPW + WDTHOLD;    // Stop WDT
    USE_VLO;                    // Configure VLO as ACLK.

    // Enable button interrupts (for sleep and reset):
    P1IE |= BUTTON;             // Enable button interrupts (also necessary).
    P1IFG &= ~BUTTON;            // Clear button interrupt flag.
    _BIS_SR(GIE);                // Enable general interrupts.

    P1DIR |= RED + GREEN;        // Debug - turn LEDs to output.
    P1OUT |= RED + GREEN;

    // Measure and set T_CRIT and TIME:
    readThresholds();
    blinkBothOnce();    // Blinks LEDs once to signify proper reset / read on settings.

    // Configure timer:
    TACCR0 = T_SAMPLE;            // Configure the Timer A count limit.
    TACCTL0 |= CCIE;            // Enable timer interrupt.
    TACTL = TASSEL_1 | MC_1;    // Configure Timer A to use ACLCK and start it.

    _BIS_SR(CPUOFF + SCG1 + SCG0 + GIE);    // Enter LPM3 w/ interrupts enabled.

} //*************************************************************************************
//***************************************************************************************
// INTERRUPT SERVICE ROUTINES
//***************************************************************************************

/*
 * Timer A0 interrupt service routine. Gets called periodically about once a second.
 * This routine must perform the temperature measurements and processing.
 */
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A_ISR (void)
{
    if(OVERAGE > TIME){
        blinkRedOnce();
    } else {
        setTemp();
        if(TEMP > T_CRIT){
            OVERAGE += TEMP - T_CRIT;
            blinkRedOnce();
        } else {    // temp <= critical temp
            blinkGreenOnce();
        }
    }
}

/*
 * ISR for button push interrupt. Every other (odd) push triggers sleep in LPM4
 * (turns the device off). Even button pushes cause a software reset, beginning
 * with new input read.
 */
#pragma vector=PORT1_VECTOR
__interrupt void Button_ISR(void)
{
    if(WAKEUP_NEXT)
    {
        //Waking up.
        WAKEUP_NEXT = 0;     // Should be done by MCU on reset anyway, but to clarify.
        P1IFG &= ~BUTTON;    // Clear push button Interrupt Flag.
        reset();            // POR reset restarts main function.
    }
    else
    {
        //Turning off and going to sleep.
        P1OUT = 0;                                //Turn off all outputs.
        WAKEUP_NEXT = 1;                        // Next button push wakes up.
        P1IFG &= ~BUTTON;                        // Clear push button Interrupt Flag.
        _BIS_SR(CPUOFF + OSCOFF + SCG1 + SCG0 + GIE);    // Enter LPM4 w/ interrupt.
    }
}

/*
 * Interrupt for the ADC10. Either reading voltage or temperature, must account for both.
 */
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
    if(T_CRIT_SET && TIME_SET) {    // If both are set, then we're taking temperature.
        TEMP = ADC10MEM;
    } else {
        if(!T_CRIT_SET) {            // T_CRIT will be set first.
            T_CRIT = ADC10MEM;
            T_CRIT_SET = 1;
        } else {                    // Then TIME is set if T_CRIT has already been set.
            TIME = ADC10MEM;
            TIME_SET = 1;
        }
    }

    // Clear CPUOFF bit from 0(SR) to stay awake and finish function.
    __bic_SR_register_on_exit(LPM3_bits);
}

  • Anyway, any and all help would be greatly appreciated.

    Thanks a ton in advance!

    -Mike

  • By the way, in all my test programs that don't use the ADC, I don't have a problem, which leads me to believe it has something to do with the ADC bits.

  • Alright - answered my own question but this might help out others with the same problem.

    It was in fact the ADC10 bits that were messing me up. For some reason, I needed to clear them after use of the ADC, even when the port pins were correctly set, to allow the port to function as an output. Now the question is - does anyone know why this is the case?

    Best,

    Mike

  • Michael Willems said:
    It was in fact the ADC10 bits that were messing me up. For some reason, I needed to clear them after use of the ADC, even when the port pins were correctly set, to allow the port to function as an output. Now the question is - does anyone know why this is the case?

    Take alook at the por tpn schematics. slas694c.pdf page 37ff

    The pin function table tells you that when ADC10AE.x is 1, P1DIR.x nd P1SEL.x are don't care and the pin is Ax input only. P1SEL.x adn P1DIR.x only switch between GPIO and timer, but the ADC does an override here.

    However, the schematics are somewhat buggy/incomplete. So ADC10AE.x only appears in P1.4 schematics (which appears to be the only correct one), but should on all others too.

  • Thanks a lot Jens! That makes a lot more sense now, and actually explains another small bug I had before and I thought seemed random.

**Attention** This is a public forum