1 /*
2 OrangutanAnalog.cpp - Library for using the analog inputs on the
3 Orangutan LV, SV, SVP, X2, Baby Orangutan B, or 3pi robot. This library also
4 provides a method for reading the temperature sensor on the LV-168.
5 */
6
7 /*
8 * Written by Ben Schmidel, May 27, 2008.
9 * Copyright (c) 2008-2012 Pololu Corporation. For more information, see
10 *
11 * http://www.pololu.com
12 * http://forum.pololu.com
13 * http://www.pololu.com/docs/0J18
14 *
15 * You may freely modify and share this code, as long as you keep this
16 * notice intact (including the two links above). Licensed under the
17 * Creative Commons BY-SA 3.0 license:
18 *
19 * http://creativecommons.org/licenses/by-sa/3.0/
20 *
21 * Disclaimer: To the extent permitted by law, Pololu provides this work
22 * without any warranty. It might be defective, in which case you agree
23 * to be responsible for all resulting costs and damages.
24 */
25
26 #include <avr/io.h>
27 #include "OrangutanAnalog.h"
28
29 #include "../OrangutanResources/include/OrangutanModel.h"
30
31
analog_read(unsigned char channel)32 extern "C" unsigned int analog_read(unsigned char channel)
33 {
34 return OrangutanAnalog::read(channel);
35 }
36
analog_read_millivolts(unsigned char channel)37 extern "C" unsigned int analog_read_millivolts(unsigned char channel)
38 {
39 return OrangutanAnalog::readMillivolts(channel);
40 }
41
analog_read_average(unsigned char channel,unsigned int samples)42 extern "C" unsigned int analog_read_average(unsigned char channel, unsigned int samples)
43 {
44 return OrangutanAnalog::readAverage(channel, samples);
45 }
46
analog_read_average_millivolts(unsigned char channel,unsigned int samples)47 extern "C" unsigned int analog_read_average_millivolts(unsigned char channel, unsigned int samples)
48 {
49 return OrangutanAnalog::readAverageMillivolts(channel, samples);
50 }
51
start_analog_conversion(unsigned char channel)52 extern "C" void start_analog_conversion(unsigned char channel)
53 {
54 OrangutanAnalog::startConversion(channel);
55 }
56
analog_conversion_result()57 extern "C" unsigned int analog_conversion_result()
58 {
59 return OrangutanAnalog::conversionResult();
60 }
61
analog_conversion_result_millivolts()62 extern "C" unsigned int analog_conversion_result_millivolts()
63 {
64 return OrangutanAnalog::conversionResultMillivolts();
65 }
66
set_millivolt_calibration(unsigned int calibration)67 extern "C" void set_millivolt_calibration(unsigned int calibration)
68 {
69 OrangutanAnalog::setMillivoltCalibration(calibration);
70 }
71
read_vcc_millivolts()72 extern "C" unsigned int read_vcc_millivolts()
73 {
74 return OrangutanAnalog::readVCCMillivolts();
75 }
76
to_millivolts(unsigned int analog_result)77 extern "C" unsigned int to_millivolts(unsigned int analog_result)
78 {
79 return OrangutanAnalog::toMillivolts(analog_result);
80 }
81
read_trimpot()82 extern "C" unsigned int read_trimpot()
83 {
84 return OrangutanAnalog::readTrimpot();
85 }
86
read_trimpot_millivolts()87 extern "C" unsigned int read_trimpot_millivolts()
88 {
89 return OrangutanAnalog::readTrimpotMillivolts();
90 }
91
92 #ifdef _ORANGUTAN_SVP
93
read_battery_millivolts_svp()94 extern "C" unsigned int read_battery_millivolts_svp()
95 {
96 return OrangutanAnalog::readBatteryMillivolts_SVP();
97 }
98
99 #elif defined(_ORANGUTAN_X2)
100
read_battery_millivolts_x2()101 extern "C" unsigned int read_battery_millivolts_x2()
102 {
103 return OrangutanAnalog::readBatteryMillivolts_X2();
104 }
105
106 #else
107
read_battery_millivolts_3pi()108 extern "C" unsigned int read_battery_millivolts_3pi()
109 {
110 return OrangutanAnalog::readBatteryMillivolts_3pi();
111 }
112
read_battery_millivolts_sv()113 extern "C" unsigned int read_battery_millivolts_sv()
114 {
115 return OrangutanAnalog::readBatteryMillivolts_SV();
116 }
117
read_battery_millivolts_sv168()118 extern "C" unsigned int read_battery_millivolts_sv168()
119 {
120 return OrangutanAnalog::readBatteryMillivolts_SV();
121 }
122
read_temperature_f()123 extern "C" int read_temperature_f()
124 {
125 return OrangutanAnalog::readTemperatureF();
126 }
127
read_temperature_c()128 extern "C" int read_temperature_c()
129 {
130 return OrangutanAnalog::readTemperatureC();
131 }
132
133 #endif // _ORANGUTAN_SVP
134
135
136 #ifdef _ORANGUTAN_SVP
137 static unsigned int fromMillivoltsToNormal(unsigned int millivolts);
138 #endif
139
140
141
142 #ifdef _ORANGUTAN_SVP
143 /* non-zero means the result from the last conversion is stored in millivolts
144 in adc_result_millivolts. The contents of ADCL and ADCH are irrelevant.
145 0 means the result from the last conversion is stored in ADCL and ADCH.
146 The contents of adc_result_millivolts are irrelevant.
147 */
148 static unsigned char adc_result_is_in_millivolts = 0;
149
150 /* adc_result_millivolts holds the last ADC result if adc_result_is_in_millivolts
151 is non-zero. David wanted to just store it in ADCL and ADCH, but those
152 registers are not writable. */
153 static unsigned int adc_result_millivolts;
154 #endif
155
156 static unsigned int millivolt_calibration = 5000; // contains VCC in millivolts
157
158
159 // constructor
OrangutanAnalog()160 OrangutanAnalog::OrangutanAnalog()
161 {
162
163 }
164
165
166 // returns the result of the previous ADC conversion.
conversionResult()167 unsigned int OrangutanAnalog::conversionResult()
168 {
169 #ifdef _ORANGUTAN_SVP
170 if (adc_result_is_in_millivolts)
171 {
172 return fromMillivoltsToNormal(adc_result_millivolts);
173 }
174 #endif
175
176 if (getMode()) // if left-adjusted (i.e. 8-bit mode)
177 {
178 return ADCH; // 8-bit result
179 }
180 else
181 {
182 return ADC; // 10-bit result
183 }
184 }
185
186 // returns the result from the previous ADC conversion in millivolts.
conversionResultMillivolts()187 unsigned int OrangutanAnalog::conversionResultMillivolts()
188 {
189 #ifdef _ORANGUTAN_SVP
190 if (adc_result_is_in_millivolts)
191 {
192 return adc_result_millivolts;
193 }
194 #endif
195
196 if (getMode()) // if left-adjusted (i.e. 8-bit mode)
197 {
198 return toMillivolts(ADCH);
199 }
200 else
201 {
202 return toMillivolts(ADC);
203 }
204 }
205
206 // the following method can be used to initiate an ADC conversion
207 // that runs in the background, allowing the CPU to perform other tasks
208 // while the conversion is in progress. The procedure is to start a
209 // conversion on a channel with startConversion(channel), and then
210 // poll isConverting in your main loop. Once isConverting() returns
211 // a zero, the result can be obtained through a call to conversionResult().
212 // NOTE: Some Orangutans and 3pis have their AREF pin connected directly to VCC.
213 // On these Orangutans, you must not use the internal voltage reference as
214 // doing so will short the internal reference voltage to VCC and could damage
215 // the AVR. It is safe to use the internal reference voltage on the
216 // Orangutan SVP.
startConversion(unsigned char channel,unsigned char use_internal_reference)217 void OrangutanAnalog::startConversion(unsigned char channel, unsigned char use_internal_reference)
218 {
219 #ifdef _ORANGUTAN_SVP
220 if (channel > 31)
221 {
222 adc_result_is_in_millivolts = 1;
223
224 if (channel == TRIMPOT){ adc_result_millivolts = OrangutanSVP::getTrimpotMillivolts(); }
225 else if (channel == CHANNEL_A){ adc_result_millivolts = OrangutanSVP::getChannelAMillivolts(); }
226 else if (channel == CHANNEL_B){ adc_result_millivolts = OrangutanSVP::getChannelBMillivolts(); }
227 else if (channel == CHANNEL_C){ adc_result_millivolts = OrangutanSVP::getChannelCMillivolts(); }
228 else if (channel == CHANNEL_D){ adc_result_millivolts = OrangutanSVP::getChannelDMillivolts(); }
229
230 return;
231 }
232
233 adc_result_is_in_millivolts = 0;
234
235 #else
236
237 // Channel numbers greater than 31 are invalid.
238 if (channel > 31)
239 {
240 return;
241 }
242
243 #endif
244
245 ADCSRA = 0x87; // bit 7 set: ADC enabled
246 // bit 6 clear: don't start conversion
247 // bit 5 clear: disable autotrigger
248 // bit 4: ADC interrupt flag
249 // bit 3 clear: disable ADC interrupt
250 // bits 0-2 set: ADC clock prescaler is 128
251 // 128 prescaler required for 10-bit resolution when FCPU = 20 MHz
252
253 // NOTE: it is important to make changes to a temporary variable and then set the ADMUX
254 // register in a single atomic operation rather than incrementally changing bits of ADMUX.
255 // Specifically, setting the ADC channel by first clearing the channel bits of ADMUX and
256 // then setting the ones corresponding to the desired channel briefly connects the ADC
257 // to channel 0, which can affect the ADC charge capacitor. For example, if you have a
258 // high output impedance voltage on channel 1 and a low output impedance voltage on channel
259 // 0, the voltage on channel 0 be briefly applied to the ADC capacitor before every conversion,
260 // which could prevent the capacitor from settling to the voltage on channel 1, even over
261 // many reads.
262 unsigned char tempADMUX = ADMUX;
263
264 tempADMUX |= 1 << 6;
265 if(use_internal_reference) // Note: internal reference should NOT be used on devices
266 { // where AREF is connected to an external voltage!
267 // use the internal voltage reference
268 tempADMUX |= 1 << 7; // 1.1 V on ATmega48/168/328; 2.56 V on ATmega324/644/1284
269 }
270 else
271 {
272 // use AVCC as a reference
273 tempADMUX &= ~(1 << 7);
274 }
275
276 tempADMUX &= ~0x1F; // clear channel selection bits of ADMUX
277 tempADMUX |= channel; // we only get this far if channel is less than 32
278 ADMUX = tempADMUX;
279 ADCSRA |= 1 << ADSC; // start the conversion
280 }
281
282 // take a single analog reading of the specified channel
read(unsigned char channel)283 unsigned int OrangutanAnalog::read(unsigned char channel)
284 {
285 startConversion(channel);
286 while (isConverting()); // wait for conversion to finish
287 return conversionResult();
288 }
289
290 // take a single analog reading of the specified channel and return the result in millivolts
readMillivolts(unsigned char channel)291 unsigned int OrangutanAnalog::readMillivolts(unsigned char channel)
292 {
293 startConversion(channel);
294 while (isConverting()); // wait for conversion to finish
295 return conversionResultMillivolts();
296 }
297
298 // take 'samples' readings of the specified channel and return the average
readAverage(unsigned char channel,unsigned int samples)299 unsigned int OrangutanAnalog::readAverage(unsigned char channel,
300 unsigned int samples)
301 {
302 unsigned int i = samples;
303 unsigned long sum = 0;
304
305 #ifdef _ORANGUTAN_SVP
306 if (channel > 31)
307 {
308 // We have not implemented averaging of the adc readings from the auxiliary
309 // processor on the SVP, so we will just return a simple reading.
310 return read(channel);
311 }
312 #endif
313
314 startConversion(channel); // call this first to set the channel
315 while (isConverting()); // wait while converting (discard first reading)
316 do
317 {
318 ADCSRA |= 1 << ADSC; // start the next conversion on current channel
319 while (isConverting()); // wait while converting
320 sum += conversionResult(); // sum the results
321 } while (--i);
322
323 if (samples < 64) // can do the division much faster
324 return ((unsigned int)sum + (samples >> 1)) / (unsigned char)samples;
325 return (sum + (samples >> 1)) / samples; // compute the rounded avg
326 }
327
328
329 // sets the value used to calibrate the conversion from ADC reading
330 // to millivolts. The argument calibration should equal VCC in millivolts,
331 // which can be automatically measured using the function readVCCMillivolts():
332 // e.g. setMillivoltCalibration(readVCCMillivolts());
setMillivoltCalibration(unsigned int calibration)333 void OrangutanAnalog::setMillivoltCalibration(unsigned int calibration)
334 {
335 millivolt_calibration = calibration;
336 }
337
338 // averages ten ADC readings of the fixed internal 1.1V bandgap voltage
339 // and computes VCC from the results. This function returns VCC in millivolts.
340 // Channel 14 is internal 1.1V BG on ATmega48/168/328, but bit 5 of ADMUX is
341 // not used, so channel 30 is equivalent to channel 14. Channel 30 is the internal
342 // 1.1V BG on ATmega324/644/1284.
readVCCMillivolts()343 unsigned int OrangutanAnalog::readVCCMillivolts()
344 {
345 unsigned char mode = getMode();
346 setMode(MODE_10_BIT);
347
348 // bandgap cannot deliver much current, so it takes some time for the ADC
349 // to settle to the BG voltage. The following read connects the ADC to
350 // the BG voltage and gives the voltage time to settle.
351 readAverage(30, 20);
352
353 unsigned int reading = readAverage(30, 20); // channel 30 is internal 1.1V BG
354 unsigned int value = (1023UL * 1100UL + (reading>>1)) / reading;
355 setMode(mode);
356 return value;
357 }
358
359 // converts the specified ADC result to millivolts
toMillivolts(unsigned int adcResult)360 unsigned int OrangutanAnalog::toMillivolts(unsigned int adcResult)
361 {
362 unsigned long temp = adcResult * (unsigned long)millivolt_calibration;
363 if (getMode()) // if 8-bit mode
364 return (temp + 127) / 255;
365 return (temp + 511) / 1023;
366 }
367
368
369 #ifdef _ORANGUTAN_SVP
fromMillivoltsToNormal(unsigned int millivolts)370 static unsigned int fromMillivoltsToNormal(unsigned int millivolts)
371 {
372 unsigned long temp;
373
374 if (OrangutanAnalog::getMode()) // if 8-bit mode
375 {
376 temp = (millivolts * 255UL + (millivolt_calibration>>1)) / millivolt_calibration;
377 if (temp > 0xFFu)
378 {
379 return 0xFFu;
380 }
381 }
382 else
383 {
384 temp = (millivolts * 1023UL + (millivolt_calibration>>1)) / millivolt_calibration;
385 if (temp > 0xFFFFu)
386 {
387 return 0xFFFFu;
388 }
389 }
390 return temp;
391 }
392
393 #elif defined(_ORANGUTAN_X2)
394
readBatteryMillivolts_X2()395 unsigned int OrangutanAnalog::readBatteryMillivolts_X2()
396 {
397 unsigned char mode = getMode();
398 setMode(MODE_10_BIT);
399 unsigned int value = (readAverageMillivolts(6, 10) * 3208UL + 500) / 1000;
400 setMode(mode);
401 return value;
402 }
403
404 #else
405
406 // The temperature sensor reading (on the Orangutan LV) can be converted into degrees C as follows:
407 // T = (Vout - 0.4) / 0.0195 Celcius
408 // The return value of this function is *tenths* of a degree Farenheit, although
409 // the accuracy of the temperature sensor is +/- 2 C.
readTemperatureF()410 int OrangutanAnalog::readTemperatureF()
411 {
412 unsigned char mode = getMode();
413 setMode(MODE_10_BIT);
414 int value = (((int)(readAverageMillivolts(TEMP_SENSOR, 20)) * 12) - 634) / 13;
415 setMode(mode);
416 return value;
417 }
418
419
420 // Orangutan LV only: The return value of this function is *tenths* of a degree Celcius.
readTemperatureC()421 int OrangutanAnalog::readTemperatureC()
422 {
423 unsigned char mode = getMode();
424 setMode(MODE_10_BIT);
425 int value = (((int)(readAverageMillivolts(TEMP_SENSOR, 20) * 20)) - 7982) / 39;
426 setMode(mode);
427 return value;
428 }
429
readBatteryMillivolts_3pi()430 unsigned int OrangutanAnalog::readBatteryMillivolts_3pi()
431 {
432 unsigned char mode = getMode();
433 setMode(MODE_10_BIT);
434 unsigned int value = (readAverageMillivolts(6, 10) * 3 + 1) / 2;
435 setMode(mode);
436 return value;
437 }
438
readBatteryMillivolts_SV()439 unsigned int OrangutanAnalog::readBatteryMillivolts_SV()
440 {
441 unsigned char mode = getMode();
442 setMode(MODE_10_BIT);
443 unsigned int value = readAverageMillivolts(6,10) * 3;
444 setMode(mode);
445 return value;
446 }
447
448 #endif
449
450 // Local Variables: **
451 // mode: C++ **
452 // c-basic-offset: 4 **
453 // tab-width: 4 **
454 // indent-tabs-mode: t **
455 // end: **
456