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: can't seem to select a different ADC12_B channel on the fly

Part Number: MSP430FR5969


Sometimes it needs to read one ADC channel, other times it needs to read the other channel, but my code does not work.

Can someone suggest why this code does not work?  I realize there is a mode for sequences etc but it looks complicated and my code must be very close to working.

Thanks in advance!  below is the code...




void init_temp_monitor_adc(void){

 
  PM5CTL0 &= ~LOCKLPM5;

  // By default, REFMSTR=1 => REFCTL is used to configure the internal reference
  while(REFCTL0 & REFGENBUSY);
 
  // voltage reference control
  REFCTL0 = REFVSEL_2 | REFON_L | REFOUT_L;
 
   // Configure ADC12
  ADC12CTL0 = ADC12SHT0_2 | ADC12ON;        // Cycle Sample Time, ADC On  
  ADC12CTL1 = ADC12SHP;                     // Source clock is sample timer
  ADC12CTL2 |= ADC12RES_2;                  // 12-bit conversion
//  ADC12IER0 |= ADC12IE0;                    // Interrupt MEM0  -- Not Used --
 
 
 // ********* This Next Line seems to select forever either ADC12INCH_12 or ADC12INCH_13, but I want to select it when I call read_temp_adc() *******
  ADC12MCTL0 |= ADC12INCH_13 | ADC12VRSEL_1; // Select A13,  Vref Source = internal VREF (set to 2.5V above)
  //  ADC12MCTL0 |= ADC12INCH_12 | ADC12VRSEL_1; // Select A12,  Vref Source = internal VREF (set to 2.5V above)
 
 
  while(!(REFCTL0 & REFGENRDY)); // Wait for reference generator to settle
 
}

