Voltímetro de 4 dígitos

Este pequeño proyecto es un voltímetro básico y expandible, digo básico porque el funcionamiento es bastante primitivo dividiendo el código en tres partes: El control multiplexado de displays, La función de conversión para separar un numero entero en 4 dígitos y La lectura del ADC para leer el valor de tensión.
Por otro lado cuando digo Expandible quiere decir que podremos aumentar su representación, cantidad de dígitos, parámetros de medición, adaptación electrónica de señal, muestro, etc...
En este caso se trata de utilizar las funciones básicas ya que en la sencillez se comprende mejor el código y su principio de funcionamiento.
Hay que tener en cuenta que la tensión máxima para el ADC del microcontrolador es de 5V continuos, si queremos realizar una medición que supere este valor tendremos que utilizar un divisor de tensión. 

En nuestro caso mediremos hasta los 4,999V, nuestro voltímetro tendrá una resolución de 4.7mV y para garantizar la estabilidad realizara una toma de 500 muestras cada unos 5ms que luego procesara su promedio y lo mostrara en el display de 4 dígitos.
El ADC del microcontrolador estará funcionando a 10Bit, es decir a 1024 cuentas que funcionando con un máximo de tensión de 4,999V tendremos una representación de 4.7mV lo cual hace poco útil tener 3 dígitos decimales, ya que con 2 dígitos decimales seria útil. (esto quedara para la segunda versión donde pondremos un limite de unos 5V).

Por otro lado nos quedara el tratado de los displays que sera la técnica común de multiplexado, es decir, dato del display 1 y se activa el display 1, mientras el resto sigue desactivado, luego dato del display 2 y se activa el display 2 mientras el resto sigue desactivado, y asi para cada display de esa forma cada dígito muestra el valor que debe, al ser una acción repetitiva y rápida se genera una persistencia que al ojo humano parecen estar los cuatro dígitos encendidos a la vez pero en realidad se activan de uno en uno.


  1. //Función que se ejecutara en paralelo al programa principal sobre el Timer0
  2. void timer0_mux(int8 dig4, int8 dig3, int8 dig2, int8 dig1){
  3.    output_c(0b00000001);               //Selección del display 1
  4.    output_b(num[dig1]);                //Valor tratado del dígito 1 en la salida B
  5.    output_high(PIN_B7);                //Prendemos el punto decimal
  6.    delay_ms(2);                        //Delay de multiplexado de 2ms
  7.    output_c(0b00000010);               //Selección del display 2
  8.    output_b(num[dig2]);                //Valor tratado del dígito 1 en la salida B
  9.    output_low(PIN_B7);                 //Apagamos el punto decimal
  10.    delay_ms(2);                        //Delay de multiplexado de 2ms
  11.    output_c(0b00000100);               //Selección del display 3
  12.    output_b(num[dig3]);                //Valor tratado del dígito 1 en la salida B
  13.    output_low(PIN_B7);                 //Apagamos el punto decimal
  14.    delay_ms(2);                        //Delay de multiplexado de 2ms
  15.    output_c(0b00001000);               //Selección del display 4
  16.    output_b(num[dig4]);                //Valor tratado del dígito 1 en la salida B
  17.    output_low(PIN_B7);                 //Apagamos el punto decimal
  18.    delay_ms(2);                        //Delay de multiplexado de 2ms
  19.    set_timer0(0);                      //Seteo del Timer0
  20. }

Por ultimo la función encargada de tomar un numero entero y dividirlo en 4 se realiza en base a división y restos, con el símbolo "/" dividimos y con "%" tomamos el resto.


  1. void convert(int16 valor){                  //Función para convertir entero en dígitos
  2. int uno, diez, cien, mil;                   //Declaración de variables de los dígitos
  3.    mil=valor/1000;                          //Operación para adquirir miles
  4.    cien=(valor/100)%10;                     //Operación para adquirir cientos
  5.    diez=(valor/10)%10;                      //Operación para adquirir decenas
  6.    uno=valor%10;                            //Operación para adquirir unidades
  7.    timer0_mux(uno,diez,cien,mil);           //Llamado a la función de multiplexado
  8. }

