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.

CCS/MSP432P401R: ADC On/Off Controlled by External Button Press and Timer A

Part Number: MSP432P401R

Tool/software: Code Composer Studio

My goal is to have code that allows me to turn on the ADC by pressing an external button and then an interrupt from Timer A will turn off the ADC after a specified amount of time (ideally, 300 ms). I have written and tested code that generates a GPIO interrupt when an external button connected to P3.0 is pressed, as well as code that turns the ADC off based on an interrupt generated by Timer A. However, when I attempted to put these two code blocks together, I am not getting the desired result.

Currently, the code that I have (shown below) displays 1 ADC value via UART when the external button is pressed, and then the green LED (P2.1) blinks continuously, indicating that timer_flag is always set. Additionally, I am unsure of how to turn off the ADC (when the timer interrupt is set). Any help that anyone could provide would be greatly appreciated. 

/* Test ADC ON by external button and OFF by Timer A */

#include "msp.h"
#include "mytiming.h"

volatile unsigned int UART_flag = 0;
volatile unsigned int timer_flag = 0;
volatile unsigned int  button_flag = 0;
volatile unsigned int var = 0;
int millivolt = 0;

void UART0_init(void);
void delayMs(int n, int freq);
void setFreq(int freq);

int main(void) {

    setFreq(FREQ_3_MHZ);

    int mv_char_0 = 0;
    int mv_char_1 = 0;
    int mv_char_2 = 0;
    int mv_char_3 = 0;
    char uart_0;
    char uart_1;
    char uart_2;
    char uart_3;

    WDT_A->CTL = WDT_A_CTL_PW |             // Stop WDT
                 WDT_A_CTL_HOLD;

    // GPIO Setup
    P5->SEL1 |= BIT4;                       // Configure P5.4 for ADC
    P5->SEL0 |= BIT4;

    //for testing
    P2->SEL1 &= ~BIT1;  //P2.1 is simple I/O
    P2->SEL0 &= ~BIT1;
    P2->DIR |= BIT1;   // P2.1 set as output pin
    P2->OUT &= ~BIT1;  // LED initially low (off)

    // Initialize Port 3 (button connections)
    P3->SEL0 = 0;  //clear register selection
    P3->SEL1 = 0;  //clear register selection
    P3->DIR = 0;
    P3->OUT = BIT0;
    P3->REN = BIT0;   //enable pull up resistor

    P3->IES = BIT0;  //interrupt on high-low transition
    P3->IFG = 0;     //clear any pending flags
    P3->IE = BIT0;   //enable port 3 interrupts

    UART0_init();

    // Enable global interrupt
    __enable_irq();

    // Enable ADC and Timer A0 interrupts in NVIC module
    NVIC_EnableIRQ(TA0_0_IRQn);
    NVIC_EnableIRQ(ADC14_IRQn);
    NVIC_EnableIRQ(PORT3_IRQn);

    // Sampling time, S&H=16, ADC14 on
    ADC14->CTL0 = ADC14_CTL0_SHT0_2 | ADC14_CTL0_SHP | ADC14_CTL0_ON;
    ADC14->CTL1 = ADC14_CTL1_RES_2;
    ADC14->MCTL[0] |= ADC14_MCTLN_INCH_1;   /* A1 ADC input select;
                                               Vref=AVCC */
    ADC14->IER0 |= ADC14_IER0_IE0;          /* Enable ADC conv
                                               complete interrupt */

    SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;  /* Wake up on exit from
                                              ISR */

    while (1){
        if (button_flag){   //button pushed, turn ADC on
            button_flag = 0;
            ADC14->CTL0 |= ADC14_CTL0_ENC | ADC14_CTL0_SC; //start sampling/conversion
        }
        if (timer_flag){   //time up, turn ADC off
            timer_flag = 0;
            P2->OUT |= BIT1;         //turn on P2.1 green LED
            delayMs(500, FREQ_3_MHZ);
            P2->OUT &= ~BIT1;        //turn off P2.1 green LED
            delayMs(500, FREQ_3_MHZ);
                                       //turn off ADC
        }
        if (UART_flag) {
            UART_flag = 0;
            millivolt = (int)((var + 1.46) / 1.2361);
            mv_char_3 = millivolt / 1000;
            mv_char_2 = millivolt / 100 - (mv_char_3 * 10);
            mv_char_1 = millivolt / 10 - (mv_char_3 * 100 + mv_char_2 * 10);
            mv_char_0 = millivolt - (mv_char_1 * 10 + mv_char_3 * 1000 + mv_char_2 * 100);
            uart_0 = (char) mv_char_0 + '0';
            uart_1 = (char) mv_char_1 + '0';
            uart_2 = (char) mv_char_2 + '0';
            uart_3 = (char) mv_char_3 + '0';
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = uart_3;
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = uart_2;
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = uart_1;
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = uart_0;
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = 0x0D;
        }
    }
}

