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.

MSP430F5529 external clock source on XT2

Other Parts Discussed in Thread: MSP430F5529

Dear MSP430 Gurus,

Our app needs low jitter and low temp drift in timing so we are using an

external clock source. The MSP430 keeps detecting osc fault condition and

switching back to the default internal source. This is my first MSP430 project,

so I'm hoping that I'm making some simple mistake which the group can point out.

Some specifics are:

Clock is Connor Winfield model D75A at 20 MHz. We buffer it through a 74LVC04

before it goes to the MSP430.

I'm using the MSP4305529. VCC = 3.5 V. Using the FET-430UIF and ccs v 4.2.5.0005

on a macbook pro 2011 running parallels desktop emulating Win XP 64 bit.

The code listed below ramps up vcore to level 3, sets XT2 to bypass, outputs

MCLK and SMCLK to port pins for monitoring, sets a timer interrupt to provide a

heartbeat signal to show the code is running, and has an ISR for osc fault

condition where it sets the ck source back to XT2 bypass. The debug version of

the code shows it spends all of it's time in the osc ISR waiting for the fault

flag to stay clear.

Things I've tried:

1. Running ccs on a real Windows XP pro machine with identical results.

2. Running the code with the internal osc selected to confirm proper operation

of the timer ISR with the heartbeat signal.

3. Running a release version of the code with the FET430 removed from the

circuit - the power supply connection to it is noisy.

4. Adding 10 uF tant, 5 uF poly and .1 uF ceramic to power supply.

5. Dead bugging a schmitt trig buffer (74LVC1G17) on top of the MSP at XT2 input

to better buffer the ck source (and bypassing this buffer power using a 1 uF and

.1 uF ceramic caps).

6. Different external power supply.

