1 /******************************************************************************
2 * Filename: adc.c
3 *
4 * Description: Driver for the ADC.
5 *
6 * Copyright (c) 2022 Texas Instruments Incorporated
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1) Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2) Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * 3) Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ******************************************************************************/
35
36 #include "adc.h"
37
38 //*****************************************************************************
39 //
40 // Sets the clock-divider value, and sample duration
41 //
42 //*****************************************************************************
ADCSetSampleDuration(uint32_t clkDiv,uint16_t clkCycles)43 void ADCSetSampleDuration(uint32_t clkDiv, uint16_t clkCycles)
44 {
45 uint32_t tempCtl;
46
47 // Read current control register
48 tempCtl = HWREG(ADC_BASE + ADC_O_CTL0);
49
50 /* Clear clk-div bits */
51 tempCtl &= ~(ADC_CTL0_SCLKDIV_M);
52
53 /* Set clk-div bits from argument */
54 tempCtl |= (clkDiv & ADC_CTL0_SCLKDIV_M);
55
56 /* Write back control register */
57 HWREG(ADC_BASE + ADC_O_CTL0) = tempCtl;
58
59 /* write sample-duration */
60 HWREG(ADC_BASE + ADC_O_SCOMP0) = (clkCycles & ADC_SCOMP0_VAL_M);
61 }
62
63 //*****************************************************************************
64 //
65 // Sets the ADC bit resolution
66 //
67 //*****************************************************************************
ADCSetResolution(uint32_t resolution)68 void ADCSetResolution(uint32_t resolution)
69 {
70 uint32_t tempCtl;
71
72 // Read current control register
73 tempCtl = HWREG(ADC_BASE + ADC_O_CTL2);
74
75 /* Clear resolution bits */
76 tempCtl &= ~(ADC_CTL2_RES_M);
77
78 /* Set resolution bits from argument */
79 tempCtl |= (resolution & ADC_CTL2_RES_M);
80
81 /* Write back control register */
82 HWREG(ADC_BASE + ADC_O_CTL2) = tempCtl;
83 }
84
85 //*****************************************************************************
86 //
87 // Sets the ADC reference source and input channel
88 //
89 //*****************************************************************************
ADCSetInput(uint32_t reference,uint8_t channel,uint32_t index)90 void ADCSetInput(uint32_t reference, uint8_t channel, uint32_t index)
91 {
92 uint32_t tempCtl;
93 /* Set internal reference to disabled by default */
94 uint32_t refCfg = ADC_REFCFG_REFEN_DIS;
95
96 /* Read current control register */
97 tempCtl = HWREG(ADC_BASE + ADC_O_MEMCTL0 + (4 * index));
98
99 /* Clear reference and channel bits */
100 tempCtl &= ~(ADC_MEMCTL0_VRSEL_M | ADC_MEMCTL0_CHANSEL_M);
101
102 /* Set channel */
103 tempCtl |= (channel & ADC_MEMCTL0_CHANSEL_M);
104
105 /* Set internal reference, if selected */
106 if ((reference == ADC_FIXED_REFERENCE_1V4) || (reference == ADC_FIXED_REFERENCE_2V5))
107 {
108 /* Enable internal reference, set bias current */
109 refCfg = ADC_REFCFG_IBPROG_VAL0 | ADC_REFCFG_REFEN_EN;
110
111 /* Set mem-ctrl register to use internal reference */
112 tempCtl |= ADC_MEMCTL0_VRSEL_INTREF;
113
114 /* Set internal reference voltage level */
115 if (reference == ADC_FIXED_REFERENCE_1V4)
116 {
117 refCfg |= ADC_REFCFG_REFVSEL_V1P4;
118 }
119 else
120 {
121 refCfg |= ADC_REFCFG_REFVSEL_V2P5;
122 }
123 }
124 else if (reference == ADC_EXTERNAL_REFERENCE)
125 {
126 /* Set external reference */
127 tempCtl |= ADC_MEMCTL0_VRSEL_EXTREF;
128 }
129 else
130 {
131 /* Neither internal, nor external reference pin selected. Set VDDS as reference */
132 tempCtl |= ADC_MEMCTL0_VRSEL_VDDS;
133 }
134
135 /* Write back control registers */
136 HWREG(ADC_BASE + ADC_O_MEMCTL0 + (4 * index)) = tempCtl;
137 HWREG(ADC_BASE + ADC_O_REFCFG) = refCfg;
138 }
139
140 //*****************************************************************************
141 //
142 // Triggers an ADC conversion
143 //
144 //*****************************************************************************
ADCManualTrigger(void)145 void ADCManualTrigger(void)
146 {
147 uint32_t tempCtl;
148
149 /* Enable conversion. This arms the peripheral and can now be triggered */
150 HWREG(ADC_BASE + ADC_O_CTL0) |= ADC_CTL0_ENC_ON;
151
152 /* Read current control register */
153 tempCtl = HWREG(ADC_BASE + ADC_O_CTL1);
154
155 /* Clear trigger-related fields */
156 tempCtl &= ~(ADC_CTL1_SAMPMODE_M | ADC_CTL1_SC_M | ADC_CTL1_TRIGSRC_M);
157
158 /* Set sampling-mode to automatic, and trigger source to software */
159 tempCtl |= ADC_CTL1_SAMPMODE_AUTO | ADC_CTL1_TRIGSRC_SOFTWARE;
160
161 /* Write back control register */
162 HWREG(ADC_BASE + ADC_O_CTL1) = tempCtl;
163
164 /* Trigger a conversion */
165 HWREG(ADC_BASE + ADC_O_CTL1) |= ADC_CTL1_SC_START;
166 }
167
168 //*****************************************************************************
169 //
170 // Set start and stop control registers
171 //
172 //*****************************************************************************
ADCSetMemctlRange(uint32_t start,uint32_t stop)173 void ADCSetMemctlRange(uint32_t start, uint32_t stop)
174 {
175 uint32_t tempCtl;
176
177 /* Read current control register */
178 tempCtl = HWREG(ADC_BASE + ADC_O_CTL2);
179
180 /* Clear start and stop address bits */
181 tempCtl &= ~(ADC_CTL2_ENDADD_M | ADC_CTL2_STARTADD_M);
182
183 /* Set start and stop address bits */
184 tempCtl |= (start << ADC_CTL2_STARTADD_S) & ADC_CTL2_STARTADD_M;
185 tempCtl |= (stop << ADC_CTL2_ENDADD_S) & ADC_CTL2_ENDADD_M;
186
187 /* Write back control register */
188 HWREG(ADC_BASE + ADC_O_CTL2) = tempCtl;
189 }
190
191 //*****************************************************************************
192 //
193 // Set conversion sequence
194 //
195 //*****************************************************************************
ADCSetSequence(uint32_t sequence)196 void ADCSetSequence(uint32_t sequence)
197 {
198 uint32_t tempCtl;
199
200 /* Read current control register */
201 tempCtl = HWREG(ADC_BASE + ADC_O_CTL1);
202
203 /* Clear sequence bits */
204 tempCtl &= ~(ADC_CTL1_CONSEQ_M);
205
206 /* Set sequence bits */
207 tempCtl |= sequence & ADC_CTL1_CONSEQ_M;
208
209 /* Write back control register */
210 HWREG(ADC_BASE + ADC_O_CTL1) = tempCtl;
211 }
212
213 //*****************************************************************************
214 //
215 // Performs ADC value gain adjustment.
216 //
217 //*****************************************************************************
ADCAdjustValueForGain(uint32_t adcValue,uint32_t bitResolution,uint16_t gain)218 uint32_t ADCAdjustValueForGain(uint32_t adcValue, uint32_t bitResolution, uint16_t gain)
219 {
220 uint32_t adcMaxCode;
221 uint32_t adjustedValue;
222
223 /* Adjust value for gain and offset. Actual gain ratio is (gain/0x8000) */
224 adjustedValue = ((adcValue * gain) + 0x4000) / 0x8000;
225
226 /* Get threshold based on resolution */
227 if (bitResolution == ADC_RESOLUTION_8_BIT)
228 {
229 adcMaxCode = 0xFF;
230 }
231 else if (bitResolution == ADC_RESOLUTION_10_BIT)
232 {
233 adcMaxCode = 0x3FF;
234 }
235 else /* Default to 12-bit */
236 {
237 adcMaxCode = 0xFFF;
238 }
239
240 /* Make sure no overflow occurs */
241 if (adjustedValue > adcMaxCode)
242 {
243 adjustedValue = adcMaxCode;
244 }
245
246 return (adjustedValue);
247 }
248
249 //*****************************************************************************
250 //
251 // Convert ADC code to microvolts
252 //
253 //*****************************************************************************
ADCValueToMicrovolts(uint32_t adcCode,uint32_t bitResolution,uint32_t referenceVoltageMicroVolt)254 uint32_t ADCValueToMicrovolts(uint32_t adcCode, uint32_t bitResolution, uint32_t referenceVoltageMicroVolt)
255 {
256 uint32_t adcMaxCode;
257 uint32_t shift;
258 uint32_t microVolts;
259
260 if (bitResolution == ADC_RESOLUTION_8_BIT)
261 {
262 adcMaxCode = 0xFF;
263 shift = 0;
264 }
265 else if (bitResolution == ADC_RESOLUTION_10_BIT)
266 {
267 adcMaxCode = 0x3FF;
268 shift = 2;
269 }
270 else /* Default to 12-bit */
271 {
272 adcMaxCode = 0xFFF;
273 shift = 4;
274 }
275
276 /* shift down voltage to avoid 32bit overflow */
277 referenceVoltageMicroVolt >>= shift;
278
279 /* Convert from code to microvolts */
280 microVolts = (((adcCode * referenceVoltageMicroVolt) + (adcMaxCode >> 1)) / adcMaxCode);
281
282 /* Shift result back up */
283 microVolts <<= shift;
284
285 return microVolts;
286 }
287
288 //*****************************************************************************
289 //
290 // Get gain value for given reference source
291 //
292 //*****************************************************************************
ADCGetAdjustmentGain(uint32_t reference)293 uint16_t ADCGetAdjustmentGain(uint32_t reference)
294 {
295 uint16_t gain;
296
297 switch (reference)
298 {
299 /* 1.4V reference */
300 case ADC_FIXED_REFERENCE_1V4:
301
302 gain = fcfg->appTrims.cc23x0r5.adcGainWord1.adcGainIntref1P4V;
303 break;
304
305 /* 2.5V reference */
306 case ADC_FIXED_REFERENCE_2V5:
307
308 gain = fcfg->appTrims.cc23x0r5.adcGainWord1.adcGainIntref2P5V;
309 break;
310
311 /* External reference */
312 case ADC_EXTERNAL_REFERENCE:
313
314 gain = fcfg->appTrims.cc23x0r5.adcGainWord0.adcGainExtref;
315 break;
316
317 /* VDDS reference */
318 case ADC_VDDS_REFERENCE:
319
320 gain = fcfg->appTrims.cc23x0r5.adcGainWord0.adcGainVdds;
321 break;
322
323 default:
324 gain = 0x8000;
325 break;
326 }
327
328 /*
329 * On an untrimmed device, the gain fields will read 0xFFFF. In this case, set the gain to unity (0x8000)
330 * The chance that an actual trimmed gain will be 0xFFFF is near zero.
331 */
332 if (gain == 0xFFFF)
333 {
334 gain = 0x8000;
335 }
336
337 return gain;
338 }
339
340 //*****************************************************************************
341 //
342 // Set offset value in ADC peripheral
343 //
344 //*****************************************************************************
ADCSetAdjustmentOffset(uint32_t reference)345 void ADCSetAdjustmentOffset(uint32_t reference)
346 {
347 int8_t offset;
348 uint32_t tmute2_temp;
349
350 switch (reference)
351 {
352 /* 1.4V reference */
353 case ADC_FIXED_REFERENCE_1V4:
354
355 offset = fcfg->appTrims.cc23x0r5.adcOffset.adcOffsetIntref1P4V;
356 break;
357
358 /* 2.5V reference */
359 case ADC_FIXED_REFERENCE_2V5:
360
361 offset = fcfg->appTrims.cc23x0r5.adcOffset.adcOffsetIntref2P5V;
362 break;
363
364 /* External reference */
365 case ADC_EXTERNAL_REFERENCE:
366
367 offset = fcfg->appTrims.cc23x0r5.adcOffset.adcOffsetExtref;
368 break;
369
370 /* VDDS reference */
371 case ADC_VDDS_REFERENCE:
372
373 offset = fcfg->appTrims.cc23x0r5.adcOffset.adcOffsetVdds;
374 break;
375
376 default:
377 offset = 0;
378 break;
379 }
380
381 /* Read out current TMUTE2 register */
382 tmute2_temp = HWREG(SYS0_BASE + SYS0_O_TMUTE2);
383
384 /* Clear offset value in TMUTE2 */
385 tmute2_temp &= ~(SYS0_TMUTE2_OFFSET_M);
386
387 /* Sign-extend the offset value from 8-bit signed to 16-bit signed. Shift and mask, and place into tmute2_temp */
388 tmute2_temp |= (((int16_t)offset) << SYS0_TMUTE2_OFFSET_S) & SYS0_TMUTE2_OFFSET_M;
389
390 /*
391 * Unlock mutable registers to allow writing back tmute2. Register-write must follow within 32 clk-cycles,
392 * after which the mutable registers will be automatically locked. Key taken from hw_sys0.h
393 */
394 HWREG(SYS0_BASE + SYS0_O_MUNLOCK) = 0xC5AF6927;
395
396 /* Write back tmute2 register */
397 HWREG(SYS0_BASE + SYS0_O_TMUTE2) = tmute2_temp;
398
399 /* Lock the mutable registers by writing something other than the key */
400 HWREG(SYS0_BASE + SYS0_O_MUNLOCK) = 0;
401 }