CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Wrong Results from LM335 temperature interfaced PIC16f877A:

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
kel



Joined: 17 Oct 2005
Posts: 68
Location: Brisbane

View user's profile Send private message

Wrong Results from LM335 temperature interfaced PIC16f877A:
PostPosted: Wed Feb 01, 2006 1:28 am     Reply with quote

I'm using LM335 temperature.I've connected thesensor to PORTA PIN 0.The results obtained from the ADC converter are very wrong:The following is the code i used for the on-borad PIC16f877a adc.Can any one help me out.The ADC interrupt occurs every 2.6us from:
Code:
setup_adc(ADC_CLOCK_INTERNAL);

I'm taking 10 readings from the adc and average them. the results obtained are then used in the proceeding calculations.
Code:
#include <16F877A.h>
#device *=16
#device adc=10
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
//#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PC_Stream)

#include <lcd_driver.c>
#include <kel_functions.c>
//extern GLOB_clock;
//void ADC_RESULT(void);
void ADC_RESULT(void);

struct time {
               int8  hrs;
               int8  mins;
               int8  secs;
               int16 mSec;
               unsigned int16 val;
               unsigned int8 ADC_Results;
               float Temp_deg;
               }GLOB_clock={15,58,45,0,0,0};


#int_AD
AD_isr()
{

}

#int_TBE
TBE_isr()
{

}

#int_RDA
RDA_isr()
{

}

#int_TIMER0
TIMER0_isr()
{
    static int16 tick=0;
     //set_timer0(96);
     tick+=256;   //   set_timer0(0); 20 mhz clock, no prescaler, set timer 0 to overflow in 3276.8us
                    // 256-(.0032768/(4*256/20000000))
   if(tick>3125) {  //2500){
         tick-=3125;
         if(++GLOB_clock.mSec>=25)  {    //1000){
            GLOB_clock.mSec-=25;        //1000;
            if(++GLOB_clock.secs>=60) {
               GLOB_clock.secs=0;
               if(++GLOB_clock.mins>=60) {
                  GLOB_clock.mins=0;
                  if(++GLOB_clock.hrs>=24) {
                  GLOB_clock.hrs=0;
                  }
               }
            }
         }
   }
}



void main()
{

   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_INTERNAL);
  // setup_psp(PSP_DISABLED);
   //setup_spi(FALSE);
  setup_timer_0(RTCC_INTERNAL|RTCC_DIV_64);
  // setup_timer_1(T1_DISABLED);
   //setup_timer_2(T2_DISABLED,0,1);
  // setup_comparator(NC_NC_NC_NC);
  // setup_vref(FALSE);
   lcd_init();
   enable_interrupts(INT_AD);
  // enable_interrupts(INT_TBE);
  // enable_interrupts(INT_RDA);
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);
   set_adc_channel(0);
   delay_us(10);

//printf("Sampling adc"

lcd_gotoxy(2,1);
lcd_putc("Sampling....\f");
delay_ms(1000);
lcd_putc("\f");

while(1)
{
display_time();
lcd_gotoxy(0,2);
ADC_RESULT();
}

}


void ADC_RESULT(void){
   static char cnt;
   struct time local_clock;
   memcpy(&local_clock,&GLOB_clock, sizeof(struct time));

   for(cnt=0; cnt<=10; cnt++)
   {
   local_clock.val += read_adc();//taking 10 samples
 //and average them
   delay_ms(10);
   }
  local_clock. ADC_Results=(local_clock.val/=10);
    local_clock.Temp_deg = ((local_clock.ADC_Results - 559L)/2);
   //Temp_Kelvin = (Temp_degrees + 273.15);
    //Temp_Kelvin =((((float)val*5)/1024)-273.15);
    printf(LCD_PUTC," %2u %2.1f\n\r",local_clock.ADC_Results,local_clock.Temp_deg);
    delay_ms(100);
}


