Arduino Differential ADC and Gain ADC

Arduino is an open source hardware kit that widely used by any electronic control and automation geeks around the world. Analog data acquisition system is useful things inside Arduino. By default, ADC programming system inside Arduino setting up in single ended mode. Means analog input in range only from 0V to +5V, or in another way from 0V to Vref, there are no negative input on Arduino analog pin.

I try to make Arduino enabled their differential input support (read ATmega2560 datasheet p.290-292 table 26-4). Other way, also I try to enabling gain inside ATmega2560 microcontroller. So, you can use this differential input ±4.5V (read datasheet p379) for your analog data acquisition system and use analog input with gain 1x, 10x, or 200x. This wiring_differential.c is a modification from wiring_analog.c inside Arduino.

/*
  wiring_differential.c - analog differential input
  by Muh Nahdhi Ahsan http://sekarlangit.com/arduino-differential-gain.php

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA

  wiring_differential.c modified from wiring_analog.c
  wiring_analog.c - Copyright (c) 2005-2006 David A. Mellis
  wiring_analog.c - modified 28 September 2010 by Mark Sproul
  wiring_analog.c - Part of Arduino - http://arduino.cc

  $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $
*/

#include "wiring_private.h"
#include "pins_arduino.h"

uint8_t analog_reference = DEFAULT;

void analogReference(uint8_t mode)
{
	// can't actually set the register here because the default setting
	// will connect AVCC and the AREF pin, which would cause a short if
	// there's something connected to AREF.
	analog_reference = mode;
}

// Read Analog Differential without gain (read datashet of ATMega1280 and ATMega2560 for refference)
// Use analogReadDiff(NUM)
// NUM	|	POS PIN		|	NEG PIN		| 	GAIN
//	0	|	A0			|	A1			|	1x
//	1	|	A1			|	A1			|	1x
//	2	|	A2			|	A1			|	1x
//	3	|	A3			|	A1			|	1x
//	4	|	A4			|	A1			|	1x
//	5	|	A5			|	A1			|	1x
//	6	|	A6			|	A1			|	1x
//	7	|	A7			|	A1			|	1x
//	8	|	A8			|	A9			|	1x
//	9	|	A9			|	A9			|	1x
//	10	|	A10			|	A9			|	1x
//	11	|	A11			|	A9			|	1x
//	12	|	A12			|	A9			|	1x
//	13	|	A13			|	A9			|	1x
//	14	|	A14			|	A9			|	1x
//	15	|	A15			|	A9			|	1x

int analogReadDiff(uint8_t pin)
{
	uint8_t low, high;

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
	if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
	if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284__)
	if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#else
	if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif

#if defined(__AVR_ATmega32U4__)
	pin = analogPinToChannel(pin);
	ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#elif defined(ADCSRB) && defined(MUX5)
	// the MUX5 bit of ADCSRB selects whether we're reading from channels
	// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
	ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
	// set the analog reference (high two bits of ADMUX) and select the
	// channel (low 4 bits).  this also sets ADLAR (left-adjust result)
	// to 0 (the default).
#if defined(ADMUX)
	ADMUX = (analog_reference << 6) | ((pin | 0x10) & 0x1F);
#endif

	// without a delay, we seem to read from the wrong channel
	//delay(1);

#if defined(ADCSRA) && defined(ADCL)
	// start the conversion
	sbi(ADCSRA, ADSC);

	// ADSC is cleared when the conversion finishes
	while (bit_is_set(ADCSRA, ADSC));

	// we have to read ADCL first; doing so locks both ADCL
	// and ADCH until ADCH is read.  reading ADCL second would
	// cause the results of each conversion to be discarded,
	// as ADCL and ADCH would be locked when it completed.
	low  = ADCL;
	high = ADCH;
#else
	// we dont have an ADC, return 0
	low  = 0;
	high = 0;
#endif

	// combine the two bytes
	return (high << 8) | low;
}

// Read Analog Differential with gain (read datashet of ATMega1280 and ATMega2560 for refference)
// Use analogReadGain(NUM)
// NUM	|	POS PIN		|	NEG PIN		| 	GAIN
//	0	|	A0			|	A0			|	10x
//	1	|	A1			|	A0			|	10x
//	2	|	A0			|	A0			|	200x
//	3	|	A1			|	A0			|	200x
//	4	|	A2			|	A2			|	10x
//	5	|	A3			|	A2			|	10x
//	6	|	A2			|	A2			|	200x
//	7	|	A3			|	A2			|	200x
//	8	|	A8			|	A8			|	10x
//	9	|	A9			|	A8			|	10x
//	10	|	A8			|	A8			|	200x
//	11	|	A9			|	A8			|	200x
//	12	|	A10			|	A10			|	10x
//	13	|	A11			|	A10			|	10x
//	14	|	A10			|	A10			|	200x
//	15	|	A11			|	A10			|	200x

