| Author |
Message |
|
|
Posted: Apr 06, 2012 - 04:04 AM |
|

Joined: Apr 06, 2012
Posts: 7
Location: Cairo , Egypt
|
|
Hi, every body..
i am facing a problem with interfacing LM35(Temperature sensor) with ATmega128's internal ADC, and here are the details.
1) i connected LM35 directly to ADC4 of ATmega128
2) i connected a capacitor (0.1uF) between Aref and GND
3) i chose AVCC as voltage reference
4) i configured ADC to be at free running mode
5) i am reading the value of ADCL only
6) i used (ADC0-3) as O/P pins for 7 segment Displaying
the problem is that it reads the value of ADC just for the first time, and then ADCL doesn't change, so to read another value i have to reset the MCU.
Here is the code
-----------------------------
Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// --> PORTF
#define BCD_A PF0
#define BCD_B PF1
#define BCD_C PF2
#define BCD_D PF3
#define TEMP PF4
// --> PORTG
#define DIGI5_EN PG3
#define DIGI6_EN PG4
void initialization();
int main(void)
{
initialization();
ADCSRA |= ( 1 << ADSC );
while(1)
{
}
}
//********************************************************//
void initialization()
{
cli();
DDRF |= ( 1 << BCD_A ) | ( 1 << BCD_B ) |
( 1 << BCD_C ) | ( 1 << BCD_D );
// Enable first 4 bits of portF as O/P
DDRG |= ( 1 << DIGI5_EN ) | ( 1 << DIGI6_EN );
PORTG |= ( 1 << DIGI5_EN ) | ( 1 << DIGI6_EN );
// initialize ADC
ADCSRA |= ( 1 << ADPS0 ) | ( 1 << ADPS1 )|
( 1 << ADPS2 );
// adjust prescaler (for 128)
ADMUX |= ( 1 << REFS0 ) | ( 1 << MUX2); //choose AVCC as a voltage reference and channel ADC4
ADCSRA |= ( 1 << ADEN );
// Enable ADC
ADCSRA |= ( 1 << ADFR);
// choose free running mode
ADCSRA |= ( 1 << ADIE);
// ADC interrupt Enable
sei();
}
//******************** Interrupts *********************//
ISR (ADC_vect)
{
if ( ADCL > 200 )
PORTF = 0x09;
else if ( ADCL > 150 )
PORTF = 0x08;
else if ( ADCL > 120 )
PORTF = 0x07;
else if ( ADCL > 100 )
PORTF = 0x06;
else if ( ADCL > 80 )
PORTF = 0x05;
else if ( ADCL > 60 )
PORTF = 0x04;
else if ( ADCL > 50 )
PORTF = 0x03;
else if ( ADCL > 20 )
PORTF = 0x02;
else if ( ADCL > 4 )
PORTF = 0x01;
}
----------------------------------------
this code is just for testing the ADC but it doesn't work as it should be..
so if some one face a problem like this before or has an experience with ATmega128 ADC, i'll be grateful for help
 |
|
|
| |
|
|
|
|
|
Posted: Apr 06, 2012 - 04:33 AM |
|

Joined: Aug 25, 2005
Posts: 911
Location: U.S.A
|
|
Without looking at the actual settings for the ADC I suspect your ISR is trying to do to many things.
Set a flag the value is ready in the isr and do all that code in the main program. Reset the flag in the main program and wait for the flag to again signal another value is ready to be processed. |
|
|
| |
|
|
|
|
|
Posted: Apr 06, 2012 - 09:26 AM |
|

Joined: Aug 19, 2003
Posts: 396
Location: Australia
|
|
| ADLAR in ADCSRA is 0 and from the datasheet
Quote:
When ADCL is read, the ADC Data Register is not updated until ADCH is read. ...
|
|
|
| |
|
|
|
|
|
Posted: Apr 06, 2012 - 10:00 AM |
|

