Atmega8, ADC, two inputs

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

Hi,

 

I'm having a strange to me problem with properly dispalying ADC values on LCD.

I'm using single conversin ADC from two inputs one after another in a loop.

1. Potentiometr,

2. LD35 temp sensor

 

When dispalying temp sensor value on LCD I'm using a function conveting from int to ASCII (itoa function), I have to different approaches:

1.

int adc_value = (Return_adc()*2.5/10); // 'reurn_adc' returns value form adc converter, I'm doing scalling to Celcius

itoa(adc_value, 10, result);

LCD_WiteText (result); // here I get constantly changing values from 19 to 36 ??

 

2.

itoa((Return_adc()*2.5/10) , 10, result);

LCD_WiteText (result);  // here i get constant result of 24 (Celcius), it's ok

 

What is the difference here, I cant understand it.

 

 

 

 

 

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

I assume you are using the gcc compiler. I assume you are linking the floating point math library (because you have fp expressions). I assume the avr is running at 16MHz and you are using an a/d prescaler of 0x87. If any of these assumptions dont agree with your c source, post it.

 

Imagecraft compiler user

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

Thanks for reply.

 

I'm using: GCC Compiler (Atmel Studio 7), stdlib.h included, avr running at 1Mhz, prescaler for ADC is 8.

Including <float.h> doesn't help, I checked.

I'm getting stable ADC values on both varaints, it must be something wtih foating point cacluations...

 

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

I'd like to see the actual copy-paste source code. Preferably for the smallest complete program that demonstrates the symptoms.

 

Why invoke floating point for that calculation?  2.5/10 is 1/4.  So your transfer function is a simple /4, isn't it?

 

I want to see the actual code, because the snippet you posted is not correct--radix is the 3rd parameter for itoa().

 

action occurs.

char* itoa ( int  val,
    char *  s,
    int  radix 
  )    

Convert an integer to a string.

The function itoa() converts the integer value from val into an ASCII representation that will be stored under s. The caller is responsible for providing sufficient storage in s.

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

Iota() function. 

I just wrote it wrong in previous post. I'm using it as theusch described it, with char as a second attribute.

 

I just want to know why is solution 1 from 1st post wrong.

@theussch: i know it can be simpler written, I like it so I can understand it after long time, what is what and from where.

 

But here it comes pices of code (interesting from that point of view):

 

#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h> 
#include <avr/pgmspace.h> 
#include <stdlib.h> 
#include "HD44780.c" 

 

int main (void)
{

 

init_pheripherials();

init_adc();

 

 

int adc_value = (return_adc()*2.5/10); // 'reurn_adc' returns value form adc converter, I'm doing scalling to Celcius

itoa(adc_value, 10, result);

LCD_WiteText (result); // here I get constantly changing values from 19 to 36 ??

 

itoa((Return_adc()*2.5/10) , 10, result);

LCD_WiteText (result);  // here i get constant result of 24 (Celcius), it's ok

 

}

 

void init_adc(void)
{
    DDRC &= ~(1<<PC0); // potentiometer in
    DDRC &= ~(1<<PC1); // LD35 in
    
    ADCSRA = (1<<ADEN) 
                  | (1<<ADPS1) | (1<<ADPS0); // prescaler 8
                                        // single conversion
    ADMUX = (1<<REFS1) | (1<<REFS0); // internal 2,57 V
                                   
}

 

int return_adc (void)
{    
    ADMUX |= (1<<MUX0); //ADC1
    ADCSRA |= (1<<ADSC); // single conversion
    while ( !(ADCSRA & (1<<ADIF))); /
    ADCSRA &= ~(1<<ADIF); 
    
    return ADC;
}

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

Looks like you are using fp. There is a command to tell the linker to link in the fp stuff, but I'm not any good with this IDE yet. Someone tell him (and me) how to do it....

 

Imagecraft compiler user

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

An update on that case.

 

1. Including libraries 'Libm' and 'libprintf_flt' didn't help.

For those how wonder how to do it in Atmel Studio 7:

- Project options, Toolchain, Linker -> Libraries, here you have to type in libm.a and libprintf_flt.a (to display point values on LCD instead of question mark).

- aslo check: linker -> General, 'use vpintf library' should be marked as well.

 

As I said any difference is that I could dispaly correctly point values on LCD, temp is still fluctuating (when changing values on  potentiometer).

 

I made a test program, displaying pure ADC values and problem is still the same.

Please take a look on complate code for that.

The probelm with changing values from ADC (temperature) occures only when potentiometer gives high enough volage to other ADC input, when ADC form potentiometer is from 0 to about 300 there is no problem.

Increasing voltage from potentiometer gives more and more fluctuation on other ADC (for LM35) (With potentiometer giving 1024, ADC from LM35 showes from 88 to 107 changing constantly).

Am I using single Conversion ADC wrong??

 

#define F_CPU 1000000UL


#define Vref 2.69 // internal reference voltage, measured

#include <avr/io.h>

#include <stdlib.h> // itoa function

#include "HD44780.c" // LCD maintenance

void init_adc(void);

char result[4]; // text buffer for LCD

int main(void)

{

init_adc();

LCD_Initalize();




while (1)

{

// Potentiometer values

ADMUX &= ~(1<<MUX0); // MUX0 -> 0, input ADC0 activ, potentiometer

ADCSRA |= (1<<ADSC); // single ADC activated

while ( !(ADCSRA & (1<<ADIF))); // waiing for ADIF -> 1, ADC finished

ADCSRA &= ~(1<<ADIF); // ADIF -> 0




itoa(ADC, result, 10); // INT to ASCII, decimal

LCD_GoTo(0, 0);

LCD_WriteText(" "); // clean old value

LCD_GoTo(0, 0);

LCD_WriteText(result);




_delay_ms(500);




// Temperature values

ADMUX |= (1<<MUX0); // MUX0 -> 1, input ADC1 activ, LM25

ADCSRA |= (1<<ADSC); // single ADC activated

while ( !(ADCSRA & (1<<ADIF))); // waiing for ADIF -> 1, ADC finished

ADCSRA &= ~(1<<ADIF); // ADIF -> 0




itoa(ADC, result, 10); // INT to ASCII, decimal

LCD_GoTo(0, 1);

LCD_WriteText(" "); // clean old value

LCD_GoTo(0, 1);

LCD_WriteText(result);

}

}

void init_adc(void)

{

DDRC &= ~(1<<PC0); // input for ADC, potentiometer

DDRC &= ~(1<<PC1); // input for ADC, LM35 temperature sensor (10mV = 1 Celsius)




ADCSRA = (1<<ADEN) //ADC activation

| (1<<ADPS1) | (1<<ADPS0); // prescaler = 8




ADMUX = (1<<REFS1) | (1<<REFS0); // refernece voltage internal 2,69 (measured)

}