The results obtained are:
local_clock. ADC_Results=(local_clock.val/=10);=61
local_clock.Temp_deg = ((local_clock.ADC_Results - 559L)/2); =32623.0

These are extremely wrong.
PS the adjustment pin on Lm335 is not connected to anything.
a resistor of 202 ohms is connected to VDD and to lm335
.Please help me....
cheers
Ttelmah
Guest







PostPosted: Wed Feb 01, 2006 4:51 am     Reply with quote

Forget using the ADC interrupt. It gains you nothing.
First of all, where do you get a '2.6uSec' figure from?. The figure is 2 to 6uSec. ADC_CLOCK_INTERNAL, selects a RC clock in the chip, which only gives relatively innaccurate times. This is also _not_ the frequency of the ADC interrupt. The sequence is:
Trigger an ADC conversion
12 cycles of the ADC clock latter, the conversion will finish, and set the ADIF flag.
Interrupt routine, will now be called, taking typically 30 instruction clocks to arrive at the routine (about another 6uSec).
The interrupt routine, now needs to read the ADC result, and clear the interrupt flag (the latter is done automatically for you by the compiler).
The routine then returns (costing another 6uSec).

So, all the interrupt has done for you, is cost an 'extra' 12 or more uSec, after the conversion has completed.
Now, nowhere in your code, that I can see, do you either trigger the ADC conversion to take place, or read the result from the conversion. Not suprising that the result doesn't make much sense then is it?...
Now the RC clock, tends to have problems at faster chip rates, so switch to using ADC_CLOCK_DIV_32.
Then get rid of the ADC interrupt.
Now in your timer code, add:
Code:

static int1 toggle=0;

if (toggle) {
   toggle=0;
   READ_ADC(ADC_START_ONLY)
}
else {
   toggle=0;
   GLOB_CLOCK.ADC_Results=READ_ADC(ADC_READ_ONLY);
}


On alternate interrupts, this will start the ADC, and on the next interrupt, read the result.

Best Wishes
Ttelmah
Guest







PostPosted: Wed Feb 01, 2006 5:05 am     Reply with quote

Make that 'toggle=1', in the second reference after the if...

Best Wishes
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Wed Feb 01, 2006 5:23 am     Reply with quote

Hi Ttelmah,
A good explanation! Too bad I found two errors in your code:
1) You never set the value of toggle to 1.
2) The very first read will fail because you do a read before doing a start conversion.

Improved version:
Code:
static int1 toggle=0;

if (toggle) {
   toggle=0;
   GLOB_CLOCK.ADC_Results=READ_ADC(ADC_READ_ONLY);
}
else {
   toggle=1;
   READ_ADC(ADC_START_ONLY)
}


Quote:
Now the RC clock, tends to have problems at faster chip rates, so switch to using ADC_CLOCK_DIV_32.
I have problems with the ADC in some of my units giving unsteady results. Do you have a link for me where I can find more information of the problems at higher clock rates? Is 16MHz considered a higher clock rate?

P.S.: Not logging in as guest you could have edited your posted code. Smile
Ttelmah
Guest







PostPosted: Wed Feb 01, 2006 4:32 pm     Reply with quote

As you saw, I spotted the toggle error.
The other won't matter, but would be 'nicer' handler properly. Since the main clock counter doesn't update till several cycles of the clock have happened, the value should be ready by the time it is needed. :-)

Best Wishes
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Wed Feb 01, 2006 4:47 pm     Reply with quote

Quote:
As you saw, I spotted the toggle error.
I should learn to type faster. I missed your updated post.
kel



Joined: 17 Oct 2005
Posts: 68
Location: Brisbane

View user's profile Send private message

Wrong Results from LM335 temperature interfaced PIC16f877A:
PostPosted: Wed Feb 01, 2006 9:40 pm     Reply with quote

Thanks to you guys.The problem has not been solved yet.First why the codes given are different,i.e.?
Ttelmah wrote:
Code:
static int1 toggle=0;