// ADC14 interrupt service routine
void ADC14_IRQHandler(void) {
    UART_flag = 1;
    var = ADC14->MEM[0];
}

//Timer A0 interrupt service routine
void TA0_0_IRQHandler(void){
    timer_flag = 1;         //flag when timer reaches value
    TA0CCTL0 &= ~CCIFG;     //clear pending interrupt flag
}

void PORT3_IRQHandler(void)       //Interrupt handler for P3.0
{
    button_flag = 1;              //Set flag to signal button press detected
    //configure Timer A0
    TA0CCR0 = 20000;            // arbitrary value (need 300 ms of ADC)
    TA0CCTL0 |= CCIE;
    TA0CTL |= TASSEL_2 | MC_1;
    P3->IFG = 0;                  //Clear P3.0 pending interrupt flag
    P3->IE  &= ~BIT0;             //Disable interrupt for P3.0 for debouncing
}

void UART0_init(void)
{
    EUSCI_A0->CTLW0 |= 1;       // put in reset mode for config
    EUSCI_A0->MCTLW = 0;        // disable oversampling
    EUSCI_A0->CTLW0 = 0x0081;   /* 1 stop bit, no parity,SMCLK,byte
                                   data */
    EUSCI_A0->BRW = 26;         // 3,000,000 / 115200 = 26
    P1->SEL0 |= 0x0C;           // P1.3, P1.2 for UART
    P1->SEL1 &= ~0x0C;
    EUSCI_A0->CTLW0 &= ~1;      // take UART out of reset mode
}

  • I think the missing piece here is that you're trying to use the timer for two different things:

    1) Run the ADC periodically (the "short" period -- 20000 ticks = 6.6ms)
    2) Stop running the ADC, after some period of time (the "long" period -- 300ms?)

    A small extension will allow you to do this: Have the timer interrupt keep a counter; when it reaches some "terminal" count, it stops the timer (TA0CTL=0). The "terminal" count is just long_period/short_period. The button ISR should clear the counter when it starts the timer.

    Since the button ISR starts the timer, and timer_flag will be used to run the ADC (ADC14_CTL0_SC), there's probably no need for button_flag any more. You may want a new "blinky_flag" which is set by the timer ISR when it stops the timer, and you can use that to do your blink sequence. This will only happen once per button push, since you won't get any more timer ISRs until you push the button again.

    Incidentally, I don't see where the button interrupt gets re-enabled after de-bouncing. Maybe the "blinky" sequence should include this as well.
  • Unfortunately, I have explained my intent in a confusing manner (and I had a type in the TACCR0 value stated in the above code). I only need the ADC to run for 300 ms after the button has been pressed. Essentially, I want the ADC to begin when a button is pressed and then run for 300 ms before finishing completely.  I have fixed my type, and corrected a few other potential errors, in the code shown below.

    /* Test ADC ON by external button and OFF by Timer A */
    
    #include "msp.h"
    #include "mytiming.h"
    
    volatile unsigned int UART_flag = 0;
    volatile unsigned int timer_flag = 0;
    volatile unsigned int  button_flag = 0;
    volatile unsigned int var = 0;
    int millivolt = 0;
    
    void UART0_init(void);
    void delayMs(int n, int freq);
    void setFreq(int freq);
    
    int main(void) {
    
        setFreq(FREQ_3_MHZ);
    
        int mv_char_0 = 0;
        int mv_char_1 = 0;
        int mv_char_2 = 0;
        int mv_char_3 = 0;
        char uart_0;
        char uart_1;
        char uart_2;
        char uart_3;
    
        WDT_A->CTL = WDT_A_CTL_PW |             // Stop WDT
                     WDT_A_CTL_HOLD;
    
        // GPIO Setup
        P5->SEL1 |= BIT4;                       // Configure P5.4 for ADC
        P5->SEL0 |= BIT4;
    
        // Initialize Port 3 (button connections)
        P3->SEL0 = 0;  //clear register selection
        P3->SEL1 = 0;  //clear register selection
        P3->DIR = 0;
        P3->OUT = BIT0;
        P3->REN = BIT0;   //enable pull up resistor
    
        P3->IES = BIT0;  //interrupt on high-low transition
        P3->IFG = 0;     //clear any pending flags
        P3->IE = BIT0;   //enable port 3 interrupts
    
        UART0_init();
    
        // Enable global interrupt
        __enable_irq();
    
        // Enable ADC and Timer A0 interrupts in NVIC module
        NVIC_EnableIRQ(TA0_0_IRQn);
        NVIC_EnableIRQ(ADC14_IRQn);
        NVIC_EnableIRQ(PORT3_IRQn);
    
        // Sampling time, S&H=16, ADC14 on
        ADC14->CTL0 = ADC14_CTL0_SHT0_2 | ADC14_CTL0_SHP | ADC14_CTL0_ON;
        ADC14->CTL1 = ADC14_CTL1_RES_2;
        ADC14->MCTL[0] |= ADC14_MCTLN_INCH_1;   /* A1 ADC input select;
                                                   Vref=AVCC */
        ADC14->IER0 |= ADC14_IER0_IE0;          /* Enable ADC conv
                                                   complete interrupt */
    
        SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;  /* Wake up on exit from
                                                  ISR */
    
        while (1){
            if (button_flag){                      //button pushed, turn ADC on
                ADC14->CTL0 |= ADC14_CTL0_ENC | ADC14_CTL0_SC; //start sampling/conversion
                button_flag = 0;       //Reset flag
                P3->IFG = 0;           //clear any pending flags
                P3->IE |= BIT0;        //re-enable P3.0 interrupt
            }
            if (timer_flag){                      //time up, turn ADC off
                timer_flag = 0;
                ADC14_CTL0_ENC = 0;   //turn off ADC
            }
            if (UART_flag) {
                UART_flag = 0;
                millivolt = (int)((var + 1.46) / 1.2361);
                mv_char_3 = millivolt / 1000;
                mv_char_2 = millivolt / 100 - (mv_char_3 * 10);
                mv_char_1 = millivolt / 10 - (mv_char_3 * 100 + mv_char_2 * 10);
                mv_char_0 = millivolt - (mv_char_1 * 10 + mv_char_3 * 1000 + mv_char_2 * 100);
                uart_0 = (char) mv_char_0 + '0';
                uart_1 = (char) mv_char_1 + '0';
                uart_2 = (char) mv_char_2 + '0';
                uart_3 = (char) mv_char_3 + '0';
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = uart_3;
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = uart_2;
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = uart_1;
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = uart_0;
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = 0x0D;
            }
        }
    }
    
    // ADC14 interrupt service routine
    void ADC14_IRQHandler(void) {
        UART_flag = 1;
        var = ADC14->MEM[0];
    }
    
    //Timer A0 interrupt service routine
    void TA0_0_IRQHandler(void){
        timer_flag = 1;         //flag when timer reaches value
        TA0CCTL0 &= ~CCIFG;     //clear pending interrupt flag
    }
    
    void PORT3_IRQHandler(void)       //Interrupt handler for P3.0
    {
        button_flag = 1;              //Set flag to signal button press detected
        //configure Timer A0
        TA0CCR0 = 900000;             //Timer length = 300ms
        TA0CCTL0 |= CCIE;
        TA0CTL |= TASSEL_2 | MC_1;
        P3->IFG = 0;                  //Clear P3.0 pending interrupt flag
        P3->IE  &= ~BIT0;             //Disable interrupt for P3.0 for debouncing
    }
    
    void UART0_init(void)
    {
        EUSCI_A0->CTLW0 |= 1;       // put in reset mode for config
        EUSCI_A0->MCTLW = 0;        // disable oversampling
        EUSCI_A0->CTLW0 = 0x0081;   /* 1 stop bit, no parity,SMCLK,byte
                                       data */
        EUSCI_A0->BRW = 26;         // 3,000,000 / 115200 = 26
        P1->SEL0 |= 0x0C;           // P1.3, P1.2 for UART
        P1->SEL1 &= ~0x0C;
        EUSCI_A0->CTLW0 &= ~1;      // take UART out of reset mode
    }
    

  • Did the changes you make resolve your issue?

    Thanks,
    Chris
  • Unfortunately it did not, and I clicked the "This Resolved My Issue" button accidentally. Currently I am getting the following error:

    #138 expression must be a modifiable value main.c /ADC_TEST line 77 C/C++ Problem

    In reference to the following line, where I am trying to turn off the ADC:

    ADC14_CTL0_ENC = 0;   //turn off ADC

    Additionally, when I replace that instruction with a blinking LED (for debugging purposes) and then press the button I see one ADC value reported via UART and then the LED blinks continuously, regardless of how many additional times the button is pressed. Ideally, I want the ADC to turn on again when the button is pressed a second time.

  • > if (button_flag){ //button pushed, turn ADC on
    ...
    > P3->IE |= BIT0; //re-enable P3.0 interrupt
    You only re-enable the button on button_flag, but you won't get a button_flag until you re-enable. I suggest you key this on timer_flag or UART_flag instead.
    ----
    > ADC14_CTL0_ENC = 0; //turn off ADC
    I suspect you want :
    > ADC14->CTL0 &= ~ADC14_CTL0_ENC; // turn off ADC
  • > You only re-enable the button on button_flag, but you won't get a button_flag until you re-enable.
    On reflection, I'm pretty sure this statement is incorrect. I still suggest moving the code, since it defeats your debouncing.

    Your LED is blinking since the timer is still running. To stop the timer, set "TA0CTL=0;", probably in the timer ISR. The next button press should re-start it.

    How were you blinking the LED? I don't see that here.
  • I was blinking the LED in place of turning off the ADC as my initial attempts at doing so were causing errors.

    Thank you very much for your help! It all seems to be working great now!

**Attention** This is a public forum