EDIT: code tags added

 

Last Edited: Thu. Nov 26, 2015 - 07:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I bet you are using a wall wart transformer for a power supply, and the ripple on the supply is getting into the analog readings. Is the value of the pot 10K? Do you have a cap on the aref pin to eat the ripple on vref? Hope these suggestions make it better.

 

Imagecraft compiler user

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

There is problem with charging internal sample&hold capacitor.

When conversion starts imediately on new selected channel, s&h cap still stores voltage from prev chan.

Observe a little delay between mux switching and conversion start.
 

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

I don't know if you have seen in the middle of my code:

_delay_ms(500);

 

I've been thinking that too, that maybe it changes to fast.

0,5 sec. is not a problem for sure.

 

@ bobgardner : right now I'm using my USB as a power source, I guess it's guite stable.

But I will check with oscilloscope whats going on there.

Last Edited: Wed. Nov 25, 2015 - 08:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// Temperature values
ADMUX |= (1<<MUX0); // MUX0 -> 1, input ADC1 activ, LM25
ADCSRA |= (1<<ADSC); // single ADC activated
while ( !(ADCSRA & (1<<ADIF))); // waiing for ADIF -> 1, ADC finished
ADCSRA &= ~(1<<ADIF); // ADIF -> 0

There is no delay between ADMUX set and ADSC set. It have to be like:

;... some code before 
  andi a,0x0F
  ori  a,(1<<REFS0)
  out  admux,a      ;change muxer
  NOP
  NOP	            ;channel stabilise
  NOP
  NOP
  sbi  adcsra,ADSC  ;start conversion

 

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

Please learn how to use code tags. You can always edit your old messages, but please add a note to say so.

Your ADC code is wrong. It is far easier to check for ADSC to clear instead of ADIF to set.
Your code does not reset the ADIF bit. You should write 1 to clear an interrupt flag. This is difficult for newcomers to understand. Hence my advice to check ADSC.

David.

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

I dont think you need any delay. When you start conversion, it uses the 1st 1.5 clks for s&h. Then 10 clks for SAR convert. I read that in the DS. Must be true.

 

Imagecraft compiler user

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

Yes, you do need a little delay after you change the Multiplexer.   A few us should be fine.    The SAH capacitor has to charge / discharge when it is switched to a different channel.

 

The effect of the channel change charge/discharge will be fairly trivial.    OTOH,  testing ADIF when it has never cleared will mean you are not even getting the most significant bit from your new conversion.

 

David.

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

I had same problem, level of channel was "shadowed" into next channel.

Little delay before start helps (as shown, 4xNOP @ 1MHz fix it)

Maybee there is a parasitic capacitance at mux circuit output.
 

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

I dont think you need any delay. When you start conversion, it uses the 1st 1.5 clks for s&h. Then 10 clks for SAR convert. I read that in the DS. Must be true.

1.5 clks for the sample and hold may not be enough.  At 125 kHz, that's only 12 us to charge S/H cap.  Typical S/H cap value is 14 pF, and resistance of the input path is between 1K and 100K (all from datasheet).

 

With a low-impedance source, say 10K, the TC will be from 1.1E4 x 14E-12 = 154 ns, to 1.1E5 x 14E-12 = 1.54 us.

 

With a higher-impedance source, say 100K, the TC will be from 1.01E5 x 14E-12 = 1.414 us, to 2.0E5 x 14E-12 = 2.8 us.

 

For a 10-bit ADC, it takes 6.93 TC to get to within 1 LSB (see here).

 

So with a low-impedance source, the S/H cap needs between 1.07 us and 10.7 us to charge up to within 1 LSB of the signal.  In these circumstances, a 1.5 clk S/H period should be plenty.

 

With a higher-impedance source, the S/H cap needs longer.  For a 100 K source, the S/H cap needs between 9.8 us and 19.4 us.  We start to see why it is important to have a strong, low-impedance drive for the ADC.

 

"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: Wed. Nov 25, 2015 - 11:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But the s&h cap doesnt charge until the conversion is started Thats when it samples. You can select channel 3 and wait a couple seconds and start the conversion, and it converts what it sampled. So I restate, delay is not needed, and doesnt do anything.

 

Imagecraft compiler user

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

But the s&h cap doesnt charge until the conversion is started.

Actually, that's not true.  The datasheet isn't clear, mind you, stating only that the S/H occurs at 1.5 clk after the start of a conversion:

 

 

This is on its face unclear at best, since 'sample' and 'hold' are two different things.  They don't happen at the same instant.  First you sample, then you hold.

 

In reality, the S/H cap is connected to the selected MUX as long as the ADEN is set, effecting a continuous sampling by the cap of the signal on that pin.  Once a conversion is started, that sampling is continued for an additional 1.5 ADC clock cycles, and then the cap is cut off from the MUX.  This is the 'hold'.  The remaining ADC clock cycles are used by the SAR algorithm to populate ADCH:L.  At the end of the conversion, the selected channel is once again routed to the cap to resume sampling.

 

Changing the MUX disconnects the cap from the previous channel and re-routes the signal from the new channel to the cap.  If the MUX is changed immediately preceding the start of a new conversion, the cap will have only 1.5 ADC clock cycles to charge to the new channel's voltage.  If the voltage differential between the previous and new channels is high, and if the new channel's source impedance is high, the cap may not have enough time to settle to the new channels' voltage before the 'hold'.

"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

But the 10 bit conversion takes 10 clks, so the s&h is the 1.5 clks right after the start conversion? Adds up to 13 clks just like the ds says.

Imagecraft compiler user

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

Well, there's 1.5 clocks not 'accounted' for, but the precise inner workings of the ADC module aren't published.  Call it 'housekeeping'.  Figure 0.5 clock between the 'hold' and the MSB, and 1 clock after the LSB to latch the SAR to ADCH:L.

"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

Some smart sparky could calc the s&h time by using a 100k pot as the source resistance and calculating the RC time const based on the voltage droop as the source R is increased. It makes my head hurt.

Imagecraft compiler user

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

Thanks everyone for help.

I managed to stabilize readings by using 20us delay. Actually it starts to stabilize from 12us. I used 20us to be sure.

I'm using it only for low voltage input (LM35 temp sensor).

I changed also use of ADIF in code. ADIF is set to 1 when ADC is finished. Is't more Natural to set it back to 0, to reset it... I'm not sure here.

// Temperature values
  ADMUX |= (1<<MUX0); //  MUX0 -> 1, input ADC1 activ, LM25
  _delay_us(20); //delay for voltage stabilization after changing the input
  ADCSRA |= (1<<ADSC); // single ADC activated
  while ( !(ADCSRA & (1<<ADIF))); // waiting for ADIF -> 1, ADC finished, flag is set
  ADCSRA |= (1<<ADIF); // ADIF flag is cleared by writing 1 to it

