|
|
View previous topic :: View next topic |
Author |
Message |
kel
Joined: 17 Oct 2005 Posts: 68 Location: Brisbane
|
Wrong Results from LM335 temperature interfaced PIC16f877A: |
Posted: Wed Feb 01, 2006 1:28 am |
|
|
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
|
|
Posted: Wed Feb 01, 2006 4:51 am |
|
|
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
|
|
Posted: Wed Feb 01, 2006 5:05 am |
|
|
Make that 'toggle=1', in the second reference after the if...
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Feb 01, 2006 5:23 am |
|
|
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. |
|
|
Ttelmah Guest
|
|
Posted: Wed Feb 01, 2006 4:32 pm |
|
|
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
|
|
Posted: Wed Feb 01, 2006 4:47 pm |
|
|
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
|
Wrong Results from LM335 temperature interfaced PIC16f877A: |
Posted: Wed Feb 01, 2006 9:40 pm |
|
|
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
|
Desperately need help!!!!!!!! |
Posted: Thu Feb 02, 2006 12:28 am |
|
|
|
|
|
Ttelmah Guest
|
|
Posted: Thu Feb 02, 2006 4:04 am |
|
|
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
|
I don't understand!!! |
Posted: Fri Feb 03, 2006 12:01 am |
|
|
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
|
|
Posted: Fri Feb 03, 2006 9:30 am |
|
|
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
|
If you may clarify this for me!! |
Posted: Sat Feb 04, 2006 10:10 pm |
|
|
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
|
|
Posted: Sun Feb 05, 2006 10:08 am |
|
|
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
|
|
Posted: Sun Feb 05, 2006 3:28 pm |
|
|
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
|
Thanks this is clear now |
Posted: Sun Feb 05, 2006 8:56 pm |
|
|
Thanks Ttelmah, it's very clear now...
I will try to test it again..
cheers |
|
|
|
|
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
|