Problema con adc pic 16f716

Darkbytes, tienes una voluntad enorme y lo has demostrado con creces, de antemano gracias.
Probaré tu etapa divisora fuera de mi placa, desmonto las resistencias que puse y lo armo en el aire. Como la PCB ya está hecha con un display de 4 x7seg CA los comunes los acciono de la manera que adjunto, de esta manera el algoritmo para escribir en los displays funciona bien con numeros de 4 digitos.

En tu código de donde aparece el 31.0 al transformar del valor leido por el ADC en el valor de voltaje?
 

Adjuntos

  • ScreenShotxd123.png
    ScreenShotxd123.png
    4 KB · Visitas: 5
El programa y el esquema que subí es para displays de cátodo común.
Para displays de ánodo común se tendrían que modificar ambos.
Como no adjuntaste un esquema, realicé el programa para displays de cátodo común.

Para controlar displays de ánodo común, se debe usar un transistor PNP con el emisor hacia positivo.
Entonces se requiere activación por 0 lógico en la base y la salida es por el colector.

Y sí, el número 31.0 será el valor máximo en voltios que será mostrado en los displays antes del punto decimal.
Se coloca el . en este caso .0, para indicarle al compilador que se está trabajando con punto flotante.

Nota que el programa que subí, está realizado para mostrar dos decimales.
Es una adaptación de un programa realizado en este tema: Voltímetro con el ADC del PIC
 
Use primero el metodo de los pnp y el 0 logico para activar los anodos y funcionaba mal, almenos en el proto, asi que preferí usar npn y colector comun esta funcionando así en el pcb y 0 problemas.

Ahora bien, en el código pones lectura = (31.0 * read_adc() / 256); así cuando hayan 5v en el ADC el valor de read_adc será 255, no debería ese 256 ser 255?

Otra cosa, la diferencia entre el algoritmo que me expones y el mio es que tu le dices al micro que cuando hayan 31V en la entrada, entonces en el RA1 habrán 5V, lo cual lo aseguras con el zenner de 5V1 y calibrandolo mediante un preset, sin duda es una buena idea (Y). En cambio, yo hago que el micro calcule el valor a mostrar en terminos de R1 y R2 cuyos valores se los paso por software, partiendo sólo de la formula de un divisor de tensión y que cuando hay 5V en RA1 read_adc vale 255. ¿Podrías darme alguna sugerencia sobre por que no funciona así como está? lo he pensado mucho y creo que es por los valores de las resistencias que he usado, voy a probar la etapa que me muestras, puesto que estoy convencido que ahi esta el error, ya que al sacar el pic y meter tension por la entrada a medir el divisor funciona bien, indicando que no hay errores ni falsos contactos en el pcb, la tenión en R2 cae a casi 0 cuando inserto el pic el PIC :S

Dormiré ahora, mañana temprano pruebo tu etapa y comento, usaré tu codigo solo para modificar el mio en cuanto a las directivas del adc y realizar la conversión, asegurando los 5V en RA1 por hardware.

Saludos y gracias de nuevo
 
no confundas los 255(0 a 255) con la 256 "pasos" de la conversión
Para 8 bit 256 desde la 0 a la 255
para 10 bit 1024 desde la 0 a la 1023
 
