|
|
View previous topic :: View next topic |
Author |
Message |
mcataldo
Joined: 04 Jul 2010 Posts: 11 Location: Santiago, Chile
|
Temperature reading |
Posted: Thu Jul 08, 2010 1:38 am |
|
|
Hi everybody, recently I've found a trouble with my system. I'm using PIC 16F877A and 2 LM35DZ. Problem is when I run Proteus to simulate, when it's an even value, LCD shows exactly half this and when is an odd value, LCD shows half (value - 1). For example, when real temperature is 10, LCD shows 5, when is 11 shows 5, and for 12 it shows 6.
I attach a code part.
Thank you.
Code: |
#include <16F877A.h>
#device ADC=8
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT
#use delay(clock=4000000)
#include <LCD.c>
#priority rb,timer2
// Declaración de variables
int temp_sp;
int temp_sp_ee;
long temp_analogo_c;
long temp_analogo_f;
int temp_f;
int temp_f1;
int temp_f2;
int temp_f3;
int temp_f4;
int temp_c;
int temp_c1;
int temp_c2;
int temp_c3;
int temp_c4;
int temp_min;
int temp_max;
short temp_read = 0;
void main()
{
set_tris_a(0xFF); // interesa que los puertos A0 y A1 sean salidas (0)
set_tris_b(0xFF); // Señal Pulsadores
set_tris_c(0x00); // Señal PWM para los servos
set_tris_d(0x00); // Puertos que van al LED
setup_adc(ADC_CLOCK_DIV_32);
setup_adc_ports(ALL_ANALOG);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_comparator(NC_NC_NC_NC);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DIV_BY_16,255,1); //el postscale es para determinar
switch (counter)
{
case 0:set_adc_channel(0);
temp_analogo_f=read_adc(); //10 bits
temp_f1=(int)temp_analogo_f; //conversion a °C (LM335) se
set_adc_channel(1);
temp_analogo_c=read_adc(); //10 bits
temp_c1=(int)temp_analogo_c; //conversion a °C (LM335) se
counter = 1;
break;
case 1:set_adc_channel(0);
temp_analogo_f=read_adc(); //10 bits
temp_f2=(int)temp_analogo_f; //conversion a °C (LM335) se
set_adc_channel(1);
temp_analogo_c=read_adc(); //10 bits
temp_c2=(int)temp_analogo_c; //conversion a °C (LM335) se
counter = 2;
break;
case 2:set_adc_channel(0);
temp_analogo_f=read_adc(); //10 bits
temp_f3=(int)temp_analogo_f; //conversion a °C (LM335) se mejora con Vref A/D
set_adc_channel(1);
temp_analogo_c=read_adc(); //10 bits
temp_c3=(int)temp_analogo_c; //conversion a °C (LM335) se
counter = 3;
break;
default:set_adc_channel(0);
temp_analogo_f=read_adc(); //10 bits
temp_f4=(int)temp_analogo_f; //conversion a °C (LM335) se
set_adc_channel(1);
temp_analogo_c=read_adc(); //10 bits
temp_c4=(int)temp_analogo_c; //conversion a °C (LM335) se
temp_f=(temp_f1+temp_f2+temp_f3+temp_f4)/4;
temp_c=(temp_c1+temp_c2+temp_c3+temp_c4)/4;
temp_min=temp_f+1;
temp_max=temp_f+4;
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc,"Tref:%2u\xDF",temp_sp);
lcd_gotoxy(9,1);
printf(lcd_putc,"Tcal:%2u\xDF",temp_c);
lcd_putc(223);
lcd_gotoxy(1,2);
printf(lcd_putc,"Tmin:%2u\xDF",temp_min);
lcd_gotoxy(9,2);
printf(lcd_putc,"Tmax:%2u\xDF",temp_max);
lcd_putc(223);
delay_ms(800);
|
Last edited by mcataldo on Thu Jul 08, 2010 11:58 am; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19546
|
|
Posted: Thu Jul 08, 2010 2:14 am |
|
|
Some comments:
Alongside your 'read_adc' lines, you have the comment '10 bits'. However you have the ADC setup to return 8 bits, in the second line of your program...
You are selecting an ADC channel, and immediately reading it. This will give incorrect result. The ADC acquisition time, is just under 20uSec for this chip.
Why are you moving the value from the ADC into a value, then into another. Seems just a waste of time?. Why not just put the read value directly into the variable you want.
You then have a remark on each of these tranfers about 'conversion to degrees C', or 'conversions to degrees F', but no such conversion is taking place. The sum of four readings, and then division by four, could be done much easier with an array.
You are then adding up four integers (remember 8 bit), will the sum be less than 255?.
Best Wishes |
|
|
mcataldo
Joined: 04 Jul 2010 Posts: 11 Location: Santiago, Chile
|
|
Posted: Thu Jul 08, 2010 11:57 am |
|
|
Ok, I made changes you told me, but LCD still shows the same values. Regarding temperatures addition, their values are small (no more than 20ºC), therefore this sum is always lower than 255.
I attach changes
Code: |
#device ADC=10
long temp_f1;
long temp_c1;
int temp_f;
int temp_c;
switch (counter)
{
case 0:set_adc_channel(0);
delay_us(20);
temp_f1=read_adc(); //10 bits
set_adc_channel(1);
delay_us(20);
temp_c1=read_adc(); //10 bits
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jul 08, 2010 12:41 pm |
|
|
Strip the program down to just a few lines. Use only one A/D channel.
Make that channel work.
If you have problems then post your complete short test program.
By complete I mean, post the #include for the PIC, #device statement,
#fuses statement, #use delay(), main(), closing braces, etc. In other
words, a compilable program. But it must be short (10 lines in main, max).
Also post your compiler version. |
|
|
mcataldo
Joined: 04 Jul 2010 Posts: 11 Location: Santiago, Chile
|
|
Posted: Thu Jul 08, 2010 1:21 pm |
|
|
Here is full code. I don't know how shrink it. Regarding ADC channels, I'm using two sensor, then I need two channels (or not?).
Code: |
#include <16F877A.h>
#device ADC=10
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT
#use delay(clock=4000000)
#include <LCD.c>
#priority rb,timer2
// Declaración de variables
int temp_sp;
int temp_sp_ee;
int temp_f;
long temp_f1;
long temp_f2;
long temp_f3;
long temp_f4;
int temp_c;
long temp_c1;
long temp_c2;
long temp_c3;
long temp_c4;
int temp_min;
int temp_max;
short temp_read = 0;
// Variables relacionadas a los Servos
signed int d_temp;
signed int d_temp_c;
signed int d_temp_f;
int r_temp;
int contador_c;
int contador_f;
int contador_p;
int N;
int counter = 0;
//short t_default = 1;
// Variables relacionadas a los pulsadores
short msg_max = 0;
short msg_min = 0;
short sp=0;
//short rebote = 0;
// Interrupción para señal PWM de Servomotores
#INT_TIMER2
void servos()
{
N++;
// El timer 2 está contando permanentemente, cuando llega al overflow se detiene
// el main y entra a la rutina de interrupción. Ejecuta los pasos que vienen a
// continuación y regresa a la línea de comandos del main.
switch (N)
{
case 1:output_low(PIN_C2);
set_timer2(contador_f);
break;
case 2:output_low(PIN_C1);
set_timer2(contador_p);
break;
case 7:output_high(PIN_C1);
output_high(PIN_C2);
set_timer2(contador_c);
N = 0;
break;
default:output_low(PIN_C1);
output_low(PIN_C2);
set_timer2(0);
break;
}
}
// Interrupción de cambio de temperatura de Setpoint por pulsadores
#INT_RB
void pulsadores()
{
disable_interrupts(INT_RB);
disable_interrupts(INT_TIMER2);
if(!input(PIN_B4))
{
if(temp_sp < temp_max)
{
temp_sp = temp_sp+1;
sp = 1;
}
else
msg_max = 1;
}
if(!input(PIN_B5))
{
if(temp_sp > temp_min)
{
temp_sp = temp_sp-1;
sp = 1;
}
else
msg_min = 1;
}
enable_interrupts(INT_TIMER2);
enable_interrupts(INT_RB);
}
void main()
{
set_tris_a(0xFF); // interesa que los puertos A0 y A1 sean salidas (0)
set_tris_b(0xFF); // Señal Pulsadores
set_tris_c(0x00); // Señal PWM para los servos
set_tris_d(0x00); // Puertos que van al LED
setup_adc(ADC_CLOCK_DIV_32);
setup_adc_ports(ALL_ANALOG);
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_comparator(NC_NC_NC_NC);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DIV_BY_16,255,1); //el postscale es para determinar cuantos
//overflows para gatillar una interrupción
// se acostumbra a poner aca el enable interrupts ...
lcd_init();
// Mensaje de Bienvenida
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc,"Ducha Electrica");
lcd_gotoxy(1,2);
printf(lcd_putc,"Automatizada V.6");
delay_ms(500);
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc," G.14 : Cataldo");
lcd_gotoxy(1,2);
printf(lcd_putc," Cortes");
delay_ms(500);
// Temperatura de Setpoint
temp_sp_ee = read_eeprom(0);
if(temp_sp_ee != 255)
{
temp_sp = temp_sp_ee;
}
else
{
temp_sp = 15;
}
//***** Setup del número al que debe contar timer 2 por defecto
enable_interrupts(GLOBAL);
enable_interrupts(INT_TIMER2);
enable_interrupts(INT_RB);
//se debe crear el primer pulso que posiciona las válvulas en reposo
output_high(PIN_C1);
output_high(PIN_C2);
contador_c = 94;
contador_f = 0;
contador_p = 156;
set_timer2(162);
N = 0;
while(TRUE)
{
// enable_interrupts(INT_RB);
// LECTURA DE TEMPERATURA ANÁLOGA
// Definición de los anchos de pulso para las señales PWM
// Los casos son los siguientes:
// Primero, para elevar la temperatura, no necesito hacer nada con las válvulas.
// Segundo, si la T° del agua caliente está por sobre la T° de setpoint, debo
// abrir la válvula de agua fría
// Agua fría
switch (counter)
{
case 0:set_adc_channel(0);
delay_us(20);
temp_f1=read_adc(); //10 bits
set_adc_channel(1);
delay_us(20);
temp_c1=read_adc(); //10 bits
counter = 1;
break;
case 1:set_adc_channel(0);
delay_us(20);
temp_f2=read_adc(); //10 bits
set_adc_channel(1);
delay_us(20);
temp_c2=read_adc(); //10 bits
counter = 2;
break;
case 2:set_adc_channel(0);
delay_us(20);
temp_f3=read_adc(); //10 bits
set_adc_channel(1);
delay_us(20);
temp_c3=read_adc(); //10 bits
counter = 3;
break;
default:set_adc_channel(0);
delay_us(20);
temp_f4=read_adc(); //10 bits
set_adc_channel(1);
delay_us(20);
temp_c4=read_adc(); //10 bits
temp_f=(temp_f1+temp_f2+temp_f3+temp_f4)/4;
temp_c=(temp_c1+temp_c2+temp_c3+temp_c4)/4;
temp_read = 1;
counter = 0;
break;
}
if (temp_read == 1)
{
temp_read = 0;
temp_min = temp_f+1;
temp_max = temp_f+4;
d_temp_c = (int)temp_c - temp_sp;
d_temp_f = temp_sp - (int)temp_f;
d_temp = (int)temp_c - (int)temp_f;
r_temp = 300*d_temp_f/d_temp;
if (d_temp_c >= 0 && d_temp > 0)
{
// Al contar hasta 31 se tienen 0,5 ms o -90°
// Al contar hasta 94 se tienen 1,5 ms o 0°
// Al contar hasta 156 se tienen 2,5 ms o +90°
// Al igualar el resultado a una variable del tipo int, ésta tomará el valor
// entero y para que quede redondeada se usa el truco de sumar 0.5
switch (r_temp)
{
case 75:contador_c = 210; // 256-46
contador_f = 162; // 256-(140-46)
contador_p = 116; // 256-140
break;
case 100:contador_c = 204; // 256-52
contador_f = 173; // 256-(135-52)
contador_p = 121; // 256-135
break;
case 150:contador_c = 194; // 256-62
contador_f = 193; // 256-(125-62)
contador_p = 131; // 256-125
break;
case 200:contador_c = 183; // 256-73
contador_f = 215; // 256-(114-73)
contador_p = 142; // 256-114
break;
case 225:contador_c = 177; // 256-79
contador_f = 225; // 256-(110-79)
contador_p = 146; // 256-110
break;
default:contador_c = 162; // 256-94
contador_f = 255;
contador_p = 162; // 256-94
break;
}
}
else
{
contador_c = 162;
contador_f = 255;
contador_p = 162;
}
set_timer2(contador_c);
//en caso de que se alcance la temperatura, entonces:
// Mostrar la temperatura leida en el Display
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc,"Tref:%2u\xDF",temp_sp);
lcd_gotoxy(9,1);
printf(lcd_putc,"Tcal:%2u\xDF",temp_c);
lcd_putc(223);
lcd_gotoxy(1,2);
printf(lcd_putc,"Tmin:%2u\xDF",temp_min);
lcd_gotoxy(9,2);
printf(lcd_putc,"Tmax:%2u\xDF",temp_max);
lcd_putc(223);
delay_ms(800);
// Guardar temperatura de setpoint
if(sp == 1)
{
sp = 0;
write_eeprom(0,temp_sp);
}
// Mostrar mensaje que se ha alcanzado temperatura máxima o mínima
if(msg_max == 1)
{
msg_max = 0;
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc,"Ha alcanzado la");
lcd_gotoxy(1,2);
printf(lcd_putc," temp. maxima ");
delay_ms(500);
}
if(msg_min == 1)
{
msg_min = 0;
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc,"Ha alcanzado la");
lcd_gotoxy(1,2);
printf(lcd_putc," temp. minima ");
delay_ms(500);
}
// if(rebote == 1);
// {
// rebote = 0;
// delay_ms(20);
// }
delay_ms(1000);
}
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jul 08, 2010 1:30 pm |
|
|
Someone else will have to help. |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Thu Jul 08, 2010 2:06 pm |
|
|
Get one channel to work properly before trying to use two channels. Write a test program that reads ONE channel and prints the raw result. Then add C or F conversion. Then add a second channel. Take many small steps. No one here is going to read your big program searching for errors. We don't have the time. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Thu Jul 08, 2010 2:33 pm |
|
|
Check your setup_adc()
you might want to be at DIV_8 instead of DIV_32.
If that doesn't help, rewrite an LCD routine to display all values and averages and call it in the DEFAULT of your CASE statement. That should show you exactly what is going right and what is going wrong. |
|
|
mcataldo
Joined: 04 Jul 2010 Posts: 11 Location: Santiago, Chile
|
|
Posted: Thu Jul 08, 2010 4:27 pm |
|
|
Thanks for your replies. I changed ADC clock value to 8 and I did a modification (I wrote the formula temp = 0.5*(read_adc()-1) and simulation shows correct values, but right now I've found another trouble: in real system, LCD shows value 160.
Thank you |
|
|
pmuldoon
Joined: 26 Sep 2003 Posts: 218 Location: Northern Indiana
|
|
Posted: Fri Jul 09, 2010 5:47 am |
|
|
Are you sure your voltages make sense?
What is the supply voltage to the PIC? +5V?
What is the voltage at the AD pin?
Since you are not using the reference inputs of the AD, the PIC will see 1023 * Vin/Vcc as the result of the AD conversion.
At 5V Vcc you should get a 10 on the LCD with about 49mV input. |
|
|
|
|
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
|