multiple ADC's

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hi guys
Quick question.
If you want to use multiple analog pins, do you set the ones you want to use at initialization or do you change the mux register values just before reading each time?
i.e if I want to use analog0 and analog1, analog0 needs:

ADMUX &= ~(1 << MUX0);
ADMUX &= ~(1 << MUX1);
ADMUX &= ~(1 << MUX2);
ADMUX &= ~(1 << MUX3);

and analog1 needs:
#

ADMUX |= (1 << MUX0); //ADC1 is set

so they conflict - so Im assuming to read analog1, I just reset the ADMUX register just before reading?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ADMUX controls analog multiplexer. Multiplexer connects ADC with ONE of the analog pins at a time.

You need to set multiplexer prior to conversion.

No RSTDISBL, no fun!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just use a code like this to clear the channel bits and set the new channel you want (one at a time as explained)

// clear the channel bits and set the new channel, adc_input can be 0,1,2,3...
   ADMUX =  (ADMUX & 0xF8) | adc_input;  

Alex

====================================================================================

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

Last Edited: Sun. Feb 3, 2013 - 12:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is not wise to use |= and &= for setting ADMUX bits.
Just use a straightforward = assignment.

ADMUX = (ADMUX & 0xF0)|(1 << MUX0); //ADC1 is set 

Yes, your 5 statements would achieve the same result but 1 stement is easier. It also means that the multiplexer only gets altered when required. Your method effectively sets different ADCn values for each of the 5 statements. (think if it had started at (15 << MUX0)

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh ok, cheers guys.
So just to conclude (for mega328p) is this the right idea:

	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescalar to 128 - 125KHz sample rate @ 16MHz

	ADMUX |= (1 << REFS0); // Set ADC reference to AVCC
	ADMUX |= (1 << ADLAR); // Left adjust ADC result to allow easy 8 bit reading

	// No MUX values needed to be changed to use ADC0
	ADCSRA |= (1 << ADEN);  // Enable ADC
	ADCSRA |= (1 << ADSC);  // Start A2D Conversions
int pin = 0;//PC0 analog pin
int value = 0;
while(1){
	ADMUX =  (ADMUX & 0xF8) | pin;
	_delay_ms(20);
	value = ADCH ;
   while ( !( UCSR0A & (1<<UDRE0)) );
   /* Put data into buffer, sends the data */
   UDR0 = value; //USART setup elsewhere
}

I have seen that the UDR0 register may want an unsigned char, but can I just pass it the integer - or should I cast it?

Last Edited: Sun. Feb 3, 2013 - 01:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why not write some little functions rather than a block of monolithic code? How about some functions like
adc_init()
uint16t adc_read(char chan)
Putchar(char ch)
You'll find debugging easier.
with admux, why not just load the value you want.
ADMUX = chan | option;
Rather than masking then oring?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sorry I was being lazy...

int main() {
	adc_init();
	int value = 0;
	while (1) {
		ADMUX = (ADMUX & 0xF0)|(1 << MUX0); //ADC1 is set
		_delay_ms(20);
		value = ADCH;
		putchar(value);
	}
}
void putchar(int value) {
	while (!(UCSR0A & (1 << UDRE0)));
	/* Put data into buffer, sends the data */
	UDR0 = value; //USART setup elsewhere
}
void adc_init() {
	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // Set ADC prescalar to 128 - 125KHz sample rate @ 16MHz

	ADMUX |= (1 << REFS0);// Set ADC reference to AVCC
	ADMUX |= (1 << ADLAR);// Left adjust ADC result to allow easy 8 bit reading

// No MUX values needed to be changed to use ADC0
	ADCSRA |= (1 << ADEN);// Enable ADC
	ADCSRA |= (1 << ADSC);// Start A2D Conversions
}

I get a lot of [00] on the serial terminal however I can put on characters directly (hard coded) its just when im reading from the ADC that I have a problem...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What do you expect to see? If you want human readable values, use itoa or printf. Have you setthe adc for single conversion or free run?
Just write a function to
Set the admux
Start the conversion
Wait for completion
Return the result.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah I tried that before, but when it gave me no better result I removed it. Ive put it back in now. Yeah I want something human readable. here is the while loop I know its a little messy, but I just wanted to see if something would come out as expected:

	while (1) {
		  char buffer [10];
		  itoa (sensor1.sensorRead(0),buffer,5);
		  for (int i = 0; i < sizeof(buffer) -1; i++){

		serial.put(buffer[i]);
		  }
		  buffer[0] = '\0';
		serial.put('\n');
		led.Toggle();
		_delay_ms(100);

	}

I put the buffer[0] = '\0'; to clear the buffer. There is a sensor attached and is set at the lowest, so I expect a 0:

Quote:

0[00]adatalo
0[00]adatalo
0[00]adatalo
0[00]adatalo
0[00]adatalo
0[00]adatalo

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Every program is basically a:

    setup();       // e.g. adc_init etc
    while (1) {
        loop();    // e.g. read ADC, show value
    }

Getting your overall program structure designed and layed out is the most important part of any programming.

There is no harm at all in creating 'helper' functions. e.g. formatting some output.

God invented printf() for C people. Beelzebub invented << for C++ people. I don't know whether avr-g++ supports the formatting 'features' of cout.

Whichever way you do things, try to make them simple. e.g. an initialisation will normally use = rather than |=. Obviously a shared 'resource' such as a GPIO port may sometimes need individual bit manipulation.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
itoa (sensor1.sensorRead(0),buffer,5);

Assuming that what you have used is http://www.nongnu.org/avr-libc/u... what is the purpose of a radix=5 ?

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

alexan_e wrote:
Quote:
itoa (sensor1.sensorRead(0),buffer,5);

Assuming that what you have used is http://www.nongnu.org/avr-libc/u... what is the purpose of a radix=5 ?

Alex

Don't you think in quintary then ?

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll repeat myself just so that i can drive the point home - the adc is in single shot mode and you start it only once. Otherwise, just keep wasting bandwidth.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Quote:
itoa (sensor1.sensorRead(0),buffer,5);

Assuming that what you have used is http://www.nongnu.org/avr-libc/u... ... e1dbf7a665 what is the purpose of a radix=5 ?

Alex


Ooops I think I was thinking of dtostrf where that is the number of sig figs, or D.P or something...
back to 10.
Kartman:
Quote:

the adc is in single shot mode and you start it only once. Otherwise, just keep wasting bandwidth.

Please can you point to where you mean? As far as I can tell I am initializing the ADC for free-running mode, and then Im just changing the MUX to which pin I want to read - Im not sure where Im doing unnecessary stuff that is "just [] wasting bandwidth".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Free running mode is only useful for reading one channel. Datasheet says when you change channels in free running mode, you always interrupt the running conversion, and you need to wait for the next conversion complete. So you wait 2 conversion times to read a channel. Hey! I thought free running mode was sposed to be faster, not slower! Better to Just call addat=readadchan(3) and live with the 104usec conversion time. Most programs have a spare 104 usecs sitting around somewhere to let the a/d conversion complete.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why reinvent this same wheel for the 10 gazillionth time? The Tutorial Forum has an article about using the ADC, why not start there if you don't understand how to operate it? (you don't).

