1 /******************************************************************************
2 * Filename: tempdiode.c
3 *
4 * Description: Driverlib for the high accuracy temperature diode
5 *
6 * Copyright (c) 2023, Texas Instruments Incorporated
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * 1) Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * 2) Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * 3) Neither the name of the ORGANIZATION nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 ******************************************************************************/
36
37 #include <stdint.h>
38
39 #include "adc.h"
40 #include "hapi.h"
41 #include "tempdiode.h"
42 #include "interrupt.h"
43
44 #include "../inc/hw_clkctl.h"
45 #include "../inc/hw_fcfg.h"
46 #include "../inc/hw_memmap.h"
47 #include "../inc/hw_pmud.h"
48 #include "../inc/hw_sys0.h"
49 #include "../inc/hw_types.h"
50
51 #ifdef __IAR_SYSTEMS_ICC__
52 __ramfunc static uint32_t TempDiodeRamHWREG(uint32_t address, uint32_t data);
53 #elif (defined(__GNUC__) || defined(__clang__))
54 static uint32_t TempDiodeRamHWREG(uint32_t address, uint32_t data) __attribute__((section(".TI.ramfunc"), noinline));
55 #else
56 #error Unsupported Compiler
57 #endif
58
59 extern int32_t voltageToTempHardcoded(uint32_t microVolts);
60 static void enableADC(void);
61 static int32_t voltageToTemp(uint32_t microVolts);
62 static uint64_t isqrt(uint64_t n);
63
64 /* Macros for finding minimum between two and three numbers */
65 #define MIN2(a, b) ((a) < (b) ? (a) : (b))
66 #define MIN3(a, b, c) MIN2(MIN2((a), (b)), (c))
67
68 //*****************************************************************************
69 //
70 // Writes data to register while executing from RAM
71 //
72 //*****************************************************************************
73 #ifdef __IAR_SYSTEMS_ICC__
TempDiodeRamHWREG(uint32_t address,uint32_t data)74 __ramfunc static uint32_t TempDiodeRamHWREG(uint32_t address, uint32_t data)
75 {
76 HWREG(address) = data;
77 /* Wait at least 11 us before proceeding with any flash operations */
78 HapiWaitUs(11);
79 return HWREG(address);
80 }
81 #elif (defined(__GNUC__) || defined(__clang__))
TempDiodeRamHWREG(uint32_t address,uint32_t data)82 static uint32_t TempDiodeRamHWREG(uint32_t address, uint32_t data)
83 {
84 HWREG(address) = data;
85 /* Wait at least 11 us before proceeding with any flash operations */
86 HapiWaitUs(11);
87 return HWREG(address);
88 }
89 #else
90 #error Unsupported Compiler
91 #endif
92
93 //*****************************************************************************
94 //
95 // Enable and configure the ADC. Parameters are the same as used during
96 // trim-procedure.
97 //
98 //*****************************************************************************
enableADC(void)99 static void enableADC(void)
100 {
101 /* Enable ADC peripheral */
102 HWREG(CLKCTL_BASE + CLKCTL_O_CLKENSET0) = CLKCTL_CLKENSET0_ADC0;
103
104 /* Configure ADC CTL-register 0 */
105 ADCSetMemctlRange(0, 0);
106
107 /* Set maximum sampling duration for highest accuracy */
108 ADCSetSampleDuration(ADC_CLOCK_DIVIDER_48, 1023);
109
110 /* Use 12-bit resolution */
111 ADCSetResolution(ADC_RESOLUTION_12_BIT);
112
113 /* Use internal 1.4V reference, sample on internal channel 14 (VA_ATEST1) */
114 ADCSetInput(ADC_FIXED_REFERENCE_1V4, ADC_MEMCTL0_CHANSEL_CHAN_14, 0);
115
116 /* Adjust ADC to 1.4V reference */
117 ADCSetAdjustmentOffset(ADC_FIXED_REFERENCE_1V4);
118
119 /* Perform single conversion */
120 ADCSetSequence(ADC_SEQUENCE_SINGLE);
121 }
122
123 //*****************************************************************************
124 //
125 // Converts a voltage (uV) measured across the diode to temperature (degC),
126 // with 4 fractional bits, using hardcoded coefficients. If the device has
127 // coefficients available in FCFG, or if different hardcoded values are needed,
128 // then this function can be overridden.
129 //
130 //*****************************************************************************
voltageToTempHardcoded(uint32_t microVolts)131 int32_t __attribute__((weak)) voltageToTempHardcoded(uint32_t microVolts)
132 {
133 /* The tempsense diode voltage (mV) as a function of temperature (degC) can
134 * be modeled as mV(T) = a*T^2 + b*T + c, where the coefficients are:
135 * a = -0.000327
136 * b = -1.435965
137 * c = 835.584465
138 *
139 * To improve the performance we find the delta between the curve fit, and
140 * the measured FCFG value. The FCFG temperature is not precisely 30 degC,
141 * but 29.5 degC instead.
142 * We calculate mV(29.5) = 792.938926 mV.
143 *
144 * The device-specific curve fit function then becomes
145 * mV(T) = a*T^2 + b*T + c + delta, where delta = (FCFG_value - mV(29.5))
146 *
147 * mV(T) = -0.000327*T^2 - 1.435965*T + 835.584465 + FCFG_value - 792.938926
148 *
149 * To bring all constants from floating-point to fixed-point integers we
150 * multiply the entire equation by a factor 2^24. This gives good enough
151 * resolution, and leaves a margin before overflowing 64 bit calculations.
152 * The shifted coefficients become as follows:
153 *
154 * aScaled = a * 2^24 = -5486
155 * bScaled = b * 2^24 = -24091495
156 * cScaled = c * 2^24 = 14018781056
157 * mV(29.5)Scaled = 792.938926 * 2^24 = 13303307632
158 *
159 */
160
161 int64_t aScaled = -5486LL; /* -0.000327 * 2^24 */
162 int64_t bScaled = -24091495LL; /* -1.435965 * 2^24 */
163 int64_t cScaled = 14018781056LL; /* 835.584465 * 2^24 */
164 int64_t mV28Scaled = 13303307632LL; /* mV(29.5) * 2^24 */
165
166 /* To find the temperature T, we solve the equation
167 * 0 = p2*T^2 + p1*T + p0, using the quadratic formula, where
168 * p2 = aScaled
169 * p1 = bScaled
170 * p0 = cScaled + fcfg_valueScaled - mV(29.5)Scaled - inputVoltageScaled
171 *
172 * T = (-p1 - sqrt(p1*p1 - 4*p2*p0)) / (2*p2)
173 */
174
175 int64_t fcfgValue = fcfg->appTrims.cc23x0r5.auxDiodeCal30C.auxDiodeVoltage -
176 fcfg->appTrims.cc23x0r5.auxDiodeCal30C.auxDiodeGnd;
177
178 /* The FCFG voltage value is already left-shifted by 4, so we only shift it
179 * 20 more.
180 */
181 int64_t fcfgValueScaled = fcfgValue << 20;
182
183 /* Convert input voltage (microvolts) to scaled millivolts */
184 int64_t inputVoltageScaled = (((int64_t)microVolts << 24) + 500) / 1000;
185
186 int64_t p2 = aScaled;
187 int64_t p1 = bScaled;
188 int64_t p0 = cScaled + fcfgValueScaled - mV28Scaled - inputVoltageScaled;
189
190 /* Apply quadratic formula, but scale numerator by a factor 16 to get 4
191 * fractional bits in the temperature result.
192 */
193 int32_t temperature = ((-p1 - (int64_t)isqrt(p1 * p1 - (4 * p2 * p0))) << 4) / (2 * p2);
194
195 return temperature;
196 }
197
198 //*****************************************************************************
199 //
200 // Converts a voltage (uV) measured across the diode to temperature (degC),
201 // with 4 fractional bits.
202 //
203 //*****************************************************************************
voltageToTemp(uint32_t microVolts)204 static int32_t voltageToTemp(uint32_t microVolts)
205 {
206 /* Check if coefficients are directly available in FCFG. If not, fall back
207 * on function using hardcoded values. Coefficients were only introduced in
208 * FCFG layout revision 6.
209 */
210 uint8_t fcfgRevision = fcfg->appTrims.revision;
211
212 if (fcfgRevision < 0x06)
213 {
214 return voltageToTempHardcoded(microVolts);
215 }
216
217 /* The tempsense diode voltage (mV) as a function of temperature (degC) can
218 * be modeled as mV(T) = a*T^2 + b*T + c, where the coefficients are:
219 * a = a_fcfg >> a_fcfg_shift
220 * b = b_fcfg >> b_fcfg_shift
221 * c = c_fcfg >> c_fcfg_shift
222 */
223
224 int64_t aScaled = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP2;
225 int64_t bScaled = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP1;
226 int64_t cScaled = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP0;
227
228 uint8_t aShiftFactor = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP2Shift;
229 uint8_t bShiftFactor = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP1Shift;
230 uint8_t cShiftFactor = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP0Shift;
231
232 /* Bring coefficients to the same scale. Try to shift towards the
233 * numerically highest coefficient (the one with the largest shift) in
234 * order to maintain highest possible resolution. Since coefficients are
235 * 16-bit, they should be scaled maximum by 16, otherwise two coefficients
236 * would overflow 64-bit when multiplied. If some coefficients need to be
237 * shifted more than 16 bits, a middle ground must be found and some
238 * coefficients must be shifted down instead.
239 */
240 uint8_t minShiftFactor = MIN3(aShiftFactor, bShiftFactor, cShiftFactor);
241
242 /* Always shift up at least 16 bits */
243 uint8_t commonShiftFactor = minShiftFactor + 16;
244
245 int64_t *coefficients[3] = {&aScaled, &bScaled, &cScaled};
246 uint8_t shiftFactors[3] = {aShiftFactor, bShiftFactor, cShiftFactor};
247
248 /* Loop over all three coefficients and scale each one. */
249 for (int i = 0; i < 3; i++)
250 {
251 if (shiftFactors[i] < commonShiftFactor)
252 {
253 /* Scale this coefficient up towards the common scale factor */
254 *coefficients[i] <<= (commonShiftFactor - shiftFactors[i]);
255 }
256 else if (shiftFactors[i] > commonShiftFactor)
257 {
258 /* Scale this coefficient down towards the common scale factor */
259 *coefficients[i] >>= (shiftFactors[i] - commonShiftFactor);
260 }
261 else
262 {
263 /* Do nothing. Scale neither up nor down */
264 }
265 }
266
267 /* Convert input voltage (microvolts) to scaled millivolts. The common
268 * shift factor can be maximum 31 + 16 = 47, and microvolts can be maximum
269 * 1,400,000, which means that (microvolts << commonShiftFactor) can in
270 * theory overflow. Practically, the characteristics of the tempsense diode
271 * tell us this will never happen, but we add a check just in case.
272 */
273 int64_t inputVoltageScaled;
274
275 if (commonShiftFactor >= 42)
276 {
277 /* If shitfting up by more than 42, this value might overflow. Divide
278 * by 1000 to convert to millivolts before shifting.
279 */
280 inputVoltageScaled = (int64_t)(microVolts / 1000) << commonShiftFactor;
281 }
282 else
283 {
284 inputVoltageScaled = (((int64_t)microVolts << commonShiftFactor) + 500) / 1000;
285 }
286
287 /* To find the temperature T, we solve the equation
288 * 0 = p2*T^2 + p1*T + p0, using the quadratic formula, where
289 * p2 = aScaled
290 * p1 = bScaled
291 * p0 = cScaled - inputVoltageScaled
292 *
293 * T = (-p1 - sqrt(p1*p1 - 4*p2*p0)) / (2*p2)
294 */
295 int64_t p2 = aScaled;
296 int64_t p1 = bScaled;
297 int64_t p0 = cScaled - inputVoltageScaled;
298
299 /* Apply quadratic formula, but scale numerator by a factor 16 to get 4
300 * fractional bits in the temperature result. Round to nearest integer.
301 */
302 int64_t dividend = (-p1 - (int64_t)isqrt(p1 * p1 - (4 * p2 * p0))) << 4;
303 int64_t divisor = 2 * p2;
304 int32_t temperature;
305
306 if ((dividend < 0LL) == (divisor < 0LL))
307 {
308 /* If dividend has same sign as divisor */
309 temperature = (dividend + (divisor / 2)) / divisor;
310 }
311 else
312 {
313 /* If dividend has opposite sign as divisor */
314 temperature = (dividend - (divisor / 2)) / divisor;
315 }
316
317 return temperature;
318 }
319
320 //*****************************************************************************
321 //
322 // Reads temperature from high accuracy temperature diode with 4 fractional bits
323 //
324 //*****************************************************************************
TempDiodeGetTemp(void)325 int32_t TempDiodeGetTemp(void)
326 {
327 uint32_t diodeVoltage = 0;
328 bool intAlreadyDisabled;
329
330 /* Unlock ATESTCFG register and connect VR_ATEST to VA_ATEST */
331 HWREG(SYS0_BASE + SYS0_O_ATESTCFG) = 0x5A000000 | SYS0_ATESTCFG_VR2VA1 | SYS0_ATESTCFG_VR2VA0;
332
333 /* Disable interrupts and keep track of whether interrupts were already
334 * disabled or not
335 */
336 intAlreadyDisabled = IntDisableMaster();
337
338 /* Connect 1uA IREF to test bus. This is done via a function executing from
339 * RAM, while interrupts are disabled, to ensure that no flash-operations
340 * are active when the reference current is enabled.
341 */
342 TempDiodeRamHWREG(PMUD_BASE + PMUD_O_PREFSYS, PMUD_PREFSYS_TEST2);
343
344 /* If interrupts were already disabled, then they should be left disabled.
345 * If interrupts were already enabled, then they should be re-enabled here.
346 */
347 if (intAlreadyDisabled == false)
348 {
349 IntEnableMaster();
350 }
351
352 enableADC();
353
354 /* Measure the high side of the diode */
355 HWREG(SYS0_BASE + SYS0_O_TSENSCFG) = SYS0_TSENSCFG_SEL_VALUE;
356
357 /* Perform a dummy-read of the ADC for better settling */
358 ADCManualTrigger();
359 CPUDelay(3);
360 ADCReadResult(0);
361
362 /* Do 4 ADC conversions for averaging */
363 for (uint32_t i = 0; i < 4; i++)
364 {
365 ADCManualTrigger();
366 CPUDelay(3);
367 diodeVoltage += ADCReadResult(0);
368 }
369
370 /* Measure ground (low side of the diode) */
371 HWREG(SYS0_BASE + SYS0_O_TSENSCFG) = SYS0_TSENSCFG_SEL_GND;
372
373 /* Perform a dummy-read of the ADC for better settling */
374 ADCManualTrigger();
375 CPUDelay(3);
376 ADCReadResult(0);
377
378 /* Do 4 ADC conversions for averaging */
379 for (uint32_t i = 0; i < 4; i++)
380 {
381 ADCManualTrigger();
382 CPUDelay(3);
383 diodeVoltage -= ADCReadResult(0);
384 }
385
386 /* Calculate an average of the 4 readings, rounded to the nearest integer */
387 diodeVoltage = (diodeVoltage + 2) >> 2;
388
389 /* Adjust ADC value to compensate for device/reference specific gain */
390 uint16_t gain = ADCGetAdjustmentGain(ADC_FIXED_REFERENCE_1V4);
391 diodeVoltage = ADCAdjustValueForGain(diodeVoltage, ADC_RESOLUTION_12_BIT, gain);
392
393 /* Convert raw reading to microvolts */
394 diodeVoltage = ADCValueToMicrovolts(diodeVoltage, ADC_RESOLUTION_12_BIT, 1400000);
395
396 /* Disable ADC */
397 HWREG(CLKCTL_BASE + CLKCTL_O_CLKENCLR0) = CLKCTL_CLKENCLR0_ADC0;
398
399 /* Reset ATB */
400 HWREG(SYS0_BASE + SYS0_O_ATESTCFG) = 0x5A00000F;
401
402 /* Disable temperature diode measurement */
403 HWREG(SYS0_BASE + SYS0_O_TSENSCFG) &= ~SYS0_TSENSCFG_SEL_M;
404
405 /* Disconnect all test reference signals */
406 intAlreadyDisabled = IntDisableMaster();
407 TempDiodeRamHWREG(PMUD_BASE + PMUD_O_PREFSYS, 0);
408 if (intAlreadyDisabled == false)
409 {
410 IntEnableMaster();
411 }
412
413 return voltageToTemp(diodeVoltage);
414 }
415
416 //*****************************************************************************
417 //
418 // Compute the integer square root of a number n. This function returns the
419 // largest integer whose square is equal to or less than n.
420 //
421 //*****************************************************************************
isqrt(uint64_t n)422 static uint64_t isqrt(uint64_t n)
423 {
424 uint64_t remainder, root;
425
426 /* Initialize the remainder and root to zero */
427 remainder = 0;
428 root = 0;
429
430 /* Loop over the 32 bits in the root */
431 for (uint32_t index = 0; index < 32; index++)
432 {
433 /*
434 * Shift the root up by a bit to make room for the new bit that is
435 * about to be computed.
436 */
437 root <<= 1;
438
439 /* Get two more bits from the input into the remainder */
440 remainder = ((remainder << 2) + (n >> 62));
441 n <<= 2;
442
443 /* Make the test root be 2n + 1 */
444 root++;
445
446 /* See if the root is greater than the remainder */
447 if (root <= remainder)
448 {
449 /* Subtract the test root from the remainder */
450 remainder -= root;
451
452 /* Increment the root, setting the second LSB */
453 root++;
454 }
455 else
456 {
457 /*
458 * The root is greater than the remainder, so the new bit of the
459 * root is actually zero
460 */
461 root--;
462 }
463 }
464
465 /* Return the computed root */
466 return root >> 1;
467 }