I'm getting some annoing warnings (I have stdlib.h included).

 

implicit declaration of dunction sprintf [-Wimplicit-function-declaration]

and

incompatibile implicit declaration of built-in-function sprintf

 

What can be the problem.

 

 

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

You MUST include < stdio.h> if you use any of the printf() family.

 

Your ADIF use should work ok.   I still reckon that ADSC is easier (and shorter).

 

David.

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

Did one of these messages mention which avr? ref0,1 in a mega328 selects 1.1 volt ref. You mention 2.69V, so something isn't right yet. I can see a problem with your program if you change the avr to a mega 1280 with 16 a/d channels. You have the whole read a/d routine copied inline for each read. Back in '57 John W. Forrester at IBM invented the subroutine, so you could save memory by reusing the same code over and over with different parameters.

 

There was a discussion of whether a delay was needed somewhere to get a good a/d reading. Since 2001 I have spent 12 months a year writing avr programs, most of which use the a/d, and I haven't found any need for a delay before, during, or after a/d readings. I'd like to get a definitive test program published here for several avrs and compilers that proves that no delays are needed without starting a cussing tirade. Its hard to prove a negative. If you show me your working a/d read subroutine that works, and it has 20usecs of delay in it, and I show you the same subroutine without the delay, and tell you it works, we'll have to go out back in the mud pit and wrassle to see who wins. I'll start. The datasheet does not mention inserting a delay between multiplexer channel selection and start of conversion. It DOES mention a recommendation for a/d input source impedance. At this time, I would like for you to state for the record what is the impedance of your source. If it is >10K, then the delay might have a good effect. A cap to gnd on the input would eliminate the need for the delay. Everyone likes their program to run faster. Especially if using a $2 computer. Its a Programmer's Badge Of Excellence.

 

Imagecraft compiler user

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

I don't know why you could not just do it yourself.

#define ADCSRA_startval (1<<ADEN)|(6<<ADPS0)|(1<<ADSC) //16MHz/64 = 125kHz
#define ADMUX_val       (1<<REFS0)|(1<<ADLAR)          //AVCC, 8-bit

void setup()
{
    Serial.begin(9600);
    Serial.println("Connect 10k from A0 to D7");
    Serial.println("Connect 10k from A1 to D6");
    Serial.println("Connect 100k from A2 to D5");
    Serial.println("Connect 100k from A3 to D4");
    Serial.println("Connect 1M from A4 to D3");
    Serial.println("Connect 1M from A5 to D2");
    Serial.println("RC = 14us for 1M and 14pF SAH");
    Serial.println("the voltage should be 3.15V (63%) after 14us");
    Serial.println("1.5 ADC cycles is 12us");
    Serial.println("With 8-bit ADC,  you are allowed to run > 125kHz");
    Serial.println();
    for (int i = 2; i < 8; i++) {
        pinMode(i, OUTPUT);
        if (i & 1) digitalWrite(i, HIGH);
        else digitalWrite(i, LOW);
    }
}

const char *TC[] = {"0.14us", "1.4us", "14us"};
void ADC_channel_delay(int channel, int us)
{
    float result;
    int count = (F_CPU / 1000000 / 4) * us;
    if (us) {
        ADMUX = ADMUX_val | (channel << MUX0); //channel #N
        _delay_loop_2(count);
    }
    else ADMUX = ADMUX_val | (channel << MUX0); //channel #1
    ADCSRA = ADCSRA_startval;
    while (ADCSRA & (1 << ADSC)) ; //wait for completion
    result = 5.0 * ADCH / 256;
    Serial.print("With " + String(us) + "us delay ADC" + String(channel));
    Serial.println(" reads " + String(result) + "V (" + TC[channel / 2] + ")");

}
void loop()
{
    int channel, us;
    for (channel = 0; channel < 6; channel++)
        ADC_channel_delay(channel, 0);
    for (channel = 0; channel < 6; channel++)
        ADC_channel_delay(channel, 1);
    for (channel = 4; channel < 6; channel++)
        ADC_channel_delay(channel, 20);
    for (channel = 4; channel < 6; channel++)
        ADC_channel_delay(channel, 50);
    Serial.println();
    delay(10000);
}

I ran this on a Uno clone.   Yes,  there will be 5-6 pF of stray capacitance from the pcb traces.

 

David.

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

Cool! What's the output look like, typically? Does this confirm or deny the "delay needed between mux select and start of conversion" superstition? Great forum.

 

Imagecraft compiler user

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

Bob, 

 

I could not have made simpler hardware or software.    I suggest that you build it yourself.    One Uno + 6 resistors.

 

It illustrates the importance of < 10k source impedance.   And the effect of a small delay after changing the MUX.

 

It also shows how handy an Arduino is.

 

David.

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

@bobgardner: I'm taking discussion not to deny what you say, I have too little experiance with C-Programming and AVR (2 months...). But i hope to learn something here not just to solve my problems. I know what is subroutine, and how to use it :-)

 

I'm using: Atmega8, 1Mhz internal RC.

For ADC puprose I'm using internal voltage reference, which is in theory 2,56V (form 2.3 to 2.9).

I measured it indirectly by checking voltage at input,  when i got full 1023 on LCD. That gave me 2,69V.

 

Output impedance: 1k pot + 1k resistor as one input for ADC.

Other input is LM35 temp. sensor with low impedance output 0,1 Ohm.

I'm Reading in DS that ADC is optimized for analog signals with an output impedance of 10k or less. I can't see a pronlem in my case.

Although adding 20us delay helps... without it doesn't work.

 

 

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

Ah-ha.   The mega8/16/32/... have a 2.56V reference.    Modern AVRs have a 1.1V reference.

 

In fact the Maths is really easy with the 2.56V reference.

I doubt if the LM35 has a 0R1 output impedance.  

However,   anything less than 10k should not need anything more than 2us for the MUX to settle.

 

If you only use one channel of the ADC,  the MUX never changes at all.

If you are running at 1MHz,   you have at least 1us for every instruction.

 

David.

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

Lahotron wrote:
Thanks everyone for help.

I managed to stabilize readings by using 20us delay. Actually it starts to stabilize from 12us. I used 20us to be sure.

In your case, this only helped because you weren't properly waiting for the conversion to complete.

 

As David has mentioned, you were not clearing ADIF correctly.  As such, only the very first conversion would be correct.  Since ADIF was never cleared, subsequent conversions would fall through this line:

    while ( !(ADCSRA & (1<<ADIF)));

... and you would then pick up the value in ADC, which was still the value of the >>previous<< conversion, with:

    itoa(ADC, result, 10);

