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.

Repeating multichannel conversion on MSP430G2332

Hi there, 

I'm currently working on a little projet with MSP430 

I need to check if the current of a certain equipment drop below a value. So I use a CT with a diode and I read 0-3V = 0-10A (3phases current) if a phase loss appear, I need to light a LED. 

I get a rough time to get multichannel conversion working  : 

I want to get 6 channels acquisition in continous mode (or one time acq. repeated like my code), my code working, but the samplig value without any input is always around 200-300 (out of 1024) for every channel. (Like if I was getting something like 0.7V)

On the other half, while using DTC how can I know (flag) when DTC transfer is done ? 


This is my code : 

#include <msp430.h>

// *** Declaration ***

int threshold = 800;  // Compare Threshold

void init(); // Init of MSP430G2332

unsigned int ADC_Value[6]; //Array of value from ADC (6 channels)

unsigned int Current[6]; //Max value in the last second

unsigned int j=0; // Used for loop

unsigned int Div =0; // Time divider

unsigned int i=0;

//

// *** Main routine ***

int main(void)
{
  init();

  while(1)
  {

  }

}

//

// Timer A0 interrupt service routine

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)

{
	if (Div >= 1000)
	{
		//P1OUT ^= BIT6;  // Toggle P1.6
		Div =0;
		if ((Current[0] > threshold) || (Current[1] > threshold) || (Current[2] > threshold))
		{
			P1OUT |= BIT6;
		}
		else
		{
			P1OUT &= ~BIT6;
		}
		if ((Current[3] > threshold) || (Current[4] > threshold) || (Current[5] > threshold))
		{
			P1OUT |= BIT7;
		}
		else
		{
			P1OUT &= ~BIT7;
		}
		Current[0] =0;
		Current[1] =0;
		Current[2] =0;
		Current[3] =0;
		Current[4] =0;
		Current[5] =0;

	}

	//__bic_SR_register_on_exit(CPUOFF);

  ADC10CTL0 &= ~ENC;
  while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
  ADC10SA = (int)ADC_Value;              // Data buffer start (Current 5 = channel 0, acq from 5 to 0)
  for (j=0;j<6;j++)
 		 {
 			 if (ADC_Value[j] > Current[j])
 			 {
 			 Current[j] = ADC_Value[j];
 			 }
 		 }
  //__delay_cycles(50);
  Div++;
  ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion ready
  //__bis_SR_register(CPUOFF + GIE);

}

#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
}


