Contiki 2.6

adc.c

00001 /** @file adc.c
00002  * @brief  ADC HAL functions
00003  *
00004  * <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved.        -->
00005  */
00006 #include PLATFORM_HEADER
00007 #include "hal/error.h"
00008 #include "hal/hal.h"
00009 #include "hal/micro/adc.h"
00010 
00011 
00012 #if (NUM_ADC_USERS > 8)
00013   #error NUM_ADC_USERS must not be greater than 8, or int8u variables in adc.c must be changed
00014 #endif
00015 
00016 static int16u adcData;             // conversion result written by DMA
00017 static int8u adcPendingRequests;   // bitmap of pending requests
00018 volatile static int8u adcPendingConversion; // id of pending conversion
00019 static int8u adcReadingValid;      // bitmap of valid adcReadings
00020 static int16u adcReadings[NUM_ADC_USERS];
00021 static int16u adcConfig[NUM_ADC_USERS];
00022 static boolean adcCalibrated;
00023 static int16s Nvss;
00024 static int16s Nvdd;
00025 /* Modified the original ADC driver for enabling the ADC extended range mode required for 
00026    supporting the STLM20 temperature sensor.
00027    NOTE: 
00028    The ADC extended range is inaccurate due to the high voltage mode bug of the general purpose ADC 
00029    (see STM32W108 errata). As consequence, it is not reccomended to use this ADC driver for getting
00030    the temperature values 
00031 */
00032 #ifdef ENABLE_ADC_EXTENDED_RANGE_BROKEN
00033 static int16s Nvref;
00034 static int16s Nvref2;
00035 #endif /* ENABLE_ADC_EXTENDED_RANGE_BROKEN */
00036 static int16u adcStaticConfig;
00037 
00038 void halAdcSetClock(boolean slow)
00039 {
00040   if (slow) {
00041     adcStaticConfig |= ADC_1MHZCLK_MASK;
00042   } else {
00043     adcStaticConfig &= ~ADC_1MHZCLK_MASK;
00044   }
00045 }
00046 
00047 void halAdcSetRange(boolean high)
00048 {
00049   if (high) {
00050     adcStaticConfig |= (ADC_HVSELP_MASK | ADC_HVSELN_MASK);
00051   } else {
00052     adcStaticConfig &= ~(ADC_HVSELP_MASK | ADC_HVSELN_MASK);
00053   }
00054 }
00055 
00056 boolean halAdcGetClock(void)
00057 {
00058   /* Fix original function code */
00059   return (adcStaticConfig & ADC_1MHZCLK_MASK) ? TRUE : FALSE;
00060 }
00061 
00062 boolean halAdcGetRange(void)
00063 {
00064   /* Fix original function code */
00065   return (adcStaticConfig & ((ADC_HVSELP_MASK | ADC_HVSELN_MASK))) ? TRUE : FALSE;
00066 }
00067 
00068 
00069 
00070 // Define a channel field that combines ADC_MUXP and ADC_MUXN
00071 #define ADC_CHAN        (ADC_MUXP | ADC_MUXN)
00072 #define ADC_CHAN_BIT    ADC_MUXN_BIT
00073 
00074 void halAdcIsr(void)
00075 {
00076   int8u i;
00077   int8u conversion = adcPendingConversion; //fix 'volatile' warning; costs no flash
00078 
00079   // make sure data is ready and the desired conversion is valid
00080   if ( (INT_ADCFLAG & INT_ADCULDFULL)
00081         && (conversion < NUM_ADC_USERS) ) {
00082     adcReadings[conversion] = adcData;
00083     adcReadingValid |= BIT(conversion); // mark the reading as valid
00084     // setup the next conversion if any
00085     if (adcPendingRequests) {
00086       for (i = 0; i < NUM_ADC_USERS; i++) {
00087         if (BIT(i) & adcPendingRequests) {
00088           adcPendingConversion = i;     // set pending conversion
00089           adcPendingRequests ^= BIT(i); //clear request: conversion is starting
00090           ADC_CFG = adcConfig[i]; 
00091           break; //conversion started, so we're done here (only one at a time)
00092         }
00093       }
00094     } else {                                // no conversion to do
00095       ADC_CFG = 0;                          // disable adc
00096       adcPendingConversion = NUM_ADC_USERS; //nothing pending, so go "idle"
00097     }
00098   }
00099   INT_ADCFLAG = 0xFFFF;
00100   asm("DMB");
00101 }
00102 
00103 // An internal support routine called from functions below.
00104 // Returns the user number of the started conversion, or NUM_ADC_USERS
00105 // otherwise.
00106 ADCUser startNextConversion()
00107 {
00108   int8u i;
00109   
00110   ATOMIC (
00111     // start the next requested conversion if any
00112     if (adcPendingRequests && !(ADC_CFG & ADC_ENABLE)) {
00113       for (i = 0; i < NUM_ADC_USERS; i++) {
00114         if ( BIT(i) & adcPendingRequests) {
00115           adcPendingConversion = i;     // set pending conversion
00116           adcPendingRequests ^= BIT(i); // clear request
00117           ADC_CFG = adcConfig[i];       // set the configuration to desired
00118           INT_ADCFLAG = 0xFFFF;
00119           INT_CFGSET = INT_ADC;
00120           break;                       //see DDTS MBTst38936
00121         }
00122       }
00123     } else {
00124       i = NUM_ADC_USERS;
00125     }
00126   )
00127   return i;
00128 }
00129 
00130 void halInternalInitAdc(void)
00131 {
00132   // reset the state variables
00133   adcPendingRequests = 0;
00134   adcPendingConversion = NUM_ADC_USERS;
00135   adcCalibrated = FALSE;
00136   adcStaticConfig = ADC_1MHZCLK | ADC_ENABLE; // init config: 1MHz, low voltage
00137 
00138   // set all adcReadings as invalid
00139   adcReadingValid = 0;
00140 
00141   // turn off the ADC
00142   ADC_CFG = 0;                   // disable ADC, turn off HV buffers
00143   ADC_OFFSET = ADC_OFFSET_RESET;
00144   ADC_GAIN = ADC_GAIN_RESET;
00145   ADC_DMACFG = ADC_DMARST;
00146   ADC_DMABEG = (int32u)&adcData;
00147   ADC_DMASIZE = 1;
00148   ADC_DMACFG = (ADC_DMAAUTOWRAP | ADC_DMALOAD);
00149 
00150   // clear the ADC interrupts and enable
00151   INT_ADCCFG = INT_ADCULDFULL;
00152   INT_ADCFLAG = 0xFFFF;
00153   INT_CFGSET = INT_ADC;
00154 
00155   stCalibrateVref();
00156 }
00157 
00158 StStatus halStartAdcConversion(ADCUser id,
00159                                ADCReferenceType reference,
00160                                ADCChannelType channel,
00161                                ADCRateType rate)
00162 {
00163  
00164    if(reference != ADC_REF_INT)
00165     return ST_ERR_FATAL;
00166 
00167   // save the chosen configuration for this user
00168   adcConfig[id] = ( ((rate << ADC_PERIOD_BIT) & ADC_PERIOD)
00169                   | ((channel << ADC_CHAN_BIT) & ADC_CHAN)
00170                   | adcStaticConfig);
00171 
00172   // if the user already has a pending request, overwrite params
00173   if (adcPendingRequests & BIT(id)) {
00174     return ST_ADC_CONVERSION_DEFERRED;
00175   }
00176 
00177   ATOMIC (
00178     // otherwise, queue the transaction
00179     adcPendingRequests |= BIT(id);
00180     // try and start the conversion if there is not one happening
00181     adcReadingValid &= ~BIT(id);
00182   )
00183   if (startNextConversion() == id)
00184     return ST_ADC_CONVERSION_BUSY;
00185   else
00186     return ST_ADC_CONVERSION_DEFERRED;
00187 }
00188 
00189 StStatus halRequestAdcData(ADCUser id, int16u *value)
00190 {
00191   //Both the ADC interrupt and the global interrupt need to be enabled,
00192   //otherwise the ADC ISR cannot be serviced.
00193   boolean intsAreOff = ( INTERRUPTS_ARE_OFF()
00194                         || !(INT_CFGSET & INT_ADC) 
00195                         || !(INT_ADCCFG & INT_ADCULDFULL) );
00196   StStatus stat;
00197 
00198   ATOMIC (
00199     // If interupts are disabled but the flag is set,
00200     // manually run the isr...
00201     //FIXME -= is this valid???
00202     if( intsAreOff 
00203       && ( (INT_CFGSET & INT_ADC) && (INT_ADCCFG & INT_ADCULDFULL) )) {
00204       halAdcIsr();
00205     }
00206 
00207     // check if we are done
00208     if (BIT(id) & adcReadingValid) {
00209       *value = adcReadings[id];
00210       adcReadingValid ^= BIT(id);
00211       stat = ST_ADC_CONVERSION_DONE;
00212     } else if (adcPendingRequests & BIT(id)) {
00213       stat = ST_ADC_CONVERSION_DEFERRED;
00214     } else if (adcPendingConversion == id) {
00215       stat = ST_ADC_CONVERSION_BUSY;
00216     } else {
00217       stat = ST_ADC_NO_CONVERSION_PENDING;
00218     }
00219   )
00220   return stat;
00221 }
00222 
00223 StStatus halReadAdcBlocking(ADCUser id, int16u *value)
00224 {
00225   StStatus stat;
00226 
00227   do {
00228     stat = halRequestAdcData(id, value);
00229     if (stat == ST_ADC_NO_CONVERSION_PENDING)
00230       break;
00231   } while(stat != ST_ADC_CONVERSION_DONE);
00232   return stat;
00233 }
00234 
00235 StStatus halAdcCalibrate(ADCUser id)
00236 {
00237   StStatus stat;
00238 /* Modified the original ADC driver for enabling the ADC extended range mode required for 
00239      supporting the STLM20 temperature sensor.
00240      NOTE: 
00241      The ADC extended range is inaccurate due to the high voltage mode bug of the general purpose ADC 
00242      (see STM32W108 errata). As consequence, it is not reccomended to use this ADC driver for getting
00243      the temperature values 
00244    */
00245 #ifdef ENABLE_ADC_EXTENDED_RANGE_BROKEN
00246   if(halAdcGetRange()){
00247     
00248     halStartAdcConversion(id,
00249                           ADC_REF_INT,
00250                           ADC_SOURCE_VREF_VREF2,
00251                           ADC_CONVERSION_TIME_US_4096);
00252     
00253     stat = halReadAdcBlocking(id, (int16u *)(&Nvref));
00254     if (stat == ST_ADC_CONVERSION_DONE) {
00255       halStartAdcConversion(id,
00256                             ADC_REF_INT,
00257                             ADC_SOURCE_VREF2_VREF2,
00258                             ADC_CONVERSION_TIME_US_4096);
00259       stat = halReadAdcBlocking(id, (int16u *)(&Nvref2));
00260     }
00261     if (stat == ST_ADC_CONVERSION_DONE) {
00262       adcCalibrated = TRUE;
00263     } else {
00264       adcCalibrated = FALSE;
00265       stat = ST_ERR_FATAL;
00266     }
00267     return stat;    
00268     
00269   }  
00270 #endif /* ENABLE_ADC_EXTENDED_RANGE_BROKEN */
00271   halStartAdcConversion(id,
00272                         ADC_REF_INT,
00273                         ADC_SOURCE_GND_VREF2,
00274                         ADC_CONVERSION_TIME_US_4096);
00275   stat = halReadAdcBlocking(id, (int16u *)(&Nvss));
00276   if (stat == ST_ADC_CONVERSION_DONE) {
00277     halStartAdcConversion(id,
00278                           ADC_REF_INT,
00279                           ADC_SOURCE_VREG2_VREF2,
00280                           ADC_CONVERSION_TIME_US_4096);
00281     stat = halReadAdcBlocking(id, (int16u *)(&Nvdd));
00282   }
00283   if (stat == ST_ADC_CONVERSION_DONE) {
00284     Nvdd -= Nvss;
00285     adcCalibrated = TRUE;
00286   } else {
00287     adcCalibrated = FALSE;
00288     stat = ST_ERR_FATAL;
00289   }
00290   return stat;
00291 }
00292 
00293 // Use the ratio of the sample reading to the of VDD_PADSA/2, known to be 900mV,
00294 // to convert to 100uV units.
00295 // FIXME: support external Vref
00296 //        use #define of Vref, ignore VDD_PADSA
00297 // FIXME: support  high voltage range 
00298 //        use Vref-Vref/2 to calibrate
00299 // FIXME: check for mfg token specifying measured VDD_PADSA
00300 int16s halConvertValueToVolts(int16u value)
00301 {
00302   int32s N;
00303   int16s V;
00304   int32s nvalue;
00305   
00306   if (!adcCalibrated) {
00307     halAdcCalibrate(ADC_USER_LQI);
00308   }
00309   if (adcCalibrated) {
00310  /* Modified the original ADC driver for enabling the ADC extended range mode required for 
00311      supporting the STLM20 temperature sensor.
00312      NOTE: 
00313      The ADC extended range is inaccurate due to the high voltage mode bug of the general purpose ADC 
00314      (see STM32W108 errata). As consequence, it is not reccomended to use this ADC driver for getting
00315      the temperature values 
00316    */
00317 #ifdef ENABLE_ADC_EXTENDED_RANGE_BROKEN
00318     if(halAdcGetRange()){  // High range.
00319       
00320       N = (((int32s)value + Nvref - 2*Nvref2) << 16)/(2*(Nvref-Nvref2));
00321       // Calculate voltage with: V = (N * VREF) / (2^16) where VDD = 1.2 volts
00322       // Mutiplying by 1.2*10000 makes the result of this equation 100 uVolts
00323       V = (int16s)((N*12000L) >> 16);
00324       if (V > 21000) {  // VDD_PADS ?
00325         V = 21000;
00326       }      
00327       
00328     }
00329     else {
00330  #endif /* ENABLE_ADC_EXTENDED_RANGE_BROKEN */
00331       assert(Nvdd);
00332       nvalue = value - Nvss;
00333       // Convert input value (minus ground) to a fraction of VDD/2.
00334       N = ((nvalue << 16) + Nvdd/2) / Nvdd;
00335       // Calculate voltage with: V = (N * VDD/2) / (2^16) where VDD/2 = 0.9 volts
00336       // Mutiplying by0.9*10000 makes the result of this equation 100 uVolts
00337       // (in fixed point E-4 which allows for 13.5 bits vs millivolts
00338       // which is only 10.2 bits).
00339       V = (int16s)((N*9000L) >> 16);
00340       if (V > 12000) {
00341         V = 12000;
00342       }
00343  #ifdef ENABLE_ADC_EXTENDED_RANGE_BROKEN    
00344     }
00345  #endif /* ENABLE_ADC_EXTENDED_RANGE_BROKEN */   
00346   } else {
00347     V = -32768;
00348   }
00349   return V;
00350 }
00351 
00352 int8u halGetADCChannelFromGPIO(int32u io)
00353 {
00354         switch(io)
00355         {
00356         case PORTB_PIN(5):
00357                 return ADC_MUX_ADC0;
00358                         
00359         case PORTB_PIN(6):
00360                 return ADC_MUX_ADC1;
00361                 
00362         case PORTB_PIN(7):
00363                 return ADC_MUX_ADC2;
00364                 
00365         case PORTC_PIN(1):
00366                 return ADC_MUX_ADC3;
00367                 
00368         case PORTA_PIN(4):
00369                 return ADC_MUX_ADC4;
00370                 
00371         case PORTA_PIN(5):
00372                 return ADC_MUX_ADC5;
00373                 
00374         case PORTB_PIN(0):
00375                 return ADC_MUX_VREF;
00376                 
00377         default :
00378                 return 0x0F; // Invalid analogue source
00379                         
00380         }
00381 }