ADC acting up

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

So I have a small ADC library thats worked until now. Maybe someone can spot the problem? uart output is either always 0 or always 1023.
AVR studio latest version
ATmega644
16mhz crystal, fuses set correctly
AREF -> cap -> ground
AVCC -> 3.3V
GND -> gnd
Pin 40 (ADC0) -> voltage divider; two 10k resistors

int main()
{
	init();

	uartSendString("\f");

	while(1)
	{
		uartPrintNum(adcGet(1),'d');
		_delay_ms(500);
		uartSendString("            \r");
	}
}

void init()
{
	uartInit('n',4800);

	adcInit();
	adcOn();
	adcSet(0b00000000);

	sei();
}

adcinit/on/set

void adcInit(void)
{
	//Set Right adjust.  AVcc reference
	ADMUX  |= (1<<REFS0);
	
	//Enable ADC, 128 Prescaler
	ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

void adcOn(void)
{
	ADCSRA |= (1<<ADEN);
}

void adcOff(void)
{
	ADCSRA &= ~(1<<ADEN);
}

void adcSet(char adcChoice)
{
	ADMUX &= 0b11100000;
	ADMUX |= adcChoice;
}

int adcGet(int avg)
{
	unsigned char temp;
	unsigned int resultTemp;
	unsigned long long result = 0;

	for ( int i=0; i

I fully don't know whats wrong.

Answer: Yes the ridiculous logic in adcGet() was because i thought it might be the problem.

Answer: Yes the uart works.

Answer: Yes I know averaging not a power of two isnt a good idea.

Cheers!
Tom

I should note also that adding ADCSRA |= (1<<ADIF) after conversion and retreaving result does not make a difference.

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

I suspect that up till now you always used ADC1 adn not ADC0

with this:

uartPrintNum(adcGet(1),'d'); 

and then

void adcSet(char adcChoice) 
{ 
   ADMUX &= 0b11100000; 
   ADMUX |= adcChoice; 
} 

you set up for ADC1 and not ADC0 as your description says.

regards

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

BTW, any int * 1023 cannot exceed the range of long, so you could change the code to

int adcGet(int avg) 
{ 
   unsigned long result = 0; 

   for ( int i=0; i

/Martin.

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

Thank you for your input.

Meslomp: adcGet(1); 1 is the number of samples I average over, not the adc number

mtaschl: I tried what you provided, but the result stays the same.

Uart keeps saying ADC is 0. Checked the voltage at AREF and at the divider. They're correct, so its not the hardware.

Any debugging ideas?

add edit: its zero whether when the pin is connected to VCC, zero when connected to ground, and zero when floating.

Last Edited: Wed. Mar 31, 2010 - 03:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

first make sure your uart is operating OK.
so end a byte of known data to see if this is received OK on the other end.

I have made a routine that takes a byte and sends it as ascii hex, so 0x12(value) = '0x12'(string)
after adc conversion is complete send out the raw data from the ADC conversion direct to the uart. If it is OK then repeat after the averaging and so follow the path till the final send routine.

just a gues without looking back inthe code. You do start and re-start the conversions after the result is read????

regards

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

start restart:

int adcGet(int avg)
{
   unsigned long result = 0;

   for ( int i=0; i

If you mean that, then yes. I'm always setting the ADSC bit, waiting for it to clear, then reading the data.

The uart works. I also have routines to send either binary/hex/decimal to hyperterminal. Ive verified that it works by checking to see if the ADC setup registers were being written correctly.

But I will try printing the ADC result right after its read (in the ADC subroutine). That is a good idea that maybe will help me locate the problem.

Thanks,
Tom

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

You need a settling time whenever you change ADMUX.

Unless you are using IRQs for something else, there is little point in calling sei().

I agree that the ADSC bit clears about the same time that the ADIF bit sets. The convention is to wait for ADIF to set. You should write to ADIF to clear the flag.

It is wise to avoid a division by zero. e.g. if (avg <= 0) avg = 1;

David.

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

I set ADMUX once (adcSet()). I know to ignore the first conversion if necessary.

There is little point in calling sei() for now. I was going to add to the code but got stuck here.

I've also tried waiting for ADIF

ADCSRA |= (1<<ADSC); //start conversion
while(!(ADCSRA&(1<<ADIF)); //wait for complete
result = ADCW; //read result
ADCSRA |= (1<<ADIF); //clear ADIF flag

It is wise to avoid a division by zero - avg will never be 0 unless I make a type-o in my program. I will always set the input as 1 or more.

Still stuck!

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

tkurowski wrote:
Pin 40 (ADC0) -> voltage divider; two 10k resistors

Just a thought but have you tested the actual voltage on pin 40?

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

maximax: you mean when it is disconnected from the system? no. what goal did you have in mind by doing this?

Tom

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

Perhaps I should post the whole code... I'm really at a loss! Can someone please look at it carefully?

MAIN

#include "global.h"
#include 
#include 
#include 
#include "uart.h"
#include "adc.h"

void init();

int main()
{
	int result;	

	init();

	uartSendString("\f");
	uartPrintNum(ADCSRA,'b');  //To verify registers
	uartPrintNum(ADMUX,'b');
	uartPrintNum(ADCSRB,'b');
	uartSendString("\r\n");

	while(1)
	{
		result = adcGet(1);
		uartPrintNum(result,'d');
		_delay_ms(500);
		uartSendString("            \r");
	}
}

void init()
{
	uartInit('n',4800);

	adcInit();
	adcOn();
	adcSet(SE_ADC0);

	sei();
}

ADC.h

#ifndef ANALOGDIGITALCONVERTER
	#define ANALOGDIGITALCONVERTER

	#include "global.h"
	#include 
	#include 

	#define SE_ADC0 			0b00000000
	#define SE_ADC1 			0b00000001
	#define SE_ADC2 			0b00000010
	#define SE_ADC3 			0b00000011
	#define SE_ADC4 			0b00000100
	#define SE_ADC5 			0b00000101
	#define SE_ADC6 			0b00000110
	#define SE_ADC7 			0b00000111
	#define PADC0_NADC0_G10 	0b00001000
	#define PADC1_NADC0_G10 	0b00001001
	#define PADC0_NADC0_G200 	0b00001010
	#define PADC1_NADC0_G200 	0b00001011
	#define PADC2_NADC2_G10 	0b00001100
	#define PADC3_NADC2_G10 	0b00001101
	#define PADC2_NADC2_G200 	0b00001110
	#define PADC3_NADC2_G200 	0b00001111
	#define PADC0_NADC1 		0b00010000
	#define PADC1_NADC1 		0b00010001
	#define PADC2_NADC1			0b00010010
	#define PADC3_NADC1 		0b00010011
	#define PADC4_NADC1 		0b00010100
	#define PADC5_NADC1 		0b00010101
	#define PADC6_NADC1 		0b00010110
	#define PADC7_NADC1 		0b00010111
	#define PADC0_NADC2 		0b00011000
	#define PADC1_NADC2 		0b00011001
	#define PADC2_NADC2 		0b00011010
	#define PADC3_NADC2 		0b00011011
	#define PADC4_NADC2 		0b00011100
	#define PADC5_NADC2 		0b00011101
	#define SE_VBG 				0b00011110
	#define SE_GND 				0b00011111

	void adcInit(void);
	void adcOn(void);
	void adcOff(void);
	void adcSet(char adcChoice);
	int adcGet(int avg);
	int adcGetAvg2PowOf(int avg);

#endif //ANALOGDIGITALCONVERTER

ADC.c

#include "adc.h"

//Initialize ADC
void adcInit(void)
{
	//Set AREF=AVCC
	ADMUX  |= (1<<REFS0);
	
	//Enable ADC, Use 128 Prescaler
	ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}  // ex. adcInit();

//Turn on ADC
void adcOn(void)
{
	ADCSRA |= (1<<ADEN);
}  // ex. adcOn();

//Turn off ADC
void adcOff(void)
{
	ADCSRA &= ~(1<<ADEN);
}  // ex. adcOff();

//Set which ADC to use in what mode (see adc.h for #define statements)
void adcSet(char adcChoice)
{
	ADMUX &= 0b11100000;
	ADMUX |= adcChoice;
}  // ex. adcSet(SE_ADC4);

//Get an ADC value averaged over 'avg' samples
int adcGet(int avg)
{
	unsigned long result = 0;

	for ( int i=0; i

Typical application output:

ADCSRA=10000111 ADMUX=01000000 ADCSRB=00000000
result=0

I tried reading the ADC result right after the conversion took place, but the problem was the same - always "0"!

Any help would be greatly appreciated!

Tom

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

You've been asked what you read as the voltage right on the pin. I don't see an answer.
What value do you see on the AREF pin?
What is connected to that pin?

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.

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

I would tidy up your get adc routine to take just 1 reading and remove all the maths (your casts when you do the averaging are a bit suspect):-


  tempAdcReading = (ADCL & 0xFF);
  tempAdcReading |= ((ADCH<<8) & 0xFF00);


also check you are actualy getting the voltage you expect on adc0 and aref using a multimeter. Have you connected both grounds?

Also you state that the code usualy works, can you still verify this (ie go back to know good?)
load your code into the simulator and verify that the adc register are as you would expect.
have you tried another part to rule out a failed mcu?
Mark

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

At the moment I do not have access to more chips or a multimeter :(
But also, when I do it, what exactly am I looking for when I test the voltage on the ADC pin? What will this tell me?
AREF voltage is correct, and as the datasheet says, I have hooked up a 0.1uF capacitor from it to ground.

I removed the averaging.
In fact I have it like this to double check everything now:

int adcGet(void)
{
	unsigned int result = 0;

		ADCSRA |= (1<<ADSC);		//Start conversion
		while(!(ADCSRA&(1<<ADIF)));	//Wait till conversion is finished
		uartPrintNum(ADCL,"b8");
		uartSendChar(' ');
		uartPrintNum(ADCH,"b8");
		uartSendChar(' ');
		result = ADCW;				//Read the result
		ADCSRA |= (1<<ADIF);		//Clear the flag

	return result;
}

putting "result = ADCW" before the uartPrintNum function uses ADCL and ADCH does not make a difference.

Value is consistently zero.
I didnt even think that the ADC could be broken - is that not the first time you've heard of something like that happening? I mean just one component of the chip breaking?

I'll get your voltage checks tomorrow evening.

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

Quote:

putting "result = ADCW" before the uartPrintNum function uses ADCL and ADCH does not make a difference.

Use either ADCW or ADCH/ADCL but not both. Can your uartPrintNum() handle "b16"? If so:

uint16_t adc_val;
...
adc_val = ADCW;
uartPrintNum(adc_val, "b16");
..
// do stuff with adc_val...

You can blow up just selected bits of chips if you do something nasty like applying high/negative voltage to just one or two pins. If there's any doubt bin the chip and try a new one.

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

Sure it can. I made the output convenient:

main

int main()
{
	init();

	uartSendString("\f");
	
	uartSendString("ADMUX\t= 0b");
	uartPrintNum(ADMUX,"b8");
	uartSendString("\r\n");

	uartSendString("ADCSRA\t= 0b");
	uartPrintNum(ADCSRA,"b8");
	uartSendString("\r\n");

	uartSendString("ADCSRB\t= 0b");
	uartPrintNum(ADCSRB,"b8");
	uartSendString("\r\n");

	uartSendString("DIDR0\t= 0b");
	uartPrintNum(DIDR0,"b8");
	uartSendString("\r\n");

	uartSendString("\r\n");

	while(1)
	{
		uartPrintNum(adcGet(1),"d");
		_delay_ms(500);
		uartSendString("            \r");
	}
}

adcGet

int adcGet(int avg)
{
	unsigned long long result = 0;

	for ( int i=0; i

Even if the casting and averaging isnt working, I should be able to see it since Im printing the ADC result right after getting it.

Typical program output:

ADMUX   = 0b01000010
ADCSRA  = 0b10000111
ADCSRB  = 0b00000000
DIDR0   = 0b00000000

0b0000000000000000 = 0

I dont think I zapped my chip! Maybe my fingers were very charged one day? I will have to try with another chip, because as someone else suggested, I loaded on my old program for ADC and it was also spitting out a fat zero. :(

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

FYI
I've noticed that its actually not always zero... I dont know if this clues somebody in on something:

Typical output:

ADMUX   = 0b01000010
ADCSRA  = 0b10000111
ADCSRB  = 0b00000000
DIDR0   = 0b00000000

0b0000000000000000 = 0

It is zero about 95% of the time, but sometimes it'll read 1 or 3 or 5 or whatever. Nothing more than 5. This happens when the pin is pulled high, pulled low, put in the voltage divider, or left floating.

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

tkurowski wrote:
maximax: you mean when it is disconnected from the system? no. what goal did you have in mind by doing this?

My appologies for any confusion, I was merely trying to point out that the problem might not be where you expect it to be. I recently spent several hours trying to debug some perfectly good code only to find the problem lay somewhere in the electronics I had taken for granted to be working. I have no idea of your setup here but if the top half of your resistive divider was somehow broken then the ADC pin would surely be pulled low (or near that) ?

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

tkurowski wrote:
Pin 40 (ADC0) -> voltage divider; two 10k resistors
Tom, are you absolutely sure that both resistors are 10K?

Having a multimeter is almost essential ... even if it is only a $5 version, it should be good enough to identify voltages and resistances values within the correct "ballpark".

Cheers,

Ross

Ross McKenzie ValuSoft Melbourne Australia

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

No no no, I mis-spoke... the voltage divider is correct and is at the right voltage, and so is the AREF pin - its just for the last three day's I've been away from my workstation so I couldnt measure anything recently. Tonight I will be returning and can re-verify all of this for you if it will help! I will also first try a new chip!

Tom

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

If you have tried some known good software and it is giving you the same error, then that strongly suggests that you have a problem with your hardware. Do you have any dev boards or any other hardware you can test on? Get something that works then use this as a reference for debugging you target.

What is your construction? you might have shorts or dry solder joints on you PCB. Has it ever worked?

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

Well, it seems that changing the chip at least can differentiate between 5V and 0V...

Measure resistor bridge on its own: 2.47V
Attach the bridge to the ADC pin, measure voltage with a multimeter: 4.53V
Output of chip is "correct" I suppose: ~975 (975/1024*5 = 4.76V), lots of noise though! This is USB powered, so I think Vref should be pretty stable... This is the only thing connected, so no crazy current.

When the ADC pin is floating, its ~1015. So it looks like it's sourcing. This is weird, since I dont initialize DDRA or PORTA, meaning it should be an input with no pullups. Whats happening?

[-ADC_PIN-]---10K---5V
            |
            |-10K---GND

[-ADC_PIN-]---10K---10K---5V
                  |
                  |-10K---GND

Both of the pin setups seem to source enough that it pulls the bridge up to 4.8V.

Cheers,
Tom

PS: when the pin is floating, its at 4.9V
PSS: this is the case for all ADC pins (single ended, 1x gain selected)

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

Quote:

Measure resistor bridge on its own: 2.47V
Attach the bridge to the ADC pin, measure voltage with a multimeter: 4.53V

Let's stop right there. Why are you configuring an ADC pin as a high-level output? Or at least a pulled-up input?

Let's see the smallest complete test program that exhibits this behavior. Tell the AVR model and toolchain again. Tell the hardware environment--what is connected to these pins?

What do you get on other pins if you change the ADC to point to them?

================
You've got to realize that our little outfit ships 10s of thousands of various AVR models each year into apps that use multiple ADC channels as an integral part of the app. No, the ADC doesn't start "acting up"--in any of them; in the first year; or in subsequent years. And we are just a drop in the puddle of AVR usage.

So take a deep breath and get bac to this basic program. You said you used this code before, but perhaps in another environment or a bit moved when you chaned models. (You ARE building for the correct model, aren't you?)

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.

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

Nothing has changed over my various posts. Like I said, I'm not setting it has a high output, or a pulled input - all I'm saying is that it LOOKS like that.

Using an ATmega644, coded in Studio, programming with Dragon.

All done on a breadboard.

The only thing I have connected is an ISP header, Vcc and Gnd (Vref=AVcc=Vcc), 16MHz crystal (128 prescaler on ADC makes it less than 200khz), uart to tell me what the ADC value is, and the ADC pin is as was in my last post:

[-ADC_PIN-]---10K---5V
            |
            |-10K---GND

[-ADC_PIN-]---10K---10K---5V
                  |
                  |-10K---GND 

-both configurations behave the same way. in this case, ADC_PIN = PA1 (ADC1).

And again, no matter what ADC pin I use (single ended 1x gain), the behaviour is identical.

Here is my smallest test program

main

#include "global.h"
#include 
#include 
#include 
#include "uart.h"
#include "adc.h"

void init();

int main()
{
	init();
	uartSendString("\f");

	while(1)
	{
		uartPrintNum(adcGet(1),"d");
		_delay_ms(500);
		uartSendString("            \r\n");
	}
}

void init()
{
	uartInit('n',4800);
	adcInit();
	adcSet(SE_ADC1);
}

adc.h

#ifndef ANALOGDIGITALCONVERTER
	#define ANALOGDIGITALCONVERTER

	#include "global.h"
	#include 
	#include 

	#define SE_ADC1 			0b00000001

	void adcInit(void);
	void adcSet(char adcChoice);
	int adcGet(int avg);

#endif //ANALOGDIGITALCONVERTER

adc.c

#include "adc.h"

//Initialize ADC
void adcInit(void)
{
	//Set AREF=AVCC
	ADMUX  |= (1<<REFS0);
	
	//Enable ADC, Use 128 Prescaler
	ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

//Set which ADC to use in what mode (see adc.h for #define statements)
void adcSet(char adcChoice)
{
	ADMUX &= 0b11100000;
	ADMUX |= adcChoice;
}

//Get an ADC value averaged over 'avg' samples
int adcGet(int avg)
{
	unsigned long long result = 0;

	for ( int i=0; i

Uart works.

And as has been stated before, if I print the "result" variable right after reading "ADCW" into it (in "adcGet(1)"), the behaviour is the same.

I have also tried explicitly adding:

DDRA &= ~(1<<PA1);
PORTA &= ~(1<<PA1);

just before exiting the "init()" function to make sure the pins are not outputs/pulled-inputs.

example program output (in hyperterminal):

988
990
942
951
943
937
950
988
991
992
941
945
954
982
983
991
946
949
940
950
982
990
991
990
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tom,

I am not going to attempt to review your code. But the evidence above does point very strongly towards there being another source of voltage being applied to the resistor divider when connected to the ADC input.

Disconnect the resistive divider from your ADC input. Connect a single 100K resistor from the ADC input to ground. Use your multimeter to measure the voltage across the 100K resistor. I bet today's lunch that you will get a non-zero reading and thus proving that the ADC pin is either sourcing the voltage ... or, there is an incorrect wiring of another source of voltage to that pin.

happy hunting.

Cheers,

Ross

Ross McKenzie ValuSoft Melbourne Australia

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

tkurowski wrote:
Here is my smallest test program
...
unsigned long long result = 0;
...
result = (int) ((float) result/(float) avg + 0.5);
This probably has nothing to do with your problem. Most likely your problem is a hardware one or you are compiling for a wrong micro. But I just could not resist. Why would you use "float" in the "smallest" test program? Or "long long"?

Eugene

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

Quote:

But the evidence above does point very strongly towards there being another source of voltage being applied to the resistor divider when connected to the ADC input.

Thus I asked about the setup as well.

Tom must get the juvenile delinquent AVRs from his source, that are acting up. Mine are regimented and fairly well behaved.

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.

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

Eugene, I was already cutting out a lot in hopes someone would read the whole thing (which didn't work out :) ). I didn't catch that part.

valuSoft, yes there is ~4.9V from ADC pin to 100k to ground.

Normally I would agree with "happy hunting", but like I said, theres nothing else connected to that pin. I can take a picture if you'd like! (breadboard!)

Help!!

PS theusch: digikey :/

PSS: ive tried three chips now. the ones with a working ADC have the same "sourcing high" phenomenon...

Last Edited: Mon. Apr 5, 2010 - 01:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That means that SOMETHING is pulling it up. 4.9V is consistent with readings of 960 out of 1024.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

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

wooooohhhhh kay...

heres what I did:

1) is the ISP header screwing it up? remove it. no change. put it back in
2) is the USB supply screwing it up? remove it, accidently hook up power with 3.3V (not enough to run my bluetooth uart module). oops!
3) replace 3.3V with 5V. Plug it in. no change. remove it
4) hook up avr dragon usb power supply again since it is just a hassle to have an external power supply. what the hell its working???

ooooeee! What up with THAT?
(http://www.hulu.com/watch/102975/saturday-night-live-what-up-with-that)
Please, somebody tell me what up with that. I really want to know.

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

Breadboard? OK unplug the AVR and measure at the 100K again ... you may have a faulty breadboard.

Ross McKenzie ValuSoft Melbourne Australia

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

You know what? I'm not even going to try that. I hooked up 3.3V to one rail, and the other rail (connected with wires) reads 2.4V. Thats just stupid. I'm chuckin' it!

Thank you everyone for your help!

Tom