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