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.

MSP430FR5969 ADC timer trigger issues

I'm trying to set up the ADC12 so that it's triggered by TimerB0 at a rate of 256Hz. I have 4 input channels to convert and the initial trigger works but the the ADC isn't triggered again. I've set the ADC12ENC to toggle in the ADC interrupt and checked that it is being toggled, but that isn't allowing the timer to trigger conversions

Here's my code

//

// ******************************************************************************

 

#include <msp430.h>

#include<stdio.h>

 

 

unsigned int ADCvar0 =0x00,ADCvar1=01,ADCvar2=02, ADCvar3=03, x=0;

unsigned long ADCtoBT;

 

int main(void)

{

  WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT

 

  CSCTL0_H = CSKEY >> 8;                    // Unlock clock registers

  CSCTL1 = DCOFSEL_3 | DCORSEL;             // Set DCO to 8MHz

  CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;

  CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;     // Set all dividers

  CSCTL0_H = 0;                             // Lock CS registers

  __no_operation();

 

  __bis_SR_register(GIE);     // LPM0, ADC12_ISR will force exit

 

   //Timer output config

  P1DIR |= BIT4;

  P1SEL0 |= BIT4;

 

  // Configure GPIO

  P1SEL1 |= BIT0;                           // Enable A/D channel A0, A1

  P1SEL0 |= BIT0;

 

  PJSEL0 |= BIT4 | BIT5; // For XT1

 

  // Disable the GPIO power-on default high-impedance mode to activate

  // previously configured port settings

  PM5CTL0 &= ~LOCKLPM5;

 

 

  //Set up internal reference

  while(REFCTL0 & REFGENBUSY);              // If ref generator busy, WAIT

    REFCTL0 |= REFVSEL_2 | REFON;             // Select internal ref = 2.5V

                                              // Internal Reference ON

    __delay_cycles(75);                       // Delay (~75us) for Ref to settle

 

  // Configure ADC12

  ADC12CTL0 |= ADC12ON | ADC12SHT0_7 | ADC12MSC;        // Turn on ADC12, set sampling time(8 cycles), set multiple inputs

  ADC12CTL1 |= ADC12SHP | ADC12CONSEQ_1 | ADC12SSEL_3;  // Use sampling timer, conversion sequence to single sequence multiple channel, use sub master clock

  ADC12MCTL0 |= ADC12VRSEL_4 | ADC12INCH_0;                // Vr+ = Vref

  ADC12MCTL1 |= ADC12VRSEL_4 | ADC12INCH_1;

  ADC12MCTL2 |= ADC12VRSEL_4 | ADC12INCH_2;

  ADC12MCTL3 |= ADC12VRSEL_4 | ADC12INCH_3 | ADC12EOS;

  ADC12CTL2 |= ADC12RES_2;                             //resolution to 12bit

  ADC12IER0 |= ADC12IE0;                    // Enable ADC conv complete interrupt

  ADC12CTL1 |=  ADC12SHS_3;                     // Set's timerB CCR1 as trigger

  ADC12CTL0 |= ADC12ENC;                    // Enable conversions

 

 

  //Configure TimerB

  //TB0CCTL0 = CCIE;

  TB0CTL = TBSSEL__SMCLK | MC__UP | ID_1;

  //TB0EX0 = TBIDEX_2;

  TB0CCR0 = 15625;

  TB0CCR1 = 100;

  TB0CCTL1 = OUTMOD_3;

 

  //Configure TimerA

  TA0CCTL0 = CCIE;

    TA0CTL = TASSEL__SMCLK | MC__UP | ID_3;

    TA0EX0 = TAIDEX_7;

    TA0CCR0 = 62500;

while(1)

       {

    __no_operation();                       // SET BREAKPOINT HERE

       }

}

 

// Timer A0 interrupt service routine

#pragma vector=TIMER0_A0_VECTOR

__interrupt void Timer_A (void)

{

       printf("%d\n",x);  //print counter

       x=0;

__no_operation();

}

 

#pragma vector = ADC12_VECTOR

__interrupt void ADC12_ISR(void)