int analogReadGain(uint8_t pin)
{
	uint8_t low, high;

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
	if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
	if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284__)
	if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#else
	if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif

#if defined(__AVR_ATmega32U4__)
	pin = analogPinToChannel(pin);
	ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#elif defined(ADCSRB) && defined(MUX5)
	// the MUX5 bit of ADCSRB selects whether we're reading from channels
	// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
	ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
	// set the analog reference (high two bits of ADMUX) and select the
	// channel (low 4 bits).  this also sets ADLAR (left-adjust result)
	// to 0 (the default).
#if defined(ADMUX)
	ADMUX = (analog_reference << 6) | ((pin | 0x08) & 0x0F);
#endif

	// without a delay, we seem to read from the wrong channel
	//delay(1);

#if defined(ADCSRA) && defined(ADCL)
	// start the conversion
	sbi(ADCSRA, ADSC);

	// ADSC is cleared when the conversion finishes
	while (bit_is_set(ADCSRA, ADSC));

	// we have to read ADCL first; doing so locks both ADCL
	// and ADCH until ADCH is read.  reading ADCL second would
	// cause the results of each conversion to be discarded,
	// as ADCL and ADCH would be locked when it completed.
	low  = ADCL;
	high = ADCH;
#else
	// we dont have an ADC, return 0
	low  = 0;
	high = 0;
#endif

	// combine the two bytes
	return (high << 8) | low;
}

For using this library, follow this procedure:

  1. Download this AnalogDiff library first. (download here)
  2. Extract to anywhere on your disk.
  3. Copy AnalogDiff folder file to this directory: <arduino_dir>/libraries. Else, just copy wiring_differential.c to <arduino_dir>/hardware/arduino/cores/arduino
  4. Add this library into your code first by writing #include <wiring_differential.c>

Use analogReadDiff(num) on your code for read analog input in differential mode without gain or use analogReadGain(num) for read analog input with gain. Please refer to this table below for num inside and analog input pin for your input.

Actually, for this now I just test for differential on A0 and A1 channel with AnalogReadDiff(0). Another test applied on Analog read differential with 10x gain by using AnalogReadGain(1) and 200x gain with AnalogReadGain(3). Please correct me about the table (I think something went wrong about pin numbering).

This is a simple sample code how to use differential ADC and gain ADC, modified from Analog Read Serial basic sample code (compiled with Arduino 1.0.1).

/*
  AnalogReadSerial
  Reads an analog input on pin 0, prints the result to the serial monitor.
  Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground.

 This example code is in the public domain.
 */

#include <wiring_differential.c>

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorSended = analogRead(A0); // reading single ended input A0 - GND
  int sensorDiff = analogReadDiff(0); // reading differential A0 - A1
  int sensorGain10 = analogReadGain(1); // reading differential gain 10x A0 - A1
  int sensorGain200 = analogReadGain(3); // reading diferential gain 200x A0 - A1

  // arrange integer value (read ATMega 2560 Datashet p.288) figure 26-15
  if (sensorDiff > 511 )
  {
    sensorDiff = sensorDiff - 1023 ;
  }
  if (sensorGain10 < 511 )
  {
    sensorGain10 = -1 * sensorGain10 ;
  }
  else
  {
    sensorGain10 = -1 * ( sensorGain10 - 1023 ) ;
  }
  if ( sensorGain200 < 511 )
  {
    sensorGain200 = -1 * sensorGain200 ;
  }
  else
  {
    sensorGain200 = -1 * ( sensorGain200 - 1023 ) ;
  }

  Serial.print("Single ended reading : ");
  Serial.println(sensorSended);
  Serial.print("Differential reading : ");
  Serial.println(sensorDiff);
  Serial.print("Differential 10x gain reading : ");
  Serial.println(sensorGain10);
  Serial.print("Differential 200x gain reading : ");
  Serial.println(sensorGain200);
  delay(1000);        // delay for a while
}

Update: March, 27th 2013

Geotermal – Energi Hijau, Energi Masa Depan

