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.

Why is the SD16A in the MSP430F47183 slower than expected?

Other Parts Discussed in Thread: MSP430F47187

I am trying to use the MSP430F47187 to read and plot an AC waveform. However, it seems that the SD16A sampling rate is a little less than the 4096 Hz that I expect. It turns out to be something more like 4090 or 4094 Hz. I'm running MCLK with the FLL off of a 32.768 crystal that seems to be stable and accurate when measured off of a buffered output.

My SD16A interrupt is just flipping a bit high, saving the results, and flipping the bit low. The _delay_cycles(x) command I used to make the signal tracewidth a bit wider for measurement purposes but its inclusion does not really alter the results.

Measuring my MCLK directly shows that, while a bit off of 16.777 MHz, it's not nearly enough to account for the discrepancy that I see. FYI if I run the SD16A in single trigger mode and start the capture with Timer A, then the SD16A ISR fires at 4096 on the nose. Thoughts?


#pragma vector=SD16A_VECTOR

__interrupt void SD16ISR(void)
{

P1OUT |= BIT1;

static unsigned int index = 0;
results[index] = SD16MEM0; // Save CH0 results (clears IFG)
results[index] = SD16MEM1;
results[index] = SD16MEM2;
_delay_cycles(100);

P1OUT &= ~BIT1;
}

My SD16A setup should be grouping the 3 channels together and continuously triggering them. The MCLK should be 16.777216 MHz and then divided down to 1.048576MHz. With the 256 oversampling, the interrupt should fire at 4096Hz.

void initSD16A(void)
{
SD16CTL = SD16REFON
| SD16SSEL_0                                     //_0 = MCLK, _1 = SMCLK, _2 = ACLK
| SD16XDIV_2
| SD16DIV_0
| SD16VMIDON;
SD16CTL &= ~SD16VMIDON;
SD16INCTL0 |= SD16INTDLY_0;      // Interrupt on 4th sample

SD16CCTL0 |= SD16GRP | SD16OSR_256 | SD16UNI;
SD16INCTL0 |= SD16GAIN_1;

SD16CCTL1 |= SD16GRP |SD16OSR_256;
SD16INCTL1 |= SD16GAIN_1;

SD16CCTL2 |= SD16OSR_256 | SD16IE;
SD16INCTL2 |= SD16GAIN_1;

volatile unsigned int i;
for (i = 0; i < 0x3600; i++);                  // Delay for 1.2V ref startup

}

My main function just sets up MCLK and sits in a while loop.

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
FLL_CTL0 |= DCOPLUS + XCAP11PF; // DCO+ set, freq = xtal x D x N+1. XCAP = 11 pF because I removed the capacitors from my test board. Set to 0 on normal pterodactyl - CEL
SCFI0 = FLLD_4+FN_4; // x4 DCO freq muliplier
SCFQCTL = 127; // (SCFQCTL) x 32768 x 4 = MCLK

initSD16A();

_EINT();

SD16CCTL2 |= SD16SC;

