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