how to use ATMEGA2560 AD differential channel

arduino support AD function with single ended conversion language such as analogRead. But ATMEGA 2560 datasheet says it has differential channel. I want to know how to read differential channel conversion result. Is there other language to read the result?

I suspect you will have to write directly to the various control registers and possible also read from them.

I presume you have a copy of the 2560 datasheet with all the gory details.

I am interested to know what you are trying to do.

...R

I have a alternative signal which has positive voltage and negetive voltage. So I have to put them into differential channel which can convert the alternative signal. But arduino does not support the function. arduino supports single ended conversion language that is analogRead. There is another way to solve the problem. I can add a direct constant voltage on the alternative signal, which can avoid the negetive voltage. But I want to read the conversion of differential channel result.

the following is about AD conversion of differential channel and single ended which is from datasheet of ATMEGA2560

After the conversion is complete (ADIF is high), the conversion result can be found in the ADC Result Registers (ADCL, ADCH).
For single ended conversion, the result is

ADC=VIN*1024/ VREF

where VIN is the voltage on the selected input pin and VREF the selected voltage reference . 0x000 represents analog ground, and 0x3FF represents the selected reference voltage minus one LSB.
If differential channels are used, the result is

ADC=(VPOS- VNEG )* 512 /VREF

As I said previously, there is no problem reading/writing any of the Atmega registers with Arduino code if there is no "helper" function for the task.

I'm interested to learn with you.

Are you saying the 2560 can accept an AC signal with positive and negative voltages (referred to Arduino GND) and (for example) an ADC value of 20 might represent -4.8v and 1000 might represent +4.8v?
What is the max negative voltage that is accepted without letting the smoke out?

OR ... does differential just mean the difference in voltage between a pair of ADC pins - to save you having to take two ADC readinsg and subtract the results?

I notice that the Leonardo seems to have the same facility.

....R

Edit. I found this and I have crossed out the "wrong" assumption above. YouTube video
...R

OR ... does differential just mean the difference in voltage between a pair of ADC pins - to save you having to take two ADC readinsg and subtract the results?

This is the case, the chip's I/O pins electrical safety limits always applies, no voltage higher the +Vcc and no negative voltages. Only the reset pin allows for voltage above +Vcc as a means to enter high voltage programming mode where +12vdc is used on the reset pin.

AD input signal has two ways, one is single ended, the other is differential. For example , AMP(amplifer such as LM324) is the typical differential input signal device. RS485 is a kind of differential signal.
Differential has two signals to input. Single ended has one signal to input.
Single ended input signal has two types, one is bipolar , the other is Unipolar . The differential input also has the two types. Single ended bipolar signal voltage has positive voltage and negetive voltage. Single ended unipolar signal voltage is positive.
one of differential bipolar signal has positive voltage and negetive voltage. the other is the same. one of differential Unipolar signal only has positive voltage. the other is the same.

hello guys!
I searched about this issue but didn't find any library! but we can use differential ADC by using registers. the code is here:

uint8_t low, high;

void setup() {
  Serial.begin(9600);
  ADMUX = 1<<REFS0;                //choose AVCC for reference ADC voltage
  ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);    //enable ADC, ADC frequency=16MB/128=125kHz (set of prescaler)
  ADCSRB = 0x00;
}

void loop() {
  Serial.println(read_differential());
  delay(100);
}

int16_t read_differential() {
  ADMUX |= (1<<MUX4);             //set MUX5:0 to 010000. Positive Differential Input => ADC0 and Negative Differential Input => ADC1 with Gain 1x. 
           
  ADCSRA |= (1<<ADSC);            //start conversion
  while (ADCSRA & (1<<ADSC));     //wait untill coversion be completed(ADSC=0);

  low = ADCL;
  high = ADCH;
  return (high << 8) | low;       //arrange (ADCH ADCL) to a 16 bit and return it's value.
}

this code can read differential ADC by A0(posetive) and A1(negetive) pins and show it on Serial Monitor. these registers are explained in the "ATmega 2560 datasheet". so you can change differential mode or differential with gain or single ended mode by changing MUX5:0 bits according to "table 26-4" at the datasheet.
But I see a problem! if we use differential mode or differential with gain, the out number is about 512(499 or 502) not 1023 :o ! but if use single ended mode the out number can be 1023.
Can any one tell me what's the problem??? :o I guess its because of its analog inner opamps. but not sure!

1 Like

amin_mdn:
But I see a problem! if we use differential mode or differential with gain, the out number is about 512(499 or 502) not 1023

Is that because it can range from -512 to +511 ?

...R

Is that because it can range from -512 to +511 ?

yes you're right :slight_smile:

I corrected my code and it works :smiley:

uint8_t low, high;

void setup() {
  Serial.begin(9600);
  ADMUX = 1<<REFS0;                //choose AVCC for reference ADC voltage
  ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);    //enable ADC, ADC frequency=16MB/128=125kHz (set of prescaler)
  ADCSRB = 0x00;
}

void loop() {
  
  Serial.println(read_differential());
  delay(100);
}

//int16_t read_differential() {
int read_differential() {
  ADMUX |= (1<<MUX4);             //set MUX5:0 to 010000. Positive Differential Input => ADC0 and Negative Differential Input => ADC1 with Gain 1x.
           
  ADCSRA |= (1<<ADSC);            //start conversion
  while (ADCSRA & (1<<ADSC));     //wait untill coversion be completed(ADSC=0);

  low = ADCL;
  high = ADCH;
  if(high & (1<<1)){              //in differential mode our value is between -512 to 511 (not 0 to 1023). it means we have 9 bits and 10th bit is the sign bit. but because 
    high |= 0b11111110;           //the number of ADCH and ADCL bits are 10, for signed number we dont have repeatition of 1 in "ADCH" byte.
  }                               //so we repeat 1 Ourselves.:) 
  return (high << 8) | low;       //arrange (ADCH ADCL) to a 16 bit and return it's value.
}

I saw about 3 bit errors at a same positive and negative voltage but its negligible :grin: