Hi Folks,
I'm using the MSP430F5659, and I want to dynamically switch the source of ACLK from REFO to an external clock signal applied to the XIN pin. When I do this, the UCS module appears to assert the XT1LFOFFG flag, and the changeover doesn't actually occur until later: much later.
To get to the bottom line first, is there any way to prevent XT1LFOFFG being set? Whether it's by careful attention to the sequence of operations, some (possibly undocumented) trick, or some other means, I don't really mind.
Now for a bit more background. We need to do some timing functions which are guaranteed to be synchronized to an external clock over a long period of time. The bomb-proof way to do that is to use the external clock to generate ACLK via XIN, and clock the timers from ACLK. The external clock has an open drain output, so it requires a pull-up, but we can not leave it enabled all the time because our system must be able to go into a deep sleep mode with the minimum possible power consumption. We can solve that by using REFO to source all clocks while we go into deep sleep, then switch the ACLK source to the external clock at XIN when the system wakes up. Everything else can continue to run from REFO, but that's not accurate enough for ACLK for what we need.
When we make the switch, the UCS asserts XT1LFOFFG, and it stays that way for 8192 cycles at XIN, as documented in the User Guide. Nothing I've tried seems to change that, and during that time ACLK continues to be derived from REFO. The kicker is that our precision external clock is 16384Hz; this is within spec for external input at XIN (10kHz to 50kHz), but nowhere near REFO at 32+kHz, so for half a second all our "precision" timing functions are off by a factor of two - a disaster. By monitoring ACLK/N at the external pin I can confirm that the frequency change is coincident with XT1LFOFFG being cleared. Once the 8192 cycle time expires everything works beautifully, but by then it's too late.
Can we avoid this 8192 cycle penalty period? The external 16384kHz input at XIN is absolutely rock solid; 50% duty cycle, nice sharp rail-to-rail square wave, and the frequency is spot on immediately with no settling delay. (The source oscillator is actually running all the time, all we really do is enable the pull-up resistor which is needed). I would be 100% comfortable with zero delay when we switch to XIN from REFO - but I can't seem to avoid those 8192 cycles.
[ As a curious aside, we sometimes see a changeover delay of about 820ms instead of 500ms. My guess is that would be 8192 cycles of the nominal 10kHz VLO clock; is that possible? Is VLO used by the fault detector as some kind of fail-safe clock in some situations, instead of XIN itself? When? This is interesting, but academic: whether 500ms or 820ms, it's WAY too long. ]
Here's our code which sets up the clock system, with everything initially running from REFO:
/*--------------------------------------------------------------------------*/
void clock_init_REFO( void )
{
// set default/initial settings: XT2 off, XT1 off (to be enabled later)
// XT2=12MHz is used only for the USB module.
// XT2DRIVE 1 8MHz < XT2 < 16MHz
// XT2BYPASS 0 XT2 sourced internally
// XT2OFF 1 XT2 is off if not used for ACLK,MCLK,SMCLK or FLL.
// XT1DRIVE 0 Lowest current consumption for XT1 LF mode.
// XTS 0 Low-frequency mode. XCAP bits in use.
// XT1BYPASS 1 XT1 sourced externally from pin.
// XCAP 0 2pF (from datasheet), irrelevant if bypassed.
// SMCLKOFF 1 SMCLK off for now.
// XT1OFF 1 XT1 is off if not used for ACLK,MCLK,SMCLK or FLL.
UCSCTL6 = XT2DRIVE|XT2OFF|XT1DRIVE|XT1OFF|XT1BYPASS|SMCLKOFF;
// set up frequency range
// DCORSEL_5 6.00-23.70 MHz (target is 14.7456MHz).
UCSCTL1 = DCORSEL_5; // set frequency range, modulation disabled
// Set up multipliers for MCLK/SMCLK.
// FLLD 0 DCOCLKDIV = DCOCLK divide by 1.
// FLLN 449 (14745600/32768 = 450).
UCSCTL2 = FLLD__1|(MCLK_REFO_DIVISOR - 1);
// Select REFOCLK (enabled automatically) as reference for the FLL.
// SELREF 2 Select REFOCLK.
// FLLREFDIV 0 DCOCLK/1.
UCSCTL3 = FLLREFDIV__1|SELREF__REFOCLK;
// Set up multipliers.
// DIVPA 5 /32, ACLK/N = 2048/32 = 64Hz.
// DIVA 4 /16, ACLK = 32768/16 = 2048Hz.
// DIVS 0 /1, SMCLK = MCLK/1.
// DIVM 0 /1, MCLK = FLL/1.
UCSCTL5 = DIVM__1|DIVS__1|DIVA__16|DIVPA__32;
// Select REFO as source for ACLK.
// SELA 2 REFO sources ACLK.
// SELS 4 DCOCLKDIV sources SMCLK
// SELM 3 DCOCLK sources MCLK.
UCSCTL4 = SELS__DCOCLKDIV|SELM__DCOCLK|SELA__REFOCLK;
// Ensure XT1,XIN not used: disable XT1 bypass and ensure SMCLK is on.
UCSCTL6 |= XT1OFF;
UCSCTL6 &= ~(SMCLKOFF|XT1BYPASS);
// Set DCO to mid tap, clear modulation counter
UCSCTL0 = DCO4;
// Wait in 32x32-cycle chunks until DCO has stabilized.
do
{
UCSCTL7 &= ~(DCOFFG);
SFRIFG1 &= ~OFIFG;
__delay_cycles(32*32);
} while (UCSCTL7 & DCOFFG);
// One last time, ensure OSC-fault interrupt flag is clear.
SFRIFG1 &= ~OFIFG;
} // clock_init_REFO()
/*--------------------------------------------------------------------------*/
Then, when we're ready to source ACLK from XIN:
/*--------------------------------------------------------------------------*/
void clock_switch_REFO_to_XIN_source( void )
{
// Enable external XT1 input and SMCLK.
ENABLE_16KHZ_PULLUP;
UCSCTL6 |= XT1BYPASS; // allow signal to get through.
UCSCTL6 &= ~(SMCLKOFF); // ensure SMCLK on.
// Reset divisors to maintain ACLK=2048Hz from 16384Hz source.
UCSCTL5 = DIVM__1|DIVS__1|DIVA__8|DIVPA__32;
// Ensure fault flag clear before selecting XIN as source of ACLK.
if (UCSCTL7 & XT1LFOFFG)
{
UCSCTL7 &= ~XT1LFOFFG;
SFRIFG1 &= ~OFIFG;
}
UCSCTL4 = SELS__DCOCLKDIV|SELM__DCOCLK|SELA__XT1CLK;
// so we can see what's going on...
DEBUG_EXPOSE_ACLK_N();
} // clock_switch_REFO_to_XIN_source()
/*--------------------------------------------------------------------------*/
Any help at all would be much appreciated.
Thanks - Graham Jones