In single shot mode (which is the one to use if multiple channels are involved) every conversion starts when you set the ADSC (start conversion) bit to one and ends when it returns to zero.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah the tutorial on ADC by Ken Worster is the one I follwoed, however it recommended the free-running mode (or at least explained it). It was only later that I realised i wasn't sure what to do if I needed more channels.
Cliff, did you manage by any chance to have a look at my pm?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I saw your PM but really you should raise any questions in forum threads where there's a chance of finding one in several thousand people who haven't planned a busy weekend rather than just one. The thing about FatFs is that there are literally hundreds of people here using/have used it so the chance of finding someone who can help is always going to be pretty high.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a.mlw.walker:
You can start with this

// mega88, 8MHz
#include 
#include 
#include 

#define REF_AVCC (1<<REFS0)            // reference = AVCC
#define REF_INT  (1<<REFS0)|(1<<REFS1) // internal reference 1.1 V 

// global variables
uint16_t adc_value0, adc_value1;
char tempstr[6];

//------------------------------------------------------
void adc_init(void)
{
   ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

//------------------------------------------------------
uint16_t adc_read(uint8_t channel)
{
   ADMUX = REF_AVCC | channel;  // set reference and channel
   ADCSRA |= (1<<ADSC);         // start conversion  
   while(ADCSRA & (1<<ADSC)){}  // wait for conversion complete  
   return ADC;
}


//======================================================

int main(void)
{
   uart_init(); 
   adc_init();
   
   while(1)
   {
      adc_value0 = adc_read(0); // ADC0 
      adc_value1 = adc_read(1); // ADC1
   // etc.

      // send adc_value0
      utoa(adc_value0, tempstr, 10); // convert number to string
      uart_puts("channel 0 = ");
      uart_puts(tempstr);
      uart_puts("\r\n");        // new line
      _delay_ms(1000);
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks guys, I think I can work it from here.
Alex