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