Última edición:
Usé primero el método de los PNP y el 0 lógico para activar los ánodos y funcionaba mal, al menos en el protoboard.
Así que preferí usar NPN y colector común. Está funcionando así en el pcb y 0 problemas.
Debe funcionar con los transistores PNP. Algo debiste tener mal en el programa o en el diseño.
Ahora bien, en el código pones lectura = (31.0 * read_adc() / 256);
Así cuando hayan 5V en el ADC el valor de read_adc será 255. ¿No debería ese 256 ser 255?
Pues resulta que no y por acá sobre el mismo tema que mencioné anteriormente se explica el por qué.
Y es precisamente por lo que te aclaró pandacba.
Otra cosa. La diferencia entre el algoritmo que me expones y el mío es que tú le dices al micro que cuando hayan 31V en la entrada, entonces en el RA1 habrán 5V, lo cual lo aseguras con el zenner de 5V1 y calibrándolo mediante un preset, sin duda es una buena idea (Y).
Así es, pero ese divisor de tensión está calculado para que cuando existan 30V en la entrada, en la salida se obtengan 5V, aún sin estar el diodo Zener.
R1 = 56KΩ y R2 debería ser de 11.2KΩ, pero como no existe un valor comercial de 11.2KΩ y para obtener un ajuste del voltaje de entrada, se usó un potenciómetro de 22KΩ en serie con una resistencia de 1KΩ
Entonces el valor medio del potenciometro deberá estar cerca de los 10KΩ para estar próximo a los 11.2KΩ que se requieren.
El diodo Zener es por protección de la entrada del PIC ante un voltaje superior a los 30V en la entrada del divisor de tensión.
Si eso ocurre, el voltaje en la salida del divisor sobrepasará los 5.5V que soporta el PIC como máximo y se dañaría el PIC.
Entonces el diodo Zener se encargará de reducir ese voltaje excedente en 5.1V.
En cambio, yo hago que el microcontrolador calcule el valor a mostrar en términos de R1 y R2, cuyos valores se los paso por software, partiendo sólo de la fórmula de un divisor de tensión y que cuando hay 5V en RA1 read_adc vale 255.
¿Podrías darme alguna sugerencia sobre por que no funciona así como está?
Deberías analizar el resultado que obtendrías con la fórmula que estás usando.
Aparte, ten en cuenta que el PIC está leyendo un voltaje, no una resistencia.
Como no estás usando una referencia de tensión externa, el PIC usará una interna por la configuración que se tiene en setup_adc(párametros); y ésta será el voltaje de alimentación.
Entonces esa referencia interna será el voltaje con el que estás alimentando al PIC.
Por lo tanto el voltaje de referencia estará en 5V +- o sea, el voltaje que entregue tu fuente de poder.
Así, cuando en la entrada existan 5.0V, el ADC estará en 255 por ser de 8 bits.

Si realizamos un cálculo sobre el valor obtenido mediante la fórmula que empleaste, se obtiene lo siguiente:
r1 = 100
r2 = 10
lectura = 255, que viene siendo cuando ADCIn = 5V.
vi = 1.9607 * ((r1 + r2) / r2) * lectura
vi = 5499.7635
Como "vi" es una variable de 16 bits, entonces no se toman en cuenta los decimales y el valor queda en 5499
Y ese valor una vez separado será lo que se mostrará en los displays.
Si se coloca el punto en el primer dígito, serán 5.499V.
Y si se coloca en el segundo dígito, serán 54.99V.
Entonces ese valor está muy fuera de los 30V que pretendes leer. ¿No? :cool:

Como aporte y parte del tema, adjunto el programa que subí anteriormente, pero ahora para displays de ánodo común.
 

Adjuntos

  • 16F716 Voltímetro 30V Displays AC.rar
    28.1 KB · Visitas: 11
Última edición:
Cierto, pero los 30V máximo extreman mucho la entrada del PIC por eso le doy un poco más, así ni por si acaso me paso. He quemado AVR metiendoles 5.1V (de un zenner) en el ADC, no sé si pasará lo mismo con los PIC pero prefiero no arriesgarme.

Respecto a lo del 256 creo que el adc no tomara nunca el valor 256 por lo que cuando hayan 31V en la entrada, aunque hayan 5V (suponiendo que la alimentacion del pic sean 5v exactos) en el divisor este leerá 255 y segun la formula que expones eso sería 30.8789 que no son 31 :S

Si hay algo que se me va me dices.
No he llegado a casa aun para probar tu etapa, llegando la pruebo y comento
 
Última edición:
He quemado AVR metiéndoles 5.1V (de un Zener) en el ADC.
No sé si pasará lo mismo con los PIC pero prefiero no arriesgarme.
Los PIC son muy tolerantes. Alguna vez en un circuito con un PIC12F675, le estaban llegando 12V en una entrada, se calentaba y hacía cosas extrañas, pero no se dañó. :eek:
Lo mismo he visto con un PIC16F88. No obstante, un PIC16F877A se dañó con tan solo poner un pin de salida hacia negativo.
Respecto a lo del 256 creo que el ADC no tomará nunca el valor 256 por lo que cuando hayan 31V en la entrada, aunque hayan 5V (suponiendo que la alimentación del PIC sean 5v exactos) en el divisor éste leerá 255 y según la fórmula que expones eso sería 30.8789 que no son 31 :S
Pues resulta que no y por acá sobre el mismo tema que mencioné anteriormente se explica el por qué.
Lee ese tema. Ahí se trata del ADC a 10 bits pero sucede lo mismo con 8 bits.
 
ya lo entendí jeje, probe la etapa divisora que expones, no tenia R de 56k así que puse una de 47K, así con R2=10k el valor máximo que puedo meter es de 32.9V que ya es más cercano a los 30V.

