|
|
View previous topic :: View next topic |
Author |
Message |
mojsa2000
Joined: 18 May 2010 Posts: 78
|
I need great HELP about A/D |
Posted: Wed Nov 03, 2010 2:01 pm |
|
|
Hi
I have a big problem with my project. I am disappointed to solve it. I need your advice seriously.
I've made an energy calculator that has 2 analog inputs. The inputs are between 0-5 volts. My project has two parts:
Main program and calibration program that calibrates inputs. In calibration program I do reading pins (A0,A1) continuously and use analog to digital (A/D) conversion module. But unfortunately the the conversion results are
not accurate. (it has a 0.1-0.3 V tolerance)
This is the calibration program. Please check it and help me by finding my faults.
thanks all
Code: |
#include <16f877a.h>
#byte H_addr=0x1e
#byte L_addr=0x9e
#FUSES XT,NOWDT,PUT,BROWNOUT,NOLVP
#DEVICE ADC=10
#use delay (clock=4000000)
#use fast_io (A)
#include <H:\PIC\My sources\flex_lcd.c>
static float energy,total_energy;
static float v_out,i_out,v_out2,i_out2;
float v_buffer,i_buffer;
static unsigned int channel;
int8 counter=0;
int8 ave_counter=0;
//********************* All flags *******************************************
int1 calculating_flag,display_flag;
int1 v_sign,i_sign;
//*****************************************************************************
#int_AD
void AD_isr(void)
{
counter++;
if(counter==3){
if(i_sign)//read channel(0)_reserved for current
{
i_buffer=read_adc(ADC_READ_ONLY);
i_sign=0;
channel=1;
}
}
if(counter==6){
if(v_sign)//read channel(1)__reserved for voltage
{
v_buffer=read_adc(ADC_READ_ONLY);
v_sign=0;
calculating_flag=1;
counter=0;
channel=0;
H_addr=0;
L_addr=0;
}
}
}
#int_TIMER2
void TIMER2_isr(void)
{
if(channel==0)
{
set_adc_channel(0);
i_sign=1;
}
else
{
set_adc_channel(1);
v_sign=1;
}
read_adc(ADC_start_ONLY);
}
void calculating()
{
if(calculating_flag)
{
ave_counter++;
v_out+=v_buffer*5/1023;
i_out+=i_buffer*5/1023;
if(ave_counter==100){
display_flag=1;
v_out2=v_out/100;
i_out2=i_out/100;
v_out=0;
i_out=0;
energy=v_out2*i_out2*0.62;//calculates the energy in per 620ms
total_energy=total_energy+energy;
ave_counter=0;
}
}
calculating_flag=0;
}
void main()
{
SET_TRIS_A( 0xFF );
enable_interrupts(GLOBAL);
enable_interrupts(INT_AD);
enable_interrupts(INT_RTCC);
setup_adc_ports(AN0_AN1_VSS_VREF);
setup_adc(ADC_CLOCK_DIV_8);
setup_timer_2(T2_DIV_BY_4,255,1);//1ms
enable_interrupts(INT_TIMER2);
setup_comparator(NC_NC_NC_NC);
set_timer2(0);
delay_ms(10);
lcd_init();
while(1)
{
calculating();
if(display_flag){
lcd_gotoxy(1,1);
printf(lcd_putc,"V=%.3f ",v_out2);
lcd_gotoxy(9,1);
printf(lcd_putc,"I=%.3f ",i_out2);
lcd_gotoxy(1,2);
printf(lcd_putc,"E=%5.3f ",energy);
lcd_gotoxy(9,2);
printf(lcd_putc,"T=%5.3f ",total_energy);
display_flag=0;
}
}
}
|
Last edited by mojsa2000 on Thu Nov 04, 2010 1:39 am; edited 1 time in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Nov 03, 2010 2:30 pm |
|
|
My advice is to get rid of all the interrupt routines. Also, get rid of the
"start only" and "read only" stuff. Just do a simple reading of the A/D.
Make your program work with a simple while() loop in main(). Then later,
after it's working, you can add the interrupt routines if you want to. |
|
|
mojsa2000
Joined: 18 May 2010 Posts: 78
|
|
Posted: Thu Nov 04, 2010 1:44 am |
|
|
dear PCM
I did the way that you suggested but the result didn't change.
Code: | while(1)
{
set_adc_channel(0);
delay_us(20);
i_buffer=read_adc (ADC_START_AND_READ);
delay_us(40);
i_out=i_buffer*5/1023;
lcd_gotoxy(9,1);
printf(lcd_putc,"I=%.3f ",i_out);
delay_ms(500);
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19506
|
|
Posted: Thu Nov 04, 2010 3:27 am |
|
|
In which case you need to look at your hardware.
First thing, if the pins is not used, connect a separate _smooth_ supply to the Vref pin, program the ADC to use this, and see if things change. I suspect you will find it will. Using the supply is _not_ a route to accuracy. Both since it will change instantaneously as other things occur in your board, and because it will tend to drift with temperature (standard Vregs are not that stable). Use a bandgap reference warranted to give at least 10bit accuracy, if you want 10bit accuracy from the ADC...
If you still have problems, then look at the stability of the circuitry generating the inputs. Does it meet the specifications for the minimum output impedance?. Noise?.
A search here will find why 'not' to use INT_AD. It is basically pointless, unless you are triggering a synchronous ADC reading using the CCP.
Remember if you select an ADC channel, you _must_ allow Tacq, before starting a read. In your original code, you don't. You select the channel, and immediately start a read. This would have given completely 'false' readings, so the simpler code should be better in terms of the reading actually having _some_ relation to the real value, but if the reading is still wrong, then you have a problem in the signals involved...
Best Wishes |
|
|
mojsa2000
Joined: 18 May 2010 Posts: 78
|
|
Posted: Thu Nov 04, 2010 2:46 pm |
|
|
Hi
thanks Ttelmah
I did your advices. Problem didn't solve. I have a new question. I used the "setup_adc_ports(AN0_AN1_VSS_VREF)" setup
when I faced to the problem I changed my ADC setup. I configure pins as
setup_adc_ports(AN0_AN1_AN2_AN4_AN5_VSS_VREF) but I use only pin A0 and pin A5. I changed program by this new
arrangements. I did reading only Pin A5 and result was "0" when the value on this pin was 2.5v.
I'm confused. I'm sure that there is a point about pins when they are in ADC mode ADC that I don't know it. |
|
|
mojsa2000
Joined: 18 May 2010 Posts: 78
|
|
Posted: Thu Nov 04, 2010 3:57 pm |
|
|
Hi again
There is strange event. I simultaneously use 2 channels A0, A1 and input values are variable.
When one of them (for example A0) increases or decreases in large value affects other channel.
I used suitable delays in program. But why this take places????
Please help me. This matter is vital for me....
Code: | void main()
{
SET_TRIS_A( 0xFF );
enable_interrupts(GLOBAL);
enable_interrupts(INT_AD);
enable_interrupts(INT_RTCC);
setup_adc_ports(AN0_AN1_VSS_VREF);
setup_adc(ADC_CLOCK_DIV_32);
setup_timer_2(T2_DIV_BY_4,255,1);//1ms
enable_interrupts(INT_TIMER2);
setup_comparator(NC_NC_NC_NC);
set_timer2(0);
delay_ms(10);
lcd_init();
while(1)
{
ave_counter++;
read_adc (ADC_OFF);
set_adc_channel(0);
delay_us(40);
v_buffer=0;
i_buffer=read_adc (ADC_START_AND_READ);
delay_us(40);
i_out+=(i_buffer*5/1023);
delay_ms(2);
read_adc (ADC_OFF);
set_adc_channel(1);
delay_us(40);
i_buffer=0;
v_buffer=read_adc (ADC_START_AND_READ);
delay_us(40);
v_out+=v_buffer*5/1023;
delay_ms(2);
if(ave_counter==100)
{
v_out2=v_out/100;//make an average
i_out2=i_out/100;
v_out=0;
i_out=0;
energy=v_out2*i_out2*0.62;//calculates the energy in per 8ms
total_energy=total_energy+energy;
ave_counter=0;
lcd_gotoxy(1,1);
printf(lcd_putc,"V=%.3f ",v_out2);
lcd_gotoxy(9,1);
printf(lcd_putc,"I=%.3f ",i_out2);
lcd_gotoxy(1,2);
printf(lcd_putc,"E=%5.3f ",energy);
lcd_gotoxy(9,2);
printf(lcd_putc,"T=%5.3f ",total_energy);
}
}
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19506
|
|
Posted: Fri Nov 05, 2010 3:03 am |
|
|
Go back to my question about the output impedance of your circuits feeding the ADC's.
The ADC on the PIC, is internally a capacitor. It takes time to charge. The capacitor in your chip, is 120pF. The multiplexer has an 'apparent' resistance of about 7KR, while the rest of the input structure in the PIC, adds another 1KR. The capacitor is specified to charge to withing 0.5 bit of the incoming voltage in under 20uSec _provided the source impedance of the circuitry feeding the ADC, is under 10KR_. If the source impedance is over this, the time rises, and the accuracy degrades (there are leakage currents which are asymmetric, so impedance's significantly over this should not be used.
You have all the symptoms of using a source that does not have a low enough impedance to properly charge the capacitor....
Best Wishes |
|
|
mojsa2000
Joined: 18 May 2010 Posts: 78
|
|
Posted: Fri Nov 05, 2010 3:26 pm |
|
|
Dear Ttelmah
thanks for your advices
I recognized my problem. When I do reading Pin A0, pin A1 must be "0" or it be read as "0", but it isn't. A very small voltage is read on PinA1 when A0 is reading (there is a low current on pinA1 that decreases/increases by decreasing/increases voltage on A0 ). There is a same story about pin A1 when is reading. When I connect the one of pins to ground (by a wire) the problem was solved.
Now the problem is cleared for me to solve it, but I need your help too.
Is there a way to solve it on my program? Or I need a hardware change on the pins?
please help me
thanks |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19506
|
|
Posted: Fri Nov 05, 2010 3:43 pm |
|
|
You need to redesign your hardware I'm afraid....
Best Wishes |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Fri Nov 05, 2010 10:06 pm |
|
|
It sounds like you do not have low impedance drives to your A0 and A1 pins.
Reread what Ttelmah said on Friday. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
mojsa2000
Joined: 18 May 2010 Posts: 78
|
|
Posted: Sat Nov 06, 2010 1:46 pm |
|
|
thanks
I'll do that said Ttelmah. I redesign my project hardware. It has two part. I think it is related to power supply designing. Also the inputs come from 2 op-amp u741.
I will report the results as soon as possible to my friends in this forum.
best wishes |
|
|
|
|
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
|