// ----------------------------------
// the parameter selects which channel to read
u16int_t read_temp_adc(u16int_t which){
  u16int_t val;
 

  if(which == 1){
    ADC12MCTL0 |= ADC12INCH_12 | ADC12VRSEL_1; // Select A12 Channel A
    
    while(!(REFCTL0 & REFGENRDY)); // Wait for reference generator to settle
    
    
    
     __delay_cycles(50);
    ADC12CTL0 |= ADC12ENC | ADC12SC;   // Start conversion
 
    // wait for conversion.
    //
    while (!(ADC12IFGR0 & ADC12IFG0)); //AA
       val = ADC12MEM0;  //
       
    ADC12MCTL0 &= ~ADC12INCH_12;
  }
  else  if(which == 2){  // TTT
    ADC12MCTL0 |= ADC12INCH_13 | ADC12VRSEL_1; // Select A13 Channel B
    
    while(!(REFCTL0 & REFGENRDY)); // Wait for reference generator to settle
    
    
    
     __delay_cycles(50);
    ADC12CTL0 |= ADC12ENC | ADC12SC;   // Start conversion
 
    // wait for conversion.
    //
    while (!(ADC12IFGR0 & ADC12IFG0)); //AA
       val = ADC12MEM0;  //
       
    ADC12MCTL0 &= ~ADC12INCH_13;
  }
   
      
  ADC12CTL0 &= ~ADC12ENC; // do we need this?
 
  return val;
}

  • Many registers have fields to which this note applies: "Can be modified only when ADC12ENC = 0." Including in ADC12MCTLx. So you need to be careful about that.

    Sequence of channels is not hard at all and well covered in the family guide.

  • thanks for your comment about ADC12ENC but I added the added the following line to the beginning of the read_temp_adc() routine with no improvement or apparent change:

    ADC12CTL0 &= ~ADC12ENC

    can you see any other problems?

  • I don't see where you configure the pins to be controlled by the ADC module but I assume you do that. I do not care at all for your use of "|=" on ADCMCTL. There aren't any set bits there you want to preserve so just use "=".

    You attempt to clear the ADC12INCH bits selected after a conversion but ADC12ENC isn't cleared so that is a problem. An operation that isn't required if you use "=".

  • here is how the pins are setup:

     
      P3SEL1 |= BIT0; // Configure P3.0 for ADC A12
      P3SEL0 |= BIT0;
     
      P3SEL1 |= BIT1; // Configure P3.1 for ADC A13
      P3SEL0 |= BIT1;

    I copied the code from example code I found somewhere, it worked to read one ADC channel, I did not know enough to question, but if not preserving other bits then you are right, it's better not to use "|=" and I'll change it.

    The funny thing is I can read from either channel, but need to recompile, see my comment: " seems to select forever " (actually, it's not forever, the last few runs it seemed that the read_temp_adc() routine succeeded in selecting the other channel, but then it was stuck on that channel. Maybe it needs time to switch channels and a delay might help. )

  • here is how the pins are setup:

     
      P3SEL1 |= BIT0; // Configure P3.0 for ADC A12
      P3SEL0 |= BIT0;
     
      P3SEL1 |= BIT1; // Configure P3.1 for ADC A13
      P3SEL0 |= BIT1;

    I copied the code from example code I found somewhere, it worked to read one ADC channel, I did not know enough to question, but if not preserving other bits then you are right, it's better not to use "|=" and I'll change it.

    The funny thing is I can read from either channel, but need to recompile, see my comment: " seems to select forever " (actually, it's not forever, the last few runs it seemed that the read_temp_adc() routine succeeded in selecting the other channel, but then it was stuck on that channel. Maybe it needs time to switch channels and a delay might help. )

    Perhaps this is the wrong approach, if someone could direct me to example code for a repeated sequence, I would love to try it!  I just need something that works!

  • it's better not to use "|=" and I'll change it.

    Does it act differently now that you've made the change?

  • it did not seem to act differently due to that change but over the weekend I experimented with the eval board and have some code that seems to change selected ADC12_B channels correctly.  I will post that code after I test is on our actual target board.

  • The following code works......

    (I am not entirely sure the dummy read is required, maybe there is a better way)



    void init_temp_monitor_adc2(void){
     
      // pin setup
      P3SEL1 |= BIT0; // Configure P3.0 for ADC A12
      P3SEL0 |= BIT0;
     
      P3SEL1 |= BIT1; // Configure P3.1 for ADC A13
      P3SEL0 |= BIT1;

      P3SEL1 |= BIT2; // Configure P3.2 for ADC A14
      P3SEL0 |= BIT2;
      //
     
      PM5CTL0 &= ~LOCKLPM5;

      // By default, REFMSTR=1 => REFCTL is used to configure the internal reference
      while(REFCTL0 & REFGENBUSY);
     
      // voltage reference control
      REFCTL0 = REFVSEL_2 | REFON_L | REFOUT_L;
     
       // Configure ADC12
      ADC12CTL0 = ADC12SHT0_2 | ADC12ON;        // Cycle Sample Time, ADC On  
      ADC12CTL1 = ADC12SHP;                     // Source clock is sample timer
      ADC12CTL2 |= ADC12RES_2;                  // 12-bit conversion
    //  ADC12IER0 |= ADC12IE0;                    // Interrupt MEM0  --- Not Used
      ADC12MCTL0 |= ADC12INCH_12 | ADC12VRSEL_1; // Select A12,  Vref = internal VREF (set to 2.5V above)   was Select A10
     
     
      while(!(REFCTL0 & REFGENRDY)); // Wait for reference generator to settle
     
     
    }

    //
    // -----------------------
    // Select and read ADC channel
    // If chan == 0 then use previously selected channel, otherwise
    // select a new channel and read it
    u16int_t read_temp_adc2(u16int_t chan){
     u16int_t val;
     
     
      if(chan != 0){
         ADC12CTL0 &= ~ADC12ENC;
        
         switch(chan){    
          case 1:   // ADC Select A12 Channel A             
                  ADC12MCTL0 &= ~ADC12INCH_14;
                  ADC12MCTL0 &= ~ADC12INCH_13;
                  ADC12MCTL0 |= ADC12INCH_12;
                  break;
                  
          case 2:   // ADC Select A13 Channel B
                  ADC12MCTL0 &= ~ADC12INCH_14;
                  ADC12MCTL0 &= ~ADC12INCH_12;
                  ADC12MCTL0 |= ADC12INCH_13;
                  break;
                  
          case 3:   // ADC Select A14 Channel C
                  ADC12MCTL0 &= ~ADC12INCH_13;
                  ADC12MCTL0 &= ~ADC12INCH_12;
                  ADC12MCTL0 |= ADC12INCH_14;
                  break;
                  
          default:      // not a valid channel
               //   return;
                  break;   
        }
      }

     
      __delay_cycles(50);
      ADC12CTL0 |= ADC12ENC | ADC12SC;
       
     
      // wait for conversion.
      //
      while (!(ADC12IFGR0 & ADC12IFG0));
         val = ADC12MEM0;  // get ADC value

        
      ADC12CTL0 &= ~ADC12ENC;
     
    return val;
    }


    // -------------------------------


    .....

    // this code calls read_temp_adc2()
    // Note that when changing channel there is a dummy read then the data is collected with read_temp_adc2(0); calls



     read_temp_adc2(1);  // <--- dummy read
      for(ii=0; ii<9; ii++){
        dat1 = read_temp_adc2(0);
         test_arr[tc_inc++] = dat1;
      }
     
    //  go to another channel.....
       read_temp_adc2(2); // <--- dummy read
      for(ii=0; ii<9; ii++){
        dat1 = read_temp_adc2(0);
         test_arr[tc_inc++] = dat1;
      }


  • That code to clear the previous INCH value is clunky. Just use ADC12MCTL0 &= ~ADC12INCH_31; and clear all of the bits in that field at once.

  • sometimes it's better to show individual operations so people see what was intended, but you are right, doing it all at once is better for code compactness.

**Attention** This is a public forum