## Solved: inaccurate conversion of LM35 output with atmega8

19 posts / 0 new
Author
Message

Hi, I am interfacing lm35 with atmega8A but somehow I am getting wrong values.

When I measure LM35 output I get 231 mV which means 23.1 degrees. But my conversion somehow gets 21.X degrees (when I input 4.5 V into the circuit). Interetingly, when I tried to increase voltage to 5.5V, my conversion suddenly showed 19.X degrees.

I chose the reference voltage 2.56 in order it to be constant. why does the adc conversion doesn't work correctly? And why does it change depending on voltage?

Here is the diagram and the code. I have left out the LED display as it is not important, it is standard 3 digit anode led, which was originally not multiplexed, but I have done that with my wires as I dont have 28 pins to spare lol.

```#ifndef F_CPU
#define F_CPU 1000000UL
#endif // F_CPU

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

#define DELAY_IN_MS 500 /* 0.5 sec */
#define NUM_OF_MEASUREMENTS 100
#define NUM_DISPLAYS 3

int numbers[] = {
0b01000000,
0b01110011,
0b00100100,
0b00100001,
0b00010011,
0b00001001,
0b00001000,
0b01100011,
0b00000000,
0b00000001,
0b11111111 // off
};

int display = 0;
int measuring = 1;
uint8_t digits[NUM_DISPLAYS];

{
// 	Internal 2.56 Voltage Reference
// Prescaler 62.5 kHz
}

{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;

//Start Single conversion

//Wait for conversion to complete

//Clear ADIF by writing one to it

return(ADC); // ten times to get decimal, division by 4 because of degree ratio
}

{
// i wanna multiply by ten to get all digits eg. 23.1 deg => 231
}

void initTimer0()
{
// Prescaler = FCPU/64
TCCR0|=(1<<CS01);

//Enable Overflow Interrupt Enable
TIMSK|=(1<<TOIE0);

//Initialize Counter
TCNT0=0;
}

uint32_t res;
ISR(TIMER0_OVF_vect)
{
// 4 steps: 0-3 display, 4 conversion
if(display == NUM_DISPLAYS) {
PORTD = numbers[10]; // display off

if(adc_read_cycle_index%2 == 0) { // update digits only once per 8 cycles
res = 0;
for(int i = 0; i < NUM_OF_MEASUREMENTS; i++) {
}

res /= NUM_OF_MEASUREMENTS;
for(int j = 2; j >= 0; j--) {
// following if says to update digits not so often
// this is just temporary workaround to avoid annoying
// fast digits changes after decimal point
digits[j] = res%10;
}
res = res / 10;
}
}

} else {
}

display = 0;
} else {
PORTB &= 0b11111000;
PORTB |= (1<<display);
PORTD = numbers[digits[display]];
display++;
}
}

int main()
{

for(int i = 0; i < NUM_OF_MEASUREMENTS; i++) {
}

DDRD = 0xFF;
PORTD = 0;

DDRB |= 0b00000111;
PORTB |= 1;

initTimer0();

//Enable Global Interrupts
sei();

// I am aware I could do the conversion here
// but I have put it in the interrupt to avoid
// having display on when converting
while(1);

return 0;
}
```

Last Edited: Sat. Feb 3, 2018 - 04:50 AM

Increase wrote:
But my conversion somehow gets 21.X degrees (when I input 4.5 V into the circuit). Interetingly, when I tried to increase voltage to 5.5V, my conversion suddenly showed 19.X degrees.

???  What are 4.5V and 5.5V?  The Vcc level, which is nominally 5V?  And your LM35 output changes when Vcc level changes?  In other words, the voltage seen at ADC0, right on the pin, changes from 231mV to something else?  That is then a problem with your LM35 then, isn't it?

Anyway, remember that the 2.56V internal reference is only nominally that; it can be off quite a bit.  Have you measured it?

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.

I see no capacitor on the AREF pin.

 11 Internal 2.56V Voltage Reference with external capacitor at AREF 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.

readDegrees is called twice; once from main(), once from ISR .

???  What are 4.5V and 5.5V?  The Vcc level, which is nominally 5V?

Vcc yes

And your LM35 output changes when Vcc level changes?

No, my LM35 output is still the same, but the result of the conversion changes.

In other words, the voltage seen at ADC0, right on the pin, changes from 231mV to something else?

No.

Anyway, remember that the 2.56V internal reference is only nominally that; it can be off quite a bit.  Have you measured it?

Sorry, I am not sure what this means. i am quite new to all this. Can you direct me to some article about it? If I put voltmeter between AREF and ground I read approximately 2.56 (+-20mV)

dbrion0606 wrote:

readDegrees is called twice; once from main(), once from ISR .

And that is correct. When I start the program, it first setup adc, than fill in first 100 measurements to variable (this happens only once at the beginning) and only then it setup timer and interruption.

Last Edited: Fri. Feb 2, 2018 - 02:13 PM

You should not call any functions from interruptions (fuction call/return eat cycles). Onlining will hide this behavior.

@theusch said:

Anyway, remember that the 2.56V internal reference is only nominally that; it can be off quite a bit.  Have you measured it?

From the ATmega8 datasheet:

Changing the supply voltage from 4.5 to 5.5, a 22% change, is bound to change Aref.  You should have a 100nF cap between Aref and GND.

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

