//########################################################################### // // FILE: sysctl.c // // TITLE: C28x system control driver. // //########################################################################### // $Copyright: // Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the // distribution. // // Neither the name of Texas Instruments Incorporated nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // $ //########################################################################### #include "cputimer.h" #include "sysctl.h" // // Define to isolate inline assembly // #define SYSCTL_DELAY __asm(" .if __TI_EABI__\n" \ " .asg SysCtl_delay , _SysCtl_delay\n" \ " .endif\n" \ " .def _SysCtl_delay\n" \ " .sect \".TI.ramfunc\"\n" \ " .global _SysCtl_delay\n" \ "_SysCtl_delay:\n" \ " SUB ACC,#1\n" \ " BF _SysCtl_delay, GEQ\n" \ " LRETR\n") #define SYSCTL_CLRC_DBGM __asm(" CLRC DBGM") // // Define Timer1 and Timer2 seed values // #define TMR1SYSCLKCTR 0xF0000000U #define TMR2INPCLKCTR 0x800U #define XTAL_CPUTIMER_PERIOD 1023U // // Macro used for adding delay between 2 consecutive writes to CLKSRCCTL1 // register. // Delay = 300 NOPs // #define SYSCTL_CLKSRCCTL1_DELAY asm(" RPT #250 || NOP \n RPT #50 || NOP") //***************************************************************************** // // SysCtl_delay() // //***************************************************************************** SYSCTL_DELAY; static void SysCtl_pollCpuTimer(void) { // // Delay for 1 ms while the XTAL powers up // // 2000 loops, 5 cycles per loop + 9 cycles overhead = 10009 cycles // SysCtl_delay(2000); // // Wait for cpu timer 2 to overflow // while(CPUTimer_getTimerOverflowStatus(CPUTIMER2_BASE) == false); { // // If your application is stuck in this loop, please check if the // input clock source is valid. // } // // Clear cpu timer 2 overflow flag // CPUTimer_clearOverflowFlag(CPUTIMER2_BASE); } //***************************************************************************** // // SysCtl_getClock() // //***************************************************************************** uint32_t SysCtl_getClock(uint32_t clockInHz) { uint32_t temp; uint32_t oscSource; uint32_t clockOut; // // Don't proceed if an MCD failure is detected. // if(SysCtl_isMCDClockFailureDetected()) { // // OSCCLKSRC2 failure detected. Returning the INTOSC1 rate. You need // to handle the MCD and clear the failure. // clockOut = SYSCTL_DEFAULT_OSC_FREQ; } else { // // If one of the internal oscillators is being used, start from the // known default frequency. Otherwise, use clockInHz parameter. // oscSource = HWREG(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) & (uint32_t)SYSCTL_CLKSRCCTL1_OSCCLKSRCSEL_M; if((oscSource == (SYSCTL_OSCSRC_OSC2 >> SYSCTL_OSCSRC_S)) || (oscSource == (SYSCTL_OSCSRC_OSC1 >> SYSCTL_OSCSRC_S))) { clockOut = SYSCTL_DEFAULT_OSC_FREQ; } else { clockOut = clockInHz; } // // If the PLL is enabled calculate its effect on the clock // if((HWREG(CLKCFG_BASE + SYSCTL_O_SYSPLLCTL1) & (SYSCTL_SYSPLLCTL1_PLLEN | SYSCTL_SYSPLLCTL1_PLLCLKEN)) == 3U) { // // Calculate portion from fractional multiplier // temp = (clockInHz * ((HWREG(CLKCFG_BASE + SYSCTL_O_SYSPLLMULT) & SYSCTL_SYSPLLMULT_FMULT_M) >> SYSCTL_SYSPLLMULT_FMULT_S)) / 4U; // // Calculate integer multiplier and fixed divide by 2 // clockOut = clockOut * ((HWREG(CLKCFG_BASE + SYSCTL_O_SYSPLLMULT) & SYSCTL_SYSPLLMULT_IMULT_M) >> SYSCTL_SYSPLLMULT_IMULT_S); // // Add in fractional portion // clockOut += temp; } if((HWREG(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) & SYSCTL_SYSCLKDIVSEL_PLLSYSCLKDIV_M) != 0U) { clockOut /= (2U * (HWREG(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) & SYSCTL_SYSCLKDIVSEL_PLLSYSCLKDIV_M)); } } return(clockOut); } //***************************************************************************** // // SysCtl_getAuxClock() // //***************************************************************************** uint32_t SysCtl_getAuxClock(uint32_t clockInHz) { uint32_t temp; uint32_t oscSource; uint32_t clockOut; oscSource = HWREG(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) & (uint32_t)SYSCTL_CLKSRCCTL2_AUXOSCCLKSRCSEL_M; // // If one of the internal oscillators is being used, start from the // known default frequency. Otherwise, use clockInHz parameter. // if(oscSource == (SYSCTL_AUXPLL_OSCSRC_OSC2 >> SYSCTL_OSCSRC_S)) { // // 10MHz Internal Clock // clockOut = SYSCTL_DEFAULT_OSC_FREQ; } else { clockOut = clockInHz; } // // If the PLL is enabled calculate its effect on the clock // if((HWREG(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) & (SYSCTL_AUXPLLCTL1_PLLEN | SYSCTL_AUXPLLCTL1_PLLCLKEN)) == 3U) { // // Calculate portion from fractional multiplier // temp = (clockInHz * ((HWREG(CLKCFG_BASE + SYSCTL_O_AUXPLLMULT) & SYSCTL_AUXPLLMULT_FMULT_M) >> SYSCTL_AUXPLLMULT_FMULT_S)) / 4U; // // Calculate integer multiplier // clockOut = clockOut * ((HWREG(CLKCFG_BASE + SYSCTL_O_AUXPLLMULT) & SYSCTL_AUXPLLMULT_IMULT_M) >> SYSCTL_AUXPLLMULT_IMULT_S); // // Add in fractional portion // clockOut += temp; } clockOut /= (1U << (HWREG(CLKCFG_BASE + SYSCTL_O_AUXCLKDIVSEL) & SYSCTL_AUXCLKDIVSEL_AUXPLLDIV_M)); return(clockOut); } //***************************************************************************** // // SysCtl_setClock() // //***************************************************************************** bool SysCtl_setClock(uint32_t config) { uint16_t divSel; uint16_t iMult = 0U, fMult = 0U, pllMult = 0U, div; bool status, sysclkInvalidFreq = true; uint16_t i, tempSCSR, tempWDCR, tempWDWCR, intStatus; uint16_t t1TCR, t1TPR, t1TPRH, t2TCR, t2TPR, t2TPRH, t2CLKCTL; uint32_t t1PRD, t2PRD, ctr1; float32_t sysclkToInClkError, mult; // // Check the arguments. // ASSERT((config & SYSCTL_OSCSRC_M) != SYSCTL_OSCSRC_M); // 3 is not valid // // Don't proceed to the PLL initialization if an MCD failure is detected. // if(SysCtl_isMCDClockFailureDetected()) { // // OSCCLKSRC2 failure detected. Returning false. You'll need to clear // the MCD error. // status = false; } else { // // Configure oscillator source // SysCtl_selectOscSource(config & SYSCTL_OSCSRC_M); // // Bypass PLL // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLCTL1) &= ~SYSCTL_SYSPLLCTL1_PLLCLKEN; EDIS; // // Delay of at least 120 OSCCLK cycles required post PLL bypass // SysCtl_delay(23U); // // Configure PLL if enabled // EALLOW; if((config & SYSCTL_PLL_ENABLE) == SYSCTL_PLL_ENABLE) { if((HWREGH(DEVCFG_BASE + SYSCTL_O_SYSDBGCTL) & SYSCTL_SYSDBGCTL_BIT_0) != 0U) { // // The user can optionally insert handler code here. This will // only be executed if a watchdog reset occurred after a failed // system PLL initialization. See your device user's guide for // more information. // // If the application has a watchdog reset handler, this bit // should be checked to determine if the watchdog reset // occurred because of the PLL. // // No action here will continue with retrying the PLL as // normal. // } // // Set dividers to /1 // HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) = 0U; // // Get the PLL multiplier settings from config // iMult |= (uint16_t)(config & SYSCTL_IMULT_M); fMult |= (uint16_t)((config & SYSCTL_FMULT_M) >> SYSCTL_FMULT_S); pllMult |= (iMult << SYSCTL_SYSPLLMULT_IMULT_S) | (fMult << SYSCTL_SYSPLLMULT_FMULT_S); // // Lock the PLL five times. This helps ensure a successful start. // Five is the minimum recommended number. The user can increase // this number according to allotted system initialization time. // for(i = 0U; i < 5U; i++) { // // Turn off PLL // HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLCTL1) &= ~SYSCTL_SYSPLLCTL1_PLLEN; asm(" RPT #60 || NOP"); // // Write multiplier, which automatically turns on the PLL // HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLMULT) = pllMult; // // Wait for the SYSPLL lock counter // while((HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLSTS) & SYSCTL_SYSPLLSTS_LOCKS) == 0U) { // // Consider to servicing the watchdog using // SysCtl_serviceWatchdog() // } } } // // Configure Dividers. Set divider to produce slower output frequency // to limit current increase. // divSel = (uint16_t)(config & SYSCTL_SYSDIV_M) >> SYSCTL_SYSDIV_S; if(divSel != (126U / 2U)) { HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) = (HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) & ~(uint16_t)SYSCTL_SYSCLKDIVSEL_PLLSYSCLKDIV_M) | (divSel + 1U); } else { HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) = (HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) & ~(uint16_t)SYSCTL_SYSCLKDIVSEL_PLLSYSCLKDIV_M) | divSel; } // // *CAUTION* // It is recommended to use the following watchdog code to monitor the // PLLstartup sequence. If your application has already cleared the // watchdog SCRS[WDOVERRIDE] bit this cannot be done. It is recommended // not to clear this bit until after the PLL has been initiated. // // // Backup User Watchdog // tempSCSR = HWREGH(WD_BASE + SYSCTL_O_SCSR); tempWDCR = HWREGH(WD_BASE + SYSCTL_O_WDCR); tempWDWCR = HWREGH(WD_BASE + SYSCTL_O_WDWCR); // // Disable windowed functionality, reset counter // HWREGH(WD_BASE + SYSCTL_O_WDWCR) = 0x0U; SysCtl_serviceWatchdog(); // // Disable global interrupts // intStatus = __disable_interrupts(); // // Configure for watchdog reset and to run at max frequency // EALLOW; HWREGH(WD_BASE + SYSCTL_O_SCSR) = 0x0U; HWREGH(WD_BASE + SYSCTL_O_WDCR) = SYSCTL_WD_CHKBITS; // // This bit is reset only by power-on-reset (POR) and will not be // cleared by a WD reset // HWREGH(DEVCFG_BASE + SYSCTL_O_SYSDBGCTL) |= SYSCTL_SYSDBGCTL_BIT_0; // // Enable PLLSYSCLK is fed from system PLL clock // HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLCTL1) |= SYSCTL_SYSPLLCTL1_PLLCLKEN; EDIS; // // Delay to ensure system is clocking from PLL prior to clearing // status bit // SysCtl_delay(3U); // // Slip Bit Monitor and SYSCLK Frequency Check using timers // Re-lock routine for SLIP condition or if SYSCLK and CLKSRC timer // counts are off by +/- 10%. At a minimum, SYSCLK check is performed. // Re-lock attempt is carried out if SLIPS bit is set. // This while loop is monitored by watchdog. // In the event that the PLL does not successfully lock, the loop will // be aborted by watchdog reset. // while(((config & SYSCTL_PLL_ENABLE) == SYSCTL_PLL_ENABLE) && (sysclkInvalidFreq == true)) { EALLOW; // // Perform PLL re-lock only if SLIPS bit is set, otherwise monitor // SYSCLK frequency with timers // if((HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLSTS) & SYSCTL_SYSPLLSTS_SLIPS) == 1U) { // // Bypass PLL // HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLCTL1) &= ~SYSCTL_SYSPLLCTL1_PLLCLKEN; // // Delay of at least 120 OSCCLK cycles required post PLL bypass // SysCtl_delay(23U); // // Turn off PLL // HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLCTL1) &= ~SYSCTL_SYSPLLCTL1_PLLEN; SysCtl_delay(3U); // // Write multiplier, which automatically turns on the PLL // HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLMULT) |= pllMult; // // Wait for the SYSPLL lock counter // while((HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLSTS) & SYSCTL_SYSPLLSTS_LOCKS) == 0U) { ; } // // Enable PLLSYSCLK is fed from system PLL clock // HWREGH(CLKCFG_BASE + SYSCTL_O_SYSPLLCTL1) |= SYSCTL_SYSPLLCTL1_PLLCLKEN; // // Delay to ensure system is clocking from PLL prior to // clearing status bit // SysCtl_delay(3U); } // // Backup timer1 and timer2 settings // t1TCR = HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR); t1PRD = HWREG(CPUTIMER1_BASE + CPUTIMER_O_PRD); t1TPR = HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TPR); t1TPRH = HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TPRH); t2CLKCTL = HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL); t2TCR = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR); t2PRD = HWREG(CPUTIMER2_BASE + CPUTIMER_O_PRD); t2TPR = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPR); t2TPRH = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPRH); // // Set up timers 1 and 2 // Configure timer1 to count SYSCLK cycles // // // Stop timer 1 // Seed timer1 counter // Set sysclock divider // Reload timer with value in PRD // Clear interrupt flag // Enable interrupt // HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TSS; HWREG(CPUTIMER1_BASE + CPUTIMER_O_PRD) = (uint32_t)TMR1SYSCLKCTR; HWREG(CPUTIMER1_BASE + CPUTIMER_O_TPR) = 0U; HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TRB; HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TIF; HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TIE; // // Configure timer2 to count Input clock cycles // switch (config & SYSCTL_OSCSRC_M) { case SYSCTL_OSCSRC_OSC1: // // Clk Src = INT_OSC1 // HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) &= ~SYSCTL_TMR2CLKCTL_TMR2CLKSRCSEL_M; HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) |= 1U; break; case SYSCTL_OSCSRC_OSC2: // // Clk Src = INT_OSC2 // HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) &= ~SYSCTL_TMR2CLKCTL_TMR2CLKSRCSEL_M; HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) |= 2U; break; case SYSCTL_OSCSRC_XTAL: // // Clk Src = XTAL // HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) &= ~SYSCTL_TMR2CLKCTL_TMR2CLKSRCSEL_M; HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) |= 3U; break; default: // // Do nothing. Not a valid clock source value. // break; } // // Clear interrupt flag // Enable interrupt // Stop timer 2 // Seed timer2 counter // Set sysclock divider // Reload timer with value in PRD // HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TIF; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TIE; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TSS; HWREG(CPUTIMER2_BASE + CPUTIMER_O_PRD) = (uint32_t)TMR2INPCLKCTR; HWREG(CPUTIMER2_BASE + CPUTIMER_O_TPR) = 0U; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TRB; // // Stop/Start timer counters // // // Stop timer 1 // Stop timer 2 // Reload timer1 with value in PRD // Reload timer2 with value in PRD // Clear timer2 interrupt flag // Start timer2 // Start timer1 // HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TSS; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TSS; HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TRB; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TRB; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TIF; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) &= ~CPUTIMER_TCR_TSS; HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) &= ~CPUTIMER_TCR_TSS; // // Wait for Timers - Stop if either timer overflows // while(((HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) & CPUTIMER_TCR_TIF) == 0U) && ((HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) & CPUTIMER_TCR_TIF) == 0U)) { ; } // // Stop timer 1 and 2 // HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TSS; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TSS; // // Calculate elapsed counts on timer1 // ctr1 = (uint32_t)TMR1SYSCLKCTR - HWREG(CPUTIMER1_BASE + CPUTIMER_O_TIM); // // Restore timer settings // HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TCR) = t1TCR; HWREG(CPUTIMER1_BASE + CPUTIMER_O_PRD) = t1PRD; HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TPR) = t1TPR; HWREGH(CPUTIMER1_BASE + CPUTIMER_O_TPRH) = t1TPRH; HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) = t2CLKCTL; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) = t2TCR; HWREG(CPUTIMER2_BASE + CPUTIMER_O_PRD) = t2PRD; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPR) = t2TPR; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPRH) = t2TPRH; // // Calculate Clock Error: // Error = (mult/div) - (timer1 count/timer2 count) // mult = (float32_t)iMult + ((float32_t)fMult / 4.0F); if((HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) & 0x3FU) == 0U) { div = 1U; } else { div = (HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) & 0x3FU) << 1; } sysclkToInClkError = (mult / (float32_t)div) - ((float32_t)ctr1 / (float32_t)TMR2INPCLKCTR); // // sysclkInvalidFreq will be set to true if sysclkToInClkError is // off by 10% // sysclkInvalidFreq = ((sysclkToInClkError > 0.10F) || (sysclkToInClkError < -0.10F)); EDIS; } // // Clear bit // EALLOW; HWREGH(DEVCFG_BASE + SYSCTL_O_SYSDBGCTL) &= ~SYSCTL_SYSDBGCTL_BIT_0; EDIS; // // Restore user watchdog, first resetting counter // SysCtl_serviceWatchdog(); // // Set the KEY bits and make sure not to set the WDOVERRIDE bit // EALLOW; HWREGH(WD_BASE + SYSCTL_O_WDCR) = tempWDCR | SYSCTL_WD_CHKBITS; HWREGH(WD_BASE + SYSCTL_O_WDWCR) = tempWDWCR; HWREGH(WD_BASE + SYSCTL_O_SCSR) = tempSCSR & ~SYSCTL_SCSR_WDOVERRIDE; EDIS; // // Restore state of ST1[INTM]. This was set by the // __disable_interrupts() intrinsic previously. // if((intStatus & 0x1U) == 0U) { EINT; } // // Restore state of ST1[DBGM]. This was set by the // __disable_interrupts() intrinsic previously. // if((intStatus & 0x2U) == 0U) { SYSCTL_CLRC_DBGM; } // // ~200 PLLSYSCLK delay to allow voltage regulator to stabilize prior // to increasing entire system clock frequency. // SysCtl_delay(40U); // // Set the divider to user value // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) = (HWREGH(CLKCFG_BASE + SYSCTL_O_SYSCLKDIVSEL) & ~SYSCTL_SYSCLKDIVSEL_PLLSYSCLKDIV_M) | divSel; EDIS; status = true; } return(status); } //***************************************************************************** // // SysCtl_setAuxClock() // //***************************************************************************** void SysCtl_setAuxClock(uint32_t config) { uint16_t pllMult = 0U; uint16_t counter = 0U, started = 0U, attempts = 0U; uint16_t mult; uint16_t i, t2TCR, t2TPR, t2TPRH, t2CLKCTL; uint32_t t2PRD; // // Check the arguments // ASSERT((config & SYSCTL_OSCSRC_M) != SYSCTL_OSCSRC_M); // 3 is not valid // // Bypass PLL // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) &= ~SYSCTL_AUXPLLCTL1_PLLCLKEN; EDIS; // // Delay of at least 120 OSCCLK cycles required post PLL bypass // SysCtl_delay(23U); // // Configure oscillator source // SysCtl_selectOscSourceAuxPLL(config & SYSCTL_OSCSRC_M); // // Get the PLL multiplier settings from config // pllMult |= (uint16_t)((config & SYSCTL_IMULT_M) << SYSCTL_AUXPLLMULT_IMULT_S); pllMult |= (uint16_t)(((config & SYSCTL_FMULT_M) >> SYSCTL_FMULT_S) << SYSCTL_AUXPLLMULT_FMULT_S); // // Get the PLL multipliers currently programmed // mult = (uint16_t)((HWREG(CLKCFG_BASE + SYSCTL_O_AUXPLLMULT) & (uint32_t)SYSCTL_AUXPLLMULT_IMULT_M) >> (uint32_t)SYSCTL_AUXPLLMULT_IMULT_S); mult |= (uint16_t)(HWREG(CLKCFG_BASE + SYSCTL_O_AUXPLLMULT) & SYSCTL_AUXPLLMULT_FMULT_M); // // Lock PLL only if the multipliers need update // if(mult != pllMult) { // // Configure PLL if enabled // if((config & SYSCTL_AUXPLL_ENABLE) == SYSCTL_AUXPLL_ENABLE) { // // Backup Timer 2 settings // t2CLKCTL = HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL); t2TCR = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR); t2PRD = HWREG(CPUTIMER2_BASE + CPUTIMER_O_PRD); t2TPR = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPR); t2TPRH = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPRH); // // Configure Timer 2 for AUXPLL as source in known configuration // - Clock source to AUXPLL // - Clock divider to divide by 1 // - Small period to detect overflow // - Interrupt disabled // EALLOW; HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) = 6U; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TSS; HWREG(CPUTIMER2_BASE + CPUTIMER_O_PRD) = 10U; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPR) = 0U; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPRH) = 0U; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) &= ~CPUTIMER_TCR_TIE; // // Set AUX Divide by 8 to ensure that AUXPLLCLK <= SYSCLK / 2 // while using Timer 2 // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXCLKDIVSEL) = 0x3U; EDIS; // // Lock the PLL up to five times. //CPU Timer 2 will monitor a successful // lock and break out of the loop earlier if detected. // while((counter < 5U) && (started == 0U)) { EALLOW; // // Turn off AUXPLL and delay for it to power down. // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) &= ~SYSCTL_AUXPLLCTL1_PLLEN; SysCtl_delay(3U); // // Set integer and fractional multiplier, which automatically // turns on the PLL // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLMULT) |= pllMult; // // Enable AUXPLL // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) |= SYSCTL_AUXPLLCTL1_PLLEN; EDIS; // // Wait for the AUXPLL lock counter // while((HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLSTS) & SYSCTL_AUXPLLSTS_LOCKS) != 1U) { // // Consider to servicing the watchdog using // SysCtl_serviceWatchdog() // } // // Enable AUXPLLCLK to be fed from AUXPLL // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) |= SYSCTL_AUXPLLCTL1_PLLCLKEN; SysCtl_delay(3U); // // CPU Timer 2 will now be setup to be clocked from AUXPLLCLK. // This is used to test that the PLL has successfully started. // // Reload and start the timer // HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TRB; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) &= ~CPUTIMER_TCR_TSS; // // Check to see timer is counting properly // for(i = 0U; i < 1000U; i++) { // // Check overflow flag // if((HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) & CPUTIMER_TCR_TIF) != 0U) { // // Clear overflow flag // HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TIF; // // Set flag to indicate PLL started and break out of // for-loop // started = 1U; break; } } // // Stop timer // HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TSS; counter++; EDIS; } if(started == 0U) { // // AUX PLL may not have started. Reset multiplier to 0 (bypass // PLL). // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLMULT) = 0U; EDIS; // // The user should put some handler code here based on how // this condition should be handled in their application. // ESTOP0; } // // Restore Timer 2 configuration // EALLOW; HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) = t2CLKCTL; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) = t2TCR; HWREG(CPUTIMER2_BASE + CPUTIMER_O_PRD) = t2PRD; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPR) = t2TPR; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPRH) = t2TPRH; // // Reload period value // HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TRB; EDIS; } } else { // // Enable AUXPLLCLK to be fed from AUXPLL // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) |= SYSCTL_AUXPLLCTL1_PLLCLKEN; SysCtl_delay(3U); EDIS; } // // Slip Bit Monitor // Re-lock routine for SLIP condition // while(((HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLSTS) & SYSCTL_AUXPLLSTS_SLIPS) != 0U) && (attempts < 10U) && ((config & SYSCTL_AUXPLL_ENABLE) == SYSCTL_AUXPLL_ENABLE)) { EALLOW; // // Bypass AUXPLL // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) &= ~SYSCTL_AUXPLLCTL1_PLLCLKEN; // // Delay of at least 120 OSCCLK cycles required post PLL bypass // SysCtl_delay(23U); // // Turn off AUXPLL // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) &= ~SYSCTL_AUXPLLCTL1_PLLEN; SysCtl_delay(3U); // // Set integer and fractional multiplier, which automatically turns // on the PLL // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLMULT) |= pllMult; // // Enable AUXPLL // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) |= SYSCTL_AUXPLLCTL1_PLLEN; // // Wait for the AUXPLL lock counter // while((HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLSTS) & SYSCTL_AUXPLLSTS_LOCKS) != 1U) { // // Consider to servicing the watchdog using // SysCtl_serviceWatchdog() // } // // Enable AUXPLLCLK to be fed from AUXPLL // HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) |= SYSCTL_AUXPLLCTL1_PLLCLKEN; SysCtl_delay(3U); attempts++; EDIS; } // // Set divider to desired value // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_AUXCLKDIVSEL) = (uint16_t)(config & SYSCTL_SYSDIV_M) >> SYSCTL_SYSDIV_S; EDIS; } //***************************************************************************** // // SysCtl_selectXTAL() // //***************************************************************************** void SysCtl_selectXTAL(void) { uint16_t t2TCR, t2TPR, t2TPRH, t2CLKCTL; uint32_t t2PRD; // // Backup CPU timer2 settings // t2CLKCTL = HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL); t2TCR = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR); t2PRD = HWREG(CPUTIMER2_BASE + CPUTIMER_O_PRD); t2TPR = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPR); t2TPRH = HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPRH); // // Backup AUX clock settings // uint16_t clksrcctl2 = HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2); uint16_t auxpllctl1 = HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1); uint16_t auxclkdivsel = HWREGH(CLKCFG_BASE + SYSCTL_O_AUXCLKDIVSEL); // // Set AUX clock source to XTAL, bypass mode. // AUXCLK is used as the CPUTimer Clock source. SYSCLK frequency must be // atleast twice the frequency of AUXCLK. SYSCLK = INTOSC2(10MHz) // Set the AUX divider to 8. The above condition will be met for XTAL // frequencies up to 40MHz // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) = (HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) & ~(SYSCTL_CLKSRCCTL2_AUXOSCCLKSRCSEL_M)) | (1U << SYSCTL_CLKSRCCTL2_AUXOSCCLKSRCSEL_S); HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) = 0x0U; HWREGH(CLKCFG_BASE + SYSCTL_O_AUXCLKDIVSEL) = SYSCTL_AUXPLLCLK_DIV_8; // // Disable cpu timer 2 interrupt // CPUTimer_disableInterrupt(CPUTIMER2_BASE); // // Stop cpu timer 2 if running // CPUTimer_stopTimer(CPUTIMER2_BASE); // // Initialize cpu timer 2 period // CPUTimer_setPeriod(CPUTIMER2_BASE, XTAL_CPUTIMER_PERIOD); // // Set cpu timer 2 clock source to XTAL // CPUTimer_selectClockSource(CPUTIMER2_BASE, CPUTIMER_CLOCK_SOURCE_AUX, CPUTIMER_CLOCK_PRESCALER_1); // // Clear cpu timer 2 overflow flag // CPUTimer_clearOverflowFlag(CPUTIMER2_BASE); // // Start cpu timer 2 // CPUTimer_startTimer(CPUTIMER2_BASE); EALLOW; // // Turn on XTAL // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) &= ~SYSCTL_CLKSRCCTL1_XTALOFF; EDIS; // // Wait for the X1 clock to overflow cpu timer 2 // SysCtl_pollCpuTimer(); // // Select XTAL as the oscillator source // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) = ((HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) & (~SYSCTL_CLKSRCCTL1_OSCCLKSRCSEL_M)) | (SYSCTL_OSCSRC_XTAL >> SYSCTL_OSCSRC_S)); EDIS; // // If a missing clock failure was detected, try waiting for the cpu timer 2 // to overflow again. // while(SysCtl_isMCDClockFailureDetected()) { // // Clear the MCD failure // SysCtl_resetMCD(); // // Wait for the X1 clock to overflow cpu timer 2 // SysCtl_pollCpuTimer(); // // Select XTAL as the oscillator source // EALLOW; HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) = ((HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) & (~SYSCTL_CLKSRCCTL1_OSCCLKSRCSEL_M)) | (SYSCTL_OSCSRC_XTAL >> SYSCTL_OSCSRC_S)); EDIS; } // // Stop cpu timer 2 // CPUTimer_stopTimer(CPUTIMER2_BASE); // // Restore Timer 2 configuration // EALLOW; HWREGH(CPUSYS_BASE + SYSCTL_O_TMR2CLKCTL) = t2CLKCTL; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) = t2TCR; HWREG(CPUTIMER2_BASE + CPUTIMER_O_PRD) = t2PRD; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPR) = t2TPR; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TPRH) = t2TPRH; HWREGH(CPUTIMER2_BASE + CPUTIMER_O_TCR) |= CPUTIMER_TCR_TRB; // // Restore AUX clock settings // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) = clksrcctl2; HWREGH(CLKCFG_BASE + SYSCTL_O_AUXPLLCTL1) = auxpllctl1; HWREGH(CLKCFG_BASE + SYSCTL_O_AUXCLKDIVSEL) = auxclkdivsel; EDIS; EDIS; } //***************************************************************************** // // SysCtl_selectOscSource() // //***************************************************************************** void SysCtl_selectOscSource(uint32_t oscSource) { ASSERT((oscSource == SYSCTL_OSCSRC_OSC1) || (oscSource == SYSCTL_OSCSRC_OSC2) || (oscSource == SYSCTL_OSCSRC_XTAL)); // // Select the specified source. // EALLOW; switch(oscSource) { case SYSCTL_OSCSRC_OSC2: // // Turn on INTOSC2 // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) &= ~SYSCTL_CLKSRCCTL1_INTOSC2OFF; SYSCTL_CLKSRCCTL1_DELAY; // // Clk Src = INTOSC2 // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) &= ~SYSCTL_CLKSRCCTL1_OSCCLKSRCSEL_M; SYSCTL_CLKSRCCTL1_DELAY; // // Turn off XTALOSC // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) |= SYSCTL_CLKSRCCTL1_XTALOFF; break; case SYSCTL_OSCSRC_XTAL: // // Select XTAL in crystal mode and wait for it to power up // SysCtl_selectXTAL(); break; case SYSCTL_OSCSRC_OSC1: // // Clk Src = INTOSC1 // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) = (HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) & ~SYSCTL_CLKSRCCTL1_OSCCLKSRCSEL_M) | (SYSCTL_OSCSRC_OSC1 >> SYSCTL_OSCSRC_S); SYSCTL_CLKSRCCTL1_DELAY; // //Turn off XTALOSC // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) |= SYSCTL_CLKSRCCTL1_XTALOFF; break; default: // // Do nothing. Not a valid oscSource value. // break; } EDIS; } //***************************************************************************** // // SysCtl_selectOscSourceAuxPLL() // //***************************************************************************** void SysCtl_selectOscSourceAuxPLL(uint32_t oscSource) { bool status = false; EALLOW; switch(oscSource) { case SYSCTL_AUXPLL_OSCSRC_OSC2: // // Turn on INTOSC2 // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) &= ~(SYSCTL_CLKSRCCTL1_INTOSC2OFF); // // Clk Src = INTOSC2 // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) &= ~(SYSCTL_CLKSRCCTL2_AUXOSCCLKSRCSEL_M); break; case SYSCTL_AUXPLL_OSCSRC_XTAL: // // Turn on XTALOSC // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL1) &= ~(SYSCTL_CLKSRCCTL1_XTALOFF); // // Clk Src = XTAL // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) = (HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) & ~(SYSCTL_CLKSRCCTL2_AUXOSCCLKSRCSEL_M)) | (1U << SYSCTL_CLKSRCCTL2_AUXOSCCLKSRCSEL_S); break; case SYSCTL_AUXPLL_OSCSRC_AUXCLKIN: // // Clk Src = AUXCLKIN // HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) = (HWREGH(CLKCFG_BASE + SYSCTL_O_CLKSRCCTL2) & ~(SYSCTL_CLKSRCCTL2_AUXOSCCLKSRCSEL_M)) | (2U << SYSCTL_CLKSRCCTL2_AUXOSCCLKSRCSEL_S); break; default: // // Do nothing. Not a valid clock source value. // break; } EDIS; } //***************************************************************************** // // SysCtl_getLowSpeedClock() // //***************************************************************************** uint32_t SysCtl_getLowSpeedClock(uint32_t clockInHz) { uint32_t clockOut; // // Get the main system clock // clockOut = SysCtl_getClock(clockInHz); // // Apply the divider to the main clock // if((HWREG(CLKCFG_BASE + SYSCTL_O_LOSPCP) & SYSCTL_LOSPCP_LSPCLKDIV_M) != 0U) { clockOut /= (2U * (HWREG(CLKCFG_BASE + SYSCTL_O_LOSPCP) & SYSCTL_LOSPCP_LSPCLKDIV_M)); } return(clockOut); } //***************************************************************************** // // SysCtl_getDeviceParametric() // //***************************************************************************** uint16_t SysCtl_getDeviceParametric(SysCtl_DeviceParametric parametric) { uint32_t value; // // Get requested parametric value // switch(parametric) { case SYSCTL_DEVICE_QUAL: // // Qualification Status // value = ((HWREG(DEVCFG_BASE + SYSCTL_O_PARTIDL) & SYSCTL_PARTIDL_QUAL_M) >> SYSCTL_PARTIDL_QUAL_S); break; case SYSCTL_DEVICE_PINCOUNT: // // Pin Count // value = ((HWREG(DEVCFG_BASE + SYSCTL_O_PARTIDL) & SYSCTL_PARTIDL_PIN_COUNT_M) >> SYSCTL_PARTIDL_PIN_COUNT_S); break; case SYSCTL_DEVICE_INSTASPIN: // // InstaSPIN Feature Set // value = ((HWREG(DEVCFG_BASE + SYSCTL_O_PARTIDL) & SYSCTL_PARTIDL_INSTASPIN_M) >> SYSCTL_PARTIDL_INSTASPIN_S); break; case SYSCTL_DEVICE_FLASH: // // Flash Size (KB) // value = ((HWREG(DEVCFG_BASE + SYSCTL_O_PARTIDL) & SYSCTL_PARTIDL_FLASH_SIZE_M) >> SYSCTL_PARTIDL_FLASH_SIZE_S); break; case SYSCTL_DEVICE_PARTID: // // PARTID Format Revision // value = ((HWREG(DEVCFG_BASE + SYSCTL_O_PARTIDL) & SYSCTL_PARTIDL_PARTID_FORMAT_REVISION_M) >> SYSCTL_PARTIDL_PARTID_FORMAT_REVISION_S); break; case SYSCTL_DEVICE_FAMILY: // // Device Family // value = ((HWREG(DEVCFG_BASE + SYSCTL_O_PARTIDH) & SYSCTL_PARTIDH_FAMILY_M) >> SYSCTL_PARTIDH_FAMILY_S); break; case SYSCTL_DEVICE_PARTNO: // // Part Number // value = ((HWREG(DEVCFG_BASE + SYSCTL_O_PARTIDH) & SYSCTL_PARTIDH_PARTNO_M) >> SYSCTL_PARTIDH_PARTNO_S); break; case SYSCTL_DEVICE_CLASSID: // // Class ID // value = ((HWREG(DEVCFG_BASE + SYSCTL_O_PARTIDH) & SYSCTL_PARTIDH_DEVICE_CLASS_ID_M) >> SYSCTL_PARTIDH_DEVICE_CLASS_ID_S); break; default: // // Not a valid value for PARTID register // value = 0U; break; } return((uint16_t)value); }