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.

MSP430F5172: MODOSC frequency for ADCCLK

Part Number: MSP430F5172

Hello, 

I am using ADC for sensing voltages and currents using MSP430f5172 microcontroller. I am using the MODOSC clk so the datasheet says it operates at 5MHz. However, when I used a simple LED on and off code to see it's execution time it is in 10s of kHz or below. Is this the correct way to measure the ADC sampling frequency? I was expecting ~300kHz since I am using sampling and holding 16 samples. 

  • There's also (10+1) conversion clocks, which limits you to about 185ksps with CONSEQ=2 [Ref User Guide (SLAU208Q) Fig 27-8].

    My first guess is that you're overrunning your LED monitor. With a 1MHz MCLK, you only get about 5 MCLKs per conversion (CONSEQ=2), which is not nearly enough to call an ISR. Can you describe your test case (e.g. post/attach your code)?

  • Hello Bruce, 

    thank you for your reply. Below is my code. I am using DMA and reading sequence of channels at once. 

    #include <msp430.h>
    #include <stdint.h>
    #include <math.h>

    //define functions
    void SetVcoreUp (unsigned int level);
    void SetADC ();
    void SetDMA();

    #define T_buck 200 // period of buck stage - 200MHz/500kHz (desired freq)=400
    #define T_boost 200 // // period of boost stage
    unsigned int ADC_Result[9]; // 10-bit ADC conversion result array
    int Iin_temp; // 10-bit ADC conversion result array
    int Vin_temp; // 10-bit ADC conversion result array
    int Vbat_temp; // 10-bit ADC conversion result array
    int Ibat_temp; // 10-bit ADC conversion result array
    int Vout_temp; // 10-bit ADC conversion result array
    int Iout_temp; // 10-bit ADC conversion result array
    int Vbuckout_temp;

    void main(void) {


    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

    // Configure PWM channels
    P1SEL |= BIT7; // Set P1.7 to output direction - High BUCK
    P1DIR |= BIT7;

    P3SEL |= BIT0; // Set P3.0 to output direction - BAT_GD_EN
    P3DIR |= BIT0;

    P2SEL |= BIT0; // Set P2.0 to output direction - Low BUCK
    P2DIR |= BIT0;

    P2SEL |= BIT2; // Set P2.2 to output direction - High BOOST
    P2DIR |= BIT2;

    P2SEL |= BIT3; // Set P2.3 to output direction - Low BOOST
    P2DIR |= BIT3;

    P2SEL |= BIT6; // Set P2.6 to output direction - Vgate BAT
    P2DIR |= BIT6;

    // configure ADC pins
    PMAPPWD = 0x02D52; // Enable Write-access to modify port mapping registers
    PMAPCTL = PMAPRECFG; // Allow reconfiguration during runtime
    //P1MAP0|= PM_ANALOG; // Modify all PxMAPy registers - A0
    P1MAP1|= PM_ANALOG; // Modify all PxMAPy registers - A1
    P1MAP2|= PM_ANALOG; // Modify all PxMAPy registers - A2
    P1MAP3|= PM_ANALOG; // Modify all PxMAPy registers - A3
    P1MAP4|= PM_ANALOG; // Modify all PxMAPy registers - A4
    P1MAP5|= PM_ANALOG; // Modify all PxMAPy registers - A5
    P3MAP5|= PM_ANALOG; // Modify all PxMAPy registers - A8
    P3MAP6|= PM_ANALOG; // Modify all PxMAPy registers - A7
    PMAPPWD = 0; // Disable Write-Access to modify port mapping registers by writing incorrect key
    P1SEL |=BIT5+BIT4+BIT3+BIT2+BIT1+BIT0; // setting the port mapping register PxMAPy to PM_ANALOG together with PxSEL.y=1 when applying analog signals
    P3SEL |=BIT6+BIT5;

    // Increase Vcore setting to level3 to support fsystem=25MHz
    // NOTE: Change core voltage one level at a time.

    SetVcoreUp (0x01);
    SetVcoreUp (0x02);
    SetVcoreUp (0x03);

    // Initialize DCO to 25MHz
    __bis_SR_register(SCG0); // Disable the FLL control loop - set SCG0
    UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx
    UCSCTL1 = DCORSEL_6; // Select DCO range 4.6MHz-88MHz operation
    UCSCTL2 = FLLD_1+763; // Set DCO Multiplier for 25MHz
    // (N + 1) * FLLRef = Fdco
    // (762 + 1) * 32768 = 25MHz
    // Set FLL Div = fDCOCLK/2

    __bic_SR_register(SCG0); // Enable the FLL control loop - clear SCG0

    // Worst-case settling time for the DCO when the DCO range bits have been
    // changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
    // UG for optimization.
    // 32 x 32 x 25 MHz / 32,768 Hz = 781250 = MCLK cycles for DCO to settle
    //__delay_cycles(781250);
    __delay_cycles(782000);

    SetADC();
    SetDMA();
    SetPWM(T_buck, T_boost);
    __delay_cycles(100); // Delay between sequence convs

    P1DIR |= BIT0; // Set P1.0 to output direction

    for(;;){

    // ADC sampling frequency 
    P1OUT ^= BIT0; // Toggle P1.0 using exclusive-OR

    while (ADC10CTL1 & BUSY); // Wait if ADC10 core is active
    ADC10CTL0 |= ADC10ENC + ADC10SC; // Sampling and conversion ready
    __bis_SR_register(CPUOFF + GIE); // LPM0, ADC10_ISR will force exit
    }
    __bis_SR_register(LPM0_bits); // Enter LPM0
    }


    // DMA ISR
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=DMA_VECTOR
    __interrupt void DMA0_ISR (void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(DMA_VECTOR))) DMA0_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
    switch(__even_in_range(DMAIV,16))
    {
    case 0: break; // No interrupt
    case 2:
    // sequence of conversions complete
    ADC10CTL0 &= ~ADC10ENC; // Disable ADC conversion
    __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;
    }
    }

    // functions

    void SetVcoreUp (unsigned int level)
    {
    // Subroutine to change core voltage
    // Open PMM registers for write
    PMMCTL0_H = PMMPW_H;
    // Set SVS/SVM high side new level
    SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level;
    // Set SVM low side to new level
    SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level;
    // Wait till SVM is settled
    while ((PMMIFG & SVSMLDLYIFG) == 0);
    // Clear already set flags
    PMMIFG &= ~(SVMLVLRIFG + SVMLIFG);
    // Set VCore to new level
    PMMCTL0_L = PMMCOREV0 * level;
    // Wait till new level reached
    if ((PMMIFG & SVMLIFG))
    while ((PMMIFG & SVMLVLRIFG) == 0);
    // Set SVS/SVM low side to new level
    SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;
    // Lock PMM registers for write access
    PMMCTL0_H = 0x00;

    }

    // Configure ADC Channels
    void SetADC (){
    ADC10CTL0 = ADC10SHT_2 + ADC10MSC + ADC10ON; // 16xADC clock cycles, multiple sample conversion, ADC10ON
    ADC10CTL1 = ADC10SSEL_0+ADC10SHP+ ADC10CONSEQ_1; // ADCCLK = MODOSC; sampling SIGNAL is sourced from the sampling timer, single seq.
    ADC10CTL2 = ADC10RES; // 10-bit conversion results
    ADC10MCTL0= ADC10INCH_8 + ADC10SREF_1; //Select ADC channel; USE VR+ = VREF and VR- = AVSS

    // By default, REFMSTR=1 => REFCTL is used to configure the internal reference
    while(REFCTL0 & REFGENBUSY); // If ref generator busy, WAIT
    REFCTL0 |= REFVSEL_2 + REFON; // Select internal ref = 2.5V
    __delay_cycles (75); // Delay (~75us) for Ref to settle -ADC10 sample & convert = (32+13)*2/SMCLK = 90/SMCLK = 75us
    }

    void SetDMA(){
    // Configure DMA0 (ADC10IFG trigger)
    DMACTL0 = DMA0TSEL_24; // ADC10IFG trigger
    __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC10MEM0); // Source single address
    __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &ADC_Result[0]); // Update destination array address
    DMA0SZ = 0x09; // 8 conversions - number of byte or word transfer
    DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMAEN +DMAIE; // Rpt, inc dest, enable int after seq of convs
    }

  • It looks as though you're converting 9 channels (INCH=8, MSC=1, CONSEQ=1). So I would expect roughly (5M/(16+11)/9) = 20k "bursts"/sec. Is that what your measurement says?

  • Yes, that’s close to the value I am getting. I have two questions though. Why are we dividing by (16+11) . I understand why 16 is there but not sure about 11. My second question is  related to using DMA vs sequential code to do sensing in each  channel. I have about 7 sensors on my hardware prototype and I thought using DMA and the auto scan mode to get all the channels would help me improve the performance. Is there a method that I should use to improve performance? I am using this in buck and boost converter stages and my closed loop is not stabilizing since my sampling frequency is very small compared to the switching frequency (500kHz) 

  • 1) The 11 refers to 10 clocks for conversion and 1 clock for moving the result. [Ref UG Fig 27-7]. Looking at UG Table 27-5, the "10" refers to an 8-bit result. Your case (10-bit) takes (12+1) for the conversion proper.

    2) CONSEQ=1 with DMA is pretty close to the best you can do. You could do CONSEQ=3, but then you wouldn't get to choose the sample rate. There's only one sampling capacitor (set), so you can't parallelize the conversions.

    One option is to reduce the sample/hold time (SHT0). There's a lower limit which is really electrical (source impedance) but if you're not at that limit you can gain back a few clocks [Ref UG Sec 27.2.5.3]. You could also do 8-bit sampling which would take 2 fewer conversion clocks. Even all this wouldn't gain you more than around 40%.

    The data sheet only claims 200ksps (asymptotically), and spread out over 9 channels that works out to about 22ksps for each channel. You won't succeed in Nyquist-sampling even a single 500kHz signal. I don't know much detail about how switching regulators work. Is there any chance you could do something in the digital domain?

  • Thank you for the detailed response. I tried lowering the sampling frequency in my PLECs simulation and it works but not in the hardware. I will look into other options. 

    Also is there a factor of 2 that could be missing in the calculation because I just checked the sampling frequency and it is actually ~ half of what the computation gives. For 9 samples, I am getting a sampling frequency of 9kHz and for 1 sample I get around 62.5KHz. Also the sampling frequency is higher when I use the submodule/master clocks than the MODOSC. I was expecting it to be higher for the MODCLCK, but also the DMA works with MODCLK so I wasn't getting the correct sensor readings when using these alternate clocks. Could I be I doing something wrong in the setup?

  • If you were using SMCLK (25MHz), keep in mind that the ADC10_A is limited to a 5.5MHz clock [Ref data sheet (SLAS619R) Sec 5.39], so you need to divide SMCLK down, at which point you might as well use MODCLK.

    I suspect you were also limited by your source impedance; this often shows up as cross-talk. This is a laws-of-physics thing (how fast can you get electrons into the sampling capacitor?) so there aren't too many tricks there. You would get the same effect if you reduced SHT0 too low.

    There's a formula in UG Sec 27.2.5.3 which gives you minimum sampling time (then translate to clocks). My experience is that the formula is a bit optimistic, and I usually have to round up.

**Attention** This is a public forum