Make Xmega Great Again!

Greg_Muth wrote:
You should have a 100nF cap between Aref and GND.

Agreed.  And maybe causing some of OP's symptoms?

Greg_Muth wrote:
Changing the supply voltage from 4.5 to 5.5, a 22% change, is bound to change Aref.

I'd have to do some experimenting to comment on that.

Quite a bit going on in that "simple" program.  The root cause could be the lack of capacitor on AREF, and the ADC doesn't have enough ref to "sip from".  But all a bit puzzling IME with AVR8 ADCs over the years.  As '88 family became mainstream over a decade ago I haven't done '8 or '8A since, so that model might well have idiosyncrasies.

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.

In passing a couple of things:

```uint16_t ReadADC(uint8_t ch)
{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;

//Start Single conversion

//Wait for conversion to complete

//Clear ADIF by writing one to it

return(ADC); // ten times to get decimal, division by 4 because of degree ratio
}
```

is probably simpler/easier as:

```uint16_t ReadADC(uint8_t ch)
{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;

//Start Single conversion

//Wait for conversion to complete

return(ADC); // ten times to get decimal, division by 4 because of degree ratio
}
```

That waits on ADSC remaining at 1 during the conversion. Now you don't have to involve ADIF at all. But also:

```	return(ADC); // ten times to get decimal, division by 4 because of degree ratio
```

The comment on that line is mis-placed. It is actually a description of what's happening here:

`	return (ReadADC(0)*10)/4;`

which is where the *10 and /4 are actually being done.

When you ultimately split the ReadADC() stuff into a separate adc.c then it's important the comment is at the point of usage - not in the "library" code.

Greg_Muth wrote:

@theusch said:

Anyway, remember that the 2.56V internal reference is only nominally that; it can be off quite a bit.  Have you measured it?

From the ATmega8 datasheet:

Changing the supply voltage from 4.5 to 5.5, a 22% change, is bound to change Aref.  You should have a 100nF cap between Aref and GND.

I have added it, thanks, it seems to work now.

Thanks man.

The strange thing happens now (not sure if it was happening before though) I will send it in the next post.

Ok now it works.

Funny thing, it goes crazy when I "cuddle" the wires (sorry dont know the proper name for it :D) - by cuddle i mean i touch the wires, anything that causes them to move (not the ends though).

I guess that has something to do with induction or something?
Can I be sure that when I solder this circuit onto a board, it will not have this issue?
Look at the nest I created:

Thanks man, I have used your enhancement. And thanks for pointing out the misplaced comment ;)

As has been pointed out, in the analog world, things are not as absolute as one would like.   You must account for and provide for variances in your readings, such as offsets and slope differences.

The LM35 and its Fahrenheit twin LM34 do a pretty good job of reporting the temperature, but you must take into account an variances in your chosen reference in the ADC.

Add a 100nf cap to the AREF pin to ground, with the 2.56v internal ref.  In the commercial designs with that sensor, we placed a low pass filter (10k, 10uf) cap on the input of the ADC to filter any noise as we expected the temperature to change fairly slowly over time.

In the software, we provided for an offset for initial calibration to room temperature and depending on the applications needs, a way to adjust the slope of the temperature curve to keep the readings with in the required precision range.   Note: the slope is different for temperature rising from single calibration point, from that of falling from cal point.

Jim

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274

As the ush wrote, you should use 2*100 nf decoupling capacitors (I only see one)

When you solder, things are likely to be much better than broad beards.

dis you look at  (google searched "solderable breadboard" + my favorite HW seller -in Paris-)

Thanks to all for your tips, it works nicely now.

PS ill check that solderable breadboard, looks good..

Funny thing, it goes crazy when I "cuddle" the wires (sorry dont know the proper name for it :D) - by cuddle i mean i touch the wires, anything that causes them to move (not the ends though).

I guess that has something to do with induction or something?

You have 10 antennae (7 segments + 3 digits) transmitting EMI at the rate the display is multiplexed.  When you touch them you become a significantly larger antenna.  When laying the circuit out on a PCB, keep the analog and digital portions separate.  Use an L/C or R/C filter between the digital VCC and AVCC.  Power the LM35 from the filtered AVCC.

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

Make Xmega Great Again!

Greg_Muth wrote:

Funny thing, it goes crazy when I "cuddle" the wires (sorry dont know the proper name for it :D) - by cuddle i mean i touch the wires, anything that causes them to move (not the ends though).

I guess that has something to do with induction or something?

You have 10 antennae (7 segments + 3 digits) transmitting EMI at the rate the display is multiplexed.  When you touch them you become a significantly larger antenna.  When laying the circuit out on a PCB, keep the analog and digital portions separate.  Use an L/C or R/C filter between the digital VCC and AVCC.  Power the LM35 from the filtered AVCC.

Thank man, can you link some of the filters you would deem fit for it? Just to be sure :) btw I have updated my stuff on the bread boards just to see it gets better and it did indeed.

For an L/C filter, I use the unscientific method of grabbing what I have in my parts drawer, which happens to be a bunch of 10uH RF chokes.  I put one of those between VCC and AVCC, with a 100nF between AVCC and the adjacent GND pin.  I've never done the math to determine if these values are reasonable.

Greg Muth

Portland, OR, US

Xplained/Pro/Mini Boards mostly

Make Xmega Great Again!