Once the ongoing conversion was complete, ADC would be updated with that new value, but this would bear little resemblance to what you were expecting to see.  While your code was commanding changes to the MUX and new conversions, the ADC peripheral would quite happily ignore all of those requests until it was good and ready i.e. the ongoing conversion was complete.  Whichever state ADMUX was in at that point would dictate what channel would actually be converted, but again this would bear little resemblance to what your code was expecting.

 

By inserting a delay, you were effectively waiting for a conversion to complete, even though you were still improperly clearing ADIF.

 

So again, in your case, >>either<< the delay >>or<< correctly clearing ADIF would have solved your problem.  Both are not required.

 

However, the best (non-interrupt-based) way to wait for an ongoing conversion is:

    while (ADCSRA & (1<<ADSC));

That's it.  When that falls through, ADC will contain the new value.  No need to clear any flags.

 

As detailed in posts #16, #18, and #20, with low-impedance signals there is absolutely no need for any programmatic delay between changing the MUX and starting a 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

Does this confirm or deny the "delay needed between mux select and start of conversion" superstition?

Not superstition.  Just math.  The math is easy to do, and easy to experimentally verify, as David has done.

 

High impedance signals require more time to sample (i.e. for the S/H cap to charge).  While (as you suggest) an external cap at the ADC input from which the ADC can 'sip' will help in many circumstances, it is not a panacea.  Much depends on the nature of the signal and the needs of the app.

 

A fast-changing, high-impedance signal will not benefit from such a cap.  In this case there isn't much to be done except buffering the signal with a unity gain op amp.

 

In the case of a slowly-changing, high-impedance signal, the op-amp might be omitted in favour of just the external 'sippy' cap, but whether or not this is sufficient will depend upon how often the MUX is changed to other channels, and what overall sample rate is required by the app.

 

If the ADC round-robins more than one channel at too high a rate, the sippy cap may be depleted faster than the high-impedance signal can charge it up.

 

If there is only one channel, and it is slowly-changing and high impedance, neither an op amp nor a sippy cap may be necessary.  If the signal is very stable, an extended sample time via a delay also may be unnecessary.

 

As with all things, 'it depends'.  No superstition.  Just proper and thorough analysis and design.

"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

I have tried it without delay and with a propper use of ADIF.

But without delay its not stable, normally i get 55 from ADC, without dealy is from 42 to 58 constantly changing.

It's starts to change when I pass apx. 1,2 V on the other input with pot.

take a look at connctions. maybe is something in there.

EDIT: changed Picture format from jpeg to png

Last Edited: Sun. Nov 29, 2015 - 07:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please don't use JPG for screenshots.  They are hard to read.  Use PNG.

 

I have tried it without delay and with a propper use of ADIF.

But without delay its not stable, normally i get 55 from ADC, without dealy is from 42 to 58 constantly changing.

Show your code for this proper use of ADIF without delay.

"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

Post your actual code. The schematic looks fine.

At 1Mhz you should not need delays anywhere. Especially since your pot and your LM35 have low source impedance.

David.

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

Mr. Prentice and Mr. Morin: We are almost there. I think Mr. Prentice needs to post the rest of his program so it will compile, and I suggest adding a description at the top describing the test parameters (vcc, clock rate, a/d clock rate, and the expected results), and I'd like it to do a full 10 bit convert, because we are talking about a/d accuracy, and I'd like to see your results. NOW anyone that thinks you're bonkers must compile your example and compare his results to yours. This is the Scientific Method. Publish your experiment and results for others to confirm.

 

I can only assume the vcc is 5V, since he mentions USB power (full of ripple). I don't like the restricted voltage range on the pot. If the avr and aref are 5V, test input should go to full scale also. 

Imagecraft compiler user

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

For testing purpose I'm using code from post nr 7, changed ADIF usage  according to post 22.

That piece of code shows exactly sypthomes described before.

As you can se i'm using the other way of checking ADC as well (while (ADCSRA & (1<<ADSC));),

Piece of code below includes dealy of 20us and works as I expect it to.

//Atmega8
//1Mhz clock

#define F_CPU 1000000UL
#define Vref 2.69 // internal reference voltage, measured
#include <avr/io.h>
#include <stdlib.h> // itoa function
#include <stdio.h> // sprintf
#include "HD44780.c" // LCD maintenance

void init_adc(void);

char result[4]; // text buffer for LCD

int main(void)
{
 init_adc();
 LCD_Initalize();
 
    while (1) 
    {
  // Potentiometer values
  ADMUX &= ~(1<<MUX0); //  MUX0 -> 0, input ADC0 activ, potentiometer
  ADCSRA |= (1<<ADSC); // single ADC activated
  while ( !(ADCSRA & (1<<ADIF))); // waiting for ADIF -> 1, ADC finished, flag is set 
  ADCSRA |= (1<<ADIF); // ADIF flag is cleared by writing 1 to it
  
  itoa(ADC, result, 10); // INT to ASCII, decimal
  LCD_GoTo(0, 0);
  LCD_WriteText("   "); // clean old value
  LCD_GoTo(0, 0);
  LCD_WriteText(result);
  
  _delay_ms(1000);
  
  // Temperature values
  ADMUX |= (1<<MUX0); //  MUX0 -> 1, input ADC1 activ, LM25
  _delay_us(20); //delay for voltage stabilization after changing the input
  ADCSRA |= (1<<ADSC); // single ADC activated
  while ( (ADCSRA & (1<<ADSC))); // waiting for ADSC -> 0, ADC finished, no need for ADIF, (this is another way to check ADC converison)
  //while ( !(ADCSRA & (1<<ADIF))); // waiting for ADIF -> 0, ADC finished, flag is set
  //ADCSRA |= (1<<ADIF); // ADIF flag is cleared by writing 1 to it
  
  itoa(ADC, result, 10); // INT to ASCII, decimal
  LCD_GoTo(0, 1);
  LCD_WriteText("   "); // clean old value
  LCD_GoTo(0, 1);
  LCD_WriteText(result);
    }
}

void init_adc(void)
{
 DDRC &= ~(1<<PC0); // input for ADC, potentiometer
 DDRC &= ~(1<<PC1); // input for ADC, LM35 temperature sensor (10mV = 1 Celsius)
 
 ADCSRA = (1<<ADEN) //ADC activation
     | (1<<ADPS1) | (1<<ADPS0); // prescaler = 8
     
 ADMUX |= ((1<<REFS0) | (1<<REFS0)); // refernece voltage internal 2,69 (measured)
}

 

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

You only changed one of the a/d routines from adif to adsc. This is the Moment Of Learning. If you had one subroutine to read an a/d converter channel, you would be done now with no mistake. When you read the aref, is the program running, so you have selected Internal Ref, and it is connected to the Aref pin? (2.69 is 5% higher than 2.56, so its within spec).

 