Sin embargo apareció otro problema, los dos ultimos digitos cambian muy rápido y no se aprecian, ¿condensador paralelo muy bajo? probe hasta .1uF y seguia el problema. adjunto como quedó el código.

PD el punto decimal era RA4 y no RA3 como tenía puesto antes, eso entraba en conflicto con setup_adc_ports(AN0_AN1_AN3) supongo.

Ahora ya al menos muestra los voltajes, falta solucionar eso del cambio muy rápido y calibrarlo. Probaré con que funciona mejor si con tu algoritmo o con el mio una vez que tenga eso solucionado, de todas formas debo tener muy en cuenta el valor real de las resistencias, la de 47K resultó ser de 46.2K segun un medidor mas preciso que tengo.

Te cuento que la idea es añadir luego un amperimetro en base a un toroide de polvo de hierro y un sensor hall lineal.

Código:
#include <16f716.h> //pic16f716
#device adc=8       //ADC 8 bits
#fuses NOBROWNOUT //
#use delay(crystal = 3500000)
#byte portb=6        // Puerto B en la dirección 06h
//#BYTE porta=5       //puerto A en 05h 
//#BYTE trisa=133     //TRISA en 85h
#BYTE trisb=134     //TRISB en 86h
#DEFINE del 2
#define punto  pin_a4

    int16 unidades (int16 numero) ;
    int16 decenas (int16 numero) ;
    int16 centenas (int16 numero) ;
    int16 miles (int16 numero) ;
    void print_seg (int16 value) ;
    
void main() 
{
   float r1;
   float vi;
   float r2;
   int16 lectura,voltaje;

   r1=46.2; 
   r2=10.0; 

   trisb=0; 

   
   setup_adc_ports(AN0_AN1_AN3); 
   setup_adc(ADC_CLOCK_INTERNAL); 
  
   while (true)
   {
         set_adc_channel(1);
         delay_us(50); 
         lectura=read_adc();
         delay_us(50);
         //vi=5500*lectura/256;
         vi=1.9607*((r1+r2)/r2)*lectura;
         //delay_ms(30);
         //vi = (32.9 * lectura / 256);
         //voltaje = (vi * 100);
         voltaje=vi;
         
         print_seg(voltaje);
         //delay_ms(50);
         //vi=5500*lectura/256;
         //print_seg(vi);
         //delay_ms(2000);
      }
      
   }

int16 unidades(int16 numero)
{
   int16 unidades = 0;
   unidades = numero % 10;
   return unidades;
}
int16 decenas(int16 numero)
{
   int16 decenas = 0;
   decenas = (numero / 10) % 10;
   return decenas;
}
int16 centenas(int16 numero)
{
   int16 centenas = 0;
   centenas = (numero / 100) % 10;
   return centenas;
}
int16 miles(int16 numero)
{
   int16 miles = 0;
   miles = (numero / 1000) % 10;
   return miles;
}
void print_seg(int16 value)
{
   portb=16+unidades(value);
   output_low(punto); //Pone a 0 RA4 para prender punto dec.
   delay_ms(del);
   output_high(punto);
   portb=32+decenas(value);
   delay_ms(del);
   portb=64+centenas(value);
   delay_ms(del);
   portb=128+miles(value);
   delay_ms(del);
}

Gracias
 
y en vez de utilizar displays y transistores, utilizas LCD, asi no tendrias problemas con los tiempos y eso, y consumiria menos corriente? digo, talvez estoy mal
 
Obviamente es mejor con LCD pero
1) con displays es más bonito, mas old school
2) mas barato, los display los recicle de un microondas
3) mejor guardar los lcd para proyectos más ambiciosos
4) ya hice el pcb para displays xd

cuando necesite algo más acabado y más util sin duda usaré un LCD, por ahora debo usar la chorrera de displays que tengo por ahi, todos reciclados :B
 
Sin embargo apareció otro problema, los dos últimos dígitos cambian muy rápido y no se aprecian. ¿Condensador paralelo muy bajo? Probé hasta .1uF y seguia el problema.
Coloca un capacitor de un valor mayor. Por ejemplo: 0.47uF. (470 Nanofaradios)
Ese es el valor que he usado para estabilizar la tensión de entrada en paralelo con el diodo Zener.
Si no logras que se estabilice, puedes ir aumentando el valor, pero no es recomendable que sea muy alto.
 
Es lo que pensaba, me dio flojera seguir cambiando valores del cap, Comprendo que la variación detensión al mover el pote puede hacerse más lenta al usar un cap muy grande. Lo pruebo en un santiamen. gracias
 