void init(void)
{
	//WATCHDOG TIMER
	  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT

	// PIN SET
	 // P1DIR |= BIT0;
	//  P1OUT &= BIT1;
	    P1DIR |= BIT6;        					// P1.6 output
   //   P1DIR |= BIT0;

	// TIMER SETUP

	  TACCTL0 = CCIE;                           // TIMER COUNTER INTERRUPT ENABLE
	  TACCR0 = 1;                               // COUNTER PERIOD TIME (12KHZ ~ 12000 = 1SEC)
	  BCSCTL3 = LFXT1S_2;						// ACLK = 12KHZ VLO CLOCK
	  TACTL = TASSEL_1 + MC_1;                  // ACLK (VLO) COUNT UP TO CCRO

	//ADC SETUP

	  ADC10CTL0 = SREF_0 + ADC10IE + ADC10ON + ADC10SHT_2; // Ref = Vcc , Interrupt Enabled, ADCON
	  ADC10CTL1 = CONSEQ_1 + ADC10SSEL_0 + INCH_5;         // Multi-Channel, single acq, from 5 to 0
	  ADC10AE0 |= 0b111111;                                // Enable pin as analog
	  ADC10DTC1 = 0x06;                                    // Number of channel
	  ADC10CTL0 |= ENC + ADC10SC + MSC;                    // Enable conversion, start conversion, multi channel

     //LOW POWER MODE + GLOBAL INTERRUPT ON
	 // __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0 w/ interrupt

		__enable_interrupt();			// Enable interrupts.

	  return;
}

  • >while using DTC how can I know (flag) when DTC transfer is done ?

    ADC IRQ will fire.

    In timer ISR you shall be only starting ADC conversion, not reading data. You read data in ADC ISR. Currently no wonder you are getting strange readings.

  • Thanks for the quick answer :

    When I use DTC, program will never go into ADC ISR

    My code with acquisition in ADC ISR

    #include <msp430.h>
    
    // *** Declaration ***
    
    int threshold = 800;  // Compare Threshold
    
    void init(); // Init of MSP430G2332
    
    unsigned int ADC_Value[6]; //Array of value from ADC (6 channels)
    
    unsigned int Current[6]; //Max value in the last second
    
    unsigned int j=0; // Used for loop
    
    unsigned int Div =0; // Time divider
    
    unsigned int i=0;
    
    //
    
    // *** Main routine ***
    
    int main(void)
    {
      init();
    
      while(1)
      {
    
      }
    
    }
    
    //
    
    // Timer A0 interrupt service routine
    
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void Timer_A (void)
    
    {
    	if (Div >= 1000)
    	{
    		//P1OUT ^= BIT6;  // Toggle P1.6
    		Div =0;
    		if ((Current[0] > threshold) || (Current[1] > threshold) || (Current[2] > threshold))
    		{
    			P1OUT |= BIT6;
    		}
    		else
    		{
    			P1OUT &= ~BIT6;
    		}
    		if ((Current[3] > threshold) || (Current[4] > threshold) || (Current[5] > threshold))
    		{
    			P1OUT |= BIT7;
    		}
    		else
    		{
    			P1OUT &= ~BIT7;
    		}
    		Current[0] =0;
    		Current[1] =0;
    		Current[2] =0;
    		Current[3] =0;
    		Current[4] =0;
    		Current[5] =0;
    
    	}
    
    	//__bic_SR_register_on_exit(CPUOFF);
    	ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion ready
        Div++;
    
      //__bis_SR_register(CPUOFF + GIE);
    
    }
    
    #pragma vector=ADC10_VECTOR
    __interrupt void ADC10_ISR(void)
    {
    	 ADC10CTL0 &= ~ENC;
    	 while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
    	 ADC10SA = (int)ADC_Value;              // Data buffer start (Current 5 = channel 0, acq from 5 to 0)
    	 for (j=0;j<6;j++)
    	 		 {
    	 			 if (ADC_Value[j] > Current[j])
    	 			 {
    	 			 Current[j] = ADC_Value[j];
    	 			 }
    	 		 }
    	  //__delay_cycles(50);
    }
    
    
    void init(void)
    {
    	//WATCHDOG TIMER
    	  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
    
    	// PIN SET
    	 // P1DIR |= BIT0;
    	//  P1OUT &= BIT1;
    	    P1DIR |= BIT6;        					// P1.6 output
       //   P1DIR |= BIT0;
    
    	// TIMER SETUP
    
    	  TACCTL0 = CCIE;                           // TIMER COUNTER INTERRUPT ENABLE
    	  TACCR0 = 1;                               // COUNTER PERIOD TIME (12KHZ ~ 12000 = 1SEC)
    	  BCSCTL3 = LFXT1S_2;						// ACLK = 12KHZ VLO CLOCK
    	  TACTL = TASSEL_1 + MC_1;                  // ACLK (VLO) COUNT UP TO CCRO
    
    	//ADC SETUP
    
    	  ADC10CTL0 = SREF_0 + ADC10IE + ADC10ON + ADC10SHT_2; // Ref = Vcc , Interrupt Enabled, ADCON
    	  ADC10CTL1 = CONSEQ_1 + ADC10SSEL_0 + INCH_5;         // Multi-Channel, single acq, from 5 to 0
    	  ADC10AE0 |= 0b111111;                                // Enable pin as analog
    	  ADC10DTC1 = 0x06;                                    // Number of channel
    	  ADC10CTL0 |= ENC + ADC10SC + MSC;                    // Enable conversion, start conversion, multi channel
    
         //LOW POWER MODE + GLOBAL INTERRUPT ON
    	 // __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0 w/ interrupt
    
    		__enable_interrupt();			// Enable interrupts.
    
    	  return;
    }
    
    

  • You shall check source code examples where you can find working ADC with DTC.
  • Using TI example : 
    Getting without any input : some 3xx reading out of 1024 



    //   MSP430G2x32/G2x52 Demo - ADC10, DTC Sample A1-0 16x, AVcc, Repeat Seq, DCO
    //
    //  Description: Use DTC to sample A1/A0 repeat sequence 16x(32 total samples)
    //  with reference to AVcc.  Software sets ADC10SC to trigger sample burst.
    //  In Mainloop MSP430 waits in LPM0 to save power until ADC10 conversion
    //  complete, ADC10_ISR will force exit from any LPMx in Mainloop on reti.
    //  ADC10 internal oscillator times sample period (16x) and conversion (13x).
    //  DTC transfers conversion code to RAM 200h - 240h.  ADC10(DTC) interrupt
    //  will return system active. P1.0 set at start of conversion burst, reset
    //  on completion.
    //
    
    #include <msp430.h>
    
    int result[2];
    
    int main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
      ADC10CTL1 = INCH_1 + CONSEQ_3;            // A1/A0, repeat multi channel
      ADC10CTL0 = ADC10SHT_2 + MSC + ADC10ON + ADC10IE;
      ADC10AE0 = 0x03;                          // P1.0,1 ADC option select
      ADC10DTC1 = 0x20;                         // 16 conversions
    
      for (;;)
      {
        ADC10CTL0 &= ~ENC;
        while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
        ADC10SA = (int)result;                        // Data buffer start                              // ONLY line that i've change 
        ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion ready
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit
        __no_operation();                       // space for debugger
        __no_operation();                       // Set Breakpoint here to read ADC
      }
    }
    
    // ADC10 interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=ADC10_VECTOR
    __interrupt void ADC10_ISR (void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(ADC10_VECTOR))) ADC10_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
    }
    

  • 1- With only one ADC no multi sequence : value is good, about 8/1024 to 12/1024 !

    ADC10CTL0 = SREF_0 + ADC10IE + ADC10ON + ADC10SHT_2;
    ADC10CTL1 = INCH_4; // Select input A1
    ADC10AE0 |= BIT4; // PA.1 ADC option select

    while(1)
    {
    ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
    __bis_SR_register(CPUOFF + GIE); // Low Power Mode 0 with interrupts enabled
    ADC_value = ADC10MEM; // Assigns the value held in ADC10MEM to the integer called ADC_value
    }
  • >Getting without any input : some 3xx reading out of 1024
    If by "without any input" you mean "nothing connected to ADC input" then indeed you shall not expect to read 0 values but close to midpoint. You better supply known fixed voltage low inpedance source to ADC input.
  • Thanks for the tips,

    Everything working fine now :

    1- With my current transformer connected I finally get my ~0 when no current (as Ilmars told, need do hang on somthing reliable)

    2- Action of ADC done in a while loop

    3- Timer trig every ~1sec

    Code included for reference if needed 

    #include <msp430.h>
    
    // *** Declaration ***
    
    int threshold = 800;  // Compare Threshold
    
    void init(); // Init of MSP430G2332
    
    unsigned int ADC_Value[6]; //Array of value from ADC (6 channels)
    
    unsigned int Current[6]; //Max value in the last second
    
    unsigned int j=0; // Used for loop
    
    unsigned int Div =0; // Time divider
    
    unsigned int i=0;
    
    //
    
    
    
    int main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
      ADC10CTL1 = INCH_5 + CONSEQ_1;            // A5/A4/A3/A2/A1/A0, single sequence
      ADC10CTL0 = ADC10SHT_2 + MSC + ADC10ON + ADC10IE;
      ADC10DTC1 = 0x06;                         // 6 conversions
      ADC10AE0 |= 0b111111;                     // P1 ADC10 option select
      P1DIR |= BIT6;                            // Set P1.6 output
    
      // TIMER SETUP
    
      TACCTL0 = CCIE;                           // TIMER COUNTER INTERRUPT ENABLE
      TACCR0 = 12000;                               // COUNTER PERIOD TIME (12KHZ ~ 12000 = 1SEC)
      BCSCTL3 = LFXT1S_2;						// ACLK = 12KHZ VLO CLOCK
      TACTL = TASSEL_1 + MC_1;                  // ACLK (VLO) COUNT UP TO CCRO
    
      for (;;)
      {
        ADC10CTL0 &= ~ENC;
        while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
        ADC10SA = (int)ADC_Value;                  // Data buffer start
        for (j=0;j<6;j++)
         		 {
         			 if (ADC_Value[j] > Current[j])
         			 {
         			 Current[j] = ADC_Value[j];
         			 }
         		 }
        ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit
      }
    }
    
    // ADC10 interrupt service routine
    #pragma vector=ADC10_VECTOR
    __interrupt void ADC10_ISR(void)
    
    {
      __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
    }
    
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void Timer_A (void)
    
    {
    	P1OUT ^= BIT6;
    	/*if ((Current[0] > threshold) || (Current[1] > threshold) || (Current[2] > threshold))
    			{
    				P1OUT |= BIT6;
    			}
    			else
    			{
    				P1OUT &= ~BIT6;
    			}
    			if ((Current[3] > threshold) || (Current[4] > threshold) || (Current[5] > threshold))
    			{
    				P1OUT |= BIT7;
    			}
    			else
    			{
    				P1OUT &= ~BIT7;
    			}
    			*/
    			Current[0] =0;
    			Current[1] =0;
    			Current[2] =0;
    			Current[3] =0;
    			Current[4] =0;
    			Current[5] =0;
    	__bic_SR_register_on_exit(CPUOFF);
    
    }
    

  • Still you are not reading ADC values _after_ they are sampled but while they are measured. This is way to nasty surprises in some (other) applications. Better you start ADC conversion sequence, put CPU into sleep to be wakened-up when conversions done so you can process new values just measured:


    ADC10SA = (int)ADC_Value; // Data buffer start
    ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
    __bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit
    for (j=0;j<6;j++)
    {
    if (ADC_Value[j] > Current[j])
    {
    Current[j] = ADC_Value[j];
    }
    }

  • Question about your comment :

    Those line aren't doing it %

    ADC is disable, while wait that ADC is down and then I get the value ?

    ADC10CTL0 &= ~ENC; // Desactivate ADC10
    while (ADC10CTL1 & BUSY); // Wait if ADC10 core is active
    ADC10SA = (int)ADC_Value;
  • When you disable ADC you don't get values because ADC is disabled and not converting. Code snippet you mention with ADC disable is only about DTC address setting. You shall pay attention to code that follows, especially taking in account comments:

    ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
    __bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit

    Those two lines mean: start ADC conversion (sequence), put CPU into sleep and let ADC ISR wake-up CPU so we can process data after that. For() cycle shall look like this:

     for (;;)
      {
        ADC10CTL0 &= ~ENC;
        while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
        ADC10SA = (int)ADC_Value;                  // Data buffer start
        ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit
     
    
        for (j=0;j<6;j++)
                 {
                     if (ADC_Value[j] > Current[j])
                     {
                     Current[j] = ADC_Value[j];
                     }
                 }
      }
    }
    

**Attention** This is a public forum