6 potentiometers with Atmega8a - All pots affect ADC result

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

Good news everyone,

 

I am assembling a device with 6 potentiometers connected to ADC inputs of Atmega8a.

I use 10k pots, adding a 330R between GND and pot lead. 

 

On prototyping board,

AREF and AVCC are connected to the same VCC net.

I have added decoupling caps between VCC and GND.

Powered with 9v battery through a voltage regulator.

 

Now, if I understand the theory right, as you turn any pot, it draws more current to Atmega input, which makes AREF go lower (?)

As a result, any pot affects the result of the conversion.

 

Please advice what am I doing wrong.

 

Attaching the diagram of this part of my circuit.

 

 

 

Attachment(s): 

This topic has a solution.
Last Edited: Sun. Dec 1, 2019 - 04:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yo-jae wrote:

Now, if I understand the theory right, as you turn any pot, it draws more current to Atmega input, which makes AREF go lower (?)

As a result, any pot affects the result of the conversion.

 

 

Errr, no.

 

I'd be looking at your code for the adc first. As for AREF - that should not be tied to AVCC. AREF shoulld just have a 100nF cap to ground.

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

Will try with 100nF cap.

 

Here is the ADC piece of my code:

uint8_t decayStep = 0;
uint8_t tabValue = 0;

ADCSRA  = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);    
ADMUX=0x05;

