I am having a problem with my latest program using an external TXCO oscillator with the XCLKIN pin. This program uses the newer clk.c and pll.c driver modules (rather than the older ExtOscSel() function in the F28069_sysctl.c library. I find that the MCU crashes when I attempt to change the DIVSEL divisor.
In the older F28069 v136 examples and library code, the ExtOscSel() function in F28069_sysctl.c file looked like this:
//--------------------------------------------------------------------------- // 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; }
The older InitSysCtrl() calling routine from F2806x_SysCtrl.c (modified for invoking the ExtOscSel instead of IntOsc1Sel) looked like this:
void InitSysCtrl(void) { // Disable the watchdog DisableDog(); // *IMPORTANT* // The Device_cal function, which copies the ADC & oscillator calibration values // from TI reserved OTP into the appropriate trim registers, occurs automatically // in the Boot ROM. If the boot ROM code is bypassed during the debug process, the // following function MUST be called for the ADC and oscillators to function according // to specification. The clocks to the ADC MUST be enabled before calling this // function. // See the device data manual and/or the ADC Reference // Manual for more information. EALLOW; SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1; // Enable ADC peripheral clock (*Device_cal)(); SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 0; // Return ADC clock to original state EDIS; // Select Internal Oscillator 1 as Clock Source (default), and turn off all unused clocks to // conserve power. ExtOscSel(); //<---- changed from default IntOsc1Sel(); // Initialize the PLL control: PLLCR and CLKINDIV // DSP28_PLLCR and DSP28_CLKINDIV are defined in F2806x_Examples.h InitPll(DSP28_PLLCR,DSP28_DIVSEL); // Initialize the peripheral clocks InitPeripheralClocks(); }
I started using the newer Motorware/Launchpad styl clk.c/clk.h modules to accomplish the same thing as the old ExtOscSel() routine.
My new code looks as follows. Note that it works fine when I have #define CLK_INTERNAL but not CLK_EXTERNAL. When I #define CLK_EXTERNAL, the code crashes.
teSTATUS INIT_fnSysInit(void) { // Initialize all the handles needed for this application myAdc = ADC_init((void *)ADC_BASE_ADDR, sizeof(ADC_Obj)); myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj)); myCpu = CPU_init((void *)NULL, sizeof(CPU_Obj)); myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj)); myPie = PIE_init((void *)PIE_BASE_ADDR, sizeof(PIE_Obj)); #ifdef _FLASH myFlash = FLASH_init((void *)FLASH_BASE_ADDR, sizeof(FLASH_Obj)); #endif // _FLASH myPwm1 = PWM_init((void *)PWM_ePWM1_BASE_ADDR, sizeof(PWM_Obj)); myPwm2 = PWM_init((void *)PWM_ePWM2_BASE_ADDR, sizeof(PWM_Obj)); myTimer0 = TIMER_init((void *)TIMER0_BASE_ADDR, sizeof(TIMER_Obj)); myTimer1 = TIMER_init((void *)TIMER1_BASE_ADDR, sizeof(TIMER_Obj)); myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj)); mySpiA = SPI_init((void *)SPIA_BASE_ADDR, sizeof(SPI_Obj)); mySpiB = SPI_init((void *)SPIB_BASE_ADDR, sizeof(SPI_Obj)); mySciA = SCI_init((void *)SCIA_BASE_ADDR, sizeof(SCI_Obj)); mySciB = SCI_init((void *)SCIB_BASE_ADDR, sizeof(SCI_Obj)); myWDog = WDOG_init((void *)WDOG_BASE_ADDR, sizeof(WDOG_Obj)); // Step 1. Initialize System Control: // PLL, WatchDog, enable Peripheral Clocks // Perform basic system initialization WDOG_disable(myWDog); CLK_enableAdcClock(myClk); (*Device_cal)(); #if( defined( CLK_INTERNAL ) ) //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( defined( CLK_EXTERNAL ) ) CLK_setXclkinPinGPIO19(myClk); // 1-GPIO19 = XCLKIN, 0-GPIO38 = XCLKIN CLK_disableCrystalOsc(myClk); // Turn off XTALOSC CLK_enableClkIn(myClk); // Turn on XCLKIN CLK_setOsc2Src(myClk, CLK_Osc2Src_External); // Switch to external clock ///<--- First bug at this line 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); //<--- Second crashes at this line // Disable the PIE and all interrupts PIE_disable(myPie); PIE_disableAllInts(myPie); CPU_disableGlobalInts(myCpu); CPU_clearIntFlags(myCpu); // If running from flash copy RAM only functions to RAM #ifdef _FLASH // copy the internal RAM functions (ramfuncs) into RAM before // they are executed (for faster access) init_fnCopyRamFuncs(); // copy the internal RAM constant data tables into RAM for faster access init_fnCopyRamConsts(); // Call Flash Initialization to setup flash waitstates // This function must reside in RAM FLASH_setup(myFlash); #endif // _FLASH // Step 2. Initalize GPIO: // Initalize GPIO INIT_fnInitIOConfig(); . . . }
The first problem I found was a bug in the definition of the CLK_Osc2Src_e enum values. CLK_Osc2Src_External and CLK_Osc2Src_Internal are reversed from what they should be. If I look at the TRM in SPRUH18D in Table 1-22 on p. 79, the definition for OSCCLKSRC2SEL is 0=External Oscillator and 1=Internal Oscillator. This bug caused a crash on the line above containing the call to CLK_setOsc2Src().
So the old enum definition below in clk.c should be fixed:
typedef enum { CLK_Osc2Src_Internal=(0 << 1), //!< Denotes an internal oscillator 2 source CLK_Osc2Src_External=(1 << 1) //!< Denotes an external oscillator 2 source } CLK_Osc2Src_e;
This was changed to:
typedef enum { CLK_Osc2Src_External=(0 << 1), //!< Denotes an external oscillator 2 source CLK_Osc2Src_Internal=(1 << 1) //!< Denotes an internal oscillator 2 source } CLK_Osc2Src_e;
Once I had made this fix, the code now crashes in PLL_Setup().
This function in pll.c looks like this:
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); //<---- Crashes at this line here } // end of PLL_setup() function
I had a look at the PLL_setDivideSelect() function and did not find any code to temporarily set PLLCR to turn off the missing clock detect logic (by setting SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1). The old InitPll() function in F2806x_SysCtrl.c looks like:
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; //<-- Note this code not found in equiv PLL_setup() fcn in pll.c
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; // <-- Note this code to reenable missing clk detect is not found in equiv PLL_setup() fcn in pll.c 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; } }
Can you please confirm whether the clk.c functions like CLK_SetOscSrc() and pll.c code for PLL_setup() have been tested for external XCLKIN clock sources? Any ideas on what my problem is? Perhaps the new PLL_setup() function in pll.c needs to call PLL_disableClkDetect() at the start and then PLL_enableClkDetect() at the end?