7. New FET430 (to get rid of power noise - didn't help).

8. .47 uF ceramic on vcore pin. Confirmed vcore using voltmeter and scope.

9. Triple checking the JTAG interface (SLAU138L fig3.2).

The ext ck signal, even after buffering, does not look as good as I'd like, but

it is not bad. There are over/undershoots of about 600 mV lasting for about 8

ns, but they are rounded and there is no ringing. The MSP MCLK output when

running off of internal osc shows the same level and duration of

over/undershoot, but with some ringing - likely due to unterminated pin on MCLK

output.

10. Numerous experiments show that the MSP is falling back to using the DCO. Control register flags are set to the values we picked. Fault flags in debug show for XT1 and DCO, but no fault flag for XT2.

More Info:

Examination of UCSCTL7 in debug shows the fault flags set for XT1LFOFFG and

DCOFFG and clear for XT2OFFG, so XT1 and DCO seem to be the osc faults holding

the MSP in the osc fault ISR. This is with the instruction UCSCTL6 |= XT1OFF; in

the config code. Maybe we're doing something wrong in config of DCO, but we've

turned off XT1. How can it be holding the chip in fault condition if it is

turned off?

I've read that the MSP430 is very quick to call osc fault on an external source,

but this seems ridiculous. I have to be doing something simple and wrong in the

code. Any suggestions will be much appreciated.

Code:

//******************************************************************************

// MSP430f5529 Genie Firmware r3.0

//

// Description;

//

// C. Wildey

// MRRA Inc.

// March 3, 2012

// Built with CCS core edition version 4.2.5.00005

//******************************************************************************

#include

"msp430f5529.h"

void

SetVCoreUp(unsigned intlevel);

volatile

unsigned int i;

unsigned

int level1 = 1;

unsigned

int level2 = 2;

unsigned

int level3 = 3;

//#include <stdio.h>

int

main(void)

{

WDTCTL = WDTPW + WDTHOLD;

// Stop watchdog timer

// Loop until XT1,XT2 & DCO stabilizes - in this case loop until XT2 settles

SetVCoreUp(level1);

SetVCoreUp(level2);

SetVCoreUp(level3);

__bis_SR_register(SCG0);

__bis_SR_register(SCG1);

// disable DCO and FLL

// UCSCTL1 |= DCORSEL_4;

P5SEL |= BIT2+BIT3;

// Port select XT2

UCSCTL6 |= XT2BYPASS;

UCSCTL3 = SELREF_5;

P5SEL &= ~BIT1;

// P5.1 set as I/O

P5DIR |= BIT1;

// P5.1 as output (LED1)

P5REN &= ~BIT1;

// Disable pullup/pulldown resistor

UCSCTL5 = 0x0000;

// divide by 1

UCSCTL6 |= XT1OFF;

UCSCTL6 &= ~XT2OFF;

UCSCTL4 = SELA_5 + SELS_5 + SELM_5;

do {

//UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);

UCSCTL7 &= ~(XT2OFFG);

// Clear XT2,XT1,DCO fault flags

//SFRIFG1 &= ~OFIFG; // Clear fault flags

__delay_cycles(10000);

//}while (SFRIFG1&OFIFG); // Test oscillator fault flag

}

while (UCSCTL7&XT2OFFG);

//Set up for external osc on xt2

P2DIR |= BIT2;

// SMCLK set out to pins

P2SEL |= BIT2;

P7DIR |= BIT7;

// MCLK set out to pins

P7SEL |= BIT7;

 

TBCCTL0 = CCIE;

// TBCCR0 interrupt enabled

TBCCR0 = 50000;

// counts to 50000

TBCTL = TBSSEL_2 +MC_1 + TBCLR;

__bis_SR_register(GIE);

while(1);

}

// Timer B0 interrupt service routine

#pragma

vector=TIMERB0_VECTOR

__interrupt

void TIMERB0_ISR (void)

{

P5OUT = 0x00;;

P5OUT = 0x02;

P5OUT = 0x00;;

P5OUT = 0x02;

}

void

SetVCoreUp(unsigned intlevel)

{

// 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;

}

  • And more info:

    We tried cutting it down to the simplest code possible. It works, MCLK = 20 MHz

    from the external clock. We then added a simple timer int and ISR and it is back

    to broken with MCLK coming from DCO. Code pasted below. Am really pulling hair

    out on this one and don't have that much left. Any suggestions very welcome.

    Code that works:

    #include "msp430f5529.h"

    void SetVCoreUp(unsigned int level);

    volatile unsigned int i;

    unsigned int level1 = 1;

    unsigned int level2 = 2;

    unsigned int level3 = 3;

    //#include <stdio.h>

    int main(void)

    {

    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

    SetVCoreUp(level1);

    SetVCoreUp(level2);

    SetVCoreUp(level3);

    P2DIR |= BIT2; // SMCLK set out to pins

    P2SEL |= BIT2;

    P7DIR |= BIT7; // MCLK set out to pins

    P7SEL |= BIT7;

     

    P5SEL |= BIT2+BIT3; // Port select XT2

    UCSCTL6 |= XT2BYPASS;

    //UCSCTL6 &= ~XT2OFF;

    //UCSCTL6 |= XT1OFF;

    do {

    //UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);

    UCSCTL7 &= ~(XT2OFFG);

    // Clear XT2,XT1,DCO fault flags

    //SFRIFG1 &= ~OFIFG; // Clear fault flags

    __delay_cycles(10000);

    //}while (SFRIFG1&OFIFG); // Test oscillator fault

    flag

    }while (UCSCTL7&XT2OFFG);

    UCSCTL5 = 0x0000; // divide by 1

    UCSCTL4 = SELA_5 + SELS_5 + SELM_5;

    while(1);

    }

    void SetVCoreUp(unsigned int level)

    {

    // 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;

    }

     

    Code that does not work: (i.e. falls back to DCO)

    #include "msp430f5529.h"

    void SetVCoreUp(unsigned int level);

    volatile unsigned int i;

    unsigned int level1 = 1;

    unsigned int level2 = 2;

    unsigned int level3 = 3;

    //#include <stdio.h>

    int main(void)

    {

    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

    SetVCoreUp(level1);

    SetVCoreUp(level2);

    SetVCoreUp(level3);

    P2DIR |= BIT2; // SMCLK set out to pins

    P2SEL |= BIT2;

    P7DIR |= BIT7; // MCLK set out to pins

    P7SEL |= BIT7;

    P5SEL &= ~BIT1; // P5.1 set as I/O

    P5DIR |= BIT1; // P5.1 as output (LED1)

    P5REN &= ~BIT1; // Disable pullup/pulldown resistor

    P5SEL |= BIT2+BIT3; // Port select XT2

    UCSCTL6 |= XT2BYPASS;

    //UCSCTL6 &= ~XT2OFF;

    //UCSCTL6 |= XT1OFF;

    do {

    UCSCTL7 &= ~(XT2OFFG); // Clear XT2,XT1,DCO fault flags

    __delay_cycles(10000);

    }while (UCSCTL7&XT2OFFG);

    UCSCTL5 = 0x0000; // divide by 1

    UCSCTL4 = SELA_5 + SELS_5 + SELM_5;

    TBCCTL0 = CCIE; // TBCCR0 interrupt enabled

    TBCCR0 = 50000; // counts to 50000

    TBCTL = TBSSEL_2 +MC_1 + TBCLR;

    __bis_SR_register(GIE);

    while(1);

    }

    #pragma vector=TIMERB0_VECTOR

    __interrupt void TIMERB0_ISR (void)

    {

    P5OUT = 0x00;

    P5OUT = 0x02;

    }

    void SetVCoreUp(unsigned int level)

    {

    // 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;

    }

  • Hi Jim,

    I noticed that you commented off these lines:

    //UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);

    ...

    //SFRIFG1 &= ~OFIFG; // Clear fault flags

    and replaced it with code to clear only XT2OFFG. This likely explains why your code is stuck in the oscillator NMI (and I don't see the ISR to handle the fault). The oscillator fault flags need to be cleared by the program, otherwise they'll stay set.

    As well, you disabled the DCO when the SMCLK/MCLK are still sourcing from it, hence the MSP430 will re-enable the DCO as a fail-safe. 

    Try enabling XT2, clear all the oscillator flags, change ACLK/SMCLK/MCLK to XT2, then disable the DCO/FLL. Also add an ISR to handle the oscillator faults; it helps especially during debugging.

    Tony

  • Besides the OFIFG and NMI issue, there are other things...

    OFFIFG can only be cleared (in a loop) if all OFFG bits are clear. To do so, XT1 must be disabled and the DCO must be moveed from the lowest tap (well, the FLL will change it sooner or later anyway).

    Also, you don't need to wait in a loop when trying to clear XT2OFFG. XT2OFFG can only be cleared when the fault condition is gone for a certain number of oscillator ticks. There is an internal timer that is reset when there is no oscillation for a certian time, and it doe snot let you clear the IFFG bit if it hasn't coutned to max. If you try to clear it when the tiemr is still counting, your attempt is ignore and the bit stays set. This is different to older families clock systems, where OFIFG could always be cleared and would come up again after some time.

  • Thanks Tony and Jens. Sometimes the simple problems are the hardest to find. For posterity, the working code:

    #include

    "msp430f5529.h"

    void

    SetVCoreUp(unsigned intlevel);

    volatile

    unsigned int i;

    unsigned

    int level1 = 1;

    unsigned

    int level2 = 2;

    unsigned

    int level3 = 3;

    //#include <stdio.h>

    int

    main(void)

    {

    WDTCTL = WDTPW + WDTHOLD;

    // Stop watchdog timer

    SetVCoreUp(level1);

    SetVCoreUp(level2);

    SetVCoreUp(level3);

    P2DIR |= BIT2;

    // SMCLK set out to pins

    P2SEL |= BIT2;

    P7DIR |= BIT7;

    // MCLK set out to pins

    P7SEL |= BIT7;

    P5SEL |= BIT7;

    // P5.7 set as I/O

    P5DIR |= BIT7;

    // P5.7 as output (PWM)

    //P5REN &= ~BIT1; // Disable pullup/pulldown resistor

    P5SEL |= BIT2+BIT3;

    // Port select XT2

    UCSCTL6 |= XT2BYPASS;

    UCSCTL6 &= ~XT2OFF;

    //enables xt2

    //UCSCTL6 |= XT1OFF;

    do {

    UCSCTL7 &= ~(XT2OFFG);

    // Clear XT2,XT1,DCO fault flags

    __delay_cycles(10000);

    }

    while (UCSCTL7&XT2OFFG);

    UCSCTL5 = 0x0000;

    // divide by 1

    UCSCTL4 = SELA_5 + SELS_5 + SELM_5;

    TBCCR0 = 4000;

    //PWM period

    TBCCTL1 = OUTMOD_7;

    //reset/set

    TBCCR1 = 2000;

    //50% duty cycle

    TBCTL = TBSSEL_2 +MC_1;

    __bis_SR_register(GIE);

    while(1);

    }

    void

    SetVCoreUp(unsigned intlevel)

    {

    // 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;

    }

  • Jim R said:
    do {

    UCSCTL7 &= ~(XT2OFFG);

    // Clear XT2,XT1,DCO fault flags

    __delay_cycles(10000);

    }

    while (UCSCTL7&XT2OFFG);

    Thiw will probably lockup. It tries to clear XT2OFFG once (which will probably fail if the crystal is not stable yet), then waits (after whcih delay you might be able to clear it, but you don't try. Then you loop until it is clear (which it either is at the beginning, or never will -> eternal loop)

    It is sufficient to do

    while (UCSCTL7 & XT2OFFG) UCSCTL7 &= ~XT2OFFG;

    XT2OFFG cannot be cleared until the crystal is working stable for at least 1024 oscillations (8192 for LFXT1), so unless the crystal is daulty and failing after some time, XT2OFFG will stay clear if you were able to clear it. No delays and retries required as in 1x family MSPs.

  • Jens-Michael Gross said:

    Thiw will probably lockup. It tries to clear XT2OFFG once (which will probably fail if the crystal is not stable yet), then waits (after whcih delay you might be able to clear it, but you don't try. Then you loop until it is clear (which it either is at the beginning, or never will -> eternal loop)

    I don't think the do-while loop will lock up, because it will keep on looping if the conditional test for oscillator fault fails. It will try to clear the XT2OFFG before the test, but the body of the loop will be repeated if the test evaluates to !=0.

    But I agree that your simple while loop is much better and wastes less cycles, esp since the external clock presumably do not need to stabilize.

    Tony

  • TonyKao said:
    I don't think the do-while loop will lock up,

    Of course you're right. I thought it was a do block and a while. The spacing was fooling me into seeing them as two separate things. Especially as the situation does not require a loop that is executed once at least.

    Sorry for the confusion.

    TonyKao said:
    esp since the external clock presumably do not need to stabilize

    And even if it does, it is smaller (less code) :)

**Attention** This is a public forum