//########################################################################### // // FILE: adc.c // // TITLE: C28x ADC driver. // //########################################################################### // $Copyright: // Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the // distribution. // // Neither the name of Texas Instruments Incorporated nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // $ //########################################################################### #include "adc.h" //***************************************************************************** // // Defines for locations of ADC calibration functions in OTP for use in // ADC_setMode() ONLY. Not intended for use by application code. // //***************************************************************************** // // The following functions calibrate the ADC linearity. Use them in the // ADC_setMode() function only. // #define ADC_calADCAINL 0x0703B4U #define ADC_calADCBINL 0x0703B2U #define ADC_calADCCINL 0x0703B0U #define ADC_calADCDINL 0x0703AEU // // This function looks up the ADC offset trim for a given condition. Use this // in the ADC_setMode() function only. // #define ADC_getOffsetTrim 0x0703ACU //***************************************************************************** // // ADC_setMode // //***************************************************************************** void ADC_setMode(uint32_t base, ADC_Resolution resolution, ADC_SignalMode signalMode) { // // Check the arguments. // ASSERT(ADC_isBaseValid(base)); // // Check for correct signal mode & resolution. In this device: // Single ended signal conversions are supported in 12-bit mode only // Differential signal conversions are supported in 16-bit mode only // if(signalMode == ADC_MODE_SINGLE_ENDED) { ASSERT(resolution == ADC_RESOLUTION_12BIT); } else { ASSERT(resolution == ADC_RESOLUTION_16BIT); } // // Apply the resolution and signalMode to the specified ADC. // EALLOW; HWREGH(base + ADC_O_CTL2) = (HWREGH(base + ADC_O_CTL2) & ~(ADC_CTL2_RESOLUTION | ADC_CTL2_SIGNALMODE)) | ((uint16_t)resolution | (uint16_t)signalMode); EDIS; // // Apply INL and offset trims // ADC_setINLTrim(base); ADC_setOffsetTrim(base); } //***************************************************************************** // // ADC_setINLTrim // //***************************************************************************** void ADC_setINLTrim(uint32_t base) { ADC_Resolution resolution; // // Check the arguments. // ASSERT(ADC_isBaseValid(base)); resolution = (ADC_Resolution) (HWREGH(base + ADC_O_CTL2) & ADC_CTL2_RESOLUTION); EALLOW; switch(base) { case ADCA_BASE: if(HWREGH(ADC_calADCAINL) != 0xFFFFU) { // // Trim function is programmed into OTP, so call it // (*((void (*)(void))ADC_calADCAINL))(); } else { // // Do nothing, no INL trim function populated // } break; case ADCB_BASE: if(HWREGH(ADC_calADCBINL) != 0xFFFFU) { // // Trim function is programmed into OTP, so call it // (*((void (*)(void))ADC_calADCBINL))(); } else { // // Do nothing, no INL trim function populated // } break; case ADCC_BASE: if(HWREGH(ADC_calADCCINL) != 0xFFFFU) { // // Trim function is programmed into OTP, so call it // (*((void (*)(void))ADC_calADCCINL))(); } else { // // Do nothing, no INL trim function populated // } break; case ADCD_BASE: if(HWREGH(ADC_calADCDINL) != 0xFFFFU) { // // Trim function is programmed into OTP, so call it // (*((void (*)(void))ADC_calADCDINL))(); } else { // // Do nothing, no INL trim function populated // } break; default: // // Invalid base address! Do nothing! // break; } // // Apply linearity trim workaround for 12-bit resolution // if(resolution == ADC_RESOLUTION_12BIT) { // // 12-bit linearity trim workaround // HWREG(base + ADC_O_INLTRIM1) &= 0xFFFF0000U; HWREG(base + ADC_O_INLTRIM2) &= 0xFFFF0000U; HWREG(base + ADC_O_INLTRIM4) &= 0xFFFF0000U; HWREG(base + ADC_O_INLTRIM5) &= 0xFFFF0000U; } EDIS; } //***************************************************************************** // // ADC_setOffsetTrim // //***************************************************************************** void ADC_setOffsetTrim(uint32_t base) { uint16_t offsetIndex = 0U; uint16_t offsetTrim = 0U; ADC_Resolution resolution; ADC_SignalMode signalMode; // // Check the arguments. // ASSERT(ADC_isBaseValid(base)); resolution = (ADC_Resolution) (HWREGH(base + ADC_O_CTL2) & ADC_CTL2_RESOLUTION); signalMode = (ADC_SignalMode) (HWREGH(base + ADC_O_CTL2) & ADC_CTL2_SIGNALMODE); switch(base) { case ADCA_BASE: offsetIndex = (uint16_t)(0U * 4U); break; case ADCB_BASE: offsetIndex = (uint16_t)(1U * 4U); break; case ADCC_BASE: offsetIndex = (uint16_t)(2U * 4U); break; case ADCD_BASE: offsetIndex = (uint16_t)(3U * 4U); break; default: // // Invalid base address! // offsetIndex = 0U; break; } // // Offset trim function is programmed into OTP, so call it // if(HWREGH(ADC_getOffsetTrim) != 0xFFFFU) { // // Calculate the index into OTP table of offset trims and call // function to return the correct offset trim // offsetIndex += ((signalMode == ADC_MODE_DIFFERENTIAL) ? 1U : 0U) + (2U * ((resolution == ADC_RESOLUTION_16BIT) ? 1U : 0U)); offsetTrim = (*((uint16_t (*)(uint16_t index))ADC_getOffsetTrim))(offsetIndex); } else { // // Offset trim function is not populated, so set offset trim to 0 // offsetTrim = 0U; } // // Apply the offset trim. Offset Trim is not updated here in case of TMX or // untrimmed devices. The default trims for TMX devices should be handled in // Device_init(). Refer to Device_init() and Device_configureTMXAnalogTrim() // APIs for more details. // if(offsetTrim > 0x0U) { EALLOW; HWREGH(base + ADC_O_OFFTRIM) = offsetTrim; EDIS; } } //***************************************************************************** // // ADC_setPPBTripLimits // //***************************************************************************** void ADC_setPPBTripLimits(uint32_t base, ADC_PPBNumber ppbNumber, int32_t tripHiLimit, int32_t tripLoLimit) { uint32_t ppbHiOffset; uint32_t ppbLoOffset; // // Check the arguments. // ASSERT(ADC_isBaseValid(base)); ASSERT((tripHiLimit <= 65535) && (tripHiLimit >= -65536)); ASSERT((tripLoLimit <= 65535) && (tripLoLimit >= -65536)); // // Get the offset to the appropriate trip limit registers. // ppbHiOffset = (ADC_PPBxTRIPHI_STEP * (uint32_t)ppbNumber) + ADC_O_PPB1TRIPHI; ppbLoOffset = (ADC_PPBxTRIPLO_STEP * (uint32_t)ppbNumber) + ADC_O_PPB1TRIPLO; EALLOW; // // Set the trip high limit. // HWREG(base + ppbHiOffset) = (HWREG(base + ppbHiOffset) & ~ADC_PPBTRIP_MASK) | ((uint32_t)tripHiLimit & ADC_PPBTRIP_MASK); // // Set the trip low limit. // HWREG(base + ppbLoOffset) = (HWREG(base + ppbLoOffset) & ~ADC_PPBTRIP_MASK) | ((uint32_t)tripLoLimit & ADC_PPBTRIP_MASK); EDIS; }