Joined: Feb 12, 2005
Posts: 16271
Location: Wormshill, England
|
|
1. ADC4 is only available if JTAG is disabled
2. You should always read ADCH, whether you use it or not.
3. Your ISR() should be fine. But you might just as well read the result in one go.
Code:
ISR (ADC_vect)
{
unsigned int result = ADCW; // read all 10 bits
if ( result > 200 )
PORTF = 0x09;
else if ( result > 150 )
PORTF = 0x08;
...
If you know your result is always < 256, you could use an unsigned char.
David. |
|
|
| |
|
|
|
|
|
Posted: Apr 06, 2012 - 07:44 PM |
|

Joined: Apr 06, 2012
Posts: 7
Location: Cairo , Egypt
|
|
Thank you metron9, i tried what you said but no better response (Actually it became worst ), it's not about flag since it updated by hardware, but thank you for Help .
mikech & David, i don't know what i can say to you, that was the problem indeed and it's fixed now, thank you a lot, i really appreciate your Help  |
|
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 05:38 AM |
|

Joined: Aug 25, 2005
Posts: 911
Location: U.S.A
|
|
I was not referring to the interrupt flag.
What I mean by setting a flag is creating a global variable that the interrupt sets when triggered so your main program can poll to see if there is a new value to be processed.
Setting a global value for the ADC value for example that gets updated when the isr triggers could then be tested against another global value in the main program and if it is not the same a subroutine could be called to update the screen and copy the new value into the current variable. Or a global bit value set in an isr can tell your main code to process new data.
Basically the idea that processing of data typically is better served outside interrupts, this would limit the interrupt to reading the ADC and setting a bit to alert the main program. It's like your own software interrupt stack you maintain similar to the hardware interrupts.
You also left one base uncovered.
What about when ADCL is <=4
It would leave the previous value set.
Should there be another
Code:
else
PORTF = 0x00;
|
|
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 02:59 PM |
|


Joined: Sep 04, 2002
Posts: 21251
Location: Orlando Florida
|
|
This is my LM35 read routine for my mega328
Code:
//----struct used for rolling average------
#define NUMSAMP 32
typedef struct{
char ndx;
long int tot;
int avg;
int dat[NUMSAMP]; //avg over NUMSAMP readings
}Trollavg;
Trollavg rollavg[8];
unsigned int addat[8]; //internal 10 bit
int avg;
float ad2volts;
float volts,volts2degC,degC,degF;
.
.
.
//--------------------
void adc_init(void){
//ADC initialize
ADCSRA = 0x00; //disable adc
ADMUX = 0x40; //select vcc and adc input 0
ACSR = 0x80;
ADCSRA = 0x87;
DIDR0 = 0x3f; //digital input disable portc 543210
}
//--------------------
void initrollavg(Trollavg *p){
//init rollavg struct
char i;
p->ndx=0;
p->tot=0;
p->avg=0;
for(i=0; i < NUMSAMP; i++){
p->dat[i]=0;
}
}
//--------------------
int calcrollavg(Trollavg *p, int w){
//return rollavg of last NUMSAMP readings
p->tot-=p->dat[p->ndx]; //subtract old value from tot
p->tot += w; //add in new value
p->dat[p->ndx++]=w; //remember new value, bump ndx
if(p->ndx==NUMSAMP) p->ndx=0; //rewind
p->avg = p->tot >> 5; //2^5=32=NUMSAMP; avg is tot/NUMSAMP
return p->avg;
}
//---------------------
unsigned int readadchan(char n){
//read ch n of internal a/d 10 bit unsigned
ADMUX= 0x40 + n; //select vcc + channel n
ADCSRA |= 0x40; //init conversion
while((ADCSRA & 0x40) != 0){}; //wait for conv complete
return ADC;
}
//------------------
void temploop(void){
//read LM35 temp sensor on ad chan 3
//10mv/deg C-> 100degc=1000mv-> 100 degC/volt
char c;
cprintf("temploop q=quit\n");
ad2volts=5.0/1023.0; //5v=1023
// cprintf("ad2volts %#7.3f \n",ad2volts);
volts2degC=10.0;
cprintf("ad V degC degF \n");
c=0;
while(c != 'q'){
if(kbhit()){
c=getchar();
}
addat[3]=readadchan(3);
avg = calcrollavg(&rollavg[3],addat[3]);
volts = avg*ad2volts;
degC = volts*volts2degC;
degF = degC * 1.8 + 32.0;
cprintf("%4d %#7.3f %#7.1f %#7.1f \r",addat[3],volts,degC,degF); //
}
}
|
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 04:31 PM |
|

Joined: Apr 06, 2012
Posts: 7
Location: Cairo , Egypt
|
|
|
metron9 wrote:
I was not referring to the interrupt flag.
What I mean by setting a flag is creating a global variable that the interrupt sets when triggered so your main program can poll to see if there is a new value to be processed.
Setting a global value for the ADC value for example that gets updated when the isr triggers could then be tested against another global value in the main program and if it is not the same a subroutine could be called to update the screen and copy the new value into the current variable. Or a global bit value set in an isr can tell your main code to process new data.
I think i got it, but actually the problem wasn't about ISR (ISR was executed properly ),it was about ADCL doesn't updated according to the input analog signal, i had to read ADCH even ADCl can be updated.
Quote:
Basically the idea that processing of data typically is better served outside interrupts, this would limit the interrupt to reading the ADC and setting a bit to alert the main program. It's like your own software interrupt stack you maintain similar to the hardware interrupts.
you right and i modified the code to be run in the main program, and ISR for only read the ADC value.
Y
Quote:
ou also left one base uncovered.
What about when ADCL is <=4
It would leave the previous value set.
Should there be another
Code:
else
PORTF = 0x00;
actually this wasn't the real code, it was just for testing the ADC can read different values of analog inputs, but the main one was executing another function.
Thank you for interest
and actually i am facing another problem now, which the ADC reads unstable value, although when measuring the output of LM35 by multimeter it gives a stable value, Do you have any Idea about how to solve it |
|
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 04:38 PM |
|


Joined: Sep 04, 2002
Posts: 21251
Location: Orlando Florida
|
|
| If you use VCC as the a/d reference, the VCC needs to have no ripple. If you use AREF as the a/d reference, you can put a ripple free reference voltage there. If you have wiggle in the bottom 3 bits of the a/d reading, averaging 16 or 32 readings will average most of them out. (See above listing for my superior rolling average code) |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 04:40 PM |
|

Joined: Apr 06, 2012
Posts: 7
Location: Cairo , Egypt
|
|
|
bobgardner wrote:
This is my LM35 read routine for my mega328
Code:
//----struct used for rolling average------
#define NUMSAMP 32
typedef struct{
char ndx;
long int tot;
int avg;
int dat[NUMSAMP]; //avg over NUMSAMP readings
}Trollavg;
Trollavg rollavg[8];
unsigned int addat[8]; //internal 10 bit
int avg;
float ad2volts;
float volts,volts2degC,degC,degF;
.
.
.
//--------------------
void adc_init(void){
//ADC initialize
ADCSRA = 0x00; //disable adc
ADMUX = 0x40; //select vcc and adc input 0
ACSR = 0x80;
ADCSRA = 0x87;
DIDR0 = 0x3f; //digital input disable portc 543210
}
//--------------------
void initrollavg(Trollavg *p){
//init rollavg struct
char i;
p->ndx=0;
p->tot=0;
p->avg=0;
for(i=0; i < NUMSAMP; i++){
p->dat[i]=0;
}
}
//--------------------
int calcrollavg(Trollavg *p, int w){
//return rollavg of last NUMSAMP readings
p->tot-=p->dat[p->ndx]; //subtract old value from tot
p->tot += w; //add in new value
p->dat[p->ndx++]=w; //remember new value, bump ndx
if(p->ndx==NUMSAMP) p->ndx=0; //rewind
p->avg = p->tot >> 5; //2^5=32=NUMSAMP; avg is tot/NUMSAMP
return p->avg;
}
//---------------------
unsigned int readadchan(char n){
//read ch n of internal a/d 10 bit unsigned
ADMUX= 0x40 + n; //select vcc + channel n
ADCSRA |= 0x40; //init conversion
while((ADCSRA & 0x40) != 0){}; //wait for conv complete
return ADC;
}
//------------------
void temploop(void){
//read LM35 temp sensor on ad chan 3
//10mv/deg C-> 100degc=1000mv-> 100 degC/volt
char c;
cprintf("temploop q=quit\n");
ad2volts=5.0/1023.0; //5v=1023
// cprintf("ad2volts %#7.3f \n",ad2volts);
volts2degC=10.0;
cprintf("ad V degC degF \n");
c=0;
while(c != 'q'){
if(kbhit()){
c=getchar();
}
addat[3]=readadchan(3);
avg = calcrollavg(&rollavg[3],addat[3]);
volts = avg*ad2volts;
degC = volts*volts2degC;
degF = degC * 1.8 + 32.0;
cprintf("%4d %#7.3f %#7.1f %#7.1f \r",addat[3],volts,degC,degF); //
}
}
thank you for the code it's complicated a little bit, but i focused on the part of configuring and reading the ADC.
and i have a question.
did you got an unstable value when you read the ADC value, or faced a problem like that ?
because i have an unstable one although the output of LM35 is stable when i measure it by multimeter |
|
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 04:45 PM |
|


Joined: Sep 04, 2002
Posts: 21251
Location: Orlando Florida
|
|
| I think you should look at the VCC with a scope. You need a Big Electrolytic cap on the board and a .1uf bypass cap on every avr vcc pin, included avcc. If you cant get a good smooth 5V, you can use internal 2.56V ref as the a/d ref. Add a cap from AREF to gnd. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 04:58 PM |
|

Joined: Apr 06, 2012
Posts: 7
Location: Cairo , Egypt
|
|
|
bobgardner wrote:
I think you should look at the VCC with a scope. You need a Big Electrolytic cap on the board and a .1uf bypass cap on every avr vcc pin, included avcc. If you cant get a good smooth 5V, you can use internal 2.56V ref as the a/d ref. Add a cap from AREF to gnd.
you right,
actually i connected a Big Electrolytic Cap after the regulator stage and connected 0.1uF on VCC & AVCC pin of MCU, but the VCC still unstable on a specific value,
so i'll try to use 2.56v as Vref, or using average routine like in your code.
Thank you,I appreciate your help |
|
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 05:06 PM |
|


Joined: Sep 04, 2002
Posts: 21251
Location: Orlando Florida
|
|
| And I appreciate being able to help. I think you should put your city and country in your profile so all the avrfreals can see their worldwide compatriots. Peace Brother. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Apr 07, 2012 - 05:38 PM |
|

Joined: Apr 06, 2012
Posts: 7
Location: Cairo , Egypt
|
|
|
bobgardner wrote:
And I appreciate being able to help. I think you should put your city and country in your profile so all the avrfreals can see their worldwide compatriots. Peace Brother.
Ok,i 'll do it Brother, and I am an Egyptian Live in Cairo |
|
|
| |
|
|
|
|
|
Posted: Apr 08, 2012 - 01:45 AM |
|

Joined: Aug 19, 2003
Posts: 396
Location: Australia
|
|
|
Quote:
... the ADC reads unstable value, although when measuring the output of LM35 by multimeter it gives a stable value...
what are the readings ?
The ADC is 'sample and hold', therefore any high-frequency noise on the VREF or analogue input can produce unstable readings.
Highly recommended is to do what bobgardner said and also put a 0.1ufd cap on the VREF line. |
|
|
| |
|
|
|
|
|
Posted: Apr 08, 2012 - 03:09 AM |
|


Joined: Sep 04, 2002
Posts: 21251
Location: Orlando Florida
|
|
| That's the AREF pin. Then init the a/d converter to use the internal 2.56 voltage ref. |
_________________ Imagecraft compiler user
|
| |
|
|
|
|
|
Posted: Apr 08, 2012 - 06:17 PM |
|

Joined: Apr 06, 2012
Posts: 7
Location: Cairo , Egypt
|
|
|
Quote:
what are the readings ?
The ADC is 'sample and hold', therefore any high-frequency noise on the VREF or analogue input can produce unstable readings.
Highly recommended is to do what bobgardner said and also put a 0.1ufd cap on the VREF line.
actually i already connected a cap on Aref pin (0.1uF) but it didn't solve the problem,so i tried another method.
thank you for reply.
Quote:
That's the AREF pin. Then init the a/d converter to use the internal 2.56 voltage ref.
i tried this solution of 2.56 Vref and replaced the Electrolytic cap. by a bigger one, but the VCC still unstable, so i used the average function like in your code, somehow the result became stable (actually the performance isn't satisfied enough but it's acceptable), i'll try to replace the regulator with another one, or apply some modifications in the power supply system.
Thank you for Help.
 |
|
|
| |
|
|
|
|
|