1 //*****************************************************************************
2 //
3 // am_hal_adc.c
4 //! @file
5 //!
6 //! @brief Functions for interfacing with the Analog to Digital Converter.
7 //!
8 //! @addtogroup adc2 Analog-to-Digital Converter (ADC)
9 //! @ingroup apollo2hal
10 //! @{
11 //
12 //*****************************************************************************
13
14 //*****************************************************************************
15 //
16 // Copyright (c) 2017, Ambiq Micro
17 // All rights reserved.
18 //
19 // Redistribution and use in source and binary forms, with or without
20 // modification, are permitted provided that the following conditions are met:
21 //
22 // 1. Redistributions of source code must retain the above copyright notice,
23 // this list of conditions and the following disclaimer.
24 //
25 // 2. Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // 3. Neither the name of the copyright holder nor the names of its
30 // contributors may be used to endorse or promote products derived from this
31 // software without specific prior written permission.
32 //
33 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
37 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
38 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43 // POSSIBILITY OF SUCH DAMAGE.
44 //
45 // This is part of revision 1.2.11 of the AmbiqSuite Development Package.
46 //
47 //*****************************************************************************
48
49 #include "am_mcu_apollo.h"
50
51 //*****************************************************************************
52 //
53 //! @brief Private SRAM view of temperature trims.
54 //!
55 //! This static SRAM union is private to the ADC HAL functions.
56 //
57 //*****************************************************************************
58 static union
59 {
60 //! These trim values are loaded as uint32_t values.
61 struct
62 {
63 //! Temperature of the package test head (in degrees Kelvin)
64 uint32_t ui32CalibrationTemperature;
65
66 //! Voltage corresponding to temperature measured on test head.
67 uint32_t ui32CalibrationVoltage;
68
69 //! ADC offset voltage measured on the package test head.
70 uint32_t ui32CalibrationOffset;
71
72 //! Flag if default (guess) or measured.
73 bool bMeasured;
74 } ui32;
75 //! These trim values are accessed as floats when used in temp calculations.
76 struct
77 {
78 //! Temperature of the package test head in degrees Kelvin
79 float fCalibrationTemperature;
80
81 //! Voltage corresponding to temperature measured on test head.
82 float fCalibrationVoltage;
83
84 //! ADC offset voltage measured on the package test head.
85 float fCalibrationOffset;
86
87 //! Flag if default (guess) or measured.
88 float fMeasuredFlag;
89 } flt;
90 } priv_temp_trims;
91
92 //*****************************************************************************
93 //
94 //! @brief Configure the ADC.
95 //!
96 //! @param psConfig - pointer to the configuration structure for the ADC.
97 //!
98 //! This function may be used to perform the initial setup of the ADC based on
99 //! setting found in a configuration structure.
100 //!
101 //! @return None.
102 //
103 //*****************************************************************************
104 void
am_hal_adc_config(am_hal_adc_config_t * psConfig)105 am_hal_adc_config(am_hal_adc_config_t *psConfig)
106 {
107 //
108 // Set general ADC configuration parameters.
109 //
110 AM_REG(ADC, CFG) = (psConfig->ui32Clock |
111 psConfig->ui32TriggerConfig |
112 psConfig->ui32Reference |
113 psConfig->ui32ClockMode |
114 psConfig->ui32PowerMode |
115 psConfig->ui32Repeat |
116 AM_REG_ADC_CFG_ADCEN(1));
117
118 //
119 // Grab the temperature trims.
120 //
121 priv_temp_trims.ui32.ui32CalibrationTemperature =
122 am_hal_flash_load_ui32(AM_HAL_ADC_CALIB_TEMP_ADDR);
123 priv_temp_trims.ui32.ui32CalibrationVoltage =
124 am_hal_flash_load_ui32(AM_HAL_ADC_CALIB_AMBIENT_ADDR);
125 priv_temp_trims.ui32.ui32CalibrationOffset =
126 am_hal_flash_load_ui32(AM_HAL_ADC_CALIB_ADC_OFFSET_ADDR);
127
128 if ( (priv_temp_trims.ui32.ui32CalibrationTemperature == 0xffffffff) ||
129 (priv_temp_trims.ui32.ui32CalibrationVoltage == 0xffffffff) ||
130 (priv_temp_trims.ui32.ui32CalibrationOffset == 0xffffffff) )
131 {
132 //
133 // Since the device has not been calibrated on the tester, we'll load
134 // default calibration values. These default values should result
135 // in worst-case temperature measurements of +-6 degress C.
136 //
137 priv_temp_trims.flt.fCalibrationTemperature = AM_HAL_ADC_CALIB_TEMP_DEFAULT;
138 priv_temp_trims.flt.fCalibrationVoltage = AM_HAL_ADC_CALIB_AMBIENT_DEFAULT;
139 priv_temp_trims.flt.fCalibrationOffset = AM_HAL_ADC_CALIB_ADC_OFFSET_DEFAULT;
140 priv_temp_trims.ui32.bMeasured = false;
141 }
142 else
143 {
144 priv_temp_trims.ui32.bMeasured = true;
145 }
146 }
147
148 //*****************************************************************************
149 //
150 //! @brief Get the temperature trim parameters after configuring the ADC.
151 //!
152 //! @param pfTemp - pointer to a location to store the calibration temperature.
153 //! @param pfVoltage - pointer to a location to store the calibration voltage.
154 //! @param pfOffsetV - pointer to a location to store the calibration offset.
155 //!
156 //! This function may be used to access the actual temperature sensor trim
157 //! values from the private structure.
158 //!
159 //! WARNING: only call this after the ADC has been configured with
160 //! am_hal_adc_config.
161 //!
162 //! @return None.
163 //
164 //*****************************************************************************
165 void
am_hal_adc_temp_trims_get(float * pfTemp,float * pfVoltage,float * pfOffsetV)166 am_hal_adc_temp_trims_get(float * pfTemp, float * pfVoltage, float * pfOffsetV)
167 {
168 //
169 // Return trim temperature as a float, if you can.
170 //
171 if ( pfTemp != NULL )
172 {
173 *pfTemp = priv_temp_trims.flt.fCalibrationTemperature;
174 }
175
176 //
177 // Return trim voltage as a float, if you can.
178 //
179 if ( pfVoltage != NULL )
180 {
181 *pfVoltage = priv_temp_trims.flt.fCalibrationVoltage;
182 }
183
184 //
185 // Return trim ADC offset voltage as a float, if you can.
186 //
187 if ( pfOffsetV != NULL )
188 {
189 *pfOffsetV = priv_temp_trims.flt.fCalibrationOffset;
190 }
191 }
192
193 //*****************************************************************************
194 //
195 //! @brief Set the ADC window parameters.
196 //!
197 //! @param ui32Upper - the upper limit for the ADC window.
198 //! @param ui32Upper - the lower limit for the ADC window.
199 //!
200 //! This function may be used to change the ADC window parameters. Please note
201 //! that the upper and lower limits are only 16-bits wide in the ADC hardware.
202 //! This function will ignore the upper 16 bits of these arguments.
203 //!
204 //! @return None.
205 //
206 //*****************************************************************************
207 void
am_hal_adc_window_set(uint32_t ui32Upper,uint32_t ui32Lower)208 am_hal_adc_window_set(uint32_t ui32Upper, uint32_t ui32Lower)
209 {
210 //
211 // Set the window limits for the ADC.
212 //
213 AM_BFW(ADC, WULIM, ULIM, ui32Upper);
214 AM_BFW(ADC, WLLIM, LLIM, ui32Lower);
215 }
216
217 //*****************************************************************************
218 //
219 //! @brief Configure a single ADC slot.
220 //!
221 //! @param ui32SlotNumber - the number of the ADC slot to be configured.
222 //! @param ui32SlotConfig - contains slot-specific options.
223 //!
224 //! This function may be used to configure the settings for an individual ADC
225 //! slot. The parameter \b ui32SlotConfig should be the logical 'OR' of a slot
226 //! average macro, a slot hold-time macro, a slot channel macro, and
227 //! optionally, the slot window enable macro.
228 //!
229 //! @return None.
230 //
231 //*****************************************************************************
232 void
am_hal_adc_slot_config(uint32_t ui32SlotNumber,uint32_t ui32SlotConfig)233 am_hal_adc_slot_config(uint32_t ui32SlotNumber, uint32_t ui32SlotConfig)
234 {
235 uint32_t ui32RegOffset;
236
237 //
238 // Make sure we're accessing a real slot.
239 //
240 am_hal_debug_assert_msg((ui32SlotNumber & 0xFFFFFFFF0) == 0,
241 "Trying to configure an ADC slot that doesn't exist.");
242
243 //
244 // Locate the correct register for this ADC slot.
245 //
246 ui32RegOffset = (AM_REG_ADCn(0) + AM_REG_ADC_SL0CFG_O + (4 * ui32SlotNumber));
247
248 //
249 // Write the register with the caller's configuration value.
250 //
251 AM_REGVAL(ui32RegOffset) = ui32SlotConfig;
252 }
253
254 //*****************************************************************************
255 //
256 //! @brief Peek at the next fifo entry.
257 //!
258 //! This function reads the oldest value in the ADC sample fifo but doesn't
259 //! actually advance the fifo to the next entry. This function is useful when
260 //! you need information from the fifo but you don't want to also empty the
261 //! fifo. This could be helpful if you want to check the FIFO depth without
262 //! pulling any data out.
263 //!
264 //! The value returned by this function is the raw 32-bit value provided by the
265 //! ADC hardware. In order to interpret this value, you will need to use one of
266 //! the following macros.
267 //!
268 //! @return 32-bit FIFO entry.
269 //!
270 //
271 //*****************************************************************************
272 uint32_t
am_hal_adc_fifo_peek(void)273 am_hal_adc_fifo_peek(void)
274 {
275 uint32_t ui32FIFOValue;
276
277 //
278 // Grab a value from the ADC FIFO.
279 //
280 ui32FIFOValue = AM_REG(ADC, FIFO);
281
282 //
283 // Return FIFO entry.
284 //
285 return ui32FIFOValue;
286 }
287
288 //*****************************************************************************
289 //
290 //! @brief
291 //!
292 //! This function reads the oldest value in the ADC fifo and then pops the
293 //! fifo. Use this function when you actually want to pull data out of the
294 //! fifo.
295 //!
296 //! @return 32-bit FIFO entry.
297 //!
298 //
299 //*****************************************************************************
300 uint32_t
am_hal_adc_fifo_pop(void)301 am_hal_adc_fifo_pop(void)
302 {
303 uint32_t ui32FIFOValue;
304
305 //
306 // Grab a value from the ADC FIFO.
307 //
308 ui32FIFOValue = AM_REG(ADC, FIFO);
309
310 //
311 // Pop the FIFO.
312 //
313 AM_REG(ADC, FIFO) = 0;
314
315 //
316 // Return FIFO valid bits.
317 //
318 return ui32FIFOValue;
319 }
320
321 //*****************************************************************************
322 //
323 //! @brief Issue Software Trigger to the ADC.
324 //!
325 //! This function issues the software trigger to the ADC.
326 //!
327 //! @return None.
328 //
329 //*****************************************************************************
330 void
am_hal_adc_trigger(void)331 am_hal_adc_trigger(void)
332 {
333 //
334 // Write to the Software trigger register in the ADC.
335 //
336 AM_REG(ADC, SWT) = 0x37;
337 }
338
339 //*****************************************************************************
340 //
341 //! @brief Enable the ADC.
342 //!
343 //! Use this function to enable the ADC.
344 //!
345 //! @return None.
346 //
347 //*****************************************************************************
348 void
am_hal_adc_enable(void)349 am_hal_adc_enable(void)
350 {
351 //
352 // Enable the ADC.
353 //
354 AM_BFW(ADC, CFG, ADCEN, 0x1);
355 }
356
357 //*****************************************************************************
358 //
359 //! @brief Disable the ADC.
360 //!
361 //! Use this function to disable the ADC.
362 //!
363 //! @return None.
364 //
365 //*****************************************************************************
366 void
am_hal_adc_disable(void)367 am_hal_adc_disable(void)
368 {
369 //
370 // Disable the ADC.
371 //
372 AM_BFW(ADC, CFG, ADCEN, 0x0);
373 }
374
375 //*****************************************************************************
376 //
377 //! @brief Enable selected ADC Interrupts.
378 //!
379 //! @param ui32Interrupt - Use the macro bit fields provided in am_hal_adc.h.
380 //!
381 //! Use this function to enable the ADC interrupts.
382 //!
383 //! @return None.
384 //
385 //*****************************************************************************
386 void
am_hal_adc_int_enable(uint32_t ui32Interrupt)387 am_hal_adc_int_enable(uint32_t ui32Interrupt)
388 {
389 //
390 // Enable the interrupts.
391 //
392 AM_REG(ADC, INTEN) |= ui32Interrupt;
393 }
394
395 //*****************************************************************************
396 //
397 //! @brief Return enabled ADC Interrupts.
398 //!
399 //! Use this function to get all enabled ADC interrupts.
400 //!
401 //! @return enabled ADC Interrupts.
402 //
403 //*****************************************************************************
404 uint32_t
am_hal_adc_int_enable_get(void)405 am_hal_adc_int_enable_get(void)
406 {
407 //
408 // Return enabled interrupts.
409 //
410 return AM_REG(ADC, INTEN);
411 }
412
413 //*****************************************************************************
414 //
415 //! @brief Disable selected ADC Interrupts.
416 //!
417 //! @param ui32Interrupt - Use the macro bit fields provided in am_hal_adc.h.
418 //!
419 //! Use this function to disable the ADC interrupts.
420 //!
421 //! @return None.
422 //
423 //*****************************************************************************
424 void
am_hal_adc_int_disable(uint32_t ui32Interrupt)425 am_hal_adc_int_disable(uint32_t ui32Interrupt)
426 {
427 //
428 // Disable the interrupts.
429 //
430 AM_REG(ADC, INTEN) &= ~ui32Interrupt;
431 }
432
433 //*****************************************************************************
434 //
435 //! @brief Clear selected ADC Interrupts.
436 //!
437 //! @param ui32Interrupt - Use the macro bit fields provided in am_hal_adc.h.
438 //!
439 //! Use this function to clear the ADC interrupts.
440 //!
441 //! @return None.
442 //
443 //*****************************************************************************
444 void
am_hal_adc_int_clear(uint32_t ui32Interrupt)445 am_hal_adc_int_clear(uint32_t ui32Interrupt)
446 {
447 //
448 // Clear the interrupts.
449 //
450 AM_REG(ADC, INTCLR) = ui32Interrupt;
451 }
452
453 //*****************************************************************************
454 //
455 //! @brief Set selected ADC Interrupts.
456 //!
457 //! @param ui32Interrupt - Use the macro bit fields provided in am_hal_adc.h.
458 //!
459 //! Use this function to set the ADC interrupts.
460 //!
461 //! @return None.
462 //
463 //*****************************************************************************
464 void
am_hal_adc_int_set(uint32_t ui32Interrupt)465 am_hal_adc_int_set(uint32_t ui32Interrupt)
466 {
467 //
468 // Set the interrupts.
469 //
470 AM_REG(ADC, INTSET) = ui32Interrupt;
471 }
472
473 //*****************************************************************************
474 //
475 //! @brief Return either enabled or raw selected ADC interrupt status.
476 //!
477 //! @param bEnabledOnly - return the status of only the enabled interrupts.
478 //!
479 //! Use this function to get the ADC interrupt status.
480 //!
481 //! @return enabled or raw ADC interrupt status.
482 //
483 //*****************************************************************************
484 uint32_t
am_hal_adc_int_status_get(bool bEnabledOnly)485 am_hal_adc_int_status_get(bool bEnabledOnly)
486 {
487 //
488 // Return the status.
489 //
490 if (bEnabledOnly)
491 {
492 uint32_t u32RetVal = AM_REG(ADC, INTEN);
493 u32RetVal &= AM_REG(ADC, INTSTAT);
494 return u32RetVal;
495 }
496 else
497 {
498 return AM_REG(ADC, INTSTAT);
499 }
500 }
501
502 //*****************************************************************************
503 //
504 //! @brief Return temperature in degrees C of supplied voltage.
505 //!
506 //! @param fVoltage - return the temperature corresponding to this voltage.
507 //!
508 //! Use this function to convert volts from the temperature sensor into degrees
509 //! C. Caller converts ADC binary code to volts based on reference used.
510 //! This routine looks up the trim parameters and returns corrected temperature.
511 //!
512 //! The computation is based on a line running through 0 degrees K.
513 //! We find the slope from the trimmed temperature calibration point.
514 //!
515 //!
516 //! @return the temperature in degrees C.
517 //
518 //*****************************************************************************
519 float
am_hal_adc_volts_to_celsius(float fVoltage)520 am_hal_adc_volts_to_celsius(float fVoltage)
521 {
522 float fTemp;
523
524 //
525 // Get calibration temperature from trimmed values & convert to degrees K.
526 //
527 float fCalibration_temp = priv_temp_trims.flt.fCalibrationTemperature;
528
529 //
530 // Get remaining trimmed values.
531 //
532 float fCalibration_voltage = priv_temp_trims.flt.fCalibrationVoltage;
533 float fCalibration_offset = priv_temp_trims.flt.fCalibrationOffset;
534
535 //
536 // Compute the temperature.
537 //
538 fTemp = fCalibration_temp;
539 fTemp /= (fCalibration_voltage - fCalibration_offset);
540 fTemp *= (fVoltage - fCalibration_offset);
541
542 //
543 // Give it back to the caller in Celsius.
544 //
545 return fTemp - 273.15f;
546 }
547
548 //*****************************************************************************
549 //
550 // End Doxygen group.
551 //! @}
552 //
553 //*****************************************************************************
554