Contiki 2.5
adc.c
1 /** @file adc.c
2  * @brief ADC HAL functions
3  *
4  * <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved. -->
5  */
6 #include PLATFORM_HEADER
7 #include "hal/error.h"
8 #include "hal/hal.h"
9 #include "hal/micro/adc.h"
10 
11 
12 #if (NUM_ADC_USERS > 8)
13  #error NUM_ADC_USERS must not be greater than 8, or int8u variables in adc.c must be changed
14 #endif
15 
16 static int16u adcData; // conversion result written by DMA
17 static int8u adcPendingRequests; // bitmap of pending requests
18 volatile static int8u adcPendingConversion; // id of pending conversion
19 static int8u adcReadingValid; // bitmap of valid adcReadings
20 static int16u adcReadings[NUM_ADC_USERS];
21 static int16u adcConfig[NUM_ADC_USERS];
22 static boolean adcCalibrated;
23 static int16s Nvss;
24 static int16s Nvdd;
25 /* Modified the original ADC driver for enabling the ADC extended range mode required for
26  supporting the STLM20 temperature sensor.
27  NOTE:
28  The ADC extended range is inaccurate due to the high voltage mode bug of the general purpose ADC
29  (see STM32W108 errata). As consequence, it is not reccomended to use this ADC driver for getting
30  the temperature values
31 */
32 #ifdef ENABLE_ADC_EXTENDED_RANGE_BROKEN
33 static int16s Nvref;
34 static int16s Nvref2;
35 #endif /* ENABLE_ADC_EXTENDED_RANGE_BROKEN */
36 static int16u adcStaticConfig;
37 
38 void halAdcSetClock(boolean slow)
39 {
40  if (slow) {
41  adcStaticConfig |= ADC_1MHZCLK_MASK;
42  } else {
43  adcStaticConfig &= ~ADC_1MHZCLK_MASK;
44  }
45 }
46 
47 void halAdcSetRange(boolean high)
48 {
49  if (high) {
50  adcStaticConfig |= (ADC_HVSELP_MASK | ADC_HVSELN_MASK);
51  } else {
52  adcStaticConfig &= ~(ADC_HVSELP_MASK | ADC_HVSELN_MASK);
53  }
54 }
55 
56 boolean halAdcGetClock(void)
57 {
58  /* Fix original function code */
59  return (adcStaticConfig & ADC_1MHZCLK_MASK) ? TRUE : FALSE;
60 }
61 
62 boolean halAdcGetRange(void)
63 {
64  /* Fix original function code */
65  return (adcStaticConfig & ((ADC_HVSELP_MASK | ADC_HVSELN_MASK))) ? TRUE : FALSE;
66 }
67 
68 
69 
70 // Define a channel field that combines ADC_MUXP and ADC_MUXN
71 #define ADC_CHAN (ADC_MUXP | ADC_MUXN)
72 #define ADC_CHAN_BIT ADC_MUXN_BIT
73 
74 void halAdcIsr(void)
75 {
76  int8u i;
77  int8u conversion = adcPendingConversion; //fix 'volatile' warning; costs no flash
78 
79  // make sure data is ready and the desired conversion is valid
80  if ( (INT_ADCFLAG & INT_ADCULDFULL)
81  && (conversion < NUM_ADC_USERS) ) {
82  adcReadings[conversion] = adcData;
83  adcReadingValid |= BIT(conversion); // mark the reading as valid
84  // setup the next conversion if any
85  if (adcPendingRequests) {
86  for (i = 0; i < NUM_ADC_USERS; i++) {
87  if (BIT(i) & adcPendingRequests) {
88  adcPendingConversion = i; // set pending conversion
89  adcPendingRequests ^= BIT(i); //clear request: conversion is starting
90  ADC_CFG = adcConfig[i];
91  break; //conversion started, so we're done here (only one at a time)
92  }
93  }
94  } else { // no conversion to do
95  ADC_CFG = 0; // disable adc
96  adcPendingConversion = NUM_ADC_USERS; //nothing pending, so go "idle"
97  }
98  }
99  INT_ADCFLAG = 0xFFFF;
100  asm("DMB");
101 }
102 
103 // An internal support routine called from functions below.
104 // Returns the user number of the started conversion, or NUM_ADC_USERS
105 // otherwise.
106 ADCUser startNextConversion()
107 {
108  int8u i;
109 
110  ATOMIC (
111  // start the next requested conversion if any
112  if (adcPendingRequests && !(ADC_CFG & ADC_ENABLE)) {
113  for (i = 0; i < NUM_ADC_USERS; i++) {
114  if ( BIT(i) & adcPendingRequests) {
115  adcPendingConversion = i; // set pending conversion
116  adcPendingRequests ^= BIT(i); // clear request
117  ADC_CFG = adcConfig[i]; // set the configuration to desired
118  INT_ADCFLAG = 0xFFFF;
119  INT_CFGSET = INT_ADC;
120  break; //see DDTS MBTst38936
121  }
122  }
123  } else {
124  i = NUM_ADC_USERS;
125  }
126  )
127  return i;
128 }
129 
130 void halInternalInitAdc(void)
131 {
132  // reset the state variables
133  adcPendingRequests = 0;
134  adcPendingConversion = NUM_ADC_USERS;
135  adcCalibrated = FALSE;
136  adcStaticConfig = ADC_1MHZCLK | ADC_ENABLE; // init config: 1MHz, low voltage
137 
138  // set all adcReadings as invalid
139  adcReadingValid = 0;
140 
141  // turn off the ADC
142  ADC_CFG = 0; // disable ADC, turn off HV buffers
143  ADC_OFFSET = ADC_OFFSET_RESET;
144  ADC_GAIN = ADC_GAIN_RESET;
145  ADC_DMACFG = ADC_DMARST;
146  ADC_DMABEG = (int32u)&adcData;
147  ADC_DMASIZE = 1;
148  ADC_DMACFG = (ADC_DMAAUTOWRAP | ADC_DMALOAD);
149 
150  // clear the ADC interrupts and enable
151  INT_ADCCFG = INT_ADCULDFULL;
152  INT_ADCFLAG = 0xFFFF;
153  INT_CFGSET = INT_ADC;
154 
155  stCalibrateVref();
156 }
157 
158 StStatus halStartAdcConversion(ADCUser id,
159  ADCReferenceType reference,
160  ADCChannelType channel,
161  ADCRateType rate)
162 {
163 
164  if(reference != ADC_REF_INT)
165  return ST_ERR_FATAL;
166 
167  // save the chosen configuration for this user
168  adcConfig[id] = ( ((rate << ADC_PERIOD_BIT) & ADC_PERIOD)
169  | ((channel << ADC_CHAN_BIT) & ADC_CHAN)
170  | adcStaticConfig);
171 
172  // if the user already has a pending request, overwrite params
173  if (adcPendingRequests & BIT(id)) {
174  return ST_ADC_CONVERSION_DEFERRED;
175  }
176 
177  ATOMIC (
178  // otherwise, queue the transaction
179  adcPendingRequests |= BIT(id);
180  // try and start the conversion if there is not one happening
181  adcReadingValid &= ~BIT(id);
182  )
183  if (startNextConversion() == id)
184  return ST_ADC_CONVERSION_BUSY;
185  else
186  return ST_ADC_CONVERSION_DEFERRED;
187 }
188 
189 StStatus halRequestAdcData(ADCUser id, int16u *value)
190 {
191  //Both the ADC interrupt and the global interrupt need to be enabled,
192  //otherwise the ADC ISR cannot be serviced.
193  boolean intsAreOff = ( INTERRUPTS_ARE_OFF()
194  || !(INT_CFGSET & INT_ADC)
195  || !(INT_ADCCFG & INT_ADCULDFULL) );
196  StStatus stat;
197 
198  ATOMIC (
199  // If interupts are disabled but the flag is set,
200  // manually run the isr...
201  //FIXME -= is this valid???
202  if( intsAreOff
203  && ( (INT_CFGSET & INT_ADC) && (INT_ADCCFG & INT_ADCULDFULL) )) {
204  halAdcIsr();
205  }
206 
207  // check if we are done
208  if (BIT(id) & adcReadingValid) {
209  *value = adcReadings[id];
210  adcReadingValid ^= BIT(id);
211  stat = ST_ADC_CONVERSION_DONE;
212  } else if (adcPendingRequests & BIT(id)) {
213  stat = ST_ADC_CONVERSION_DEFERRED;
214  } else if (adcPendingConversion == id) {
215  stat = ST_ADC_CONVERSION_BUSY;
216  } else {
217  stat = ST_ADC_NO_CONVERSION_PENDING;
218  }
219  )
220  return stat;
221 }
222 
223 StStatus halReadAdcBlocking(ADCUser id, int16u *value)
224 {
225  StStatus stat;
226 
227  do {
228  stat = halRequestAdcData(id, value);
229  if (stat == ST_ADC_NO_CONVERSION_PENDING)
230  break;
231  } while(stat != ST_ADC_CONVERSION_DONE);
232  return stat;
233 }
234 
235 StStatus halAdcCalibrate(ADCUser id)
236 {
237  StStatus stat;
238 /* Modified the original ADC driver for enabling the ADC extended range mode required for
239  supporting the STLM20 temperature sensor.
240  NOTE:
241  The ADC extended range is inaccurate due to the high voltage mode bug of the general purpose ADC
242  (see STM32W108 errata). As consequence, it is not reccomended to use this ADC driver for getting
243  the temperature values
244  */
245 #ifdef ENABLE_ADC_EXTENDED_RANGE_BROKEN
246  if(halAdcGetRange()){
247 
248  halStartAdcConversion(id,
249  ADC_REF_INT,
250  ADC_SOURCE_VREF_VREF2,
251  ADC_CONVERSION_TIME_US_4096);
252 
253  stat = halReadAdcBlocking(id, (int16u *)(&Nvref));
254  if (stat == ST_ADC_CONVERSION_DONE) {
255  halStartAdcConversion(id,
256  ADC_REF_INT,
257  ADC_SOURCE_VREF2_VREF2,
258  ADC_CONVERSION_TIME_US_4096);
259  stat = halReadAdcBlocking(id, (int16u *)(&Nvref2));
260  }
261  if (stat == ST_ADC_CONVERSION_DONE) {
262  adcCalibrated = TRUE;
263  } else {
264  adcCalibrated = FALSE;
265  stat = ST_ERR_FATAL;
266  }
267  return stat;
268 
269  }
270 #endif /* ENABLE_ADC_EXTENDED_RANGE_BROKEN */
271  halStartAdcConversion(id,
272  ADC_REF_INT,
273  ADC_SOURCE_GND_VREF2,
274  ADC_CONVERSION_TIME_US_4096);
275  stat = halReadAdcBlocking(id, (int16u *)(&Nvss));
276  if (stat == ST_ADC_CONVERSION_DONE) {
277  halStartAdcConversion(id,
278  ADC_REF_INT,
279  ADC_SOURCE_VREG2_VREF2,
280  ADC_CONVERSION_TIME_US_4096);
281  stat = halReadAdcBlocking(id, (int16u *)(&Nvdd));
282  }
283  if (stat == ST_ADC_CONVERSION_DONE) {
284  Nvdd -= Nvss;
285  adcCalibrated = TRUE;
286  } else {
287  adcCalibrated = FALSE;
288  stat = ST_ERR_FATAL;
289  }
290  return stat;
291 }
292 
293 // Use the ratio of the sample reading to the of VDD_PADSA/2, known to be 900mV,
294 // to convert to 100uV units.
295 // FIXME: support external Vref
296 // use #define of Vref, ignore VDD_PADSA
297 // FIXME: support high voltage range
298 // use Vref-Vref/2 to calibrate
299 // FIXME: check for mfg token specifying measured VDD_PADSA
300 int16s halConvertValueToVolts(int16u value)
301 {
302  int32s N;
303  int16s V;
304  int32s nvalue;
305 
306  if (!adcCalibrated) {
307  halAdcCalibrate(ADC_USER_LQI);
308  }
309  if (adcCalibrated) {
310  /* Modified the original ADC driver for enabling the ADC extended range mode required for
311  supporting the STLM20 temperature sensor.
312  NOTE:
313  The ADC extended range is inaccurate due to the high voltage mode bug of the general purpose ADC
314  (see STM32W108 errata). As consequence, it is not reccomended to use this ADC driver for getting
315  the temperature values
316  */
317 #ifdef ENABLE_ADC_EXTENDED_RANGE_BROKEN
318  if(halAdcGetRange()){ // High range.
319 
320  N = (((int32s)value + Nvref - 2*Nvref2) << 16)/(2*(Nvref-Nvref2));
321  // Calculate voltage with: V = (N * VREF) / (2^16) where VDD = 1.2 volts
322  // Mutiplying by 1.2*10000 makes the result of this equation 100 uVolts
323  V = (int16s)((N*12000L) >> 16);
324  if (V > 21000) { // VDD_PADS ?
325  V = 21000;
326  }
327 
328  }
329  else {
330  #endif /* ENABLE_ADC_EXTENDED_RANGE_BROKEN */
331  assert(Nvdd);
332  nvalue = value - Nvss;
333  // Convert input value (minus ground) to a fraction of VDD/2.
334  N = ((nvalue << 16) + Nvdd/2) / Nvdd;
335  // Calculate voltage with: V = (N * VDD/2) / (2^16) where VDD/2 = 0.9 volts
336  // Mutiplying by0.9*10000 makes the result of this equation 100 uVolts
337  // (in fixed point E-4 which allows for 13.5 bits vs millivolts
338  // which is only 10.2 bits).
339  V = (int16s)((N*9000L) >> 16);
340  if (V > 12000) {
341  V = 12000;
342  }
343  #ifdef ENABLE_ADC_EXTENDED_RANGE_BROKEN
344  }
345  #endif /* ENABLE_ADC_EXTENDED_RANGE_BROKEN */
346  } else {
347  V = -32768;
348  }
349  return V;
350 }
351 
352 int8u halGetADCChannelFromGPIO(int32u io)
353 {
354  switch(io)
355  {
356  case PORTB_PIN(5):
357  return ADC_MUX_ADC0;
358 
359  case PORTB_PIN(6):
360  return ADC_MUX_ADC1;
361 
362  case PORTB_PIN(7):
363  return ADC_MUX_ADC2;
364 
365  case PORTC_PIN(1):
366  return ADC_MUX_ADC3;
367 
368  case PORTA_PIN(4):
369  return ADC_MUX_ADC4;
370 
371  case PORTA_PIN(5):
372  return ADC_MUX_ADC5;
373 
374  case PORTB_PIN(0):
375  return ADC_MUX_VREF;
376 
377  default :
378  return 0x0F; // Invalid analogue source
379 
380  }
381 }