mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
targets/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_vdac.c
- Committer:
- AnnaBridge
- Date:
- 2019-02-20
- Revision:
- 189:f392fc9709a3
- Parent:
- 179:b0033dcd6934
File content as of revision 189:f392fc9709a3:
/***************************************************************************//** * @file em_vdac.c * @brief Digital to Analog Converter (VDAC) Peripheral API * @version 5.3.3 ******************************************************************************* * # License * <b>Copyright 2016 Silicon Laboratories, Inc. http://www.silabs.com</b> ******************************************************************************* * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no * obligation to support this Software. Silicon Labs is providing the * Software "AS IS", with no express or implied warranties of any kind, * including, but not limited to, any implied warranties of merchantability * or fitness for any particular purpose or warranties against infringement * of any proprietary rights of a third party. * * Silicon Labs will not be liable for any consequential, incidental, or * special damages, or any other relief, or for any claim by any third party, * arising from your use of this Software. * ******************************************************************************/ #include "em_vdac.h" #if defined(VDAC_COUNT) && (VDAC_COUNT > 0) #include "em_cmu.h" /***************************************************************************//** * @addtogroup emlib * @{ ******************************************************************************/ /***************************************************************************//** * @addtogroup VDAC * @{ ******************************************************************************/ /******************************************************************************* ******************************* DEFINES *********************************** ******************************************************************************/ /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ /** Validation of VDAC channel for assert statements. */ #define VDAC_CH_VALID(ch) ((ch) <= 1) /** Max VDAC clock */ #define VDAC_MAX_CLOCK 1000000 /** Max clock frequency of internal clock oscillator, 10 MHz + 20%. */ #define VDAC_INTERNAL_CLOCK_FREQ 12000000 /** @endcond */ /******************************************************************************* ************************** GLOBAL FUNCTIONS ******************************* ******************************************************************************/ /***************************************************************************//** * @brief * Enable/disable VDAC channel. * * @param[in] vdac * Pointer to VDAC peripheral register block. * * @param[in] ch * Channel to enable/disable. * * @param[in] enable * true to enable VDAC channel, false to disable. ******************************************************************************/ void VDAC_Enable(VDAC_TypeDef *vdac, unsigned int ch, bool enable) { EFM_ASSERT(VDAC_REF_VALID(vdac)); EFM_ASSERT(VDAC_CH_VALID(ch)); if (ch == 0) { if (enable) { vdac->CMD = VDAC_CMD_CH0EN; } else { vdac->CMD = VDAC_CMD_CH0DIS; while (vdac->STATUS & VDAC_STATUS_CH0ENS) ; } } else { if (enable) { vdac->CMD = VDAC_CMD_CH1EN; } else { vdac->CMD = VDAC_CMD_CH1DIS; while (vdac->STATUS & VDAC_STATUS_CH1ENS) ; } } } /***************************************************************************//** * @brief * Initialize VDAC. * * @details * Initializes common parts for both channels. This function will also load * calibration values from the Device Information (DI) page into the VDAC * calibration register. * To complete a VDAC setup, channel control configuration must also be done, * please refer to VDAC_InitChannel(). * * @note * This function will disable both channels prior to configuration. * * @param[in] vdac * Pointer to VDAC peripheral register block. * * @param[in] init * Pointer to VDAC initialization structure. ******************************************************************************/ void VDAC_Init(VDAC_TypeDef *vdac, const VDAC_Init_TypeDef *init) { uint32_t cal, tmp = 0; uint32_t const volatile *calData; EFM_ASSERT(VDAC_REF_VALID(vdac)); /* Make sure both channels are disabled. */ vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS; while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) ; /* Get OFFSETTRIM calibration value. */ cal = ((DEVINFO->VDAC0CH1CAL & _DEVINFO_VDAC0CH1CAL_OFFSETTRIM_MASK) >> _DEVINFO_VDAC0CH1CAL_OFFSETTRIM_SHIFT) << _VDAC_CAL_OFFSETTRIM_SHIFT; if (init->mainCalibration) { calData = &DEVINFO->VDAC0MAINCAL; } else { calData = &DEVINFO->VDAC0ALTCAL; } /* Get correct GAINERRTRIM calibration value. */ switch (init->reference) { case vdacRef1V25Ln: tmp = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25LN_MASK) >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25LN_SHIFT; break; case vdacRef2V5Ln: tmp = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5LN_MASK) >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5LN_SHIFT; break; case vdacRef1V25: tmp = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25_MASK) >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM1V25_SHIFT; break; case vdacRef2V5: tmp = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5_MASK) >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIM2V5_SHIFT; break; case vdacRefAvdd: case vdacRefExtPin: tmp = (*calData & _DEVINFO_VDAC0MAINCAL_GAINERRTRIMVDDANAEXTPIN_MASK) >> _DEVINFO_VDAC0MAINCAL_GAINERRTRIMVDDANAEXTPIN_SHIFT; break; } /* Set GAINERRTRIM calibration value. */ cal |= tmp << _VDAC_CAL_GAINERRTRIM_SHIFT; /* Get GAINERRTRIMCH1 calibration value. */ switch (init->reference) { case vdacRef1V25Ln: case vdacRef1V25: case vdacRefAvdd: case vdacRefExtPin: tmp = (DEVINFO->VDAC0CH1CAL && _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1A_MASK) >> _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1A_SHIFT; break; case vdacRef2V5Ln: case vdacRef2V5: tmp = (DEVINFO->VDAC0CH1CAL && _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1B_MASK) >> _DEVINFO_VDAC0CH1CAL_GAINERRTRIMCH1B_SHIFT; break; } /* Set GAINERRTRIM calibration value. */ cal |= tmp << _VDAC_CAL_GAINERRTRIMCH1_SHIFT; tmp = ((uint32_t)init->asyncClockMode << _VDAC_CTRL_DACCLKMODE_SHIFT) | ((uint32_t)init->warmupKeepOn << _VDAC_CTRL_WARMUPMODE_SHIFT) | ((uint32_t)init->refresh << _VDAC_CTRL_REFRESHPERIOD_SHIFT) | (((uint32_t)init->prescaler << _VDAC_CTRL_PRESC_SHIFT) & _VDAC_CTRL_PRESC_MASK) | ((uint32_t)init->reference << _VDAC_CTRL_REFSEL_SHIFT) | ((uint32_t)init->ch0ResetPre << _VDAC_CTRL_CH0PRESCRST_SHIFT) | ((uint32_t)init->outEnablePRS << _VDAC_CTRL_OUTENPRS_SHIFT) | ((uint32_t)init->sineEnable << _VDAC_CTRL_SINEMODE_SHIFT) | ((uint32_t)init->diff << _VDAC_CTRL_DIFF_SHIFT); /* Write to VDAC registers. */ vdac->CAL = cal; vdac->CTRL = tmp; } /***************************************************************************//** * @brief * Initialize a VDAC channel. * * @param[in] vdac * Pointer to VDAC peripheral register block. * * @param[in] init * Pointer to VDAC channel initialization structure. * * @param[in] ch * Channel number to initialize. ******************************************************************************/ void VDAC_InitChannel(VDAC_TypeDef *vdac, const VDAC_InitChannel_TypeDef *init, unsigned int ch) { uint32_t vdacChCtrl, vdacStatus; EFM_ASSERT(VDAC_REF_VALID(vdac)); EFM_ASSERT(VDAC_CH_VALID(ch)); /* Make sure both channels are disabled. */ vdacStatus = vdac->STATUS; vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS; while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) ; vdacChCtrl = ((uint32_t)init->prsSel << _VDAC_CH0CTRL_PRSSEL_SHIFT) | ((uint32_t)init->prsAsync << _VDAC_CH0CTRL_PRSASYNC_SHIFT) | ((uint32_t)init->trigMode << _VDAC_CH0CTRL_TRIGMODE_SHIFT) | ((uint32_t)init->sampleOffMode << _VDAC_CH0CTRL_CONVMODE_SHIFT); if (ch == 0) { vdac->CH0CTRL = vdacChCtrl; } else { vdac->CH1CTRL = vdacChCtrl; } /* Check if the channel must be enabled. */ if (init->enable) { if (ch == 0) { vdac->CMD = VDAC_CMD_CH0EN; } else { vdac->CMD = VDAC_CMD_CH1EN; } } /* Check if the other channel had to be turned off above * and needs to be turned on again. */ if (ch == 0) { if (vdacStatus & VDAC_STATUS_CH1ENS) { vdac->CMD = VDAC_CMD_CH1EN; } } else { if (vdacStatus & VDAC_STATUS_CH0ENS) { vdac->CMD = VDAC_CMD_CH0EN; } } } /***************************************************************************//** * @brief * Set the output signal of a VDAC channel to a given value. * * @details * This function sets the output signal of a VDAC channel by writing @p value * to the corresponding CHnDATA register. * * @param[in] vdac * Pointer to VDAC peripheral register block. * * @param[in] channel * Channel number to set output of. * * @param[in] value * Value to write to the channel output register CHnDATA. ******************************************************************************/ void VDAC_ChannelOutputSet(VDAC_TypeDef *vdac, unsigned int channel, uint32_t value) { switch (channel) { case 0: VDAC_Channel0OutputSet(vdac, value); break; case 1: VDAC_Channel1OutputSet(vdac, value); break; default: EFM_ASSERT(0); break; } } /***************************************************************************//** * @brief * Calculate prescaler value used to determine VDAC clock. * * @details * The VDAC clock is given by input clock divided by prescaler+1. * * VDAC_CLK = IN_CLK / (prescale + 1) * * Maximum VDAC clock is 1 MHz. Input clock is HFPERCLK when VDAC synchronous * mode is selected, or an internal oscillator of 10 MHz +/- 20% when * asynchronous mode is selected. * * @note * If the requested VDAC frequency is low and the max prescaler value can not * adjust the actual VDAC frequency lower than requested, the max prescaler * value is returned, resulting in a higher VDAC frequency than requested. * * @param[in] vdacFreq VDAC frequency target. The frequency will automatically * be adjusted to be below max allowed VDAC clock. * * @param[in] syncMode Set to true if you intend to use VDAC in synchronous * mode. * * @param[in] hfperFreq Frequency in Hz of HFPERCLK oscillator. Set to 0 to * use currently defined HFPERCLK clock setting. This parameter is only used * when syncMode is set to true. * * @return * Prescaler value to use for VDAC in order to achieve a clock value less than * or equal to @p vdacFreq. ******************************************************************************/ uint32_t VDAC_PrescaleCalc(uint32_t vdacFreq, bool syncMode, uint32_t hfperFreq) { uint32_t ret, refFreq; /* Make sure selected VDAC clock is below max value */ if (vdacFreq > VDAC_MAX_CLOCK) { vdacFreq = VDAC_MAX_CLOCK; } if (!syncMode) { refFreq = VDAC_INTERNAL_CLOCK_FREQ; } else { if (hfperFreq) { refFreq = hfperFreq; } else { refFreq = CMU_ClockFreqGet(cmuClock_HFPER); } } /* Iterate in order to determine best prescale value. Start with lowest */ /* prescaler value in order to get the first equal or less VDAC */ /* frequency value. */ for (ret = 0; ret <= _VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT; ret++) { if ((refFreq / (ret + 1)) <= vdacFreq) { break; } } /* If ret is higher than the max prescaler value, make sure to return the max value. */ if (ret > (_VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT)) { ret = _VDAC_CTRL_PRESC_MASK >> _VDAC_CTRL_PRESC_SHIFT; } return ret; } /***************************************************************************//** * @brief * Reset VDAC to same state as after a HW reset. * * @param[in] vdac * Pointer to VDAC peripheral register block. ******************************************************************************/ void VDAC_Reset(VDAC_TypeDef *vdac) { /* Disable channels, before resetting other registers. */ vdac->CMD = VDAC_CMD_CH0DIS | VDAC_CMD_CH1DIS; while (vdac->STATUS & (VDAC_STATUS_CH0ENS | VDAC_STATUS_CH1ENS)) ; vdac->CH0CTRL = _VDAC_CH0CTRL_RESETVALUE; vdac->CH1CTRL = _VDAC_CH1CTRL_RESETVALUE; vdac->CH0DATA = _VDAC_CH0DATA_RESETVALUE; vdac->CH1DATA = _VDAC_CH1DATA_RESETVALUE; vdac->CTRL = _VDAC_CTRL_RESETVALUE; vdac->IEN = _VDAC_IEN_RESETVALUE; vdac->IFC = _VDAC_IFC_MASK; vdac->CAL = _VDAC_CAL_RESETVALUE; } /** @} (end addtogroup VDAC) */ /** @} (end addtogroup emlib) */ #endif /* defined(VDAC_COUNT) && (VDAC_COUNT > 0) */