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.

Use of XTALOSC with CLK_setOscSrc() and PLL_setup() to replace older code

Other Parts Discussed in Thread: MOTORWARE

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?