Imagecraft compiler user

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

 

char result[4]; // text buffer for LCD

This is too small to take a full-scale reading >= 1000.  You need an extra byte for the terminating null.  To be safe, I would make it long enough to accomodate any int, since that's what itoa() will take:

char result[8]; // text buffer for LCD

 

 

 ADMUX |= ((1<<REFS0) | (1<<REFS0)); // refernece voltage internal 2,69 (measured)

Nope.  This selects AVcc as the reference.

 

 

 

You don't have a delay after switching the MUX to the pot, but you >>do<< still hav a delay after switching the MUX to the LM35:

  _delay_us(20); //delay for voltage stabilization after changing the input

Is this deliberate, or an oversight?

 

Have you found that a delay 'helps' for each source?  Only one source?  Which one?

 

In any event this won't compile at all because you haven't included <util/delay.h>.  This, combined with the fact that your reference is definitely not the 2.56V internal reference, makes me suspicious that you have not copy/pasted the real, actual, bona fide code you are using for testing.  Please only copy/paste source code in its entirety, don't edit it afterwords to make it resemble what you think you're working with.  More often than not such edits either obscure the problem we're trying to help you find, or they introduce new unrelated issues.  No-one likes a wild goose chase.

"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

Yes I used different ways of checking ADC, but according to previous comments, both were ok.

But I get Your point, he it comes code with subroutine for ADC raad.

I don't have any voltage on Aref, is just capacitor 100n to 0V.

 

//Atmega8
//1Mhz clock

#define F_CPU 1000000UL
#define Vref 2.69 // internal reference voltage, measured
#include <avr/io.h>
#include <stdlib.h> // itoa function
#include <stdio.h> // sprintf
#include "HD44780.c" // LCD maintenance

void init_adc(void);
int read_ADC (void);

char result[4]; // text buffer for LCD

int main(void)
{
 init_adc();
 LCD_Initalize();
 
    while (1)
    {
  // Potentiometer values
  ADMUX &= ~(1<<MUX0); //  MUX0 -> 0, input ADC0 activ, potentiometer
  itoa(read_ADC(), result, 10); // INT to ASCII, decimal
  LCD_GoTo(0, 0);
  LCD_WriteText("   "); // clean old value
  LCD_GoTo(0, 0);
  LCD_WriteText(result);
  
  _delay_ms(1000);
  
  // Temperature values
  ADMUX |= (1<<MUX0); //  MUX0 -> 1, input ADC1 activ, LM25
  itoa(read_ADC(), result, 10); // INT to ASCII, decimal
  LCD_GoTo(0, 1);
  LCD_WriteText("   "); // clean old value
  LCD_GoTo(0, 1);
  LCD_WriteText(result);
    }
}

void init_adc(void)
{
 DDRC &= ~(1<<PC0); // input for ADC, potentiometer
 DDRC &= ~(1<<PC1); // input for ADC, LM35 temperature sensor (10mV = 1 Celsius)
 
 ADCSRA = (1<<ADEN) //ADC activation
     | (1<<ADPS1) | (1<<ADPS0); // prescaler = 8
    
 ADMUX |= ((1<<REFS1) | (1<<REFS0)); // refernece voltage internal 2,69 (measured)
}

int read_ADC (void)
{
 _delay_us(20); //delay for voltage stabilization after changing the input
 ADCSRA |= (1<<ADSC); // single ADC activated
 while ( (ADCSRA & (1<<ADSC))); // waiting for ADSC -> 0, ADC finished,
 return ADC;
}

EDIT: ADMUX |= ((1<<REFS1) | (1<<REFS0)); // refernece voltage internal 2,69 (measured)

 

Last Edited: Sun. Nov 29, 2015 - 09:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is still wrong:

 ADMUX |= ((1<<REFS0) | (1<<REFS0)); // refernece voltage internal 2,69 (measured)

 

 

"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 guys reply way too fast :-)

 

Yes, it was set to Avcc, I forgot to change it back after some tests. it's is ok now, I edidted previous code.

Yes, I still have a dealy, it doesn't work properly without delay.

Delay helps for LM35, i don't need it for 1k pot to get proper readings.

Delay.h is included in LCD Library.

 

 

 

 

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

Then there is something wrong with your LM35, or your AVR, or your circuit is not as you have shown in the schematic.

 

Replace the LM35 with a second identical resistor/pot arrangement.  Remove the delay.  Now what happens?

 

Also, show the build output.

"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

In the init adc routine, there is a 2 line statement with a //comment between the 2 lines. If this messes up the adc init, we have found the problem.

 

Imagecraft compiler user

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

bobgardner wrote:
If this messes up the adc init, we have found the problem.
It does not:

