TODOPIC
Microcontroladores PIC => Todo en microcontroladores PIC => Mensaje iniciado por: lopb en 03 de Noviembre de 2010, 16:44:25
-
Hola, estoy tratando de asignarle a una variable int16 un valor de 10 bits del ADC de un 16f876a
Pero me devuelve solo ocho bits.
mi codigo en el header (16f876a.h) es el siguiente:
#device PIC16F876A
#device ADC=10
Mi programa es el siguiente
#if defined(__PCM__) //.
#include <16F876A.h>
#include <stdlib.h>
#use delay(clock=20000000)
#fuses HS,NOWDT,NOPROTECT
#use rs232(baud=2400, xmit=PIN_C6, rcv=PIN_C7)
int16 temp;
int value;
float tempan1;
float voltsan0;
void main(void){
set_tris_b (0x00);
while (TRUE){
setup_adc (adc_clock_div_32);
setup_adc_ports (ALL_ANALOG);
set_adc_channel (0); //Elige canal a medir RA0
delay_us (20);
value=read_adc(); //Capturamos señal analógica que entra por el canal previamente seleccionado (RA0)
set_adc_channel (1); //Elige canal a medir RA0
delay_us (20);
temp=read_adc(); //Capturamos señal analógica que entra por el canal previamente seleccionado (RA0)
voltsan0 = 5.0 - (value * (5.0 / 256.0 ));
tempan1 = temp * (5.0*100.0/256.0);
printf("Valor volts, temp, hextempout:: %1.2f, %2.2f , %4X\n\r", voltsan0, tempan1, temp);
output_high(PIN_B1); //(34)
delay_ms(500);
output_low(PIN_B1);
delay_ms(500);
}
}
gracias
-
Hola,
no programo en CCS pero por lo pronto me parece interesante que "castees" los enteros a float antes de operar con ellos... es probable que el "casteo" por defecto de CCS te coja por defecto un INT8 cuando se trata de un entero....
voltsan0 = 5.0 - ((float)value * (5.0 / 256.0 ));
tempan1 = (float)temp * (5.0*100.0/256.0);
No te aseguro que sea esto...
Saludos
-
temp si lo tienes como int16 pero value lo tienes como int .
-
En teoría no es necesario hacer cast porque según las reglas de promoción automática de expresiones , el compilador hará las conversiones necesarias para llevar todos los operandos al tipo mayor, y al indicar 5.0, 256.0, el tipo mayor es float, pero bueno es CCS así que se puede esperar cualquier cosa :D Lo que veo si, es que Value es de 8 bits, en cambio Temp si es de 16-bits.
Saludos!
-
En teoría no es necesario hacer cast porque según las reglas de promoción automática de expresiones , el compilador hará las conversiones necesarias para llevar todos los operandos al tipo mayor, y al indicar 5.0, 256.0, el tipo mayor es float, pero bueno es CCS así que se puede esperar cualquier cosa :D Lo que veo si, es que Value es de 8 bits, en cambio Temp si es de 16-bits.
Saludos!
Estoy de acuerdo... pero a veces el cast automático te juega malas pasadas; p.e. si no casteas un literal en una operación con una variable... el resultado va efectivamente al tipo "mayor": al de la variable. Es decir: si un int8 como var= 100 ==>> 10*var = (1000%255) en vez de 1000... no se si me explico???
Saludos
-
HOla, gracias por las respuestas tan rapidas.
Converti las la variable temp a int16 y sigue igual
De paso lei en la ayuda del compilador una constante que se llama con getenv y te devuelve el valor de bits ADC
getenv("ADC_RESOLUTION");
La ejecute y me esta devolviendo 10, asi que el pic esta funcionando a 10 evidentemente, ahora el tema es que me devuelve 8.
Me dijeron que en asembler hay que tomar los 2 bytes por separado.
Puede ser?
gracias
-
Value es la variable de 8-bits :mrgreen:
-
Hola lopb, estuve mirando tu programa y le veo algunas inconsistencias. Sabiendo que el valor que va a devolver read_adc() es de 10 bits, debes configurar a las dos variables que va a leer el ADC como long temp, value;. La instrucción de configuración set_tris() no debe ir dentro del bucle y finalmente en los cálculos para 10 bits los debes hacer basado en 1024 (10 bits) y no en 256 (8bits). Te ordené un poco el código para que lo ensayes y nos cuentes como te va:
#include <16F876A.h>
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=20000000)
#use rs232(baud=2400, xmit=PIN_C6, rcv=PIN_C7)
long temp, value;
float tempan1, voltsan0;
void main(void)
{
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_DIV_32);
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_ccp1(CCP_OFF);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
set_tris_b (0x00);
while (TRUE)
{
set_adc_channel (0); //Elige canal a medir RA0
delay_us (20);
value=read_adc(); //Capturamos señal analógica que entra por el canal previamente seleccionado (RA0)
set_adc_channel (1); //Elige canal a medir RA0
delay_us (20);
temp=read_adc(); //Capturamos señal analógica que entra por el canal previamente seleccionado (RA0)
voltsan0 = 5.0 - (value * (5.0 / 1024.0 ));
tempan1 = temp * (5.0 * 100.0 / 1024.0);
printf("Valor volts, temp, hextempout:: %1.2f, %2.2f , %4X\n\r", voltsan0, tempan1, temp);
output_high(PIN_B1); //(34)
delay_ms(500);
output_low(PIN_B1);
delay_ms(500);
}
}
Por curiosidad, para que es la variable tempan1?
Saludos!
-
Hola lopb, estuve mirando tu programa y le veo algunas inconsistencias. Sabiendo que el valor que va a devolver read_adc() es de 10 bits, debes configurar a las dos variables que va a leer el ADC como long temp, value;. La instrucción de configuración set_tris() no debe ir dentro del bucle y finalmente en los cálculos para 10 bits los debes hacer basado en 1024 (10 bits) y no en 256 (8bits). Te ordené un poco el código para que lo ensayes y nos cuentes como te va:
#include <16F876A.h>
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=20000000)
#use rs232(baud=2400, xmit=PIN_C6, rcv=PIN_C7)
long temp, value;
float tempan1, voltsan0;
void main(void)
{
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_DIV_32);
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_ccp1(CCP_OFF);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
set_tris_b (0x00);
while (TRUE)
{
set_adc_channel (0); //Elige canal a medir RA0
delay_us (20);
value=read_adc(); //Capturamos señal analógica que entra por el canal previamente seleccionado (RA0)
set_adc_channel (1); //Elige canal a medir RA0
delay_us (20);
temp=read_adc(); //Capturamos señal analógica que entra por el canal previamente seleccionado (RA0)
voltsan0 = 5.0 - (value * (5.0 / 1024.0 ));
tempan1 = temp * (5.0 * 100.0 / 1024.0);
printf("Valor volts, temp, hextempout:: %1.2f, %2.2f , %4X\n\r", voltsan0, tempan1, temp);
output_high(PIN_B1); //(34)
delay_ms(500);
output_low(PIN_B1);
delay_ms(500);
}
}
Por curiosidad, para que es la variable tempan1?
Saludos!
EDITADO:
Bmb, te pasaste en darme un codigo :)
Sos muy amable.
Bueno, en conclusion les cuento que los valores aun oscilan. Tengo temperaturas desde 19 grados a 24, con este ultimo codigo.
Habia puesto una respuesta anterior que funcionaba, pero me equivoque y tenia conectada la entrada analogica a un divisor resistivo que me entrega media fuente para ver si la salida hexa se imprime en 10 bits. Pero no, sigue trabajando en ocho bits, a pesar de que se que esta en 10 bit por la funcion getenv("ADC_RESOLUTION");
:huh:
-
Bueno, en conclusion les cuento que los valores aun oscilan. Tengo temperaturas desde 19 grados a 24, con este ultimo codigo.
Ensaya cambiando esta instrucción: setup_adc(ADC_CLOCK_DIV_32); por esta: setup_adc(ADC_CLOCK_INTERNAL);
Que tipo de sensor de temperatura le tienes? Como está conectado? Si puedes subir el circuito mejor.
Saludos!
-
Bueno, en conclusion les cuento que los valores aun oscilan. Tengo temperaturas desde 19 grados a 24, con este ultimo codigo.
Ensaya cambiando esta instrucción: setup_adc(ADC_CLOCK_DIV_32); por esta: setup_adc(ADC_CLOCK_INTERNAL);
Que tipo de sensor de temperatura le tienes? Como está conectado? Si puedes subir el circuito mejor.
Saludos!
Es un lm35, el circuito no lo tengo aun, estoy haciendo pruebas.
El lm35 tiene 3 patitas, dos son alimentacion y una va directo a an1 del 16f.
Los valores oscilan bastane, yo supongo porque esta trabajando en 8 bits o hay un error de quantizacion.
voy a probar con eso que me dijiste =
-
Mmmmhh, creo que mejor le das una mirada a este enlace (http://www.todopic.com.ar/foros/index.php?topic=14917.msg108938#msg108938) donde el colega dogflu66 plantea un acondicionador de señal para el LM35.
Saludos!
-
Mmmmhh, creo que mejor le das una mirada a este enlace (http://www.todopic.com.ar/foros/index.php?topic=14917.msg108938#msg108938) donde el colega dogflu66 plantea un acondicionador de señal para el LM35.
Saludos!
No se si armar ese circuito... a mi me parece que el problema viene por otro lado.
El conversor esta trabajando en 10 bits pero a la variable se le asignan unicamente los primeros 8 bits, debe ser algun error muy simple.
pero no se me ocurre que.
-
Por lo que puedo ver en la hoja de datos del LM35 está dando 10 mV por grado centígrado. Teniendo en cuenta que el voltaje de referencia de ADC que tienes actualmente configurado es el mismo de la fuente del PIC (5V) y suponiendo que la temperatura es de 25ºC:
Salida del LM35 = 25º * 0.01V = 0.25 Voltios
1024 / 5 * 0.25 = 51 aprox.
Como puedes ver para una temperatura de 25 grados centígrados el ADC del micro te estará entregando un valor de aproximadamente 51 que es por supuesto un valor de 8 bits y que con cualquier mínima variación se va a ver reflejada en el valor de la temperatura debido a la muy baja resolución. Por eso es que se hace necesario implementar un acondicionador de señal. Otra solución es que le pongas al PIC un voltaje de referencia muy bajo correspondiente digamos al valor máximo de salida que te va a dar el LM35.
Saludos!
Edito: por cierto me acabo de dar cuenta que el voltaje en AN0 no sería: voltsan0 = 5.0 - (value * (5.0 / 1024.0 )); sino que sería: voltsan0 = value * 5.0 / 1024.0;
-
Por lo que puedo ver en la hoja de datos del LM35 está dando 10 mV por grado centígrado. Teniendo en cuenta que el voltaje de referencia de ADC que tienes actualmente configurado es el mismo de la fuente del PIC (5V) y suponiendo que la temperatura es de 25ºC:
Salida del LM35 = 25º * 0.01V = 0.25 Voltios
1024 / 5 * 0.25 = 51 aprox.
Como puedes ver para una temperatura de 25 grados centígrados el ADC del micro te estará entregando un valor de aproximadamente 51 que es por supuesto un valor de 8 bits y que con cualquier mínima variación se va a ver reflejada en el valor de la temperatura debido a la muy baja resolución. Por eso es que se hace necesario implementar un acondicionador de señal. Otra solución es que le pongas al PIC un voltaje de referencia muy bajo correspondiente digamos al valor máximo de salida que te va a dar el LM35.
Saludos!
Edito: por cierto me acabo de dar cuenta que el voltaje en AN0 no sería: voltsan0 = 5.0 - (value * (5.0 / 1024.0 )); sino que sería: voltsan0 = value * 5.0 / 1024.0;
Hola, error mio!
resulta que la fuente no tenia filtrado y el micro estaba conmutando mientras leia y caia un poco la tension, por ende el valor de referencia del micro en este caso vref+ y - variaban un poco de modo que la salida final tambien variaba.
Ahora esta filtrado con un capacitor para altas y bajas, y ya se estabilizo la medicion.
Respecto a el valor de 10 bits, pude imprimir la variable, pase todas a float y la converti en una cadena y la imprimi sobre el display (porque desactive el rs232).
Gracias por su ayuda.
Lo unico que no entendi es que estaba imprimiendo el valor en hexa de temp y me daba ff cuando estaba lleno y 3f a media fuente, lo cual me dio la impresion de que la variable estaba en 8 bits, pero despues lo reviso bien y les cuento.
Para que quede, use getenv("ADC_RESOLUTION") para ver si estaba en 10 bits el micro.
salu2
-
Gente una consulta: Estoy haciendo una simulacion en Proteus de un voltimetro y estoy usando un 16F873A. El tema es que sin querer declare #device adc=16 y el compilador me lo tomo. Usando el wizard del PICC hay una opcion que te pone el adc=16... Como puede ser esto? La resolucion de este PIC no es de 10 bits? Gracias!!!
-
Eso deberia ser un error de compilacion..
A no ser que genere esos 16 bits por oversampling, lo cual quiere decir que es posible hacerlo. Puede que me vaya por las ramas aca pero bueno lo pongo por las dudas:
Donde se requiere 4^n mas muestras por cada n bits mayor al ADC.
Con un ADC de 12, para obtener 13 bits deberia generar 4 veces mas muestras, Entonces esas 4 muestras se "promedian", en realidad se usa algo llamado "Decimation" que se usa en tratamiento digital de señales. Matematica discreta para todos gratis
Algo simple, la suma de los cuatro valaores y rotado una ves a la derecha ( divido por 2 ), la suma te dejaria con un resultado de 14 bits y la division con un resultado de 13bits.
Pero para que esto sea valido deberia de variar lo menos posible la señal de entrada mientras se este muestreando.
Para 4 bits serian 256 muestras mas :/.. Tambien esto mejora la relacion señal-ruido. Algunos documentos que hablan del tema:
http://ww1.microchip.com/downloads/en/AppNotes/Achieving%20Higher%20ADC%20Resolution%20Using%20Oversampling%2001152A.pdf
http://www.atmel.com/images/doc8003.pdf
http://en.wikipedia.org/wiki/Decimation_(signal_processing)
-
Lo que hace CCS para obtener 16 bits es simplemente no rotar el valor del ADC y dejarlo tal cual es recibido del conversor A/D.
Si le das 5 volts a la entrada analogica y haces la conversion vas a ver un resultado en decimal de hasta 65472 que corresponde al binario 1111111111000000.
Esto es por lo que te digo... alli no hay decimation, solo aprovechan como viene el valor desde el conversor A/D... :lol: :lol:
-
Adjunto imagen:
(http://sia1.subirimagenes.net/img/2015/03/08/150308053053315892.png) (http://www.subirimagenes.net/i/150308053053315892.png)
El voltimetro de la izquierda es la señal a medir y el voltimetro de la derecha es lo que esta entrando a la entrada analógica del micro. Estan entrando exactamente 5V. En la fila superior del display estoy mostrando el valor de conversion y fijense que me esta tomando hasta 65408.. Eso es cercano a los 16 bits.
-
Si haces cambiar en pasos de 100 mV y tomas el valor decimal y lo pasas a binario, veras que de los 16 bits los 6 de menor peso NUNCA CAMBIAN, esto es porque tienes solo 10 bits de la conversión y no le pidas mas peras al olmo... :mrgreen: :mrgreen:
-
xD usa el justificado a la izquierda y listo xD ya tenes 16 bits (?
-
Por muy precisa que sea tu conversión ADC, así tengas 16 bits reales de precisión, el LM35 especifica en su hoja de datos que es confiable a intervalos de medio grado, de manera que desde 0° hasta 100° no tendrás más que 200 valores diferentes, muy por debajo de los 1023 valores de un ADC de 10 bits; en teoría un ADC de 8 bits tendría un trabajo cómodo con un LM35.
Si requieres más precisión te recomiendo cambiar de sensor.