em_adc.c

Go to the documentation of this file.
00001 /***************************************************************************/
00034 #include "em_adc.h"
00035 #if defined(ADC_COUNT) && (ADC_COUNT > 0)
00036 
00037 #include "em_cmu.h"
00038 #include "em_assert.h"
00039 
00040 /***************************************************************************/
00045 /***************************************************************************/
00051 /*******************************************************************************
00052  *******************************   DEFINES   ***********************************
00053  ******************************************************************************/
00054 
00058 #define ADC_REF_VALID(ref)    ((ref) == ADC0)
00059 
00061 #define ADC_MAX_CLOCK    13000000
00062 
00064 #define ADC_MIN_CLOCK    32000
00065 
00069 /*******************************************************************************
00070  ***************************   LOCAL FUNCTIONS   *******************************
00071  ******************************************************************************/
00072 
00075 /***************************************************************************/
00093 static void ADC_CalibrateLoadScan(ADC_TypeDef *adc, ADC_Ref_TypeDef ref)
00094 {
00095   uint32_t cal;
00096 
00097   /* Load proper calibration data depending on selected reference */
00098   /* NOTE: We use ...SCAN... defines below, they are the same as */
00099   /* similar ...SINGLE... defines. */
00100   switch (ref)
00101   {
00102   case adcRef1V25:
00103     cal  = adc->CAL & ~(_ADC_CAL_SCANOFFSET_MASK | _ADC_CAL_SCANGAIN_MASK);
00104     cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_1V25_GAIN_MASK) >>
00105             _DEVINFO_ADC0CAL0_1V25_GAIN_SHIFT) << _ADC_CAL_SCANGAIN_SHIFT;
00106     cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_1V25_OFFSET_MASK) >>
00107             _DEVINFO_ADC0CAL0_1V25_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT;
00108     adc->CAL = cal;
00109     break;
00110 
00111   case adcRef2V5:
00112     cal  = adc->CAL & ~(_ADC_CAL_SCANOFFSET_MASK | _ADC_CAL_SCANGAIN_MASK);
00113     cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_2V5_GAIN_MASK) >>
00114             _DEVINFO_ADC0CAL0_2V5_GAIN_SHIFT) << _ADC_CAL_SCANGAIN_SHIFT;
00115     cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_2V5_OFFSET_MASK) >>
00116             _DEVINFO_ADC0CAL0_2V5_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT;
00117     adc->CAL = cal;
00118     break;
00119 
00120   case adcRefVDD:
00121     cal  = adc->CAL & ~(_ADC_CAL_SCANOFFSET_MASK | _ADC_CAL_SCANGAIN_MASK);
00122     cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_VDD_GAIN_MASK) >>
00123             _DEVINFO_ADC0CAL1_VDD_GAIN_SHIFT) << _ADC_CAL_SCANGAIN_SHIFT;
00124     cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_VDD_OFFSET_MASK) >>
00125             _DEVINFO_ADC0CAL1_VDD_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT;
00126     adc->CAL = cal;
00127     break;
00128 
00129   case adcRef5VDIFF:
00130     cal  = adc->CAL & ~(_ADC_CAL_SCANOFFSET_MASK | _ADC_CAL_SCANGAIN_MASK);
00131     cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_5VDIFF_GAIN_MASK) >>
00132             _DEVINFO_ADC0CAL1_5VDIFF_GAIN_SHIFT) << _ADC_CAL_SCANGAIN_SHIFT;
00133     cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_MASK) >>
00134             _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT;
00135     adc->CAL = cal;
00136     break;
00137 
00138   case adcRef2xVDD:
00139     /* Gain value not of relevance for this reference, leave as is */
00140     cal  = adc->CAL & ~_ADC_CAL_SCANOFFSET_MASK;
00141     cal |= ((DEVINFO->ADC0CAL2 & _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_MASK) >>
00142             _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT;
00143     adc->CAL = cal;
00144     break;
00145 
00146   /* For external references, the calibration must be determined for the */
00147   /* specific application and set explicitly. */
00148   default:
00149     break;
00150   }
00151 }
00152 
00153 /***************************************************************************/
00171 static void ADC_CalibrateLoadSingle(ADC_TypeDef *adc, ADC_Ref_TypeDef ref)
00172 {
00173   uint32_t cal;
00174 
00175   /* Load proper calibration data depending on selected reference */
00176   /* NOTE: We use ...SCAN... defines below, they are the same as */
00177   /* similar ...SINGLE... defines. */
00178   switch (ref)
00179   {
00180   case adcRef1V25:
00181     cal  = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SINGLEGAIN_MASK);
00182     cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_1V25_GAIN_MASK) >>
00183             _DEVINFO_ADC0CAL0_1V25_GAIN_SHIFT) << _ADC_CAL_SINGLEGAIN_SHIFT;
00184     cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_1V25_OFFSET_MASK) >>
00185             _DEVINFO_ADC0CAL0_1V25_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT;
00186     adc->CAL = cal;
00187     break;
00188 
00189   case adcRef2V5:
00190     cal  = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SINGLEGAIN_MASK);
00191     cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_2V5_GAIN_MASK) >>
00192             _DEVINFO_ADC0CAL0_2V5_GAIN_SHIFT) << _ADC_CAL_SINGLEGAIN_SHIFT;
00193     cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_2V5_OFFSET_MASK) >>
00194             _DEVINFO_ADC0CAL0_2V5_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT;
00195     adc->CAL = cal;
00196     break;
00197 
00198   case adcRefVDD:
00199     cal  = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SINGLEGAIN_MASK);
00200     cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_VDD_GAIN_MASK) >>
00201             _DEVINFO_ADC0CAL1_VDD_GAIN_SHIFT) << _ADC_CAL_SINGLEGAIN_SHIFT;
00202     cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_VDD_OFFSET_MASK) >>
00203             _DEVINFO_ADC0CAL1_VDD_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT;
00204     adc->CAL = cal;
00205     break;
00206 
00207   case adcRef5VDIFF:
00208     cal  = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SINGLEGAIN_MASK);
00209     cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_5VDIFF_GAIN_MASK) >>
00210             _DEVINFO_ADC0CAL1_5VDIFF_GAIN_SHIFT) << _ADC_CAL_SINGLEGAIN_SHIFT;
00211     cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_MASK) >>
00212             _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT;
00213     adc->CAL = cal;
00214     break;
00215 
00216   case adcRef2xVDD:
00217     /* Gain value not of relevance for this reference, leave as is */
00218     cal  = adc->CAL & ~_ADC_CAL_SINGLEOFFSET_MASK;
00219     cal |= ((DEVINFO->ADC0CAL2 & _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_MASK) >>
00220             _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT;
00221     adc->CAL = cal;
00222     break;
00223 
00224   /* For external references, the calibration must be determined for the */
00225   /* specific application and set explicitly. */
00226   default:
00227     break;
00228   }
00229 }
00230 
00233 /*******************************************************************************
00234  **************************   GLOBAL FUNCTIONS   *******************************
00235  ******************************************************************************/
00236 
00237 /***************************************************************************/
00255 void ADC_Init(ADC_TypeDef *adc, const ADC_Init_TypeDef *init)
00256 {
00257   uint32_t tmp;
00258 
00259   EFM_ASSERT(ADC_REF_VALID(adc));
00260 
00261   /* Make sure conversion is not in progress */
00262   adc->CMD = ADC_CMD_SINGLESTOP | ADC_CMD_SCANSTOP;
00263 
00264   tmp = ((uint32_t)(init->ovsRateSel) << _ADC_CTRL_OVSRSEL_SHIFT) |
00265         (((uint32_t)(init->timebase) << _ADC_CTRL_TIMEBASE_SHIFT) & _ADC_CTRL_TIMEBASE_MASK) |
00266         (((uint32_t)(init->prescale) << _ADC_CTRL_PRESC_SHIFT) & _ADC_CTRL_PRESC_MASK) |
00267         ((uint32_t)(init->lpfMode) << _ADC_CTRL_LPFMODE_SHIFT) |
00268         ((uint32_t)(init->warmUpMode) << _ADC_CTRL_WARMUPMODE_SHIFT);
00269 
00270   if (init->tailgate)
00271   {
00272     tmp |= ADC_CTRL_TAILGATE;
00273   }
00274 
00275   adc->CTRL = tmp;
00276 }
00277 
00278 
00279 /***************************************************************************/
00299 void ADC_InitScan(ADC_TypeDef *adc, const ADC_InitScan_TypeDef *init)
00300 {
00301   uint32_t tmp;
00302 
00303   EFM_ASSERT(ADC_REF_VALID(adc));
00304 
00305   /* Make sure scan sequence is not in progress */
00306   adc->CMD = ADC_CMD_SCANSTOP;
00307 
00308   /* Load proper calibration data depending on selected reference */
00309   ADC_CalibrateLoadScan(adc, init->reference);
00310 
00311   tmp = ((uint32_t)(init->prsSel) << _ADC_SCANCTRL_PRSSEL_SHIFT) |
00312         ((uint32_t)(init->acqTime) << _ADC_SCANCTRL_AT_SHIFT) |
00313         ((uint32_t)(init->reference) << _ADC_SCANCTRL_REF_SHIFT) |
00314         init->input |
00315         ((uint32_t)(init->resolution) << _ADC_SCANCTRL_RES_SHIFT);
00316 
00317   if (init->prsEnable)
00318   {
00319     tmp |= ADC_SCANCTRL_PRSEN;
00320   }
00321 
00322   if (init->leftAdjust)
00323   {
00324     tmp |= ADC_SCANCTRL_ADJ_LEFT;
00325   }
00326 
00327   if (init->diff)
00328   {
00329     tmp |= ADC_SCANCTRL_DIFF;
00330   }
00331 
00332   if (init->rep)
00333   {
00334     tmp |= ADC_SCANCTRL_REP;
00335   }
00336 
00337   adc->SCANCTRL = tmp;
00338 }
00339 
00340 
00341 /***************************************************************************/
00361 void ADC_InitSingle(ADC_TypeDef *adc, const ADC_InitSingle_TypeDef *init)
00362 {
00363   uint32_t tmp;
00364 
00365   EFM_ASSERT(ADC_REF_VALID(adc));
00366 
00367   /* Make sure single conversion is not in progress */
00368   adc->CMD = ADC_CMD_SINGLESTOP;
00369 
00370   /* Load proper calibration data depending on selected reference */
00371   ADC_CalibrateLoadSingle(adc, init->reference);
00372 
00373   tmp = ((uint32_t)(init->prsSel) << _ADC_SINGLECTRL_PRSSEL_SHIFT) |
00374         ((uint32_t)(init->acqTime) << _ADC_SINGLECTRL_AT_SHIFT) |
00375         ((uint32_t)(init->reference) << _ADC_SINGLECTRL_REF_SHIFT) |
00376         ((uint32_t)(init->input) << _ADC_SINGLECTRL_INPUTSEL_SHIFT) |
00377         ((uint32_t)(init->resolution) << _ADC_SINGLECTRL_RES_SHIFT);
00378 
00379   if (init->prsEnable)
00380   {
00381     tmp |= ADC_SINGLECTRL_PRSEN;
00382   }
00383 
00384   if (init->leftAdjust)
00385   {
00386     tmp |= ADC_SINGLECTRL_ADJ_LEFT;
00387   }
00388 
00389   if (init->diff)
00390   {
00391     tmp |= ADC_SINGLECTRL_DIFF;
00392   }
00393 
00394   if (init->rep)
00395   {
00396     tmp |= ADC_SINGLECTRL_REP;
00397   }
00398 
00399   adc->SINGLECTRL = tmp;
00400 }
00401 
00402 
00403 /***************************************************************************/
00420 uint8_t ADC_PrescaleCalc(uint32_t adcFreq, uint32_t hfperFreq)
00421 {
00422   uint32_t ret;
00423 
00424   /* Make sure selected ADC clock is within valid range */
00425   if (adcFreq > ADC_MAX_CLOCK)
00426   {
00427     adcFreq = ADC_MAX_CLOCK;
00428   }
00429   else if (adcFreq < ADC_MIN_CLOCK)
00430   {
00431     adcFreq = ADC_MIN_CLOCK;
00432   }
00433 
00434   /* Use current HFPER frequency? */
00435   if (!hfperFreq)
00436   {
00437     hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER);
00438   }
00439 
00440   ret = (hfperFreq + adcFreq - 1) / adcFreq;
00441   if (ret)
00442   {
00443     ret--;
00444   }
00445 
00446   return (uint8_t)ret;
00447 }
00448 
00449 
00450 /***************************************************************************/
00461 void ADC_Reset(ADC_TypeDef *adc)
00462 {
00463   /* Stop conversions, before resetting other registers. */
00464   adc->CMD        = ADC_CMD_SINGLESTOP | ADC_CMD_SCANSTOP;
00465   adc->SINGLECTRL = _ADC_SINGLECTRL_RESETVALUE;
00466   adc->SCANCTRL   = _ADC_SCANCTRL_RESETVALUE;
00467   adc->CTRL       = _ADC_CTRL_RESETVALUE;
00468   adc->IEN        = _ADC_IEN_RESETVALUE;
00469   adc->IFC        = _ADC_IFC_MASK;
00470   adc->BIASPROG   = _ADC_BIASPROG_RESETVALUE;
00471 
00472   /* Load calibration values for the 1V25 internal reference. */
00473   ADC_CalibrateLoadSingle(adc, adcRef1V25);
00474   ADC_CalibrateLoadScan(adc, adcRef1V25);
00475 
00476   /* Do not reset route register, setting should be done independently */
00477 }
00478 
00479 
00480 /***************************************************************************/
00490 uint8_t ADC_TimebaseCalc(uint32_t hfperFreq)
00491 {
00492   if (!hfperFreq)
00493   {
00494     hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER);
00495 
00496     /* Just in case, make sure we get non-zero freq for below calculation */
00497     if (!hfperFreq)
00498     {
00499       hfperFreq = 1;
00500     }
00501   }
00502 #if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY)
00503   /* Handle errata on Giant Gecko, max TIMEBASE is 5 bits wide or max 0x1F */
00504   /* cycles. This will give a warmp up time of e.g. 0.645us, not the       */
00505   /* required 1us when operating at 48MHz. One must also increase acqTime  */
00506   /* to compensate for the missing clock cycles, adding up to 1us in total.*/
00507   /* See reference manual for details. */
00508   if( hfperFreq > 32000000 )
00509   {
00510     hfperFreq = 32000000;
00511   }
00512 #endif
00513   /* Determine number of HFPERCLK cycle >= 1us */
00514   hfperFreq += 999999;
00515   hfperFreq /= 1000000;
00516 
00517   /* Return timebase value (N+1 format) */
00518   return (uint8_t)(hfperFreq - 1);
00519 }
00520 
00521 
00524 #endif /* defined(ADC_COUNT) && (ADC_COUNT > 0) */