if (toggle) {
   toggle=0;
READ_ADC(ADC_START_ONLY)
   }
else {
   toggle=1;
GLOB_CLOCK.ADC_Results=READ_ADC(ADC_READ_ONLY);

   }

ckielstra wrote:
Code:
static int1 toggle=0;

if (toggle) {
   toggle=0;
   GLOB_CLOCK.ADC_Results=READ_ADC(ADC_READ_ONLY);
}
else {
   toggle=1;
   READ_ADC(ADC_START_ONLY)
}


ckielstra starts the data read from the first test of the flag(toggle=0) Ttelmah while starts reading from the else statement!!
secondly this is what i have added to the code and the results are still very grave and extremely disappointing.
Code:
void ADC_RESULT(void){
   static char cnt;
static int1 toggle=0;
   struct time local_clock;
   memcpy(&local_clock,&GLOB_clock, sizeof(struct time));
//The flag is tested here
if(toggle)
{
   toggle=0;
   for(cnt=0; cnt<=10; cnt++)
   {
   local_clock.val += read_adc(ADC_READ_ONLY);//taking 10 samples
 //and average them
   delay_ms(10);
   }
}
else
 {
toggle=1;
read_adc(ADC_START_ONLY);//
}

//FIND THE AVERAGE OF THE FIRST 10 SAMPLES
 local_clock. ADC_Results=(local_clock.val/=10);
 local_clock.Temp_deg = ((local_clock.ADC_Results - 559L)/2);
   //Temp_Kelvin = (Temp_degrees + 273.15);
    //Temp_Kelvin =((((float)val*5)/1024)-273.15);
    printf(LCD_PUTC," %2u %2.1f\n\r",local_clock.ADC_Results,local_clock.Temp_deg);
    delay_ms(100);
}


THEN THIS IS HOW I CALL THE FUNCTION READ_ADC WHOLE CODE IS AS FOLLOW:
Code:
#include <16F877A.h>
#device *=16
#device adc=10
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
//#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PC_Stream)

#include <lcd_driver.c>
#include <kel_functions.c>
//extern GLOB_clock;
//void ADC_RESULT(void);
void ADC_RESULT(void);

struct time {
               int8  hrs;
               int8  mins;
               int8  secs;
               int16 mSec;
               unsigned int16 val;
               unsigned int8 ADC_Results;
               float Temp_deg;
               }GLOB_clock={15,58,45,0,0,0};



#int_TBE
TBE_isr()
{

}

#int_RDA
RDA_isr()
{

}

#int_TIMER0
TIMER0_isr()
{
    static int16 tick=0;
     //set_timer0(96);
     tick+=256;   //   set_timer0(0); 20 mhz clock, no prescaler, set timer 0 to overflow in 3276.8us
                    // 256-(.0032768/(4*256/20000000))
   if(tick>3125) {  //2500){
         tick-=3125;
         if(++GLOB_clock.mSec>=25)  {    //1000){
            GLOB_clock.mSec-=25;        //1000;
            if(++GLOB_clock.secs>=60) {
               GLOB_clock.secs=0;
               if(++GLOB_clock.mins>=60) {
                  GLOB_clock.mins=0;
                  if(++GLOB_clock.hrs>=24) {
                  GLOB_clock.hrs=0;
                  }
               }
            }
         }
   }
}



void main()
{

   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_DIV_32);
   set_adc_channel(0);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_64);
   lcd_init();
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);

lcd_gotoxy(2,1);
lcd_putc("Sampling....\f");
delay_ms(1000);
lcd_putc("\f");

while(1)
{
display_time();
lcd_gotoxy(0,2);
ADC_RESULT();
}

}


The results obtained are:
local_clock. ADC_Results=(local_clock.val/=10);=56 :THIS VALUES KEEPS FLIPPING FROM 56 TO 0 AND VICEVERSA
local_clock.Temp_deg = ((local_clock.ADC_Results - 559L)/2); =32517.0
THE LAST 3 DIGITS KEEPS ON FLIPPING
i NEED YOUR HELPS MATES
CAN ANYONE SEE THE NEW PROBLEM??
kel