void init_adc(void) {
  DDRC &= ~(1<<PC0);                              // input for ADC, potentiometer
  38:	a0 98       	cbi	0x14, 0	; 20
  DDRC &= ~(1<<PC1);                              // input for ADC, LM35 temperature sensor (10mV = 1 Celsius)
  3a:	a1 98       	cbi	0x14, 1	; 20

  ADCSRA = (1<<ADEN)                              //ADC activation
  3c:	83 e8       	ldi	r24, 0x83	; 131
  3e:	86 b9       	out	0x06, r24	; 6
    | (1<<ADPS1) | (1<<ADPS0);                    // prescaler = 8

  ADMUX |= ((1<<REFS1) | (1<<REFS0));             // refernece voltage internal 2,69 (measured)
  40:	87 b1       	in	r24, 0x07	; 7
  42:	80 6c       	ori	r24, 0xC0	; 192
  44:	87 b9       	out	0x07, r24	; 7
  46:	08 95       	ret

 

"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

Shucks. I was just hoping. I guess after you burn and hit reset, the program is running and the a/d is initialized, and the aref pin should have the internal aref on it. Is this how the aref was measured? (I cant think of any other way actually). Sorry for asking the question twice. Sometimes a message with 2 questions only gets one answered.

 

 

Imagecraft compiler user

Last Edited: Mon. Nov 30, 2015 - 01:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You said delay.h is included in the lcd library, but isnt that a separate compilation? I dont see how you can call delayus without an error if you havent included delay.h in the current file being compiled?

 

Imagecraft compiler user

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

If lcd.h includes delay.h then there is no problem.

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

Because the addr of delayus is fixed in the link I guess. Its just an extern at compile time. (I write a lot of one file programs...)

Imagecraft compiler user

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

Nothing to do with the linker or address of the function. #include just pastes the file in. So if the main file includes lcd.h that includes delay.h then the main file has the prototypes for the delay functions.

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

I dont see any include of lcd.h in the main program. You just hoping its in hd44780.c, or should we look at it?

 

Imagecraft compiler user

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

Bob, the confusion might be that <util/delay.h> is a standalone header, with no delay.c to compile anywhere.  All you need to do is include the header.  Both _delay_ms() and _delay_us() are static inline functions which boil down to wrappers for __builtin_avr_delay_cycles() with a bit of compile-time math to convert (constant) ms/us into cycles.

 

Notice how the OP is #include-ing HD44780.c, and it almost certainly #include-s <util/delay.h> to handle all of the timing-sensitive sequences.

#include "HD44780.c" // LCD maintenance

While this is of course a bad idea, I doubt it is in any way responsible for the ADC-related timing issues.

"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: Tue. Dec 1, 2015 - 02:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anybody else think the problem might be in the heretofore unseen HD44780.c file? Can Mr. Lahotron With The TRS80 post the file for all to gawk at?

 

Imagecraft compiler user

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

I did a test using just another 1k pot instead and the programme works fine without delay... sic.

As some of you mentioned before, there is probabely sth wrong with my LM35.

I recall connecting it wrong way (out to Vcc) at a very first time, rookie mistake with looking at data sheet where pins where 'up-side-down'.

After that I got right amounts of voltage out of it so I thought all is fine, but maybe something happend with output impedance?

Is there any way of measuring output impedance in such a device? I know I can measure voltage without and with load and calculate impedance, but what will be the load in that case?

Anyway I'm glad to solve the problem, I've learned a lot by all that case...

 

EDIT: Yes, there is <util/delay.h> in header file.

Last Edited: Wed. Dec 2, 2015 - 07:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

Lahotron wrote:
I know I can measure voltage without and with load and calculate impedance
That's for AC signals.  Your LM35 will be outputting more-or-less a steady voltage for a given temperature.

 

You could try this:

   VCC
    |
    |
----------+
          |    R
     LM35 +---\/\/\----+------+-------> SCOPE/AVR
          |            |      |
----+-----+           --- C   + |
    |                 ---       |SW
    |                  |      + |
    |                  |      |
   GND                GND    GND
  1. With the switch open, measure the voltage at the switch with a 'scope or an AVR.
  2. Press the switch to discharge the cap.
  3. Release the switch, and measure how long it takes for the voltage at the switch to reach 63% of the voltage you measured in step 1.  This is the TC of the circuit.

 

To calculate the output impedance Z of the LM35:

   TC = (Z + R) * C
    Z = (TC/C) - R

 

So for an R of 5K6 560R, and a C of 100 uF, you can calculate Z:

    Z = (TC/100E-6) - 560

    Z = 10,000 * TC - 560

 

For the expected Z of 0.1R, you'd expect to see a TC of:

    Z = 10000 * TC - 560

   TC = (Z + 560) / 10000

      = (560 + 0.1) / 10000

      = 560.1 / 10000

      = 560.1 / 10000

      = 56.01 ms

 

If the LM35 is fried and has an impedance of 100K:

   TC = (Z + 560) / 10000

      = (100000 + 560) / 10000

      = 100560 / 10000

      = 10.056 seconds

 

You can of course use different values for R and C as you see fit.

 

"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: Fri. Dec 4, 2015 - 12:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for very good explanation. I appriciate it.

I did your test with same resistor and capacitor values.

Using my Atmega and ADC as a voltage meter.

I'm using interrupts with Timer0 overflow.

Below part of code responsible for interrupts from timer0.

I'm getting 6 interrupts into buffer, means apx. 6 ms., (did a test with 470uF capacitor and got 12 ms).

How can I easily calculate what is delay caused by instuctions done in interrupt mode?

Anyway output impedance of LM35 looks ok to me....

void init_timer(void)
{
 TCCR0 |= ((1<<CS02) | (1<<CS00) ); // timer0 activated, prescaler 1024
 TIMSK |= (1<<TOIE0); // timer overflow activates interrupt
 sei(); //interrupts activated 
}

//1.000.000 Mhz / 1024 = 976 Hz -> 1/976 = 1,024ms * 1 = 1,024 ms
ISR (TIMER0_OVF_vect) 
{ 
 TCNT0 = 255; // start value for timer,
 if (ADC==0)
  buffer = 0;
 else if (ADC < 65) // apx. 100 is a value with room temperature
  buffer+=1;
}

 

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

It's not clear what's happening with that code fragment.  Post the whole test program.

"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: Thu. Dec 3, 2015 - 10:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you're seeing >>much<< longer overflow times than you expect.  Again, it's not clear since you haven't posted all of the test code, but if TIMER0 is running in normal mode, the overflow flag TOV0 is set when TCNT0 = 255:

 

 

This means that when the ISR executes, TCNT0 already has a value of 255.  The ISR then write that value to it again.  Another overflow won't happen for 256 ticks of the timer.  At a prescaler of 1024 and 1 MHz, that's 262 ms.  If you're seeing 6 overflows, that's 1.57 seconds.

 

Assuming R = 560 and C = 100 uF:

Z = 10,000 * TC - 560

  = 10,000 * 1.57 - 560

  = 15,729 - 560

  = 15,088

 

If with C = 470 you see 12 overflows, that's 3.15 seconds:

Z = (3.145728/470E-6) - 560

Z = 6,693 - 560

  = 6,133

 

Electrolytic capacitors can be off by quite a bit.  I would try lower-value ceramic caps, and change your timer prescaler to accommodate the new value.

 

However, I think it's clear that the LM35 has a much higher impedance than it should.  Get a new one and see what happens.

 

"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: Thu. Dec 3, 2015 - 10:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, I just noticed a typo in my post #54.  I'd said R = 5K6, but then I calculated with R = 560R.  Which did you use?
 

"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

I used 560 Ohm resistor.

I'm displaying the value of ADC on the LCD as well, and I can see it goes up quickly (<1s).

I will do some scope tests next week, and present results as well as whole code.

 

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

Hi again.

There is a hole code I used to check TC of circuit, 100uF capacitor and 560 (measured 544) Ohm resistor.

I'm getting 47 overflows from timer1, set to 1,024ms by overflow.

That gives us little bit over 48ms.

Using formula given by joeymorin in post 54 will give some strange values...

 Z = (TC/100E-6) - 544

 Z = 10,000 * TC - 544

 Z = 10,000 * 0,048 - 544 = 480 - 544 = - 64 (???)

I did some tests with Scope.

The resullt was exactly the same.

Scope screen below shows charging up og capacitor by LM35.

Given that max. voltage is 282mV, TC of the circuit is 48ms.

 

 

In my previous post I presented result og 6ms (post nr 55).

As I found out there was something wrong when using _delay_ms() script.

I used it to avoid blinking of LCD values on LCD screen.

In code below I commented that delay.

 

/*
 Atmega8
 1,000,000 Mhz
 */ 

#define F_CPU 1000000UL
#define Vref 2.69   		// internal reference voltage, measured
#include <avr/io.h>
#include <stdlib.h>   		// itoa
#include <stdio.h>   		// sprintf
#include <avr/interrupt.h>
#include "HD44780.c"  		// LCD maintenance

void init_adc(void);
int read_ADC (void);
void init_timer (void);

char result[4];      		// buffer for LCD, ADC values
unsigned int buffer = 0;   	// buffer for LCD, number of overflows 

int main(void)
{
 DDRB |= ((1<<PB3) | (1<<PB4));  // PIN3 and PIN4 as outputs
 init_adc();
 LCD_Initalize();
 init_timer();
  
  while (1)
  {
  //_delay_ms(100);
  ADMUX |= (1<<MUX0);    	// MUX0 -> 1, input ADC1 activ, LM25
  itoa(read_ADC(), result, 10); // INT to ASCII, decimal
  LCD_GoTo(0, 1);
  LCD_WriteText("   ");   	// clean old value
  LCD_GoTo(0, 1);
  LCD_WriteText(result);
  
  itoa(buffer, result, 10);  
  LCD_GoTo(7, 0);
  LCD_WriteText("   ");   
  LCD_GoTo(7, 0);
  LCD_WriteText(result);
  }
}

void init_adc(void)
{
 DDRC &= ~(1<<PC1);      		// input for ADC, LM35 temperature sensor (10mV = 1 Celsius)
 
 ADCSRA = (1<<ADEN)      		// ADC enabled
     | (1<<ADPS1) | (1<<ADPS0);         // prescaler = 8
    
 ADMUX |= ((1<<REFS1) | (1<<REFS0));    // internal reference voltage 2,69V (measured)
}

int read_ADC (void)
{
 ADCSRA |= (1<<ADSC);     		// single ADC activated
 while ( (ADCSRA & (1<<ADSC)));   	// waiting for ADSC -> 0, ADC finished,
 return ADC;
}

void init_timer(void)
{
 TCCR1B |= ( (1<<CS11) | (1<<CS10) );   // timer0 activated, prescaler 64
 TIMSK |= (1<<TOIE1);     		// timer1 overflow interrupt enabled
 sei();         			// global interrupt enabled
}

// 1.000.000 Mhz / 64 = 15625 Hz -> 1/15625 = 0,064ms * 16 = 1,024ms (timer overflow)
ISR (TIMER1_OVF_vect) 	// Interrupt Service Routine,
{ 
 PORTB |= (1<<PB3);  	// interrupt time check - start
 TCNT1 = 0xFFEF;   	// start value for timer, 16 clock times left
 if (ADC < 1)
   buffer = 0;
 else if (ADC < 65)
   buffer += 1; 	// buffer shows time in ms before 63% of voltage  
 PORTB ^= (1<<PB3);  	// interrupt time check - stop
}

The open question are:

1. Why is impedance in minus based on my calculations.

2. How delay_ms script affects given code.

Last Edited: Mon. Dec 14, 2015 - 07:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I reviewed the thread, and at this point I have no idea what problem is trying to be solved.

 

The first part of the thread was back-and-forth ab0o0ut two channels.  There seemed to be two fundamental issues:  "Multi-channel" conversion procedures had some holes; and readings were not rock-solid with out a delay after setting ADMUX.  [Now, it was commented above and also IME, that usually isn't a problem when the signal has enough drive.  Yes, in certain cases a delay helps.  Yes, in certain cases double-convert on a channel helps.]  Are these issues now cleared up, and we are moving on to another issue?

"   "

to "clean" old value?  Three spaces?  You never plan to get to 1000 counts (as was mentioned earlier)?

 

So, what issue are we trying to solve here?  LM35?  cap decay?  You code is, essentially, nonsense.  As far as I can see, there is one ADC reading in the main loop and then a print of the results which takes some milliseconds.  The timer ISR just looks at the current ADC counts.  If you are measuring decay time, don't you do continuous conversions until you get the "complete" signal, and >>then<< output the results?

 

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 will try to sum up:

1. Primarily I had problem with switching between channels for ADC.

    In theory there was no need for dealy (low output impedance of devices), but in practice there is a problem with oscillation when switchning channels.

    The output impedance of LM35 is said to be 0,1 Ohm., other one was 1k pot.

2. We started to investigate how to measure that impedance of LM35, check if it really is 0.1 Ohm.

    Joeymorin comes with idea in post 54. Later on is just all around that.

3. The code I presented recently, checks TC of that circuit, shows result on LCD as well.

    That piece of code is not 'nonsense', it just helps to calculate TC... has nothing to do with my original code.

 

 

The open question are:

1. Why is output impedance of LM35 in minus based on my calculations.

2. How delay_ms script affects given code, (instead of 48 overflows from timer 1, there is only 6).

 

 

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

That piece of code is not 'nonsense', it just helps to calculate TC...

yes it is nonsense.  You are doing  one ADC conversion, then spending some milliseconds to repaint the LCD display.  Meanwhile your timer tick is running, and every millisecond it uses a stale value of "ADC".

 

Indeed, I think you will get to a few milliseconds.  But your issue seems to be the calculations.

 

I haven't looked back at the sample code you are trying to pattern.  But I'd think the algorithm would be something like:

 

-- Charge capacitor

-- Start the timing when the discharge is started

-- Do continuous conversions until you hit the "trigger" value

-- Get your timestamp

-- THEN do one display of the results

 

Everyone has their own preferences.  If you know about the timing range you need, then use your 16-bit timer to advantage.  IIRC you are running at 1MHz.  One microsecond per timer count at /1.  A "reach" of 65ms.  If that is appropriate--e.g., you expect a value up tp 50ms--then why are you fussing with 1ms overflows?  Above "start timing" is setting TCNT1 to 0 and use /1 prescaler.  When you hit your "trigger" ADC value, you read TCNT1--the value in microseconds.

 

Now, if you do get an overflow, then simply repeat with a higher prescaler and again read directly (with the new units).

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 14, 2015 - 09:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

(yes, one can test the "drive" of the LM35 with an AVR sequence.  But to test if it is busted, test the open-loop value.  Then into 1megohm.  Then 100K then 10k. Then 1k.  Still holding the same value?  Done.  Don't even have to wire anything--hold resistor leads in place.  Or, since you want to measure V at the same time, that's what the handful of clip-leads on the bench are for.)

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 assume your 100 uF cap is an electrolytic.  As I've mentioned, the tolerance can be as poor at +/- 50% or worse.

 

If we take your results and assume an impedance of 0, we can solve for C:

 

   TC = (Z + R) * C

    C = TC / (Z + R)

      = 0.048 (0 + 560)

      = 0.000085714 F

      = 85 uF

 

So that would be only 15% off.

 

Remember also that the resistor you are using is likely to be +/- 5%.

 

If you have a capacitance meter, you can verify the actual value of your 100 uF.

 

Even if you have only a good DMM, you can measure the resistor accurately, and then use the same technique to measure the capacitor.  Instead of feeding the RC filter with the LM35, feed it with VCC, and time how long it takes to get to 63.5% of VCC.  Use the same formula with Z = 0.

 

Once you've got an accurate measure of the capacitor, you can try with the LM35 again.

 

All of this is an interesting intellectual exercise, but you've already established that the second ADC channel responds correctly with a pot is connected.  This pretty much implicates the LM35.  Get a fresh one and see what results you get.

 

Alternately, put a 100 nF or 1 uF capacitor across the LM35/ADC and ground in your actual app, this will let the ADC 'sip' from the cap even if the LM35 has too high an impedance.

"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: 1

Here's a relevant paragraph from the LM35's datasheet:

The temperature-sensing element is then buffered by an amplifier and provided to the VOUT pin. The amplifier has a simple class A output stage with typical 0.5-Ω output impedance as shown in the Functional Block Diagram. Therefore the LM35 can only source current and it's sinking capability is limited to 1 μA.

In other words, if you tie the LM35 to a voltage higher than that which it is currently outputting, it has a higher impedance.  So, if in your app the other ADC channel charges the S/H cap to a voltage higher than the LM35's output, the effective TC will be much longer than the 0.5 ohm impedance when sourcing.

 

Is this at all consistent with your original results?  That is, do you see inaccurate conversions of the LM35 output when the channel with the pot has a lower voltage than the LM35?  Or only when the pot's voltage is higher?

"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

I think we are close to conclusions.

In post nr 7 (22nd november) I wrote:

The probelm with changing values from ADC (temperature) occures only when potentiometer gives high enough volage to other ADC input, when ADC form potentiometer is from 0 to about 300 there is no problem.

Which gives an answer to joeymorin last question.

In that case my conclusion is that my LM35 is perfectly ok, gives a proper temperature what is most important here.

Looks like using 'unfamous' delay of 20us in that particular case.

 

@theush: I admit that my previosus code was a mess (gives a right results though :-) )

i tried to fix it up just for the record.

The result is around 47600 us. Very similar to previous results of 48ms.

 

// Check of time constant for RC circuit by use of timer1
// Rising edge on dip-switch activates for single check,
/*
 Atmega8
 1,000,000 Mhz
 */

#define F_CPU 1000000UL
#define Vref 2.69   	// internal reference voltage, measured
#include <avr/io.h>
#include <stdlib.h>     // itoa
#include <stdio.h>      // sprintf
#include "HD44780.c"    // LCD maintenance

void init_adc(void);
int read_ADC (void);
void update_lcd (void);

char result[6];         // buffer for LCD, 16bit timer1 value, 5 signs + string termination
uint16_t buffer;        // buffer for timer1 value
uint8_t now, last;      // used for rising edge simulation
uint8_t ADC_value; 	// actual ADC value

int main(void)
{
 DDRB   |= ((1<<PB3) | (1<<PB4));  // PIN3 and PIN4 as outputs, diodes
 DDRD   &= ~(1<<PD6);     	   // dip-switch input
 PORTD  |= (1<<PD6);               // pull-up resistor enabled for PD6
 ADMUX  |= (1<<MUX0);              // MUX0 -> 1, input ADC1 enabled, LM35
 TCCR1B |= (1<<CS10);              // timer1 activated, no prescaler, -> 1us (for each timer1 tick)
 init_adc();
 LCD_Initalize();
 update_lcd();

    while (1)
    {
        now = PIND & (1<<PD6);    // dip-switch
        if (now!=last)            // true only after rising edge on PIN, just for one calculation
         {
            if (now > 0)
             {
                while (read_ADC() < 1) {}
                TCNT1 = 0x0000;
                while ((ADC_value = read_ADC()) < 64) { }  // time constant of RC circuit is time when 63% of capacitor voltage when charging up 
							   // (or 37% when discharging)
                buffer = TCNT1;             		   // time constant for RC ciruit in us
                update_lcd();
             }
            last = now;
         }

   if (PIND & (1<<PD6))
   PORTB |= (1<<PB3);
   else
   PORTB &= ~(1<<PB3);
    }
}

void init_adc(void)
{
 DDRC &= ~(1<<PC1);                   // input for ADC, LM35 temperature sensor (10mV = 1 Celsius)

 ADCSRA = (1<<ADEN)                   // ADC enabled
     | (1<<ADPS1) | (1<<ADPS0);       // prescaler = 8

 ADMUX |= ((1<<REFS1) | (1<<REFS0));  // internal reference voltage 2,69V (measured)
}

int read_ADC (void)
{
 ADCSRA |= (1<<ADSC);                 // single ADC activated
 while ( (ADCSRA & (1<<ADSC)));       // waiting for ADSC -> 0, ADC finished,
 return ADC;
}

void update_lcd (void)
{
    itoa(ADC_ value, result, 10);      // INT to ASCII, decimal
    LCD_GoTo(0, 1);
    LCD_WriteText("      ");           // clean old value
    LCD_GoTo(0, 1);
    LCD_WriteText(result);

    ltoa(buffer, result, 10);          // ltoa because of unsigned int in buffer
    //sprintf (result, "%u", buffer);     // alternative for ltoa, 'u'-unsigned int, takes more memory space
    LCD_GoTo(7, 0);
    LCD_WriteText("      ");
    LCD_GoTo(7, 0);
    LCD_WriteText(result);
}

EDIT: code corrected

Last Edited: Thu. Dec 17, 2015 - 07:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Still a mess IMO -- update LCD does another read of the ADC, after the "calculation" is supposedly done?  Why?

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

That's right.

i corrected the code with New variable ADC_value.

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

If someone has ADC issues i am 99% sure that he is doing something wrong.

 

Displays, delays and another programming stuff do not affect the conversion.

 

Firstly please answer those 2 questions.

 

1. How do you connect ADC input pin to the measured voltage?

2. How do you connect atmega to the power supply?

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

franekz,

 

Have another read of this (enormously long) thread.  Code messiness aside, the issue has been resolved.  What was originally seen to be a potential problem with ADC code has been determined to be not a problem at all.  The 'issue' had to do with the nature of the signal being sampled (i.e. low impedance, but only when sourcing current, not when sinking).

 

Lahotron,

 

You can continue to include the delay, but I would instead add a 100 nF or 1 uF cap to the LM35 output.  I suspect you won't need the delay at all.

 

"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]