{

  switch(__even_in_range(ADC12IV, ADC12IV_ADC12RDYIFG))

  {

    case  0: break;                         // Vector  0:  No interrupt

    case  2: break;                         // Vector  2:  ADC12MEMx Overflow

    case  4: break;                         // Vector  4:  Conversion time overflow

    case  6: break;                         // Vector  6:  ADC12HI

    case  8: break;                         // Vector  8:  ADC12LO

    case 10: break;                         // Vector 10:  ADC12IN

    case 12:

       // Vector 12:  ADC12MEM0 Interrupt

      ADCvar0 = (ADCvar0<<16)|ADC12MEM0;

      __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

      //break;                                // Clear CPUOFF bit from 0(SR)

    case 14:                          // Vector 14:  ADC12MEM1

       ADCvar1 =(ADCvar1<<16)| ADC12MEM1;

       __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

       //break;

    case 16:                         // Vector 16:  ADC12MEM2

       ADCvar2 = (ADCvar2<16) | ADC12MEM2;

       __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

       //break;

    case 18:                         // Vector 18:  ADC12MEM3

       ADCvar3 = (ADCvar3<16) |  ADC12MEM3;

       ADCtoBT = (ADCvar0<74) | (ADCvar1<56) | (ADCvar2<38) | (ADCvar3<20) | 11;

       ADC12CTL0 &= ~ADC12ENC;

       ADC12CTL0 |= ADC12ENC;

       x++;                                            //increase counter

       __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

       break;

    case 20: break;                         // Vector 20:  ADC12MEM4

    case 22: break;                         // Vector 22:  ADC12MEM5

    case 24: break;                         // Vector 24:  ADC12MEM6

    case 26: break;                         // Vector 26:  ADC12MEM7

    case 28: break;                         // Vector 28:  ADC12MEM8

    case 30: break;                         // Vector 30:  ADC12MEM9

    case 32: break;                         // Vector 32:  ADC12MEM10

    case 34: break;                         // Vector 34:  ADC12MEM11

    case 36: break;                         // Vector 36:  ADC12MEM12

    case 38: break;                         // Vector 38:  ADC12MEM13

    case 40: break;                         // Vector 40:  ADC12MEM14

    case 42: break;                         // Vector 42:  ADC12MEM15

    case 44: break;                         // Vector 44:  ADC12MEM16

    case 46: break;                         // Vector 46:  ADC12MEM17

    case 48: break;                         // Vector 48:  ADC12MEM18

    case 50: break;                         // Vector 50:  ADC12MEM19

    case 52: break;                         // Vector 52:  ADC12MEM20

    case 54: break;                         // Vector 54:  ADC12MEM21

    case 56: break;                         // Vector 56:  ADC12MEM22

    case 58: break;                         // Vector 58:  ADC12MEM23

    case 60: break;                         // Vector 60:  ADC12MEM24

    case 62: break;                         // Vector 62:  ADC12MEM25

    case 64: break;                         // Vector 64:  ADC12MEM26

    case 66: break;                         // Vector 66:  ADC12MEM27

    case 68: break;                         // Vector 68:  ADC12MEM28

    case 70: break;                         // Vector 70:  ADC12MEM29

    case 72: break;                         // Vector 72:  ADC12MEM30

    case 74: break;                         // Vector 74:  ADC12MEM31

    case 76: break;                         // Vector 76:  ADC12RDY

    default: break;

  }

}

 

 

 

 

 

  • Hi Arwen,


    I'm sorry but I am wrestling with trying to understand the ADC12 on the FR5969 myself, however, I have noted a probable problem with your code and thought I'd give you a heads-up; I note that printf is being called in the Timer_A interrupt handler which is a no-no because printf is considered to be non-reentrant and as a 'rule' should not be called from interrupts as it may conflict with a prior interrupt or with the main thread.

    A possible solution could be to call the printf function in the main while() loop and basically delete the code in the Timer_A interrupt handler.

    So, you could replace the main while loop with something like:

    while(1)

    {

    static unsigned int oldX = 0;

    if(x != oldX)

    {

    oldX = x;

    printf("%d\n", x);

    }

    }; // end of while loop

     

    In this example, if x changes, then printf will be executed. There are still subtle issues with what I have coded, but it should be enough to kick you off in the right direction.

    I hope this helps. Cheers,

    Pete.

  • Hi Pete,

    Thanks for replying.

    TimerA is being used just to check that my sample rate is right, if I remove it and insert a breakpoint in my ADC interrupt I run into the same problem of the ADC only being triggered once.

  • I've solved the problem, a coworker found an existing bug.

    Here's the working code, the needed change is underlined in case 18 of the ADC interrupt

    //

    // ******************************************************************************

     

    #include <msp430.h>

    #include<stdio.h>

     

     

    unsigned int ADCvar0 =0x00,ADCvar1=01,ADCvar2=02, ADCvar3=03, x=0;

    unsigned long ADCtoBT;

     

    int main(void)

    {

      WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT

     

      CSCTL0_H = CSKEY >> 8;                    // Unlock clock registers

      CSCTL1 = DCOFSEL_3 | DCORSEL;             // Set DCO to 8MHz

      CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;

      CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;     // Set all dividers

      CSCTL0_H = 0;                             // Lock CS registers

      __no_operation();

     

      __bis_SR_register(GIE);     // LPM0, ADC12_ISR will force exit

     

       //Timer output config

      P1DIR |= BIT4;

      P1SEL0 |= BIT4;

     

      // Configure GPIO

      P1SEL1 |= BIT0;                           // Enable A/D channel A0, A1

      P1SEL0 |= BIT0;

     

      PJSEL0 |= BIT4 | BIT5; // For XT1

     

      // Disable the GPIO power-on default high-impedance mode to activate

      // previously configured port settings

      PM5CTL0 &= ~LOCKLPM5;

     

     

      //Set up internal reference

      while(REFCTL0 & REFGENBUSY);              // If ref generator busy, WAIT

        REFCTL0 |= REFVSEL_2 | REFON;             // Select internal ref = 2.5V

                                                  // Internal Reference ON

        __delay_cycles(75);                       // Delay (~75us) for Ref to settle

     

      // Configure ADC12

      ADC12CTL0 |= ADC12ON | ADC12SHT0_7 | ADC12MSC;        // Turn on ADC12, set sampling time(8 cycles), set multiple inputs

      ADC12CTL1 |= ADC12SHP | ADC12CONSEQ_1 | ADC12SSEL_3;  // Use sampling timer, conversion sequence to single sequence multiple channel, use sub master clock

      ADC12MCTL0 |= ADC12VRSEL_4 | ADC12INCH_0;                // Vr+ = Vref

      ADC12MCTL1 |= ADC12VRSEL_4 | ADC12INCH_1;

      ADC12MCTL2 |= ADC12VRSEL_4 | ADC12INCH_2;

      ADC12MCTL3 |= ADC12VRSEL_4 | ADC12INCH_3 | ADC12EOS;

      ADC12CTL2 |= ADC12RES_2;                             //resolution to 12bit

      ADC12IER0 |= ADC12IE0;                    // Enable ADC conv complete interrupt

      ADC12CTL1 |=  ADC12SHS_3;                     // Set's timerB CCR1 as trigger

      ADC12CTL0 |= ADC12ENC;                    // Enable conversions

     

     

      //Configure TimerB

      //TB0CCTL0 = CCIE;

      TB0CTL = TBSSEL__SMCLK | MC__UP | ID_1;

      //TB0EX0 = TBIDEX_2;

      TB0CCR0 = 15625;

      TB0CCR1 = 100;

      TB0CCTL1 = OUTMOD_3;

     

    while(1)

           {

        __no_operation();                       // SET BREAKPOINT HERE

           }

    }

     

     

    #pragma vector = ADC12_VECTOR

    __interrupt void ADC12_ISR(void)

    {

      switch(__even_in_range(ADC12IV, ADC12IV_ADC12RDYIFG))

      {

        case  0: break;                         // Vector  0:  No interrupt

        case  2: break;                         // Vector  2:  ADC12MEMx Overflow

        case  4: break;                         // Vector  4:  Conversion time overflow

        case  6: break;                         // Vector  6:  ADC12HI

        case  8: break;                         // Vector  8:  ADC12LO

        case 10: break;                         // Vector 10:  ADC12IN

        case 12:

           // Vector 12:  ADC12MEM0 Interrupt

          ADCvar0 = (ADCvar0<<16)|ADC12MEM0;

          __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

          //break;                                // Clear CPUOFF bit from 0(SR)

        case 14:                          // Vector 14:  ADC12MEM1

           ADCvar1 =(ADCvar1<<16)| ADC12MEM1;

           __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

           //break;

        case 16:                         // Vector 16:  ADC12MEM2

           ADCvar2 = (ADCvar2<<16) | ADC12MEM2;

           __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

           //break;

        case 18:                         // Vector 18:  ADC12MEM3

           ADCvar3 = (ADCvar3<<16) |  ADC12MEM3;

           ADCtoBT = (ADCvar0<<74) | (ADCvar1<<56) | (ADCvar2<<38) | (ADCvar3<<20) | 11;

           while(ADC12BUSY & ADC12CTL1);

           ADC12CTL0 &= ~ADC12ENC;

           ADC12CTL0 |= ADC12ENC;

           x++;                                            //increase counter

           __bic_SR_register_on_exit(LPM0_bits); // Exit active CPU

           break;

        case 20: break;                         // Vector 20:  ADC12MEM4

        case 22: break;                         // Vector 22:  ADC12MEM5

        case 24: break;                         // Vector 24:  ADC12MEM6

        case 26: break;                         // Vector 26:  ADC12MEM7

        case 28: break;                         // Vector 28:  ADC12MEM8

        case 30: break;                         // Vector 30:  ADC12MEM9

        case 32: break;                         // Vector 32:  ADC12MEM10

        case 34: break;                         // Vector 34:  ADC12MEM11

        case 36: break;                         // Vector 36:  ADC12MEM12

        case 38: break;                         // Vector 38:  ADC12MEM13

        case 40: break;                         // Vector 40:  ADC12MEM14

        case 42: break;                         // Vector 42:  ADC12MEM15

        case 44: break;                         // Vector 44:  ADC12MEM16

        case 46: break;                         // Vector 46:  ADC12MEM17

        case 48: break;                         // Vector 48:  ADC12MEM18

        case 50: break;                         // Vector 50:  ADC12MEM19

        case 52: break;                         // Vector 52:  ADC12MEM20

        case 54: break;                         // Vector 54:  ADC12MEM21

        case 56: break;                         // Vector 56:  ADC12MEM22

        case 58: break;                         // Vector 58:  ADC12MEM23

        case 60: break;                         // Vector 60:  ADC12MEM24

        case 62: break;                         // Vector 62:  ADC12MEM25

        case 64: break;                         // Vector 64:  ADC12MEM26

        case 66: break;                         // Vector 66:  ADC12MEM27

        case 68: break;                         // Vector 68:  ADC12MEM28

        case 70: break;                         // Vector 70:  ADC12MEM29

        case 72: break;                         // Vector 72:  ADC12MEM30

        case 74: break;                         // Vector 74:  ADC12MEM31

        case 76: break;                         // Vector 76:  ADC12RDY

        default: break;

      }

    }

     

     

     

     

     

  • The comment about using printf inside an ISR (especially if the output goes through UART or, worse, console I/O) is surely right.
    It may cause lock-ups.
    But that’s not the problem.

    All the empty cases in the source code are superfluous, a simple ‘default’ case would do. In case of many possible cases but only two or three of interest, not using the __even_in_range intrinsic will result in smaller (but slightly slower) code. But that’s not a problem too.

    The ADC12 isn’t configured correctly.

    It's a bad idea to wait inside the ISR for the ADC being no longer busy. The reason why you have to wait is that you don't handle the interrupts correctly.

    You did set ADC12IE0. This means, after doing the conversion for ADC12MEM0, the interrupt is triggered. Your ISR handles it, but then falls through (no break after the case) to handling the ADCMEM1/2/3 results, which have not been converted yet. And at the end of doing the ADC12MEM3 part, the ADC is still doing the conversion for ADC12MEM1 and you have to wait for it to complete the sequence before you cna trigger the next.

    You should set ADC12IE3 and not ADC12IE0. This will call the ADC12 ISR when the last conversion of the sequence (ADC12MEM3) is complete. In the ISR, for case 18, you then read an process all four results.

    And I wonder what you mean with ‘ADCvar0<74’. This is an expression that is true or false. It is undefined what ‘true’ will mean, but usually its arithmetical result is -1/0xFFFF, so ORing something to it doesn’t make much sense. Perhaps you meant a shift operation , which would be “<<”. However, shifting a variable more than its bit size (16, in this case) will result in 0.
    If it was really meant to be a comparison, then you should define what to be done if the comparison is true or false
    For example:
    ((ADCvar0<74)?ADCvar0:0) this returns ADCvar0, if ADCvar0 is <74, and 0 if it is >=74.

**Attention** This is a public forum