CC26xx Driver Library
osc.c
Go to the documentation of this file.
1 /******************************************************************************
2 * Filename: osc.c
3 * Revised: 2016-07-07 19:12:02 +0200 (Thu, 07 Jul 2016)
4 * Revision: 46848
5 *
6 * Description: Driver for setting up the system Oscillators
7 *
8 * Copyright (c) 2015 - 2016, Texas Instruments Incorporated
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are met:
13 *
14 * 1) Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 *
17 * 2) Redistributions in binary form must reproduce the above copyright notice,
18 * this list of conditions and the following disclaimer in the documentation
19 * and/or other materials provided with the distribution.
20 *
21 * 3) Neither the name of the ORGANIZATION nor the names of its contributors may
22 * be used to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 *
37 ******************************************************************************/
38 
39 #include <inc/hw_types.h>
40 #include <inc/hw_ccfg.h>
41 #include <inc/hw_fcfg1.h>
42 #include <driverlib/aon_batmon.h>
43 #include <driverlib/aon_rtc.h>
44 #include <driverlib/osc.h>
45 
46 //*****************************************************************************
47 //
48 // Handle support for DriverLib in ROM:
49 // This section will undo prototype renaming made in the header file
50 //
51 //*****************************************************************************
52 #if !defined(DOXYGEN)
53  #undef OSCClockSourceSet
54  #define OSCClockSourceSet NOROM_OSCClockSourceSet
55  #undef OSCClockSourceGet
56  #define OSCClockSourceGet NOROM_OSCClockSourceGet
57  #undef OSCHF_GetStartupTime
58  #define OSCHF_GetStartupTime NOROM_OSCHF_GetStartupTime
59  #undef OSCHF_TurnOnXosc
60  #define OSCHF_TurnOnXosc NOROM_OSCHF_TurnOnXosc
61  #undef OSCHF_AttemptToSwitchToXosc
62  #define OSCHF_AttemptToSwitchToXosc NOROM_OSCHF_AttemptToSwitchToXosc
63  #undef OSCHF_SwitchToRcOscTurnOffXosc
64  #define OSCHF_SwitchToRcOscTurnOffXosc NOROM_OSCHF_SwitchToRcOscTurnOffXosc
65  #undef OSCHF_DebugGetCrystalAmplitude
66  #define OSCHF_DebugGetCrystalAmplitude NOROM_OSCHF_DebugGetCrystalAmplitude
67  #undef OSCHF_DebugGetExpectedAverageCrystalAmplitude
68  #define OSCHF_DebugGetExpectedAverageCrystalAmplitude NOROM_OSCHF_DebugGetExpectedAverageCrystalAmplitude
69  #undef OSC_HPOSCRelativeFrequencyOffsetGet
70  #define OSC_HPOSCRelativeFrequencyOffsetGet NOROM_OSC_HPOSCRelativeFrequencyOffsetGet
71  #undef OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert
72  #define OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert NOROM_OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert
73 #endif
74 
75 //*****************************************************************************
76 //
77 // OSCHF switch time calculator defines and globals
78 //
79 //*****************************************************************************
80 
81 #define RTC_CV_TO_MS(x) (( 1000 * ( x )) >> 16 )
82 #define RTC_CV_TO_US(x) (( 1000000 * ( x )) >> 16 )
83 
84 typedef struct {
86  uint32_t timeXoscOff_CV ;
87  uint32_t timeXoscOn_CV ;
88  uint32_t timeXoscStable_CV ;
89  int32_t tempXoscOff ;
91 
93 
94 
95 //*****************************************************************************
96 //
97 // Configure the oscillator input to the a source clock.
98 //
99 //*****************************************************************************
100 void
101 OSCClockSourceSet(uint32_t ui32SrcClk, uint32_t ui32Osc)
102 {
103  //
104  // Check the arguments.
105  //
106  ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
107  (ui32SrcClk & OSC_SRC_CLK_MF) ||
108  (ui32SrcClk & OSC_SRC_CLK_HF));
109  ASSERT((ui32Osc == OSC_RCOSC_HF) ||
110  (ui32Osc == OSC_RCOSC_LF) ||
111  (ui32Osc == OSC_XOSC_HF) ||
112  (ui32Osc == OSC_XOSC_LF));
113 
114  //
115  // Request the high frequency source clock (using 24 MHz XTAL)
116  //
117  if(ui32SrcClk & OSC_SRC_CLK_HF)
118  {
119  //
120  // Enable the HF XTAL as HF clock source
121  //
125  ui32Osc);
126  }
127 
128  //
129  // Configure the medium frequency source clock
130  //
131  if(ui32SrcClk & OSC_SRC_CLK_MF)
132  {
136  ui32Osc);
137  }
138 
139  //
140  // Configure the low frequency source clock.
141  //
142  if(ui32SrcClk & OSC_SRC_CLK_LF)
143  {
144  //
145  // Change the clock source.
146  //
150  ui32Osc);
151  }
152 }
153 
154 //*****************************************************************************
155 //
156 // Get the source clock settings
157 //
158 //*****************************************************************************
159 uint32_t
160 OSCClockSourceGet(uint32_t ui32SrcClk)
161 {
162  uint32_t ui32ClockSource;
163 
164  //
165  // Check the arguments.
166  //
167  ASSERT((ui32SrcClk & OSC_SRC_CLK_LF) ||
168  (ui32SrcClk & OSC_SRC_CLK_HF));
169 
170  //
171  // Return the source for the selected clock.
172  //
173  if(ui32SrcClk == OSC_SRC_CLK_LF)
174  {
178  }
179  else
180  {
184  }
185  return (ui32ClockSource);
186 }
187 
188 //*****************************************************************************
189 //
190 // Returns maximum startup time (in microseconds) of XOSC_HF
191 //
192 //*****************************************************************************
193 uint32_t
194 OSCHF_GetStartupTime( uint32_t timeUntilWakeupInMs )
195 {
196  uint32_t deltaTimeSinceXoscOnInMs ;
197  int32_t deltaTempSinceXoscOn ;
198  uint32_t newStartupTimeInUs ;
199 
200  deltaTimeSinceXoscOnInMs = RTC_CV_TO_MS( AONRTCCurrentCompareValueGet() - oscHfGlobals.timeXoscOn_CV );
201  deltaTempSinceXoscOn = AONBatMonTemperatureGetDegC() - oscHfGlobals.tempXoscOff;
202 
203  if ( deltaTempSinceXoscOn < 0 ) {
204  deltaTempSinceXoscOn = -deltaTempSinceXoscOn;
205  }
206 
207  if ( (( timeUntilWakeupInMs + deltaTimeSinceXoscOnInMs ) > 3000 ) ||
208  ( deltaTempSinceXoscOn > 5 ) ||
209  ( oscHfGlobals.timeXoscStable_CV < oscHfGlobals.timeXoscOn_CV ) ||
210  ( oscHfGlobals.previousStartupTimeInUs == 0 ) )
211  {
212  newStartupTimeInUs = 2000;
214  newStartupTimeInUs = (( HWREG( CCFG_BASE + CCFG_O_MODE_CONF_1 ) &
217  // Note: CCFG startup time is "in units of 100us" adding 25% margin results in *125
218  }
219  } else {
220  newStartupTimeInUs = RTC_CV_TO_US( oscHfGlobals.timeXoscStable_CV - oscHfGlobals.timeXoscOn_CV );
221  newStartupTimeInUs += ( newStartupTimeInUs >> 2 ); // Add 25 percent margin
222  if ( newStartupTimeInUs < oscHfGlobals.previousStartupTimeInUs ) {
223  newStartupTimeInUs = oscHfGlobals.previousStartupTimeInUs;
224  }
225  }
226 
227  if ( newStartupTimeInUs < 200 ) {
228  newStartupTimeInUs = 200;
229  }
230  if ( newStartupTimeInUs > 4000 ) {
231  newStartupTimeInUs = 4000;
232  }
233  return ( newStartupTimeInUs );
234 }
235 
236 
237 //*****************************************************************************
238 //
239 // Turns on XOSC_HF (but without switching to XOSC_HF)
240 //
241 //*****************************************************************************
242 void
244 {
247 }
248 
249 
250 //*****************************************************************************
251 //
252 // Switch to XOSC_HF if XOSC_HF is ready.
253 //
254 //*****************************************************************************
255 bool
257 {
258  uint32_t startupTimeInUs;
259  uint32_t prevLimmit25InUs;
260 
262  // Already on XOSC - nothing to do
263  return ( 1 );
264  }
265  if ( OSCHfSourceReady()) {
267 
268  //
269  // Store startup time, but limit to 25 percent reduction each time.
270  //
272  startupTimeInUs = RTC_CV_TO_US( oscHfGlobals.timeXoscStable_CV - oscHfGlobals.timeXoscOn_CV );
273  prevLimmit25InUs = oscHfGlobals.previousStartupTimeInUs;
274  prevLimmit25InUs -= ( prevLimmit25InUs >> 2 ); // 25 percent margin
275  oscHfGlobals.previousStartupTimeInUs = startupTimeInUs;
276  if ( prevLimmit25InUs > startupTimeInUs ) {
277  oscHfGlobals.previousStartupTimeInUs = prevLimmit25InUs;
278  }
279  return ( 1 );
280  }
281  return ( 0 );
282 }
283 
284 
285 //*****************************************************************************
286 //
287 // Switch to RCOSC_HF and turn off XOSC_HF
288 //
289 //*****************************************************************************
290 void
292 {
293  //
294  // Set SCLK_HF and SCLK_MF to RCOSC_HF without checking
295  // Doing this anyway to keep HF and MF in sync
296  //
298 
299  //
300  // Do the switching if not already running on RCOSC_HF
301  //
304  }
305 
307  oscHfGlobals.tempXoscOff = AONBatMonTemperatureGetDegC();
308 }
309 
310 //*****************************************************************************
311 //
312 // Calculate the temperature dependent relative frequency offset of HPOSC
313 //
314 //*****************************************************************************
315 int32_t
317 {
318  // Estimate HPOSC frequency, using temperature and curve fitting parameters
319  uint32_t fitParams = HWREG(FCFG1_BASE + FCFG1_O_FREQ_OFFSET);
320  // Extract the P0,P1,P2 params, and sign extend them via shifting up/down
321  int32_t paramP0 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_S))
322  >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P0_W));
323  int32_t paramP1 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_S))
324  >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P1_W));
325  int32_t paramP2 = ((((int32_t) fitParams) << (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_W - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_S))
326  >> (32 - FCFG1_FREQ_OFFSET_HPOSC_COMP_P2_W));
327  int32_t paramP3 = ((((int32_t) HWREG(FCFG1_BASE + FCFG1_O_MISC_CONF_2))
328  << (32 - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_W - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_S))
329  >> (32 - FCFG1_MISC_CONF_2_HPOSC_COMP_P3_W));
330 
331  // Now we can find the HPOSC freq offset, given as a signed variable d, expressed by:
332  //
333  // F_HPOSC = F_nom * (1 + d/(2^22)) , where: F_HPOSC = HPOSC frequency
334  // F_nom = nominal clock source frequency (e.g. 48.000 MHz)
335  // d = describes relative freq offset
336 
337  // We can estimate the d variable, using temperature compensation parameters:
338  //
339  // d = P0 + P1*(t - T0) + P2*(t - T0)^2 + P3*(t - T0)^3, where: P0,P1,P2,P3 are curve fitting parameters from FCFG1
340  // t = current temperature (from temp sensor) in deg C
341  // T0 = 27 deg C (fixed temperature constant)
342  int32_t tempDelta = (tempDegC - 27);
343  int32_t tempDeltaX2 = tempDelta * tempDelta;
344  int32_t d = paramP0 + ((tempDelta*paramP1)>>3) + ((tempDeltaX2*paramP2)>>10) + ((tempDeltaX2*tempDelta*paramP3)>>18);
345 
346  return ( d );
347 }
348 
349 //*****************************************************************************
350 //
351 // Converts the relative frequency offset of HPOSC to the RF Core parameter format.
352 //
353 //*****************************************************************************
354 int16_t
356 {
357  // The input argument, hereby referred to simply as "d", describes the frequency offset
358  // of the HPOSC relative to the nominal frequency in this way:
359  //
360  // F_HPOSC = F_nom * (1 + d/(2^22))
361  //
362  // But for use by the radio, to compensate the frequency error, we need to find the
363  // frequency offset "rfcFreqOffset" defined in the following format:
364  //
365  // F_nom = F_HPOSC * (1 + rfCoreFreqOffset/(2^22))
366  //
367  // To derive "rfCoreFreqOffset" from "d" we combine the two above equations and get:
368  //
369  // (1 + rfCoreFreqOffset/(2^22)) = (1 + d/(2^22))^-1
370  //
371  // Which can be rewritten into:
372  //
373  // rfCoreFreqOffset = -d*(2^22) / ((2^22) + d)
374  //
375  // = -d * [ 1 / (1 + d/(2^22)) ]
376  //
377  // To avoid doing a 64-bit division due to the (1 + d/(2^22))^-1 expression,
378  // we can use Taylor series (Maclaurin series) to approximate it:
379  //
380  // 1 / (1 - x) ~= 1 + x + x^2 + x^3 + x^4 + ... etc (Maclaurin series)
381  //
382  // In our case, we have x = - d/(2^22), and we only include up to the first
383  // order term of the series, as the second order term ((d^2)/(2^44)) is very small:
384  //
385  // freqError ~= -d + d^2/(2^22) (+ small approximation error)
386  //
387  // The approximation error is negligible for our use.
388 
389  int32_t rfCoreFreqOffset = -HPOSC_RelFreqOffset + (( HPOSC_RelFreqOffset * HPOSC_RelFreqOffset ) >> 22 );
390 
391  return ( rfCoreFreqOffset );
392 }
393 
394 //*****************************************************************************
395 //
396 // Get crystal amplitude (assuming crystal is running).
397 //
398 //*****************************************************************************
399 uint32_t
401 {
402  uint32_t oscCfgRegCopy ;
403  uint32_t startTime ;
404  uint32_t deltaTime ;
405  uint32_t ampValue ;
406 
407  //
408  // The specified method is as follows:
409  // 1. Set minimum interval between oscillator amplitude calibrations.
410  // (Done by setting PER_M=0 and PER_E=1)
411  // 2. Wait approximately 4 milliseconds in order to measure over a
412  // moderately large number of calibrations.
413  // 3. Read out the crystal amplitude value from the peek detector.
414  // 4. Restore original oscillator amplitude calibrations interval.
415  // 5. Return crystal amplitude value converted to millivolt.
416  //
417  oscCfgRegCopy = HWREG( AON_WUC_BASE + AON_WUC_O_OSCCFG );
419  startTime = AONRTCCurrentCompareValueGet();
420  do {
421  deltaTime = AONRTCCurrentCompareValueGet() - startTime;
422  } while ( deltaTime < ((uint32_t)( 0.004 * FACTOR_SEC_TO_COMP_VAL_FORMAT )));
423  ampValue = ( HWREG( AUX_DDI0_OSC_BASE + DDI_0_OSC_O_STAT1 ) &
426  HWREG( AON_WUC_BASE + AON_WUC_O_OSCCFG ) = oscCfgRegCopy;
427 
428  return ( ampValue * 15 );
429 }
430 
431 //*****************************************************************************
432 //
433 // Get the expected average crystal amplitude.
434 //
435 //*****************************************************************************
436 uint32_t
438 {
439  uint32_t ampCompTh1 ;
440  uint32_t highThreshold ;
441  uint32_t lowThreshold ;
442 
443  ampCompTh1 = HWREG( AUX_DDI0_OSC_BASE + DDI_0_OSC_O_AMPCOMPTH1 );
444  highThreshold = ( ampCompTh1 & DDI_0_OSC_AMPCOMPTH1_HPMRAMP3_HTH_M ) >>
446  lowThreshold = ( ampCompTh1 & DDI_0_OSC_AMPCOMPTH1_HPMRAMP3_LTH_M ) >>
448 
449  return ((( highThreshold + lowThreshold ) * 15 ) >> 1 );
450 }
static void OSCHfSourceSwitch(void)
Switch the high frequency clock.
Definition: osc.h:320
#define OSC_SRC_CLK_MF
Definition: osc.h:113
uint32_t OSCHF_DebugGetCrystalAmplitude(void)
Get crystal amplitude (assuming crystal is running).
Definition: osc.c:400
uint32_t OSCHF_DebugGetExpectedAverageCrystalAmplitude(void)
Get the expected average crystal amplitude.
Definition: osc.c:437
uint32_t timeXoscOff_CV
Definition: osc.c:86
uint32_t timeXoscStable_CV
Definition: osc.c:88
int32_t AONBatMonTemperatureGetDegC(void)
Get the current temperature measurement as a signed value in Deg Celsius.
Definition: aon_batmon.c:60
uint32_t OSCClockSourceGet(uint32_t ui32SrcClk)
Get the source clock settings.
Definition: osc.c:160
void DDI16BitfieldWrite(uint32_t ui32Base, uint32_t ui32Reg, uint32_t ui32Mask, uint32_t ui32Shift, uint16_t ui32Data)
Write a bitfield via the DDI using 16-bit maskable write.
Definition: ddi.c:131
uint32_t OSCHF_GetStartupTime(uint32_t timeUntilWakeupInMs)
Returns maximum startup time (in microseconds) of XOSC_HF.
Definition: osc.c:194
#define OSC_SRC_CLK_HF
Definition: osc.h:112
bool OSCHF_AttemptToSwitchToXosc(void)
Switch to XOSC_HF if XOSC_HF is ready.
Definition: osc.c:256
#define OSC_RCOSC_HF
Definition: osc.h:116
static bool OSCHfSourceReady(void)
Check if the HF clock source is ready to be switched.
Definition: osc.h:290
#define OSC_XOSC_HF
Definition: osc.h:117
#define OSC_SRC_CLK_LF
Definition: osc.h:114
uint32_t previousStartupTimeInUs
Definition: osc.c:85
uint16_t DDI16BitfieldRead(uint32_t ui32Base, uint32_t ui32Reg, uint32_t ui32Mask, uint32_t ui32Shift)
Read a bitfield via the DDI using 16-bit read.
Definition: ddi.c:221
static OscHfGlobals_t oscHfGlobals
Definition: osc.c:92
#define ASSERT(expr)
Definition: debug.h:74
#define OSC_RCOSC_LF
Definition: osc.h:118
int32_t OSC_HPOSCRelativeFrequencyOffsetGet(int32_t tempDegC)
Calculate the temperature dependent relative frequency offset of HPOSC.
Definition: osc.c:316
int32_t tempXoscOff
Definition: osc.c:89
void OSCHF_SwitchToRcOscTurnOffXosc(void)
Switch to RCOSC_HF and turn off XOSC_HF.
Definition: osc.c:291
#define FACTOR_SEC_TO_COMP_VAL_FORMAT
Definition: aon_rtc.h:151
uint32_t timeXoscOn_CV
Definition: osc.c:87
#define RTC_CV_TO_US(x)
Definition: osc.c:82
uint32_t AONRTCCurrentCompareValueGet(void)
Get the current value of the RTC counter in a format that matches RTC compare values.
Definition: aon_rtc.c:62
void OSCClockSourceSet(uint32_t ui32SrcClk, uint32_t ui32Osc)
Configure the oscillator input to the a source clock.
Definition: osc.c:101
void OSCHF_TurnOnXosc(void)
Turns on XOSC_HF (but without switching to XOSC_HF).
Definition: osc.c:243
int16_t OSC_HPOSCRelativeFrequencyOffsetToRFCoreFormatConvert(int32_t HPOSC_RelFreqOffset)
Converts the relative frequency offset of HPOSC to the RF Core parameter format.
Definition: osc.c:355
#define OSC_XOSC_LF
Definition: osc.h:119
#define RTC_CV_TO_MS(x)
Definition: osc.c:81