Xmega Application Note


adc.h

Go to the documentation of this file.
00001 
00038 #ifndef ADC_H
00039 #define ADC_H
00040 
00041 #include <compiler.h>
00042 #include <conf_adc.h>
00043 #include <conf_intlvl.h>
00044 #include <nvm.h>
00045 #include <parts.h>
00046 #include <sleepmgr.h>
00047 #include <sysclk.h>
00048 
00049 #ifdef __cplusplus
00050 extern "C" {
00051 #endif
00052 
00103 #if XMEGA_A
00104 #  define ADC_NR_OF_CHANNELS    4
00105 #elif XMEGA_D
00106 #  define ADC_NR_OF_CHANNELS    1
00107 #endif
00108 
00110 struct adc_config {
00111 #if XMEGA_A
00112         uint8_t ctrla;
00113 #endif
00114         uint8_t ctrlb;
00115         uint8_t refctrl;
00116         uint8_t evctrl;
00117         uint8_t prescaler;
00118         uint16_t cmp;
00119 };
00120 
00127 
00129 #define ADCACAL0      offsetof(NVM_PROD_SIGNATURES_t, ADCACAL0)
00130 
00131 #define ADCACAL1      offsetof(NVM_PROD_SIGNATURES_t, ADCACAL1)
00132 
00133 #define ADCBCAL0      offsetof(NVM_PROD_SIGNATURES_t, ADCBCAL0)
00134 
00135 #define ADCBCAL1      offsetof(NVM_PROD_SIGNATURES_t, ADCBCAL1)
00136 
00137 #define TEMPSENSE0    offsetof(NVM_PROD_SIGNATURES_t, TEMPSENSE0)
00138 
00139 #define TEMPSENSE1    offsetof(NVM_PROD_SIGNATURES_t, TEMPSENSE1)
00140 
00142 
00144 enum adc_calibration_data {
00145         ADC_CAL_ADCA,    
00146         ADC_CAL_ADCB,    
00147 
00152         ADC_CAL_TEMPSENSE,
00153 };
00154 
00156 
00157 
00158 #define ADC_CH0      (1U << 0)                 //!< ADC channel 0.
00159 
00160 #if XMEGA_A
00161 #  define ADC_CH1    (1U << 1)                 //!< ADC channel 1.
00162 #  define ADC_CH2    (1U << 2)                 //!< ADC channel 2.
00163 #  define ADC_CH3    (1U << 3)                 //!< ADC channel 3.
00164 #endif /* XMEGA_A */
00165 
00167 
00169 
00170 
00171 #define ADC_INT_TEMPSENSE    ADC_TEMPREF_bm    //!< Temperature sensor.
00172 #define ADC_INT_BANDGAP      ADC_BANDGAP_bm    //!< Bandgap reference.
00173 
00175 
00182 enum adc_trigger {
00184         ADC_TRIG_MANUAL,
00189         ADC_TRIG_FREERUN_SWEEP,
00196         ADC_TRIG_EVENT_SINGLE,
00197 #if XMEGA_A
00198 
00202         ADC_TRIG_EVENT_SWEEP,
00207         ADC_TRIG_EVENT_SYNCSWEEP,
00208 #endif
00209 };
00210 
00212 enum adc_sign {
00213         ADC_SIGN_OFF,                    
00214         ADC_SIGN_ON = ADC_CONMODE_bm,    
00215 };
00216 
00218 enum adc_resolution {
00220         ADC_RES_8       = ADC_RESOLUTION_8BIT_gc,
00222         ADC_RES_12      = ADC_RESOLUTION_12BIT_gc,
00224         ADC_RES_12_LEFT = ADC_RESOLUTION_LEFT12BIT_gc,
00225 };
00226 
00228 enum adc_reference {
00230         ADC_REF_BANDGAP = ADC_REFSEL_INT1V_gc,
00232         ADC_REF_VCC     = ADC_REFSEL_VCC_gc,
00234         ADC_REF_AREFA   = ADC_REFSEL_AREFA_gc,
00236         ADC_REF_AREFB   = ADC_REFSEL_AREFB_gc,
00237 };
00238 
00239 #if defined(CONFIG_ADC_CALLBACK_ENABLE) || defined(__DOXYGEN__)
00240 
00241 
00242 
00251 #if !defined(CONFIG_ADC_CALLBACK_ENABLE) || defined(__DOXYGEN__)
00252 #  define CONFIG_ADC_CALLBACK_ENABLE
00253 #endif
00254 
00269 #if !defined(CONFIG_ADC_CALLBACK_TYPE) || defined(__DOXYGEN__)
00270 #  define CONFIG_ADC_CALLBACK_TYPE    uint16_t
00271 #endif
00272 
00274 typedef CONFIG_ADC_CALLBACK_TYPE adc_result_t;
00275 
00283 typedef void (*adc_callback_t)(ADC_t *adc, uint8_t ch, adc_result_t res);
00284 
00285 void adc_set_callback(ADC_t *adc, adc_callback_t callback);
00286 
00288 #endif /* CONFIG_ADC_CALLBACK_ENABLE */
00289 
00291 
00292 
00293 void adc_enable(ADC_t *adc);
00294 void adc_disable(ADC_t *adc);
00295 bool adc_is_enabled(ADC_t *adc);
00296 
00307 static inline void adc_start_conversion(ADC_t *adc, uint8_t ch_mask)
00308 {
00309         irqflags_t flags = cpu_irq_save();
00310         adc->CTRLA |= ch_mask << ADC_CH0START_bp;
00311         cpu_irq_restore(flags);
00312 }
00313 
00327 static inline uint8_t adc_get_interrupt_flag(ADC_t *adc, uint8_t ch_mask)
00328 {
00329         return (adc->INTFLAGS >> ADC_CH0IF_bp) & ch_mask;
00330 }
00331 
00342 static inline void adc_clear_interrupt_flag(ADC_t *adc, uint8_t ch_mask)
00343 {
00344         adc->INTFLAGS = ch_mask << ADC_CH0IF_bp;
00345 }
00346 
00359 static inline void adc_wait_for_interrupt_flag(ADC_t *adc, uint8_t ch_mask)
00360 {
00361         do { } while (adc_get_interrupt_flag(adc, ch_mask) != ch_mask);
00362         adc_clear_interrupt_flag(adc, ch_mask);
00363 }
00364 
00376 static inline void adc_flush(ADC_t *adc)
00377 {
00378         irqflags_t flags = cpu_irq_save();
00379         adc->CTRLA |= ADC_FLUSH_bm;
00380         cpu_irq_restore(flags);
00381 }
00382 
00394 #define adc_set_compare_value(adc, val)                                        \
00395         do {                                                                   \
00396                 irqflags_t ATPASTE2(adc_flags, __LINE__) =  cpu_irq_save();    \
00397                 (adc)->CMP = val;                                              \
00398                 cpu_irq_restore(ATPASTE2(adc_flags, __LINE__));                \
00399         } while (0)
00400 
00416 #define adc_get_compare_value(adc)    ((adc)->CMP)
00417 
00426 static inline int16_t adc_get_signed_compare_value(ADC_t *adc)
00427 {
00428         int16_t val;
00429         irqflags_t flags;
00430 
00431         flags = cpu_irq_save();
00432         val = adc->CMP;
00433         cpu_irq_restore(flags);
00434 
00435         return val;
00436 }
00437 
00446 static inline uint16_t adc_get_unsigned_compare_value(ADC_t *adc)
00447 {
00448         uint16_t val;
00449         irqflags_t flags;
00450 
00451         flags = cpu_irq_save();
00452         val = adc->CMP;
00453         cpu_irq_restore(flags);
00454 
00455         return val;
00456 }
00457 
00463 static inline uint16_t adc_get_calibration_data(enum adc_calibration_data cal)
00464 {
00465         uint16_t data;
00466 
00467         switch (cal) {
00468 #ifdef ADCA
00469         case ADC_CAL_ADCA:
00470                 data = nvm_read_production_signature_row(ADCACAL1);
00471                 data <<= 8;
00472                 data |= nvm_read_production_signature_row(ADCACAL0);
00473                 break;
00474 #endif
00475 
00476 #ifdef ADCB
00477         case ADC_CAL_ADCB:
00478                 data = nvm_read_production_signature_row(ADCBCAL1);
00479                 data <<= 8;
00480                 data |= nvm_read_production_signature_row(ADCBCAL0);
00481                 break;
00482 #endif
00483 
00484 #if defined(ADCA) || defined(ADCB)
00485         case ADC_CAL_TEMPSENSE:
00486                 data = nvm_read_production_signature_row(TEMPSENSE1);
00487                 data <<= 8;
00488                 data |= nvm_read_production_signature_row(TEMPSENSE0);
00489                 break;
00490 #endif
00491 
00492         default:
00493                 Assert(0);
00494                 data = 0;
00495         }
00496 
00497         return data;
00498 }
00499 
00501 
00503 
00504 
00505 void adc_write_configuration(ADC_t *adc, const struct adc_config *conf);
00506 void adc_read_configuration(ADC_t *adc, struct adc_config *conf);
00507 
00523 static inline void adc_set_clock_rate(struct adc_config *conf, uint32_t clk_adc)
00524 {
00525         uint32_t clk_per;
00526         uint16_t ratio;
00527         uint8_t  psc;
00528 
00529         Assert(clk_adc);
00530 #if XMEGA_A
00531         Assert(clk_adc <= 2000000UL);
00532 #elif XMEGA_D
00533         Assert(clk_adc <= 1400000UL);
00534 #endif
00535 
00536         clk_per = sysclk_get_per_hz();
00537         ratio = clk_per / clk_adc;
00538 
00539         // Round ratio up to the nearest prescaling factor.
00540         if(ratio <= 4) {
00541                 psc = ADC_PRESCALER_DIV4_gc;
00542         } else if (ratio <= 8) {
00543                 psc = ADC_PRESCALER_DIV8_gc;
00544         } else if (ratio <= 16) {
00545                 psc = ADC_PRESCALER_DIV16_gc;
00546         } else if (ratio <= 32) {
00547                 psc = ADC_PRESCALER_DIV32_gc;
00548         } else if (ratio <= 64) {
00549                 psc = ADC_PRESCALER_DIV64_gc;
00550         } else if (ratio <= 128) {
00551                 psc = ADC_PRESCALER_DIV128_gc;
00552         } else if (ratio <= 256) {
00553                 psc = ADC_PRESCALER_DIV256_gc;
00554         } else {
00555                 psc = ADC_PRESCALER_DIV512_gc;
00556         }
00557 
00558         conf->prescaler = psc;
00559 }
00560 
00572 static inline void adc_set_conversion_parameters(struct adc_config *conf,
00573                 enum adc_sign sign, enum adc_resolution res,
00574                 enum adc_reference ref)
00575 {
00576         // Preserve all but conversion and resolution config.
00577         conf->ctrlb &= ~(ADC_CONMODE_bm | ADC_RESOLUTION_gm);
00578         conf->ctrlb |= (uint8_t)res | (uint8_t)sign;
00579 
00580         conf->refctrl &= ~ADC_REFSEL_gm;
00581         conf->refctrl |= ref;
00582 }
00583 
00605 static inline void adc_set_conversion_trigger(struct adc_config *conf,
00606                 enum adc_trigger trig, uint8_t nr_of_ch, uint8_t base_ev_ch)
00607 {
00608         Assert(nr_of_ch);
00609         Assert(nr_of_ch <= ADC_NR_OF_CHANNELS);
00610 #if XMEGA_A
00611         Assert(base_ev_ch <= 7);
00612 #elif XMEGA_D
00613         Assert(base_ev_ch <= 3);
00614 #endif
00615 
00616         switch (trig) {
00617         case ADC_TRIG_MANUAL:
00618                 conf->ctrlb &= ~ADC_FREERUN_bm;
00619                 conf->evctrl = ADC_EVACT_NONE_gc;
00620                 break;
00621 
00622         case ADC_TRIG_FREERUN_SWEEP:
00623                 conf->ctrlb |= ADC_FREERUN_bm;
00624                 conf->evctrl = (nr_of_ch - 1) << ADC_SWEEP_gp;
00625                 break;
00626 
00627         case ADC_TRIG_EVENT_SINGLE:
00628                 conf->ctrlb &= ~ADC_FREERUN_bm;
00629                 conf->evctrl = (base_ev_ch << ADC_EVSEL_gp) |
00630                                 (nr_of_ch << ADC_EVACT_gp);
00631                 break;
00632 
00633 #if XMEGA_A
00634         case ADC_TRIG_EVENT_SWEEP:
00635                 conf->ctrlb &= ~ADC_FREERUN_bm;
00636                 conf->evctrl = (nr_of_ch - 1) << ADC_SWEEP_gp |
00637                                 (base_ev_ch << ADC_EVSEL_gp) |
00638                                 ADC_EVACT_SWEEP_gc;
00639                 break;
00640 
00641         case ADC_TRIG_EVENT_SYNCSWEEP:
00642                 conf->ctrlb &= ~ADC_FREERUN_bm;
00643                 conf->evctrl = ((nr_of_ch - 1) << ADC_SWEEP_gp) |
00644                                 (base_ev_ch << ADC_EVSEL_gp) |
00645                                 ADC_EVACT_SYNCHSWEEP_gc;
00646                 break;
00647 #endif
00648 
00649         default:
00650                 Assert(0);
00651         }
00652 }
00653 
00654 #if XMEGA_A
00655 
00669 static inline void adc_set_dma_request_group(struct adc_config *conf,
00670                 uint8_t nr_of_ch)
00671 {
00672         Assert(nr_of_ch <= ADC_NR_OF_CHANNELS);
00673         Assert(nr_of_ch != 1);
00674 
00675         if (nr_of_ch) {
00676                 conf->ctrla = (nr_of_ch - 1) << ADC_DMASEL_gp;
00677         } else {
00678                 conf->ctrla = ADC_DMASEL_OFF_gc;
00679         }
00680 }
00681 #endif
00682 
00691 static inline void adc_enable_internal_input(struct adc_config *conf,
00692                 uint8_t int_inp)
00693 {
00694         conf->refctrl |= int_inp;
00695 }
00696 
00705 static inline void adc_disable_internal_input(struct adc_config *conf,
00706                 uint8_t int_inp)
00707 {
00708         conf->refctrl &= ~int_inp;
00709 }
00710 
00717 #define adc_set_config_compare_value(conf, val)    \
00718         do {                                       \
00719                 conf->cmp = (uint16_t)val;         \
00720         } while (0)
00721 
00727 #define adc_get_config_compare_value(conf)    (conf->cmp)
00728 
00730 
00752 #if !defined(CONFIG_ADC_INTLVL) || defined(__DOXYGEN__)
00753 #  define CONFIG_ADC_INTLVL    ADC_CH_INTLVL_LO_gc
00754 #endif
00755 
00757 struct adc_channel_config {
00758         uint8_t ctrl;
00759         uint8_t muxctrl;
00760         uint8_t intctrl;
00761 };
00762 
00772 enum adcch_positive_input {
00773         ADCCH_POS_PIN0,
00774         ADCCH_POS_PIN1,
00775         ADCCH_POS_PIN2,
00776         ADCCH_POS_PIN3,
00777         ADCCH_POS_PIN4,
00778         ADCCH_POS_PIN5,
00779         ADCCH_POS_PIN6,
00780         ADCCH_POS_PIN7,
00781 
00783 
00784 #if XMEGA_A4 || XMEGA_D
00785         ADCCH_POS_PIN8,
00786         ADCCH_POS_PIN9,
00787         ADCCH_POS_PIN10,
00788         ADCCH_POS_PIN11,
00789 #endif
00790 #if XMEGA_D3
00791         ADCCH_POS_PIN12,
00792         ADCCH_POS_PIN13,
00793         ADCCH_POS_PIN14,
00794         ADCCH_POS_PIN15,
00795 #endif
00796 
00797 
00799 
00800         ADCCH_POS_TEMPSENSE,     
00801         ADCCH_POS_BANDGAP,       
00802         ADCCH_POS_SCALED_VCC,    
00803 #if XMEGA_A
00804         ADCCH_POS_DAC,           
00805 #endif
00806 
00807 };
00808 
00818 enum adcch_negative_input {
00820 
00821         ADCCH_NEG_PIN0,
00822         ADCCH_NEG_PIN1,
00823         ADCCH_NEG_PIN2,
00824         ADCCH_NEG_PIN3,
00826 
00828 
00829         ADCCH_NEG_PIN4,
00830         ADCCH_NEG_PIN5,
00831         ADCCH_NEG_PIN6,
00832         ADCCH_NEG_PIN7,
00834 
00835         ADCCH_NEG_NONE,          
00836 };
00837 
00839 enum adcch_mode {
00841         ADCCH_MODE_COMPLETE = ADC_CH_INTMODE_COMPLETE_gc,
00843         ADCCH_MODE_BELOW    = ADC_CH_INTMODE_BELOW_gc,
00845         ADCCH_MODE_ABOVE    = ADC_CH_INTMODE_ABOVE_gc,
00846 };
00847 
00849 
00850 
00866 #define adcch_get_result(adc, ch)    ((&(adc)->CH0 + ch)->RES)
00867 
00879 static inline int16_t adcch_get_signed_result(ADC_t *adc, uint8_t ch)
00880 {
00881         int16_t    val;
00882         irqflags_t flags;
00883         ADC_CH_t   *adc_ch;
00884 
00885         adc_ch = &adc->CH0 + ch;
00886 
00887         flags = cpu_irq_save();
00888         val = adc_ch->RES;
00889         cpu_irq_restore(flags);
00890 
00891         return val;
00892 }
00893 
00905 static inline uint16_t adcch_get_unsigned_result(ADC_t*adc, uint8_t ch)
00906 {
00907         uint16_t   val;
00908         irqflags_t flags;
00909         ADC_CH_t   *adc_ch;
00910 
00911         adc_ch = &adc->CH0 + ch;
00912 
00913         flags = cpu_irq_save();
00914         val = adc_ch->RES;
00915         cpu_irq_restore(flags);
00916 
00917         return val;
00918 }
00919 
00921 
00923 
00924 
00925 void adcch_write_configuration(ADC_t *adc, uint8_t ch,
00926                 const struct adc_channel_config *ch_conf);
00927 void adcch_read_configuration(ADC_t *adc, uint8_t ch,
00928                 struct adc_channel_config *ch_conf);
00929 
00940 static inline uint8_t adcch_get_gain_setting(uint8_t gain)
00941 {
00942         switch (gain) {
00943         case 1: return ADC_CH_GAIN_1X_gc;
00944 
00945         case 2: return ADC_CH_GAIN_2X_gc;
00946 
00947         case 4: return ADC_CH_GAIN_4X_gc;
00948 
00949         case 8: return ADC_CH_GAIN_8X_gc;
00950 
00951         case 16: return ADC_CH_GAIN_16X_gc;
00952 
00953         case 32: return ADC_CH_GAIN_32X_gc;
00954 
00955         case 64: return ADC_CH_GAIN_64X_gc;
00956 
00957         default:
00958                 Assert(0);
00959                 return 0;
00960         }
00961 }
00962 
00981 static inline void adcch_set_input(struct adc_channel_config *ch_conf,
00982                 enum adcch_positive_input pos, enum adcch_negative_input neg,
00983                 uint8_t gain)
00984 {
00985         Assert(gain);
00986 
00987         // Configure for internal input.
00988         if (pos >= ADCCH_POS_TEMPSENSE) {
00989                 Assert(gain == 1);
00990                 Assert(neg == ADCCH_NEG_NONE);
00991 
00992                 ch_conf->ctrl = ADC_CH_INPUTMODE_INTERNAL_gc;
00993                 ch_conf->muxctrl = (pos - ADCCH_POS_TEMPSENSE) <<
00994                                 ADC_CH_MUXPOS_gp;
00995                 return;
00996         }
00997 
00998         // Configure for single-ended measurement.
00999         if (neg == ADCCH_NEG_NONE) {
01000                 Assert(gain == 1);
01001 
01002                 ch_conf->ctrl = ADC_CH_INPUTMODE_SINGLEENDED_gc;
01003                 ch_conf->muxctrl = pos << ADC_CH_MUXPOS_gp;
01004 
01005         // Configure for differential measurement.
01006         } else {
01007                 /* Pins 0-3 can only be used for negative input if the gain
01008                  * stage is not used, i.e., unity gain.
01009                  */
01010                 if (neg < ADCCH_NEG_PIN4) {
01011                         Assert(gain == 1);
01012 
01013                         ch_conf->ctrl = ADC_CH_INPUTMODE_DIFF_gc;
01014                         ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) |
01015                                         (neg << ADC_CH_MUXNEG_gp);
01016                 } else {
01017                 /* Pins 4-7 can be used for all gain settings, including unity
01018                  * gain, which is available even if the gain stage is active.
01019                  */
01020                         ch_conf->ctrl = ADC_CH_INPUTMODE_DIFFWGAIN_gc |
01021                                         adcch_get_gain_setting(gain);
01022                         ch_conf->muxctrl = (pos << ADC_CH_MUXPOS_gp) |
01023                                         ((neg - ADCCH_NEG_PIN4) <<
01024                                         ADC_CH_MUXNEG_gp);
01025                 }
01026         }
01027 }
01028 
01035 static inline void adcch_set_interrupt_mode(struct adc_channel_config *ch_conf,
01036                 enum adcch_mode mode)
01037 {
01038         ch_conf->intctrl &= ~ADC_CH_INTMODE_gm;
01039         ch_conf->intctrl |= mode;
01040 }
01041 
01047 static inline void adcch_enable_interrupt(struct adc_channel_config *ch_conf)
01048 {
01049         ch_conf->intctrl &= ~ADC_CH_INTLVL_gm;
01050         ch_conf->intctrl |= CONFIG_ADC_INTLVL;
01051 }
01052 
01058 static inline void adcch_disable_interrupt(struct adc_channel_config *ch_conf)
01059 {
01060         ch_conf->intctrl &= ~ADC_CH_INTLVL_gm;
01061         ch_conf->intctrl |= ADC_CH_INTLVL_OFF_gc;
01062 }
01063 
01065 
01070 #ifdef __cplusplus
01071 }
01072 #endif
01073 
01074 #endif /* ADC_H */
@DOC_TITLE@
Generated on Fri Oct 22 12:15:25 2010 for AVR1300 Using the Xmega ADC by doxygen 1.6.3