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.

problem sampling 2 inputs using ADC10 and timerB0 on MSP430FR5728

I am trying to modify the MSP430FR57xx_adc10_10.c example (which samples 3 inputs in a sequence and transfers to an array using DMA).

The main change I want to make, is to trigger the sampling using timerB0 (to generate a fixed sampling time) instead of using ADC10SC (software triggering)

I want to get the amplitude of two AC signals, so the idea is that the DMA interrupt routine is called 180 times (which is a bit more that one period of the signal) during which it will search for max and min values of the signal. After the 180'th measurement it falls out of sleep mode and the main program continues.

The problem is that the ADC seems to hang each time after the first series of 180 measurements, so the MCU stays in sleep mode the second time that it goes through the infinite wile loop.


When I break the program after it hangs, the debugger tells me it is at the __no_operation() line. TimerB0 is still running (value changes if I continue/stop). The ADC's Busy bit stays ON and the ADC10MCTL0 has counted to zero.

The ADC is no longer sampling (value in ADC10MEM0 doesn't change any more).

Does anybody have any idea why it stops calling the DMA interrupt routine after the first series of 180?

The code is below:

Thanks,

Bart

#include <msp430.h>

/*
 * main.c
 */
unsigned short ADC_Result[2];                // 8-bit ADC conversion result array
unsigned char pos=0;

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  // Configure ADC pins
  P1SEL0 |= BIT0 + BIT1;
  P1SEL1 |= BIT0 + BIT1;
  // Configure ADC10
  ADC10CTL0 = ADC10SHT_2 + ADC10MSC + ADC10ON;// 16ADCclks(dont go lower!!), MultipleSampleConversion, ADC ON
  ADC10CTL1 = ADC10SHP + ADC10CONSEQ_1 + ADC10SHS_3;     // sampling timer, channel sequence, using timerB0 as trigger
  ADC10CTL2 |= ADC10RES;                   // 10-bit resolution
  ADC10MCTL0 = ADC10INCH_1;                 // A0,A1(EoS), AVCC reference

  // ADC conversion trigger signal - TimerA0.0 (32ms ON-period)
  TB0CCR0 = 2048-1;                          // PWM Period
  TB0CCR1 = 1024-1;                           // TB0.1 ADC trigger
  TB0CCTL1 = OUTMOD_4;                       // TB0CCR0 toggle
//  TB0CTL = TBSSEL_2 + MC_1 + TBCLR;                           // MCLK source (8MHz)

  // Configure DMA0 (ADC10IFG trigger)
  DMACTL0 = DMA0TSEL__ADC10IFG;             // ADC10IFG trigger
  __data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &ADC10MEM0);
                                            // Source single address
  __data16_write_addr((unsigned short) &DMA0DA,(unsigned long) &ADC_Result[0]);
                                            // Destination array address
  DMA0SZ = 0x02;                            // 3 conversions
  DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMAEN + DMAIE;
                                            // Rpt, inc dest, byte access,
                                            // enable int after seq of convs
  while(1)
  {
    pos=0;
    TB0CTL = TBSSEL_2 + MC_1 + TBCLR;        // start timer from 0
    ADC10CTL0 &= ~ADC10ENC;
    while (ADC10CTL1 & BUSY);               // Wait if ADC10 core is active
    ADC10CTL0 |= ADC10ENC;// + ADC10SC;        // Sampling and conversion start
    __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit
//    __delay_cycles(2);                   // needed or ADC hangs
    __no_operation();                       // BREAKPOINT; check ADC_Result
    __no_operation();                       // BREAKPOINT; check ADC_Result
  }
}

#pragma vector=DMA_VECTOR
__interrupt void DMA0_ISR (void)
{
  switch(__even_in_range(DMAIV,16))
  {
    case  0: break;                          // No interrupt
    case  2:
      // sequence of conversions complete
//      __bic_SR_register_on_exit(CPUOFF);     // exit LPM
        ADC10CTL0 &= ~ADC10ENC;
        ADC10CTL0 |= ADC10ENC;
        if (pos>180) {
            __bic_SR_register_on_exit(CPUOFF);     // exit LPM
        }
      break;                                 // DMA0IFG
    case  4: break;                          // DMA1IFG
    case  6: break;                          // DMA2IFG
    case  8: break;                          // Reserved
    case 10: break;                          // Reserved
    case 12: break;                          // Reserved
    case 14: break;                          // Reserved
    case 16: break;                          // Reserved
    default: break;
  }
  pos++;
}

  • Bart,

    I'm looking into this.

    Regards,

    David

  • Dear David,

    I have found a solution/workaround:

    The best thing is to disable TimerB0 immediately after performing the 180 samplings in the interrupt code itself, before waking the device up. Before I do the next measurement/samplingseries, I switch it on and reset it in the main program.

    Still, it is strange that in the old program (the one above), the Timer can fully lock up: I even tried to switch it off (ADC10CTL0 &= ~ADC10ON;), but when I look into the registers immediately after, it is still on!

    I must admit that I am not running the latest version of CCS (5.3.0.00090)

    I have one other question:

    Is there any chance that the DMA and the input MUX of the ADC might become de-synchronised (thereby switching the readings for channel A0 and A1)?

    Might it be better to re-initialise both before each sampling series/measurement?

    Regards,

    Bart

  • Bart,

    Bart Van Thielen said:
    Still, it is strange that in the old program (the one above), the Timer can fully lock up: I even tried to switch it off (ADC10CTL0 &= ~ADC10ON;), but when I look into the registers immediately after, it is still on!

    I noticed when you toggle ADC10ENC “start the next sequence”  the overflow flag (ADC10OVIFG) is getting set and that is why you are not getting more DMA interrupts. So I did the following:

    On main
      while(1)
      {
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit
        __no_operation();                       // BREAKPOINT; check ADC_Result
    
        ADC10CTL0 &= ~ADC10ENC;
        ADC10CTL0 |= ADC10ENC;
    
        TB0CTL = TBSSEL_2 + MC_1 + TBCLR;        // start timer from 0
      }
    
    On the DMA0_ISR
        case  2:
            if (pos>190) {
            	TB0CTL = TBSSEL_2 + MC_0 + TBCLR;      // stop timer
                __bic_SR_register_on_exit(CPUOFF);     // exit LPM
            }
            else
            {
            	ADC10CTL0 &= ~ADC10ENC;
            	ADC10CTL0 |= ADC10ENC;
            }
            pos++;
          break;    

    Bart Van Thielen said:
    Is there any chance that the DMA and the input MUX of the ADC might become de-synchronised (thereby switching the readings for channel A0 and A1)?

    I do not think so, especially because how the sequence is getting re-started. But it does not hurt to check the interrupt flag register.

    Regards,

    David

**Attention** This is a public forum