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 }