La medicion se realiza en base a un canal ADC el cual se acondiciona multiplicando por los 5V del fondo de escala del ADC y se lo divide por la cantidad de muestras del ADC que seran 1024.

Luego para mejorar la estabilidad de la medición utilizaremos el calculo del promedio de la medición, es decir, mediremos 500 veces el valor, realizaremos la sumatoria de cada uno de ellos y al finalizar el lazo de iteracion dividiremos el valor final por 500 para que nos de el valor estable. De esta forma el bit menos significativo sera mas estable, pero como lo comentaba al principio del proyecto no es necesario tener 3 decimales con 10 bit de representación.


  1.    while(true){                             //Lazo repetitivo principal
  2.       set_adc_channel(0);                   //Selección del canal 0 del ADC
  3.       delay_ms(5);                          //Delay para la conversión de 5ms
  4.       tension=read_adc()*5.0/1023.0;        //Lee el ADC y convierte en valor de tensión
  5.       for(i=0;i<500;i++){                   //Lazo de 500 iteraciones para sacar promedio
  6.          tensionAnt=tension+tensionAnt;     //Suma del acumulado para sacar promedio
  7.       }        
  8.       convert((tensionAnt/500)*1000);       //Llama a la función que convierte a dígitos
  9.       tensionAnt=0;                         //Inicializa variable de promedio
  10.    }

Ahora dejare el código completo para poder observar las funciones del mismo de forma mas sencilla y la interacción entre ellas.


  1. #include <16F883.h>                         //Microcontrolador a Elegir
  2. #device adc=10                              //Resolución del ADC a 10Bit
  3. #use delay(clock=4000000)                   //Oscilador Externo a 4MHz
  4. //Vector que aloja los 10 valores numéricos para el display de 7 segmentos
  5. int8 num[10]={0b00111111,0b00000110,0b01011011,0b01001111,0b01100110,
  6.               0b01101101,0b01111101,0b00000111,0b01111111,0b01101111};
  7. #int_timer0                                 //Función del Timer0
  8. //Función que se ejecutara en paralelo al programa principal sobre el Timer0
  9. void timer0_mux(int8 dig4, int8 dig3, int8 dig2, int8 dig1){
  10.    output_c(0b00000001);                    //Selección del display 1
  11.    output_b(num[dig1]);                     //Valor tratado del dígito 1 en la salida B
  12.    output_high(PIN_B7);                     //Prendemos el punto decimal
  13.    delay_ms(2);                             //Delay de multiplexado de 2ms
  14.    output_c(0b00000010);                    //Selección del display 2
  15.    output_b(num[dig2]);                     //Valor tratado del dígito 1 en la salida B
  16.    output_low(PIN_B7);                      //Apagamos el punto decimal
  17.    delay_ms(2);                             //Delay de multiplexado de 2ms
  18.    output_c(0b00000100);                    //Selección del display 3
  19.    output_b(num[dig3]);                     //Valor tratado del dígito 1 en la salida B
  20.    output_low(PIN_B7);                      //Apagamos el punto decimal
  21.    delay_ms(2);                             //Delay de multiplexado de 2ms
  22.    output_c(0b00001000);                    //Selección del display 4
  23.    output_b(num[dig4]);                     //Valor tratado del dígito 1 en la salida B
  24.    output_low(PIN_B7);                      //Apagamos el punto decimal
  25.    delay_ms(2);                             //Delay de multiplexado de 2ms
  26.    set_timer0(0);                           //Seteo del Timer0
  27. }
  28. void convert(int16 valor){                  //Función para convertir entero en dígitos
  29. int uno, diez, cien, mil;                   //Declaración de variables de los dígitos
  30.    mil=valor/1000;                          //Operación para adquirir miles
  31.    cien=(valor/100)%10;                     //Operación para adquirir cientos
  32.    diez=(valor/10)%10;                      //Operación para adquirir decenas
  33.    uno=valor%10;                            //Operación para adquirir unidades
  34.    timer0_mux(uno,diez,cien,mil);           //Llamado a la función de multiplexado
  35. }
  36. void main(){                                //Función principal del programa
  37.    setup_adc_ports(sAN0|VSS_VDD);           //Configuración del canal del ADC
  38.    setup_adc(ADC_CLOCK_DIV_2);              //Configuración de velocidad del ADC
  39.    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_8); //Configuración del divisor del Timer0
  40.    set_timer0(0);                           //Seteo del Timer0
  41.    enable_interrupts(INT_TIMER0);           //Habilitación de interrupción Timer0
  42.    enable_interrupts(GLOBAL);               //Habilitación de interrupciones
  43.    float tension, tensionAnt=0;             //Definición de variables flotantes
  44.    int16 i;                                 //Definición de entero de 16 bit
  45.    while(true){                             //Lazo repetitivo principal
  46.       set_adc_channel(0);                   //Selección del canal 0 del ADC
  47.       delay_ms(5);                          //Delay para la conversión de 5ms
  48.       tension=read_adc()*5.0/1023.0;        //Lee el ADC y convierte en valor de tensión
  49.       for(i=0;i<500;i++){                   //Lazo de 500 iteraciones para sacar promedio
  50.          tensionAnt=tension+tensionAnt;     //Suma del acumulado para sacar promedio
  51.       }        
  52.       convert((tensionAnt/500)*1000);       //Llama a la función que convierte a dígitos
  53.       tensionAnt=0;                         //Inicializa variable de promedio
  54.    }
  55. }