Armé el proyecto para ver lo que hacía físicamente, y con displays de 7 segmentos es muy notorio el cambio en los dígitos de las unidades.
Colocando el capacitor no fue la solución definitiva, así que cambié el valor de retardo a 4 ms.
Valores mayores a 4 ms ya hacen que se presente el efecto estroboscópico en los displays.
Con ese retardo se mejoró bastante la transición de los dígitos y por lógica también aumentó el brillo.
No conseguí una mejoría óptima, pero si se mantuvieron más estables.

Adjunto unas fotos de las lecturas que realicé.
 

Adjuntos

  • DSC00094.JPG
    DSC00094.JPG
    56.7 KB · Visitas: 7
  • DSC00095.JPG
    DSC00095.JPG
    58.4 KB · Visitas: 9
  • DSC00096.JPG
    DSC00096.JPG
    55.2 KB · Visitas: 7
Última edición:
Ya solucioné el problema del cambio rápido. Era debido a que luego del regulador no puse un capacitor para filtrar.
Así la referencia cambiaba muy rápido. Con un capacitor de 220uF se solucionó.

---------- Actualizado ----------


No logro calibrar bien el voltímetro por software.
Como ya tengo el pcb hecho, no tengo como ponerle un potenciómetro en serie con la R de 10k.
La otra la puse de 47K, así si en la de 10k hay 5V, entonces en la entrada del divisor hay 28,5v (se que no son 30v pero la fuente donde pretendo meter el voltimetro lleva un LM317 hasta 24V.
De todas formas le puse un Zener de 5v1 en paralelo con la R de 10k)
Así que la fórmula que uso en el código es:

voltaje = (28.5*read_adc()/256)

Luego multiplico por 100. ¿Alguna forma de lograr una buena calibración por software?
 
Última edición por un moderador:
Hola,
Aprovecho para preguntar sobre este tema dado que tengo una consulta con respecto a los ADC de los PIC.
Realicé un programa sencillo para un voltímetro a través de un LCD pero notaba que la tensión que mostraba variaba mucho.
Luego, una prueba que hice fue modificar el programa de modo que realice 40 mediciones y muestre la máxima, la mínima y el promedio.
El resultado de varias mediciones fue de hasta una diferencia de 20 cuentas.
Para la tensión de referencia utilicé un TL431 y para alimentar al PIC un 7805. El circuito está montado sobre un protoboard.

La consulta es ¿es normal que mida con tantas cuentas de diferencia en cada medición, o si lo normal es que la variación sea de +-1cta?

Código:
#include <16f887.h>
#device ADC = 10

#FUSES HS                       //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#use delay(clock=12M)

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES NOPUT                    //No Power Up Timer
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOBROWNOUT               //No brownout reset

#BYTE TRISA = 0X86
#BYTE PORTB = 0X06

#define LCD_ENABLE_PIN  PIN_D3
#define LCD_RS_PIN      PIN_D1                                  
#define LCD_RW_PIN      PIN_D2                                    
#define LCD_DATA4       PIN_D4                                    
#define LCD_DATA5       PIN_D5                                    
#define LCD_DATA6       PIN_D6                                    
#define LCD_DATA7       PIN_D7 

#include <lcd.c> 

int16 conv[40],i=0,max=0,min=1000;
int16 suma=0;

void main()
{
   TRISA = 0X00; // Todo Puerto B como salida
   
   lcd_init();   
   lcd_putc("Voltimetro");
   
   SETUP_ADC(ADC_CLOCK_DIV_32);
   SETUP_ADC_PORTS(sAN0|sAN1|VSS_VREF); // LEYENDO EL 16F887.H NOS ENCONTRAMOS CON:
     
   while(i<90)
   {
      SET_ADC_CHANNEL(0);
      delay_us(100);
      conv[i]=READ_ADC();
      i++;
   }
   for(i=0;i<40;i++)
   {
      if(conv[i]>max)
         max=conv[i];
      if(conv[i]<min)
         min=conv[i];
      suma+=conv[i];   
   }     
 
   lcd_init();
   printf(lcd_putc,"Prom=%f",(float)suma/40);
   lcd_gotoxy(1,2);
   printf(lcd_putc,"max=%Lu min=%Lu",max,min);
}


Actualizo:
Creo que me apuré en preguntar, encontré esta respuesta que es la que estaba buscando:
https://www.forosdeelectronica.com/posts/24567/


De todos modos quedo atento los comentarios de confirmación.
 
Última edición:
Atrás
Arriba