Accessing the ADC pins

Hi,

I am trying to learn Nano coding using a an existing program called component tester which uses the ADC pins extensively.

The simple way of reading an analog pin is via analogRead() but this program is using a different method of reading a pin possibly via assembly language coding from inside the sketch.
Here is a small extract which appears to be doing this job:

unsigned int ReadADC (uint8_t Probe) {
  unsigned int U; 		// return value (mV)
  uint8_t Samples; 		// loop counter
  unsigned long Value; 		// ADC value
  Probe |= (1 << REFS0); 	// use internal reference anyway

Can any body direct me to am area where this is discussed and explained please?

Please, for the benefit of all, at least show the whole function. What you have shown isn't reading the analog input, there must be more.

Without seeing all the code we can't get any context, so it is impossible to offer help until we have it all.

Try looking up 'REFS0' in the datasheet.

Sorry. the sketch itself is more than 5000 lines long so in order not to complicate matters I opted to keep it simple.

Here is a more extensive extract:

unsigned int ReadADC (uint8_t Probe) {
  unsigned int U; 		// return value (mV)
  uint8_t Samples; 		// loop counter
  unsigned long Value; 		// ADC value
  Probe |= (1 << REFS0); 	// use internal reference anyway

  #ifdef AUTOSCALE_ADC
sample:
  #endif

  ADMUX = Probe; 	// set input channel and U reference

  #ifdef AUTOSCALE_ADC
    // if voltage reference changes, wait for voltage stabilization
    if ((Probe & (1 << REFS1)) != 0) {
      // switch to 1.1V Reference
      #ifdef NO_AREF_CAP
        wait100us(); 		// time for voltage stabilization
      #else
        wait_about10ms(); 	// time for voltage stabilization
      #endif
    }
  #endif

  // allways do one dummy read of ADC, 112us
  StartADCwait();		// start ADC and wait

  // sample ADC readings
  Value = 0UL; 			// reset sampling variable
  Samples = 0; 			// number of samples to take

  while (Samples < ADCconfig.Samples) {		// take samples
    StartADCwait();				// start ADC and wait
    Value += ADCW; 				// add ADC reading

    #ifdef AUTOSCALE_ADC
      // auto-switch voltage reference for low readings
      if ((Samples == 4) && (ADCconfig.U_Bandgap > 255) && ((uint16_t)Value < 1024) && !(Probe & (1 << REFS1))) {
        Probe |= (1 << REFS1); 		// select internal bandgap reference

        #if PROCESSOR_TYP == 1280
          Probe &= ~(1 << REFS0);	// ATmega640/1280/2560 1.1V Reference with REFS0=0
        #endif

        goto sample; 	// re-run sampling
      }
    #endif

    Samples++; 		// one more done
  }

  #ifdef AUTOSCALE_ADC
    // convert ADC reading to voltage - single sample: U = ADC reading * U_ref / 1024
    // get voltage of reference used
    if (Probe & (1 << REFS1)) U = ADCconfig.U_Bandgap; 	// bandgap reference
    else U = ADCconfig.U_AVCC; 	// Vcc reference
  #else                                 
    U = ADCconfig.U_AVCC; 	// Vcc reference
  #endif

  // convert to voltage
  Value *= U; 		// ADC readings * U_ref
  Value /= 1023; 	// / 1024 for 10bit ADC

  // de-sample to get average voltage
  Value /= ADCconfig.Samples;
  U = (unsigned int)Value;
  return U;
  //return ((unsigned int)(Value / (1023 * (unsigned long)ADCconfig.Samples)));
}

I am guessing REFS0, REFS1 ADCW refer to registers.

REFS0 and REFS1 are bits in the ADMUX register.
ADCW is a variable defined somewhere else in the program
ADCW is the ADC data it reads both the ADCL and ADCH registers

You will find the 328 datasheet and app notes here

Must be a function in a Library which is not included in your code.

Start with the Examples in the ide.

Simple == no real help.

If your code is 5K long, and you have only just come to this problem your code development skills are sadly lacking. You should test as you add more code, not try and write it all at once.

Your best bet is to write some sample code that illustrates you problem in a much smaller way. Doing this often makes you spot the mistake yourself.

If it does not then post your code for us to try, along with a schematic (not pictures joined by lines or a Fritzing diagram.

Hand drawn and photographed is fine.

1. The language used in the sketch is not Assembbly Lanuage; it is called Register Level Coding.

2. Look into the following Block Diagram (Fig-1) of the ADC Module of Arduino UNO/NAN to understand Register Level Coding.


Figure-1:

3. Select analog channel (A0 - A5) with the help of ADMUX Register (Fig-2).


Figure-2:

Let us Ch-1 (A1):
Store 0000 into MUX3-MUX0 positions of ADMUX Register without disturbing other bits:
Codes:

ADMUX = 0x00;
ADMUX |= bit(MUX0);

4. Select reference voltage (5V) for the ADC.
Store 01 into REFS1-REFS0 bits of ADMUX Register without disturbing other bits:
codes:

ADMUX |= bit(REFS0);    // or ADMUX |= _BV(REFS0);  or ADMUX |= 1<<REFS0;    

5. Select ADC converso clock tp 125 kHz with the help of ADCSRA Register (Fig-1).
Store 111 into ASPS2-ASPS0 bits to divide the system clock frequency (16 MHz) by 128 (16 MHz /128 = 125 kHz).
Codes:

ADCSRA |= bit(ASPS2) | bit(ADPS1) | bit(ADPS0);

6. and more.....

@Grumpy_Mike ,

Sorry I think you have not read my messages from the beginning. The 5000+ line code is not code that I wrote myself, who as I said, is a beginner in the Arduino world. It would be madness for a beginner to start off writing all that code.

It is code which I have downloaded from the internet and is supposed to work off the shelf. It is available from various sources.

I am using it to help me expand my coding in C as well as with using the Atmega328p. So far I have managed to adapt the software to work with an SH1106 oled display which was not envisaged with the original code.

I have of course looked into the examples in the IDE but these all use the analogRead() method. Not useful at all to me.

Clearly this programmer, who appears quite accomplished to me, opted to access the ADC from a lower level, probable to gain better accuracy.

For those who are intrigued by this method here is a link from Github to the full program which uses this method:

The area of interest is around line 3954.

Regards

To learn about low-level ADC on an Arduino you might like:

If you are trying to learn about low-level register usage, the essential resource is the datasheet--if you see a confusing all-caps constant used but not defined in the code, try searching for it in the appropriate datasheet.

@xuraax Despite all the above. Your original post suggested the lines of code you posted were doing some alternate form of ADC read. I asked for more context, as you were incorrect. The line I quote here is the actual register read of a new value, being summed in order to do some averaging. Hope that helps.

You need to understand that due to your failure to provide that context up front several hours have passed, several volunteers have tried, and only now you have an answer- which only points you to the data sheet for a deeper read. Could have been answered in post #2 or #3.

@camsysca, I apologize if I have wasted people's time. That is precisely the opposite of what I had intended when keeping things short.

@GolamMostafa , thank you for the info you have provided.

@DaveX, thanks for the link.

Why?
If you look in the Library all the Registers is setup as in your sample code.
What gain is there to not use analogRead();

If you just want to learn how it's done search for Atmega AVR analogread.

https://maker.pro/custom/tutorial/how-to-take-analog-readings-with-an-avr-microcontroller

/*
 * AVR IO.c
 *
 * Created: 03/01/2018 11:25:21
 * Author : RobinLaptop
 */ 

#define	F_CPU 1000000UL

#define TRIGPOINT 128

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
	// Configure PORT D bit 0 to an output
	DDRD = 0b00000001;

	// Configure PORT C bit 0 to an input
	DDRC = 0b00000000;

	// Configure ADC to be left justified, use AVCC as reference, and select ADC0 as ADC input
	ADMUX = 0b01100000;

	// Enable the ADC and set the prescaler to max value (128)
	ADCSRA = 0b10000111;



	// Main program loop
    while (1) 
    {
		// Start an ADC conversion by setting ADSC bit (bit 6)
		ADCSRA = ADCSRA | (1 << ADSC);
		
		// Wait until the ADSC bit has been cleared
		while(ADCSRA & (1 << ADSC));

		if(ADCH > TRIGPOINT)
		{
			// Turn LED on
			PORTD = PORTD | (1 << PD0);
		}
		else
		{
			// Turn LED off
			PORTD = PORTD & ~(1 << PD0);
		}
	}
}

@mikedb , Thank you for the link you provided.

In your comment above you suggested to look into the Library to see how the Registers are set up.

Can you please indicate where I should look?

I think @mikedb means the Arduino Source Code for analogRead:

Thanks Library was not the correct word correct.

@xuraax
As you can see from DaveX link the code does make provision for a lot of different Atmega chips .
Still don't know why you don't want to use analogRead.

Where exactly analogRead source code is ? I must try and find it again.

That is exactly it in #17. I find my way to things like that by googling something like "github arduino core analogWrite" and click on the the line-numbers to get a permalink.

@DaveX
Thanks.

I used a search software (long ago)that had the option to search inside text files , that's how I find it , but that software is not on this PC.