1 /******************************************************************************
2 *  Filename:       aux_adc.c
3 *  Revised:        2020-08-19 12:18:33 +0200 (Wed, 19 Aug 2020)
4 *  Revision:       58172
5 *
6 *  Description:    Driver for the AUX Time to Digital Converter interface.
7 *
8 *  Copyright (c) 2015 - 2020, 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 "aux_adc.h"
40 #include "../inc/hw_memmap.h"
41 #include "../inc/hw_aux_sysif.h"
42 #include "../inc/hw_fcfg1.h"
43 #include "adi.h"
44 #include "event.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  AUXADCDisable
54     #define AUXADCDisable                   NOROM_AUXADCDisable
55     #undef  AUXADCEnableAsync
56     #define AUXADCEnableAsync               NOROM_AUXADCEnableAsync
57     #undef  AUXADCEnableSync
58     #define AUXADCEnableSync                NOROM_AUXADCEnableSync
59     #undef  AUXADCEnableSyncNoBugWorkaround
60     #define AUXADCEnableSyncNoBugWorkaround NOROM_AUXADCEnableSyncNoBugWorkaround
61     #undef  AUXADCDisableInputScaling
62     #define AUXADCDisableInputScaling       NOROM_AUXADCDisableInputScaling
63     #undef  AUXADCFlushFifo
64     #define AUXADCFlushFifo                 NOROM_AUXADCFlushFifo
65     #undef  AUXADCReadFifo
66     #define AUXADCReadFifo                  NOROM_AUXADCReadFifo
67     #undef  AUXADCPopFifo
68     #define AUXADCPopFifo                   NOROM_AUXADCPopFifo
69     #undef  AUXADCGetAdjustmentGain
70     #define AUXADCGetAdjustmentGain         NOROM_AUXADCGetAdjustmentGain
71     #undef  AUXADCGetAdjustmentOffset
72     #define AUXADCGetAdjustmentOffset       NOROM_AUXADCGetAdjustmentOffset
73     #undef  AUXADCValueToMicrovolts
74     #define AUXADCValueToMicrovolts         NOROM_AUXADCValueToMicrovolts
75     #undef  AUXADCMicrovoltsToValue
76     #define AUXADCMicrovoltsToValue         NOROM_AUXADCMicrovoltsToValue
77     #undef  AUXADCAdjustValueForGainAndOffset
78     #define AUXADCAdjustValueForGainAndOffset NOROM_AUXADCAdjustValueForGainAndOffset
79     #undef  AUXADCUnadjustValueForGainAndOffset
80     #define AUXADCUnadjustValueForGainAndOffset NOROM_AUXADCUnadjustValueForGainAndOffset
81 #endif
82 
83 //*****************************************************************************
84 //
85 // Disables the ADC
86 //
87 //*****************************************************************************
88 void
AUXADCDisable(void)89 AUXADCDisable(void)
90 {
91     // Disable the ADC reference
92     ADI8BitsClear(AUX_ADI4_BASE, ADI_4_AUX_O_ADCREF0, ADI_4_AUX_ADCREF0_EN_M | ADI_4_AUX_ADCREF0_REF_ON_IDLE_M | ADI_4_AUX_ADCREF0_SRC_M);
93 
94     // Assert reset and disable the ADC
95     ADI8BitsClear(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M | ADI_4_AUX_ADC0_SMPL_MODE_M | ADI_4_AUX_ADC0_SMPL_CYCLE_EXP_M);
96 
97     // Ensure that scaling is enabled by default before next use of the ADC
98     ADI8BitsClear(AUX_ADI4_BASE, ADI_4_AUX_O_ADC1, ADI_4_AUX_ADC1_SCALE_DIS_M);
99 
100     // Flush the FIFO before disabling the clocks
101     HWREGBITW(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL, 1) = 1; // CMD: EN(1) -> FLUSH(3)
102     HWREGBITW(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL, 1) = 0; // CMD: FLUSH(3) -> EN(1)
103 
104     // Disable the ADC clock (no need to wait since IOB_WUC_ADCCLKCTL_ACK goes low immediately)
105     HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) = 0;
106 
107     // Disable the ADC data interface
108     HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = 0;
109 }
110 
111 //*****************************************************************************
112 //
113 // Enables the ADC for asynchronous operation
114 //
115 //*****************************************************************************
116 void
AUXADCEnableAsync(uint32_t refSource,uint32_t trigger)117 AUXADCEnableAsync(uint32_t refSource, uint32_t trigger)
118 {
119     // Enable the ADC reference, with the following options:
120     // - SRC: Set when using relative reference
121     // - REF_ON_IDLE: Always cleared since there is no idle state in asynchronous operation
122     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADCREF0, refSource | ADI_4_AUX_ADCREF0_EN_M);
123 
124     // Enable the ADC clock
125     HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) = AUX_SYSIF_ADCCLKCTL_REQ_M;
126     while (!(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) & AUX_SYSIF_ADCCLKCTL_ACK_M));
127 
128     // Enable the ADC data interface
129     if (trigger == AUXADC_TRIGGER_MANUAL) {
130         // Manual trigger: No need to configure event routing from GPT
131         HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_NO_EVENT | AUX_ANAIF_ADCCTL_CMD_EN;
132     } else {
133         // GPT trigger: Configure event routing via MCU_EV to the AUX domain
134         HWREG(EVENT_BASE + EVENT_O_AUXSEL0) = trigger;
135         HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_MCU_EV | AUX_ANAIF_ADCCTL_CMD_EN;
136     }
137 
138     // Configure the ADC
139     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_SMPL_MODE_M);
140 
141     // Release reset and enable the ADC
142     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M);
143 }
144 
145 //*****************************************************************************
146 //
147 // Enables the ADC for synchronous operation
148 //
149 // On CC26X2, this function needs a different name to enable a wrapper function
150 // in flash to implement a workaround for a HW bug.
151 //*****************************************************************************
152 void
AUXADCEnableSyncNoBugWorkaround(uint32_t refSource,uint32_t sampleTime,uint32_t trigger)153 AUXADCEnableSyncNoBugWorkaround(uint32_t refSource, uint32_t sampleTime, uint32_t trigger)
154 {
155     // Enable the ADC reference, with the following options:
156     // - SRC: Set when using relative reference
157     // - REF_ON_IDLE: Set when using fixed reference and sample time < 21.3 us
158     uint8_t adcref0 = refSource | ADI_4_AUX_ADCREF0_EN_M;
159     if (!refSource && (sampleTime < AUXADC_SAMPLE_TIME_21P3_US)) {
160         adcref0 |= ADI_4_AUX_ADCREF0_REF_ON_IDLE_M;
161     }
162     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADCREF0, adcref0);
163 
164     // Enable the ADC clock
165     HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) = AUX_SYSIF_ADCCLKCTL_REQ_M;
166     while (!(HWREG(AUX_SYSIF_BASE + AUX_SYSIF_O_ADCCLKCTL) & AUX_SYSIF_ADCCLKCTL_ACK_M));
167 
168     // Enable the ADC data interface
169     if (trigger == AUXADC_TRIGGER_MANUAL) {
170         // Manual trigger: No need to configure event routing from GPT
171         HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_NO_EVENT | AUX_ANAIF_ADCCTL_CMD_EN;
172     } else {
173         // GPT trigger: Configure event routing via MCU_EV to the AUX domain
174         HWREG(EVENT_BASE + EVENT_O_AUXSEL0) = trigger;
175         HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL) = AUX_ANAIF_ADCCTL_START_SRC_MCU_EV | AUX_ANAIF_ADCCTL_CMD_EN;
176     }
177 
178     // Configure the ADC
179     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, sampleTime << ADI_4_AUX_ADC0_SMPL_CYCLE_EXP_S);
180 
181     // Release reset and enable the ADC
182     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M);
183 }
184 
185 //*****************************************************************************
186 //
187 // Enables the ADC for synchronous operation
188 //
189 //*****************************************************************************
190 void
AUXADCEnableSync(uint32_t refSource,uint32_t sampleTime,uint32_t trigger)191 AUXADCEnableSync(uint32_t refSource, uint32_t sampleTime, uint32_t trigger)
192 {
193     // The original AUXADCEnableSync() implementation requires a workaround,
194     // consisting of a delay, after its invocation. This delay is implemented by
195     // repeating the last ADI write operation three times.
196     //
197     // We need to make a call to the ROM symbol directly. Otherwise, the
198     // current build process will call the implementation in flash instead even
199     // if a ROM version is available.
200 #if defined(ROM_AUXADCEnableSyncNoBugWorkaround) && !defined(DRIVERLIB_NOROM) && !defined(DOXYGEN)
201     ROM_AUXADCEnableSyncNoBugWorkaround(refSource, sampleTime, trigger);
202 #else
203     AUXADCEnableSyncNoBugWorkaround(refSource, sampleTime, trigger);
204 #endif
205 
206 
207     // Repeat this write another three times to add a delay. This is required
208     // to prevent the device from hanging if we generate a manual ADC trigger
209     // immediately after enabling the ADC and the Sensor Controller turns on
210     // or off the XOSC_HF as a reference for the TDC at the same time.
211     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M);
212     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M);
213     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC0, ADI_4_AUX_ADC0_EN_M | ADI_4_AUX_ADC0_RESET_N_M);
214 }
215 
216 //*****************************************************************************
217 //
218 // Disables scaling of the ADC input
219 //
220 //*****************************************************************************
221 void
AUXADCDisableInputScaling(void)222 AUXADCDisableInputScaling(void)
223 {
224     ADI8BitsSet(AUX_ADI4_BASE, ADI_4_AUX_O_ADC1, ADI_4_AUX_ADC1_SCALE_DIS_M);
225 }
226 
227 //*****************************************************************************
228 //
229 // Flushes the ADC FIFO
230 //
231 //*****************************************************************************
232 void
AUXADCFlushFifo(void)233 AUXADCFlushFifo(void)
234 {
235     HWREGBITW(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL, 1) = 1; // CMD: EN(1) -> FLUSH(3)
236     HWREGBITW(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCCTL, 1) = 0; // CMD: FLUSH(3) -> EN(1)
237 }
238 
239 //*****************************************************************************
240 //
241 // Waits for and returns the first sample in the ADC FIFO
242 //
243 //*****************************************************************************
244 uint32_t
AUXADCReadFifo(void)245 AUXADCReadFifo(void) {
246 
247     // Wait until there is at least one sample in the FIFO
248     while (HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFOSTAT) & AUX_ANAIF_ADCFIFOSTAT_EMPTY_M);
249 
250     // Return the first sample from the FIFO
251     return HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFO);
252 }
253 
254 //*****************************************************************************
255 //
256 // Returns the first sample in the ADC FIFO, without waiting
257 //
258 //*****************************************************************************
259 uint32_t
AUXADCPopFifo(void)260 AUXADCPopFifo(void) {
261 
262     // Return the first sample from the FIFO. If the FIFO is empty, this
263     // generates ADC FIFO underflow
264     return HWREG(AUX_ANAIF_BASE + AUX_ANAIF_O_ADCFIFO);
265 }
266 
267 //*****************************************************************************
268 //
269 // Returns the gain value used when adjusting for ADC gain/offset
270 //
271 //*****************************************************************************
272 int32_t
AUXADCGetAdjustmentGain(uint32_t refSource)273 AUXADCGetAdjustmentGain(uint32_t refSource)
274 {
275     int32_t gain;
276     if (refSource == AUXADC_REF_FIXED) {
277         // AUXADC_REF_FIXED ==> ABS_GAIN
278         gain = (HWREG(FCFG1_BASE + FCFG1_O_SOC_ADC_ABS_GAIN) & FCFG1_SOC_ADC_ABS_GAIN_SOC_ADC_ABS_GAIN_TEMP1_M) >> FCFG1_SOC_ADC_ABS_GAIN_SOC_ADC_ABS_GAIN_TEMP1_S;
279     } else {
280       // AUXADC_REF_VDDS_REL ==> REL_GAIN
281         gain = (HWREG(FCFG1_BASE + FCFG1_O_SOC_ADC_REL_GAIN) & FCFG1_SOC_ADC_REL_GAIN_SOC_ADC_REL_GAIN_TEMP1_M) >> FCFG1_SOC_ADC_REL_GAIN_SOC_ADC_REL_GAIN_TEMP1_S;
282     }
283     return gain;
284 }
285 
286 //*****************************************************************************
287 //
288 // Returns the offset value used when adjusting for ADC gain/offset
289 //
290 //*****************************************************************************
291 int32_t
AUXADCGetAdjustmentOffset(uint32_t refSource)292 AUXADCGetAdjustmentOffset(uint32_t refSource)
293 {
294     int8_t offset;
295     if ( refSource == AUXADC_REF_FIXED ) {
296         // AUXADC_REF_FIXED ==> ABS_OFFSET
297         offset = HWREG(FCFG1_BASE + FCFG1_O_SOC_ADC_OFFSET_INT) >> FCFG1_SOC_ADC_OFFSET_INT_SOC_ADC_ABS_OFFSET_TEMP1_S;
298     } else {
299         // AUXADC_REF_VDDS_REL ==> REL_OFFSET
300         offset = HWREG(FCFG1_BASE + FCFG1_O_SOC_ADC_OFFSET_INT) >> FCFG1_SOC_ADC_OFFSET_INT_SOC_ADC_REL_OFFSET_TEMP1_S;
301     }
302     return offset;
303 }
304 
305 //*****************************************************************************
306 //
307 // Converts an "ideal" ADC value to microvolts
308 //
309 //*****************************************************************************
310 int32_t
AUXADCValueToMicrovolts(int32_t fixedRefVoltage,int32_t adcValue)311 AUXADCValueToMicrovolts(int32_t fixedRefVoltage, int32_t adcValue)
312 {
313     // Chop off 4 bits during calculations to avoid 32-bit overflow
314     fixedRefVoltage >>= 4;
315     return (((adcValue * fixedRefVoltage) + 2047) / 4095) << 4;
316 }
317 
318 //*****************************************************************************
319 //
320 // Converts a number of microvolts to corresponding "ideal" ADC value
321 //
322 //*****************************************************************************
323 int32_t
AUXADCMicrovoltsToValue(int32_t fixedRefVoltage,int32_t microvolts)324 AUXADCMicrovoltsToValue(int32_t fixedRefVoltage, int32_t microvolts)
325 {
326     // Chop off 4 bits during calculations to avoid 32-bit overflow
327     fixedRefVoltage >>= 4;
328     microvolts >>= 4;
329     return ((microvolts * 4095) + (fixedRefVoltage / 2)) / fixedRefVoltage;
330 }
331 
332 //*****************************************************************************
333 //
334 // Performs ADC value gain and offset adjustment
335 //
336 //*****************************************************************************
337 int32_t
AUXADCAdjustValueForGainAndOffset(int32_t adcValue,int32_t gain,int32_t offset)338 AUXADCAdjustValueForGainAndOffset(int32_t adcValue, int32_t gain, int32_t offset)
339 {
340     // Apply gain and offset adjustment
341     adcValue = (((adcValue + offset) * gain) + 16384) / 32768;
342 
343     // Saturate
344     if (adcValue < 0) {
345         return 0;
346     } else if (adcValue > 4095) {
347         return 4095;
348     } else {
349         return adcValue;
350     }
351 }
352 
353 //*****************************************************************************
354 //
355 // Performs the inverse of the ADC value gain and offset adjustment
356 //
357 //*****************************************************************************
358 int32_t
AUXADCUnadjustValueForGainAndOffset(int32_t adcValue,int32_t gain,int32_t offset)359 AUXADCUnadjustValueForGainAndOffset(int32_t adcValue, int32_t gain, int32_t offset)
360 {
361     // Apply inverse gain and offset adjustment
362     adcValue = (((adcValue * 32768) + (gain / 2)) / gain) - offset;
363 
364     // Saturate
365     if (adcValue < 0) {
366         return 0;
367     } else if (adcValue > 4095) {
368         return 4095;
369     } else {
370         return adcValue;
371     }
372 }
373