Other Parts Discussed in Thread: MOTORWARE
Hi Trey,
In the Piccolo LaunchPad F28027 and F28069 code, the newer Motorware style pll.c module contains a function called PLL_setup(). This function seems to emulate much of the behavior of the older style code in the InitPll() function from F2806x_SysCtrl.c. The PLL_setup() function behaves well when used after setting up the CLK module IF an internal oscillator 1 is selected as the clock source. However, when we use code to set an EXTERNAL oscillator clock source using the XCLKIN pin (GPIO19 on out F28069), then the subsequent PLL_setup() call will crash when it invokes the final call to PLL_setDivideSelect().
Our older code for configuring the clock and PLL for an External clock source using ExtOscSel() function in the F2806x_SysCtrl.c module works just fine.
//---------------------------------------------------------------------------
// Example: ExtOscSel:
//---------------------------------------------------------------------------
// This function switches to External oscillator and turns off all other clock
// sources to minimize power consumption.
void ExtOscSel (void) {
EALLOW;
SysCtrlRegs.XCLK.bit.XCLKINSEL = 1; // 1-GPIO19 = XCLKIN, 0-GPIO38 = XCLKIN
SysCtrlRegs.CLKCTL.bit.XTALOSCOFF = 1; // Turn on XTALOSC
SysCtrlRegs.CLKCTL.bit.XCLKINOFF = 0; // Turn on XCLKIN
SysCtrlRegs.CLKCTL.bit.OSCCLKSRC2SEL = 0; // Switch to external clock
SysCtrlRegs.CLKCTL.bit.OSCCLKSRCSEL = 1; // Switch from INTOSC1 to INTOSC2/ext clk
SysCtrlRegs.CLKCTL.bit.WDCLKSRCSEL = 0; // Clock Watchdog off of INTOSC1 always
SysCtrlRegs.CLKCTL.bit.INTOSC2OFF = 1; // Turn off INTOSC2
SysCtrlRegs.CLKCTL.bit.INTOSC1OFF = 0; // Leave INTOSC1 on
EDIS;
}
I suspect that the issue we encounter when PLL_setDivideSelect() crashes when called from PLL_setup() is that the code to temporarily disable the missing clock detect circuit while waiting for the PLL to lock up was not added to the new PLL_setup() function.
Here is the existing PLL_setup() code:
void PLL_setup(PLL_Handle pllHandle, const PLL_ClkFreq_e clkMult, const PLL_DivideSelect_e divSelect)
{
if(PLL_getClkStatus(pllHandle) == PLL_ClkStatus_Missing)
{
// The clock is missing so we cannot setup the PLL correctly
asm(" ESTOP0");
}
// Set divider to max value (/4) for safety
PLL_setDivideSelect(pllHandle, PLL_DivideSelect_ClkIn_by_4);
// Set the desired multiplier
PLL_setClkFreq(pllHandle, clkMult);
while(PLL_getLockStatus(pllHandle) != PLL_LockStatus_Done)
{
}
// Set the desired divider
PLL_setDivideSelect(pllHandle, divSelect);
} // end of PLL_setup() function
I suggest that this function needs to have the PLL_disableClkDetect() and PLL_enableClkDetect() calls added as below:
void PLL_setup(PLL_Handle pllHandle, const PLL_ClkFreq_e clkMult, const PLL_DivideSelect_e divSelect)
{
if(PLL_getClkStatus(pllHandle) == PLL_ClkStatus_Missing)
{
// The clock is missing so we cannot setup the PLL correctly
asm(" ESTOP0");
}
// Set divider to max value (/4) for safety
// DIVSEL MUST be 0 before PLLCR can be changed from
// 0x0000. It is set to 0 by an external reset XRSn
PLL_setDivideSelect(pllHandle, PLL_DivideSelect_ClkIn_by_4);
// Before setting PLLCR turn off missing clock detect logic
PLL_disableClkDetect(pllHandle);
// Set the desired multiplier
PLL_setClkFreq(pllHandle, clkMult);
// Wait for the PLL lock bit to be set.
while(PLL_getLockStatus(pllHandle) != PLL_LockStatus_Done)
{
}
// After the PLL has locked, we reenable missing clock detect logic
PLL_enableClkDetect(pllHandle);
// Set the desired divider
PLL_setDivideSelect(pllHandle, divSelect);
} // end of PLL_setup() function
Compare this against the older InitPll() function:
void InitPll(Uint16 val, Uint16 divsel)
{
volatile Uint16 iVol;
// Make sure the PLL is not running in limp mode
if (SysCtrlRegs.PLLSTS.bit.MCLKSTS != 0)
{
EALLOW;
// OSCCLKSRC1 failure detected. PLL running in limp mode.
// Re-enable missing clock logic.
SysCtrlRegs.PLLSTS.bit.MCLKCLR = 1;
EDIS;
// Replace this line with a call to an appropriate
// SystemShutdown(); function.
__asm(" ESTOP0"); // Uncomment for debugging purposes
}
// DIVSEL MUST be 0 before PLLCR can be changed from
// 0x0000. It is set to 0 by an external reset XRSn
// This puts us in 1/4
if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0)
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;
EDIS;
}
// Change the PLLCR
if (SysCtrlRegs.PLLCR.bit.DIV != val)
{
EALLOW;
// Before setting PLLCR turn off missing clock detect logic
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1;
SysCtrlRegs.PLLCR.bit.DIV = val;
EDIS;
// Optional: Wait for PLL to lock.
// During this time the CPU will switch to OSCCLK/2 until
// the PLL is stable. Once the PLL is stable the CPU will
// switch to the new PLL value.
//
// This time-to-lock is monitored by a PLL lock counter.
//
// Code is not required to sit and wait for the PLL to lock.
// However, if the code does anything that is timing critical,
// and requires the correct clock be locked, then it is best to
// wait until this switching has completed.
// Wait for the PLL lock bit to be set.
// The watchdog should be disabled before this loop, or fed within
// the loop via ServiceDog().
// Uncomment to disable the watchdog
DisableDog();
while(SysCtrlRegs.PLLSTS.bit.PLLLOCKS != 1)
{
// Uncomment to service the watchdog
// ServiceDog();
}
EALLOW;
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 0;
EDIS;
}
// If switching to 1/2
if((divsel == 1)||(divsel == 2))
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = divsel;
EDIS;
}
// If switching to 1/1
// * First go to 1/2 and let the power settle
// The time required will depend on the system, this is only an example
// * Then switch to 1/1
if(divsel == 3)
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = 2;
DELAY_US(50L);
SysCtrlRegs.PLLSTS.bit.DIVSEL = 3;
EDIS;
}
}
Notice that the older code disables and reenables the value of the SysCtrlRegs.PLLSTS.bit.MCLKOFF bit while waiting for the PLL to lock.
Incidently, my new code to setup the internal or external clock source and the PLL looks like the following (if you are interested in testing an external clock). We use a TCXO (temp-compensated external oscillator) on our custom board for an external clock source tied to GPIO19.
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// Perform basic system initialization
WDOG_disable(myWDog);
CLK_enableAdcClock(myClk);
(*Device_cal)();
#if CLK_INTERNAL == 1
//Select the internal oscillator 1 as the clock source
// CLK_setOscSrc(myClk, CLK_OscSrc_Internal); // old code
CLK_enableOsc2(myClk); // Turn on INTOSC2
CLK_setOsc2Src(myClk, CLK_Osc2Src_External); // Switch to INTOSC2
CLK_disableClkIn(myClk); // Turn off XCLKIN
CLK_disableCrystalOsc(myClk); // Turn off XTALOSC
CLK_setOscSrc(myClk, CLK_OscSrc_Internal); // Switch to Internal Oscillator 2
CLK_setWatchDogSrc(myClk, CLK_WdClkSrc_IntOsc1); // Clock Watchdog off of INTOSC1 always
CLK_enableOsc1(myClk); // Leave INTOSC1 on
#elif CLK_EXTERNAL == 1
CLK_setXclkinPin(myClk, XCLKIN_GPIO19); // Select either GPIO19 or GPIO38 for XCLKIN
CLK_disableCrystalOsc(myClk); // Turn off XTALOSC
CLK_enableClkIn(myClk); // Turn on XCLKIN
CLK_setOsc2Src(myClk, CLK_Osc2Src_External); // Switch to external clock
CLK_setOscSrc(myClk, CLK_OscSrc_External); // Switch from INTOSC1 to INTOSC2/ext clk
CLK_setWatchDogSrc(myClk, CLK_WdClkSrc_IntOsc1); // Clock Watchdog off of INTOSC1 always
CLK_disableOsc2(myClk); // Turn off INTOSC2
CLK_enableOsc1(myClk); // Leave INTOSC1 on
#endif
// Setup the PLL for x18 /2 which will yield 90Mhz = 10Mhz * 18 / 2
PLL_setup(myPll, PLL_ClkFreq_90_MHz, PLL_DivideSelect_ClkIn_by_2);
// Disable the PIE and all interrupts
PIE_disable(myPie);
PIE_disableAllInts(myPie);
CPU_disableGlobalInts(myCpu);
CPU_clearIntFlags(myCpu);
Can you perhaps confirm this issue if using an external oscillator source?
Best regards,
Gordon Finlay