while(1); // Loop in place
}

  • Hi Chris,

    I think your error that you are seeing is falling within the allowable tolerance of the clocks.  The difference between 4090Hz (what you observed) and 4096Hz (ideal) is ~0.15%, which is really small, and likely less than the DCO/FLL drift/jitter/error will be.

    To test that the SD16A was operating correctly, I took your code, added a couple lines of code to output SMCLK and ACLK (buffered), and captures ~200mS worth the data using a logic analyzer.  Then I used the logic analyzer's software to count the number of rising edges between two rising edges on the ADC sample IO.  If the number of rising edges is 4096 (or a multiple of 4096), then I know the ADC is counting for the correct number of clock cycles.  Here is the screenshot showing the measurements:

    As you can see, I measured 10ADC Sample intervals, for an average frequency of 4.093kHz.  During this same time period, ACLK had an average frequency of 32.77KHz, and SMCLK had an average frequency of 16.77MHz.  Also, there were 40960 rising edges on SMCLK, indicating that there were exactly 4096 clocks between ADC samples, as expected.

    If you used the timer method, I would expect the same number of SMCLK cycles between ADC interrupts.  However, due to clock drift/jitter/error, I would expect some reasonable amount of deviation from 4096Hz exactly.  If your timer test yielded more accurate results, it is possible that the DCO/FLL just happened to be running a little closer to the desired frequency at that moment.

    Hopefully this addresses your concerns.

    Mike

  • So, I ended up doing some longer term testing because I had assumed that the jitter would average out over the long run and also, I've seen much better than 0.15% accuracy on these clocks so I didn't want to sell it short. I had my oscope setup up in each case for about 5 minutes so the std dev and average clock rates should be well to steady state.

    The first image is of my Timer A solution. My Timer A ISR is simply setting SD16CCTL2 |= SD16SC to start the conversion. My SD16A interrupt is doing the same thing as before, setting the bit high, iterating a for loop for some counts, setting the bit low. You can see that the histogram has a fairly normal distribution and the sampling rate is about 4095.7 Hz.

    The second picture is the SD16A timer only, with a for loop up to 50 counts. The histogram looks a bit worse and the sampling rate is something more like 4093.4 Hz.

    Interestingly, if I increase that for loop up to something more like 250 counts, the average sampling rate drops lower, to around 4091 typically (sorry, didn't have histogram set for this one).

    We're going to do some longer term testing with both situations to determine if there is a real difference or, as you said, good luck when I happened to be measuring the Timer A solution.

    Timer A firing single conversion

  • I forgot to mention in my post that I can't see the screenshot of your data. Your description of it however matches what I believe was happening.
  • Chris,

    Does your scope have the ability to count edges between two markers? If so, it would be really useful to count the number of SMCLK (or MCLK) edges between two rising edges on the GPIO signaling SD interrupts to ensure this was 4096 clock cycles (or a multiple of 4096 based on how many SD interrupts you are measuring for). This is the same test I did above, and should verify the SD16_A module is operating as expected (note that you may see plus or minus a clock edge or two based on slight delays when the interrupt fires before jumping into the ISR (plus the time for the actual jump), or even from just race conditions on the scope sampling, etc.

    If you can, run the same test with the software timer to count SMCLK edges between SD interrupts to verify that it is also 4096 clock cycles. It is possible the software timer is actually taking something slightly different than 4096 clock cycles (with the triggers, interrupts, etc timings all possibly having a slight impact on overall timing), which would change the frequency slightly. Combine this with a slightly slow clock and it is very easy for the SD only approach to measure slightly slow, while the timer approach is measuring slightly faster. I do not suspect this is the case, and I expect to see 4096 counts here, but lets test just to make sure.

    If your equipment cannot support counting clock edges, post your timer based code (so we are comparing the same solution) and I can test it with my LSA.

    The tests I ran yesterday prove the SD module is operating as expected, and any variation from the expected frequency comes directly from the variations on the clock source (the DCO/FLL in this case). If both the SD and timer approaches count the same 4096 clock edges between SD interrupts, then I think the answer is probably temperature related. The DCO on the F471x7 devices have a temperature drift of -0.4%/C, which means we would just need less than 0.5C increase in temperature on the SD approach to explain a DCO slowdown of 0.15% (4096Hz to 4090Hz). I suspect that running the SD16_A continuously with oversampling is pulling a little more current than triggering the SD with a timer, which could very easily explain an increase in internal temperature of 0.5C.

    To test this theory, output MCLK during both methods (SD only and timer triggered) and see if there is any slight variation in frequency.

    Mike

    P.S. I'm not sure why my images didn't attach, but I seem to have deleted them off my computer. If you need me to recreate the timing diagram, please let me know and I will be happy to do so, but I'm not sure its necessary at this time.
  • Thanks again Mike,

    I'm looking into edge counting capabilities of our hardware. What I did forget to mention is that I'm triggering Timer A off of the 32.768k crystal because I had a hunch that the DCO tolerance was causing the issue with the SD16A continuous mode triggered from MCLK. I realize this is a major difference and might explain the difference. I'm trying to figure out an easy way to attach a text file but I'll just paste the code for Timer A here.

    #include "msp430.h"

    #define Num_of_Results 8

    unsigned int results[Num_of_Results];

    void initClockOut(void);
    void initTimerA(void);
    void initSD16A(void);

    void main(void)
    {
    initTimerA();


    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    FLL_CTL0 |= DCOPLUS + XCAP11PF; // DCO+ set, freq = xtal x D x N+1. XCAP = 11 pF because I removed the capacitors from my test board. Set to 0 on normal pterodactyl - CEL
    SCFI0 = FLLD_4+FN_4; // x4 DCO freq muliplier
    SCFQCTL = 127; // (SCFQCTL) x 32768 x 4 = MCLK

    initSD16A();
    initClockOut();
    _EINT();



    while(1); // Loop in place
    }

    void initClockOut(void)
    {
    P1SEL |= BIT0;
    P1DIR |= BIT0;

    P1DIR = BIT1 + BIT4 + BIT5; // P1.1, P1.4 & P1.5 to output direction
    //P1SEL = BIT1 + BIT4 + BIT5; // P1.1, P1.4 & P1.5 to output SMCLK & ACLK
    }

    void initTimerA(void)
    {
    CCTL0 = CCIE;
    CCR0 = 7; //Timer will count up to CCR0
    TACTL |= TASSEL_1 | MC_1; //ISR will trigger when count reaches CCR0 + 1
    }

    void initSD16A(void)
    {
    SD16CTL = SD16REFON
    | SD16SSEL_0 //_0 = MCLK, _1 = SMCLK, _2 = ACLK
    | SD16XDIV_0
    | SD16DIV_0
    | SD16VMIDON;
    SD16CTL &= ~SD16VMIDON;
    SD16INCTL0 |= SD16INTDLY_0; // Interrupt on 4th sample

    SD16CCTL0 |= SD16GRP | SD16OSR_256 | SD16SNGL;
    SD16INCTL0 |= SD16GAIN_1;

    SD16CCTL1 |= SD16GRP |SD16OSR_256 | SD16SNGL;
    SD16INCTL1 |= SD16GAIN_1;

    SD16CCTL2 |= SD16OSR_256 | SD16IE | SD16SNGL;
    SD16INCTL2 |= SD16GAIN_1;

    volatile unsigned int i;
    for (i = 0; i < 0x3600; i++); // Delay for 1.2V ref startup
    }


    #pragma vector=SD16A_VECTOR
    __interrupt void SD16ISR(void)
    {

    _DINT();

    P1OUT |= BIT1;

    static unsigned int index = 0;
    results[index] = SD16MEM0; // Save CH0 results (clears IFG)
    results[index] = SD16MEM1;
    results[index] = SD16MEM2;

    _EINT();

    _delay_cycles(100);

    P1OUT &= ~BIT1;


    }

    // Timer B0 interrupt service routine
    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A (void)
    {
    //P1OUT ^= BIT1;
    SD16CCTL2 |= SD16SC;
    //P1OUT ^= BIT1;

    }
  • Hi Chris,

    Yes, using a different clock source for the timer will definitely change the results.  When using different clock sources, you can expect slightly different error/noise/jitter, which is what accounts for the ~0.15% error between the SD16A method (using SMCLK from the DCO/FLL) and the timer A method (using ALCK from LFXT).  Since ACLK is sourced directly from the external crystal, there is minimal noise added, and the spec for the crystal defines the accuracy.  My test board crystal measured 32.77KHz, which is really accurate and gave sample intervals using the timer method of exactly 4096Hz.  However, the DCO/FLL cannot achieve this same level of accuracy, which accounts for the error you are seeing.

    If I modify the timer code you provided to use SMCLK instead of ACLK, I get the same results as with the SD16A method (~4090Hz to 4093Hz).  To test this for yourself, just change the timer setup to the following:

        CCTL0 = CCIE;
        CCR0 = 4095; //Timer will count up to CCR0
        TACTL |= TASSEL_2 | MC_1; //ISR will trigger when count reaches CCR0 + 1

    Mike

**Attention** This is a public forum