De la misma manera hay que utilizar resistencias de 330R a 680R para los 7 segmentos que no esta en el diagrama de simulación pero si las he utilizado en la practica con un pack DIL de resistencias.





7 comentarios:

  1. Buen proyecto, muy util para iniciarse en el mundo de la multiplexación de displays de 7 segmentos.

    ResponderBorrar
  2. Hola esta fantastico por el limite de medicion, ahora bien mi proyecto es un medidor de presion con un sensor de presion de 3 pines masa señal y +5v, segun la presion el sensor entrega con 330bar alrededor de 4.6v y con 25 bar 0,5v (valores aproximados dentro de ese rango de voltajes), como haria para que a 4,6v no muestre voltaje sino 330,0 BAR, si es posible me pongo de cabeza a aprender a programar el chip PIC y por otra parte seria posible que exitando alguna entrada al pic pueda cambiar de BAR a Psi digamos con un pulsador.
    Que posibilidades habria de colectar datos y enviarlos a la placa de audio de pc, le explico me dedico a mecanica de equipos viales las bombas hidraulicas de pistones suelen gastarse los pistones como tienen 6 o mas uno que este gastado provoca fluctuacion de caudal si se pudiera hacer un registro grafico a distintos regimenes de rpm se haria un analisis de estado sin desarmar el equipo para ver como esta su bomba, con una simple captacion de datos se puede dar un veredicto de si conviene o no desarmar.
    Me gusta tu proyecto felicitaciones y muchas gracias.

    ResponderBorrar
  3. Me gustaría hacerlo y mostrar tres resultados con un pulsador, como?

    ResponderBorrar
  4. sabes que no me funciona en el proteus, todos 88888 me pone

    ResponderBorrar
  5. buena noche amigo, me intereza aserlo, pero con punto desimal movible, q si el voltaje sea 5.033, ese valor se muestre, pero ya al pasar a decemas cambie el punto desimal por ejemplo, 14.56. crees que usando este codigo de muestra se pueda aser esta funsion?

    ResponderBorrar
  6. buen dia amigo, el codigo se puede ocupar para el pic16f88??, porque quiero usarlo para crear un amperimetro, podrias ayudarme? porvafor

    ResponderBorrar