Joined: 17 Oct 2005
Posts: 68
Location: Brisbane

View user's profile Send private message

Desperately need help!!!!!!!!
PostPosted: Thu Feb 02, 2006 12:28 am     Reply with quote

Embarassed
Ttelmah
Guest







PostPosted: Thu Feb 02, 2006 4:04 am     Reply with quote

The routine, is designed to go into the timer interrupt, not into a seperate routine. The reason for the 'swap' between Ckielstra's code and mine, is that mine will return garbage on the first read, and swapping the order of operations avoids this.
Seriously, you seem to be trying to do things one way, and then not using the approach you are actually generating!. If you just want to take ten adc values, and average them, then just make normal ADC readings in the sampling loop. The 'point' about the approach being shown was that the readings go on, independant of the display loop, and without the processor having to wait for the readings to occur (becaue of the delays between the timer loops, the next value is always waiting).
[code]
#include <16F877A.h>
#device *=16
#device adc=10
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
//#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=PC_Stream)

#include <lcd_driver.c>
#include <kel_functions.c>
//extern GLOB_clock;

struct time {
int8 hrs;
int8 mins;
int8 secs;
int16 mSec;
unsigned int16 val;
unsigned int8 ADC_Results;
float Temp_deg;
}GLOB_clock={15,58,45,0,0,0};



#int_TBE
TBE_isr()
{

}

#int_RDA
RDA_isr()
{

}

#int_TIMER0
TIMER0_isr()
{
static int16 tick=0;
static int1 toggle=0;
tick+=256; // set_timer0(0); 20 mhz clock, no prescaler, set timer 0 to overflow in 3276.8us
// 256-(.0032768/(4*256/20000000))

if (toggle) {
toggle=0;
GLOB_CLOCK.ADC_Results=READ_ADC(ADC_READ_ONLY);
}
else {
toggle=1;
READ_ADC(ADC_START_ONLY)
}
if(tick>3125) { //2500){
tick-=3125;
if(++GLOB_clock.mSec>=25) { //1000){
GLOB_clock.mSec-=25; //1000;
if(++GLOB_clock.secs>=60) {
GLOB_clock.secs=0;
if(++GLOB_clock.mins>=60) {
GLOB_clock.mins=0;
if(++GLOB_clock.hrs>=24) {
GLOB_clock.hrs=0;
}
}
}
}
}
}

void ADC_RESULT(void){
static char cnt;
static int32 avgsum;
int16 adcval;
struct time local_clock;
memcpy(&local_clock,&GLOB_clock, sizeof(struct time));
avgsum+=local_clock.ADC_Results;
adcval=avgsum/8;
avgsum-=adcval;
//Average value - using /8, since in binary, this is quicker...

//Display your time here.
lcd_gotoxy(0,2);
local_clock.Temp_deg = ((adcval - 559L)/2);
printf(LCD_PUTC," %2u %2.1f\n\r",adcval,local_clock.Temp_deg);
}

void main()
{

setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_32);
set_adc_channel(0);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_64);
lcd_init();
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);

lcd_gotoxy(2,1);
lcd_putc("Sampling....\f");
delay_ms(1000);
lcd_putc("\f");

while(1) {
//display_time();
//Display the time in the result output routine. There is no point in
//copying the clock block twice.
ADC_RESULT();
delay_ms(100);
}
}
kel



Joined: 17 Oct 2005
Posts: 68
Location: Brisbane

View user's profile Send private message

I don't understand!!!
PostPosted: Fri Feb 03, 2006 12:01 am     Reply with quote

