Contiki 2.6
|
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 }