Indonesia memiliki potensi panas bumi terbesar di dunia, yaitu 40% dari seluruh potensi panas bumi di dunia. Potensi ini merupakan salah satu modal untuk memenuhi kebutuhan listrik yang terus meningkat secara eksponen. Energi yang dihasilkan dari sistem pembangkit listrik yang ada saat ini baru memenuhi 76% dari kebutuhan listrik Indonesia. Keadaan ini memaksa pemerintah untuk terus mengusahakan terpenuhinya kebutuhan listrik dengan mempercepat pembangunan pembangkit listrik dengan sumber energi baru terbarukan dan ramah lingkungan. Diantara beberapa sistem pembangkit listrik yang dikembangkan, sistem pembangkit listrik yang diharapkan dapat memenuhi kebutuhan listrik adalah pembangkit listrik tenaga panas bumi (PLTP).

Roadmap pengembangan panas bumi di Indonesia berdasarkan Keputusan Presiden No. 5/2006 tentang kebijakan energi nasional merencanakan pada tahun 2025 nanti sebanyak 9500 MW kebutuhan listrik dipenuhi dari geotermal. Dalam roadmap tersebut dibuat target-target jangka pendek, salah satunya pada tahun 2012 ini ditargetkan kapasitas terpasang sebesar 3442 MW. Kenyataannya, berdasarkan data Dirjen EBTKE kapasitas terpasang pada akhir tahun 2011 masih pada angka 1226 MW. Hanya sekitar setengah dari target crash program tahap pertama tahun 2012.

Berdasarkan data Badan Geologi tahun 2010, cadangan panas bumi di Indonesia sebesar 29.038 MW tersebar di 276 lokasi panas bumi. Kapasitas terpasang (installed capacity) sebesar 1.226 MW dari lapangan Sibayak, Gunung Salak, Wayang Windu, Kamojang, Darajat, Dieng, dan Lahendong. Masih ada potensi geotermal yang tersebar di berbagai wilayah Indonesia sebesar 95,8% belum dimanfaatkan. Data-data ini membuktikan bahwa banyak potensi geotermal yang bisa dimanfaatkan sebagai pembangkit listrik di Indonesia. Sampai tahun 2011, tujuh lapangan panas bumi yang telah beroperasi (total menghasilkan 1226 MW) di Indonesia semuanya dikelola oleh PT. Pertamina Geothermal Energy (PGE) sebagai pemegang ijin usaha pertambangan panas bumi. Sebanyak 51 lapangan panas bumi (WKP, Wilayah Kerja Pertambangan Panas Bumi) telah ditetapkan oleh Kementerian ESDM pada tahun 2012.

Geotermal dipilih sebagai sumber energi masa depan bukan tanpa alasan. Geotermal dapat dijadikan sebagai pembangkit listrik jangka panjang karena sifat sumber panas magmatik yang berumur panjang. Jenis sumber energi ini merupakan sumber energi terbarukan karena dalam sistem geotermal terdapat siklus perputaran antara fluida yang keluar dan fluida yang masuk terus menerus secara alami. Geotermal menjadi pilihan sebagai sumber energi baru karena kelimpahan potensi energi geotermal yang ada di Indonesia sangat besar dan merupakan sumber energi terbarukan.

Energi panas bumi memiliki banyak keunggulan dibandingkan dengan sumber energi lain. Panas bumi menghasilkan emisi gas CO2 lima kali lebih sedikit dibandingkan dengan minyak bumi sebagai pembangkit listrik. Panas bumi hampir tidak menghasilkan emisi H2S jika dibandingkan dengan minyak bumi. Geotermal juga memiliki resiko kerentanan lebih rendah terhadap tidakan terorisme dibandingkan instalasi nuklir. Material sisa dari produksi energi dari panas bumi merupakan material yang memiliki nilai ekonomis, misal SiO2 dan Zn. Selain itu, geotermal juga hanya membutuhkan area kecil di permukaan dibandingkan energi matahari dan angin. Karena kelebihan ini, sumber energi geotermal dikategorikan sebagai sumber energi yang ramah lingkungan.

Panas bumi layak dianggap sebagai masa depan sumber energi Indonesia. Kenyataan bahwa potensi panas bumi di Indonesia merupakan yang terbesar di dunia adalah salah satu kelebihan yang harus dioptimalkan. Memanfaatkan sumber energi panas bumi adalah cara untuk mengurangi ketergantungan bangsa terhadap minyak bumi yang produksinya kian menurun. Solusi sumber energi baru terbarukan bukan lagi sebagai alternatif tetapi sebuah keharusan untuk bisa mandiri energi.