Thanks alot Ttelmah..mate forgive me if i'm too much but i need to clarify the followings.
I don't understand why you are doing the following since there is no for loop involved...
I thought it would looks like:
Code:
for(cnt=0; cnt<8; cnt++)
{avgsum+=local_clock.ADC_Results;
}
adcval=avgsum/8;
instead of :
avgsum+=local_clock.ADC_Results;
adcval=avgsum/8;
Code:
avgsum-=adcval; ???
//Average value - using /8, since in binary, this is quicker...

why then are you subtracting i.e.avgsum=avgsum-adcval??
rnielsen



Joined: 23 Sep 2003
Posts: 852
Location: Utah

View user's profile Send private message

PostPosted: Fri Feb 03, 2006 9:30 am     Reply with quote

Make sure the result, in registers ADRESH & ADRESL are justified properly. On reset the bit ADFM, in ADCON1, is a 0. This will Left justify the result which will use the upper 10 bits. If you want the result to be Right justified ADFM needs to be set to a 1. Check out section 11.4.1 of the data sheet which explains this, in case this is what's causing your bad numbers.

Ronald
kel



Joined: 17 Oct 2005
Posts: 68
Location: Brisbane

View user's profile Send private message

If you may clarify this for me!!
PostPosted: Sat Feb 04, 2006 10:10 pm     Reply with quote

Thanks alot Ttelmah..mate forgive me if i'm too much but i need to clarify the followings.
I don't understand why you are doing the following since there is no for loop involved...
I thought it would looks like:
Code:
Code:
for(cnt=0; cnt<8; cnt++)
{avgsum+=local_clock.ADC_Results;
}
adcval=avgsum/8;

instead of :
Code:
avgsum+=local_clock.ADC_Results;
adcval=avgsum/8;
Code:
avgsum-=adcval; ???
//Average value - using /8, since in binary, this is quicker...

why then are you subtracting i.e.avgsum=avgsum-adcval??
jecottrell



Joined: 16 Jan 2005
Posts: 559
Location: Tucson, AZ

View user's profile Send private message

PostPosted: Sun Feb 05, 2006 10:08 am     Reply with quote

I think what RJ is doing is a running average instead a grouped average. After a quick glance at the code it looks as though each time through the main loop a adc reading is taken. That value is added to the running average and then divided by eight (the number of readings in the "accumulator"). After the current average is taken the latest value is removed from the accumulator... Since there isn't any "pre-packing" of the accumulator, it will take a while for the running average to come up to speed and give an accurate reading.

Does that make sense? There are many ways to filter the readings from an ADC. Another interesting one is the"Olympic Scoring" scheme that is used by members of this forum.

Hope this makes sense and it is what RJ was intending.

Good luck,

John
Ttelmah
Guest







PostPosted: Sun Feb 05, 2006 3:28 pm     Reply with quote

Yes.
There seem to be a number of 'fighting' targets going on!. The original post was trying to use ADC interrupts. Now this would normally only be used if you have very accurate timing requirements, or needed to keep the sampling 'synchronous'. So I then posted how to sample synchronously using the existing timer interrupt, and wrote of the problems (because of the interrupt overhead), with using the ADC interrupt. This code was then modified to try to use it simply 'in place' of the existing ADC code, which makes it pointless. So I posted instead a method of obtaining a 'rolling' average, of the last eight readings (which given the update rate of the loop, is much more likely to work well, than adding the huge overhead of taking and averaging eight readings each time).
I suspect the original poster, does not really understand the implications of some of the things being done. For instance, you can simply call 'read_adc' eight times, and average these results, without using any of the 'tricks' being shown, but this will add a very long delay, into the loop. If the loop wants to be quick, then instead use the single sample, and rolling average system. I must admit, that I assumed, given the original method was one targeted at speed, that this was a design requirement...

Best Wishes
kel



Joined: 17 Oct 2005
Posts: 68
Location: Brisbane

View user's profile Send private message

Thanks this is clear now
PostPosted: Sun Feb 05, 2006 8:56 pm     Reply with quote

Thanks Ttelmah, it's very clear now...
I will try to test it again..
cheers
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group