while(1){
    if(ADCSRA & (1<<ADSC)){
	switch(ADMUX){
   	    case 0b0101: //ADC5
		tabStep = (ADCW >> 3) + 1;
                ADMUX = 0b0100;
	  	break;
	    case 0b0100: //ADC4
	        ADMUX = 0b0011;
		break;
	    case 0b0011: //ADC3	
	        ADMUX = 0b0010;
		break;
	    case 0b0010: //ADC2	
	 	ADMUX = 0b0001;
	 	break;
	    case 0b0001: //ADC1	
	 	ADMUX = 0b0000;
	 	break;
	    case 0b0000: //ADC0	
	 	decayStep = ADCW;
                ADMUX = 0b0101;
                break;
    } else {
	ADCSRA |= (1<<ADSC);
    }    

//do stuff here,
//in particular output decayStep to 10 output bits wit LEDs connected

}//end main loop

 

 

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

Aref is the reference voltage used by the ADC, it has nothing to do with your signal.

 

 

so you get an ADC coun:t  count= k* signal/Aref    k may vary depending on whether you use the full 10 bit mode or 8 bit mode.

 

For low noise & some antialiasing filtering, each ADC input should have a cap right at the AVR pin. 

 

add a 10K pullup to reset

 

Why the 330 ohms in the gnd of each pot?

 

 

Why all the case statement for ADMUX?  just set it  & later increment it (or decrement it)

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sun. Dec 1, 2019 - 02:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So AREF does need to go through a cap to ground?

 

Makes sense to add caps to ADC line too.

 

330Ohm resistors to limit from bottom.

I have noticed when pot is all the way to GND it makes MCU freak out and it gets stuck.

 

All case for ADMUX is because I need to read values for all 6 pots. I read one and yield CPU time for other operations.

Whenever the conversion is done I check for which one was it done and store the result.

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

I have noticed when pot is all the way to GND it makes MCU freak out and it gets stuck.

  ---That's false, you must have some other issue

 

All case for ADMUX is because I need to read values for all 6 pots. I read one and yield CPU time for other operations.

   --so what, no case needed

 

       for (int dog=1; dog<=5;dog++) {

          admux=dog

          other stuff

 }

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Sun. Dec 1, 2019 - 07:55 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yo-jae wrote:

    if(ADCSRA & (1<<ADSC)){
	switch(ADMUX){
   	    case 0b0101: //ADC5
		tabStep = (ADCW >> 3) + 1;
                ADMUX = 0b0100;
	  	break;
	    case 0b0100: //ADC4
	        ADMUX = 0b0011;
		break;
	    case 0b0011: //ADC3	
	        ADMUX = 0b0010;
		break;
	    case 0b0010: //ADC2	
	 	ADMUX = 0b0001;
	 	break;
	    case 0b0001: //ADC1	
	 	ADMUX = 0b0000;
	 	break;
	    case 0b0000: //ADC0	
	 	decayStep = ADCW;
                ADMUX = 0b0101;
                break;
    } else {
	ADCSRA |= (1<<ADSC);
    }    

This isn't doing what you think it's doing.

 

Your 'if' is true only while a conversion is underway.  So you're constantly changing the MUX and reading ADCW before a conversion has even completed.  No wonder your results are bananas.

 

Your use braces and indentation are obfuscatory.  It won't even compile.  Running bcpp against that if block yields:

if(ADCSRA & (1<<ADSC))
{
  switch(ADMUX)
  {
    case 0b0101:                                  //ADC5
      tabStep = (ADCW >> 3) + 1;
      ADMUX = 0b0100;
      break;
    case 0b0100:                                  //ADC4
      ADMUX = 0b0011;
      break;
    case 0b0011:                                  //ADC3
      ADMUX = 0b0010;
      break;
    case 0b0010:                                  //ADC2
      ADMUX = 0b0001;
      break;
    case 0b0001:                                  //ADC1
      ADMUX = 0b0000;
      break;
    case 0b0000:                                  //ADC0
      decayStep = ADCW;
      ADMUX = 0b0101;
      break;
  }
  else
  {
    ADCSRA |= (1<<ADSC);
  }

So the if clause is missing a the closing brace.  This won't even compile.  Please don't post code that doesn't represent the actual code you are running.

 

You probably want something more like this:

if (!(ADCSRA & (1<<ADSC)))
{
  switch(ADMUX)
  {
    case 0b0101:                                  //ADC5
      tabStep = (ADCW >> 3) + 1;
      ADMUX = 0b0100;
      break;
    case 0b0100:                                  //ADC4
      ADMUX = 0b0011;
      break;
    case 0b0011:                                  //ADC3
      ADMUX = 0b0010;
      break;
    case 0b0010:                                  //ADC2
      ADMUX = 0b0001;
      break;
    case 0b0001:                                  //ADC1
      ADMUX = 0b0000;
      break;
    case 0b0000:                                  //ADC0
      decayStep = ADCW;
      ADMUX = 0b0101;
      break;
  }
  ADCSRA |= (1<<ADSC);
}

This skips everything while a conversion is underway, but once it's complete it changes the MUX, grabs the results (for the channels for which you've written that), and starts the next conversion.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Thank you for pointing this out.

I must have got confused that the ADSC bit is cleared when conversion is done.

That else at the end was just ridiculous.

Fixed the code, now it works. 

 

Regarding AREF - connecting to ground with 0.1uF did nothing good.

I still think AREF is the top voltage for the conversion reference.

Please correct me if I'm wrong

 

Indentation - I was afraid somebody say about it. The code didn't get pasted correctly into the forum. I had to manually add spaces wherever they were missing. I am not kind of a guy with indentation all over :)

 

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

yo-jae wrote:
The code didn't get pasted correctly into the forum. I had to manually add spaces wherever they were missing.

 

Yes, teh code editor does at times strip things away, but if the original code is a bit of a mess, then the editor only makes it worse.  At least for the future you know what to do.

 

yo-jae wrote:
Regarding AREF - connecting to ground with 0.1uF did nothing good.

 

Yes it did, you just don't see it as your code is working now.  It needs to be there.

 

yo-jae wrote:
I still think AREF is the top voltage for the conversion reference.

It is.  But depending on how you set ADMUX dictates if the reference is generated internally and is output on the AREF pin, or if the AREF pin is an input.  In your case it appears that you are using it as an input.  For that I would recommend a 10uh inductor in series between AVCC and AREF with a .1uf cap to ground on the AREF pin, and a 10uF tantalum cap on the AVCC side of the inductor.

 

Reread the section on ADMUX and it should be a little clearer.

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

When you had AREF connected to AVCC externally, then it was OK to set ADMUX the way you were.  However, as others have said, it's a bad idea to connect AREFF to AVCC.  Instead, use the REFSn bits in ADMUX to select AVCC as the reference.

 

  switch (ADMUX & (7 << MUX0))
  {
    case 5:                                       //ADC5
      tabStep = (ADCW >> 3) + 1;
      ADMUX = (1 << REFS0) | 4;
      break;
    case 4:                                       //ADC4
      ADMUX = (1 << REFS0) | 3;
      break;
    case 3:                                       //ADC3
      ADMUX = (1 << REFS0) | 2;
      break;
    case 2:                                       //ADC2
      ADMUX = (1 << REFS0) | 1;
      break;
    case 1:                                       //ADC1
      ADMUX = (1 << REFS0) | 0;
      break;
    case 0:                                       //ADC0
    default:
      decayStep = ADCW;
      ADMUX = (1 << REFS0) | 5;
      break;
  }

You still need a 100 nF cap from AREF to GND.  Nothing else on AREF.  The only reason you would connect AREF to something else is if none of the other references provided by the AVR itself are suitable for your needs.  The m8 provides AVCC and a 2.56V bandgap reference.

 

EDIT:  code edits

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sun. Dec 1, 2019 - 05:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Again, Aref is the reference voltage for the ADC , it has nothing to do with the pots (unaffected by them), it is only involved in measuring whatever voltage is coming in on the adc pinc.

Again, you only need a loop or channel variable counting (modulo) to sweep through your channels (whether you want to wait for each channel or not)--it only requires 1 line of code to switch to the next channel.

Take a good read of the datasheet section of the ADC--there's a lot of good infor there.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

avrcandies wrote:

Again, you only need a loop or channel variable counting (modulo) to sweep through your channels (whether you want to wait for each channel or not)--it only requires 1 line of code to switch to the next channel.

You'll note the OP does different things with each channel (although he's not populated all cases yet).  While the MUX could be handled by a loop, a switch/case approach is not a bad solution.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

You still need a 100 nF cap from AREF to GND.  Nothing else on AREF.  

Also, get rid of the wire from Aref to Avcc...it is switched internally ...on some of the AVR's this can cause a short if the internal ref is selected (since internal ref voltage will try to go out the aref pin)...They updated this on some of the newer AVR chips.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

yo-jae wrote:

 switch(ADMUX)
  {
    case 0b0101:   ...

I'd never do it that way on an AVR8.  You change the ref source, or change ADLAR, and now the code mysteriously "breaks".  Do it right and use a mask.

 

Better yet (IMO/IME), keep the channel selection separate for ref selection and other options.  Rebuild ADMUX each time.  [bet when you count the RMW cycles it doesn't cost you anything]

https://www.avrfreaks.net/commen...

https://www.avrfreaks.net/commen...

https://www.avrfreaks.net/commen... and links to prior

...

 

https://www.avrfreaks.net/commen...

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Mon. Dec 2, 2019 - 05:55 PM