Digital DC Voltmeter using 7-Segment Display and Pic Microcontroller

In this tutorial, we will design a digital voltmeter using a 7-segment display and pic microcontroller. Usually, we use LCDs to display sensor data. In the last tutorial, we designed a DC digital voltmeter with a 16×2 Liquid crystal display. But in this post, instead of using LCD, we will use a four-digit 7-segment display to print voltage value. We will use the ADC module of PIC16F877a microcontroller to measure DC voltage.

Components Required

  1. PIC16F877A Microcontroller
  2. 4-digital Seven Segment Display
  3. Resistors
  4. Crystal Oscillator
  5. Capacitors
  6. NPN transistors

Digital Voltmeter with 7-segment display circuit diagram

A picture below shows the circuit diagram of DC voltmeter with four digit seven-segment display and voltage divider circuits interfacing with PIC16F877A microcontroller. But you can use any other pic microcontroller also.

digital voltmeter with seven segment display and pic microcontroller

How to measure DC voltage?

Firstly, lets discuss do we need to use a voltage divider circuit. The simple and basic reason to use voltage divider circuit is that pic microcontroller operating voltage is between 0-5 volts. Hence, built-in ADC module of PIC16F877A microcontroller can read analog signal in the range 0 to 5 volts only. Therefore, in order to measure dc voltages higher than this level, we need come up with a solution to step down input voltage. This is the reason a voltage divider circuit is required in this project.

A voltage divider circuit as its name suggests divides the voltage between across two resistors. As you can see from the above circuit of dc voltmeter, we used two resistors

R9 = 180K
R7 =20k

The formula of voltage divider is very simple that is

VOUT = (R7/(R7+R9) x VIN)

For example, if input voltage is 19 volts, the output voltage will be:

 VOUT = (20k/(20k+180k) x 19) = 1.9 volts

1.9 volts is less than 5 volts. Therefore, we can measure this voltage with ADC of PIC16F877A microcontroller. If you see from the voltage divider formula, the voltage step down or reduction factor is 20/200 = 1/10

Step down factor = 1/10

We can use this reduction factor in our programming to get actual voltage by measuring only the output voltage of voltage divider. We will measure step -down voltage that is the output of voltage divider circuit. But, inside our program, we will multiply measured voltage value with the reduction factor to get actual value of input voltage.

We used analog channel AN0/RA0 of PIC16F877A microcontroller. We have already post a guide on how to use analog digital module of Pic Microcontroller, you can read this post:

Connection with 4-digital seven-segment device

As you know that there are two types of 7-segment displays: namely common anode and common cathode type. We will use a common cathode type 4-digit device to display measure voltage value.

Connect PORTD with A-g pins of 4-digital seven-segment display using 330ohm current limiting resistors. Also connect RB0-RB3 pins of PORTB with control pins 1-4 of display device. Same data lines are used to send binary pattern to each digit of display device, but control lines select that at which place (7-segment) we want to display a digit.

To explore further on 7-segment displays interfacing with pic microcontrollers, go through these in-depth guides:

Digital voltmeter with 7-segment display Simulation

This circuit is simulated with proteus. As you can see, we connect a 50 volts DC source with a voltage divider circuit through a variable resistor. We use this variable resistor to apply a variable voltage to the circuit.

We can observe the same voltage on a 4-digit seven-segment device according to the input voltage variation.

MikroC Code

This code is written using MikroC for Pic compiler.  Create a new project with MikroC compiler by selecting PIC16F877A microcontroller and set frequency to 8MHz. If you don’t know how create new project in mikroC, we suggest you read this post:

After creating a new project with MikroC, set configuration bits to these values:

CONFIG : $2007 : 0x2F4A

After that copy this code and compile it with MikroC for Pic compiler.

// define name to each control signal for 7-segment
sbit digit1 at PORTB.B0;
sbit digit2 at PORTB.B1;
sbit digit3 at PORTB.B2;
sbit digit4 at PORTB.B3;
// This array stores binary bit pattern that will be send to PORTD
unsigned char binary_pattern[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; // without Dp turn
unsigned char display1[10]= {0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xE7};    // with dp turn on

// variables to store digits, digital value and output voltage
unsigned int a1,a2,a3,a4; // temporary variables to store data of adc
int adc_value; //store output value from Analog Read functoion
unsigned int number;
long tlong;
unsigned int voltage;

// this function retrive each digita that will displayed on device
void get_digits()
  {
           a1 =  voltage / 1000u;   // holds 1000's digit
           a2 = ((voltage/100u)%10u); // holds 100's digit
           a3 = ((voltage/10u)%10u);  // holds 10th digit
           a4 = (voltage%10u);  // holds unit digit value
   }

// this function displays measured voltage on seven-segments
void display_voltage()
 {
            PORTD = binary_pattern[a2]; // send 1000's place data to fourth digit
            digit1 = 0;   //  turn on forth display unit
            delay_ms(3);
            digit1 = 1;   //  turn off forth display unit
            PORTD = display1[a3];  // send 100's place data to 3rd digit
            digit2 = 0;    //  turn on 3rd display unit
            delay_ms(3);
            digit2 = 1;  //  turn off 3rd display unit
            PORTD = binary_pattern[a4];  // send 10th place data to 2nd digit
            digit3 = 0;  //  turn on 2nd display unit
            delay_ms(3);
            digit3 = 1;   //  turn off 2nd display unit
            PORTD=binary_pattern[a1];  // send unit place data to 1st digit
            digit4 = 0;  //  turn on 1st display unit
            delay_ms(3);
            digit4 = 1;  //  turn off 1st display unit
   }
void interrupt()
{

            get_digits();   // call function to split data
            display_voltage(); //call display_data() function to output value to seven segment

            T0IF_bit = 0;  // clear source of timer0 interrupt
}
void main(void)
{
    TMR0   = 0;         // timer0 reset bit
    OPTION_REG = 0x83;  // select prescalar value 1:16 for timer0
    INTCON = 0xA0;      // turn on global interrupt and timer0 overflow interrupt
    TRISD = 0x00; //define PORTD as a output pin
    PORTD=0x00;   // initialize PORTD pins to active low
    TRISB=0x00;// Set PORTB as a output port
  // set control pins pins initially active high
    digit1 = 1;
    digit2 = 1;
    digit3 = 1;
    digit4 = 1;

    while(1)
    {

           adc_value = ADC_Read(0);  // read data from channel 0
           tlong = (float)adc_value*0.488768555;
           voltage = tlong;
           //delay_ms(100);  // wait 100 milliseconds

    }

}

MPAB XC8 Code

This code is for MPLAB XC8 Compiler. If you don’t know how to use MPLAB and XC8 compiler, you can read this complete in-depth guide:

After creating a new project, set configuration bits by generating configuration bit file with MPLAB XC8. While generating this file, select the HS crystal option and leave the remaining setting as to default settings.

#include <xc.h>
#define _XTAL_FREQ 20000000 //define crystal frequency to 20MHz
#define digit1 PORTBbits.RB0
#define digit2 PORTBbits.RB1
#define digit3 PORTBbits.RB2
#define digit4 PORTBbits.RB3
// This array stores binary bit pattern that will be send to PORTD
unsigned char binary_pattern[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
unsigned char display1[10]= {0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xE7};    // with dp turn on
unsigned int a1,a2,a3,a4;
unsigned int counter = 0;
int adc_value; //store output value from Analog Read functoion
unsigned int number;
long tlong;
unsigned int voltage;
void Analog_setting(){
    ADCON0 = 0x81;
    ADCON1 = 0x02;
}

unsigned int Analog_read(unsigned char channel){
    int aadc,bbdc, ccdc;
    if(channel>7)return 0;
    ADCON0 = ADCON0 & 0xC5;
    ADCON0 = ADCON0 | (channel << 3);
    __delay_ms(2);
    ADCON0bits.GO_DONE = 1;
    while(ADCON0bits.GO_DONE);
    aadc = ADRESH;
    aadc = aadc<<2;
    bbdc = ADRESL;
    bbdc = bbdc >>6;
    ccdc = aadc|bbdc;
    return ccdc;

}
void main(void) 
{
    Analog_setting();
    TRISD = 0x00; //define PORTD as a output pin
    PORTD=0X00;   // initialize PORTD pins to active low
    TRISB=0X00;
    digit1 = 1;
    digit2 = 1; 
    digit3 = 1;
    digit4 = 1;
    while(1)
    {
          adc_value = Analog_read(0);  // read data from channel 0
           tlong = (float)adc_value*0.488768555;
           voltage = tlong;
        
           a1 = voltage / 1000;   // holds 1000's digit
           a2 = ((voltage/100)%10); // holds 100's digit
           a3 = ((voltage/10)%10);  // holds 10th digit
           a4 = (voltage%10);  // holds unit digit value
           
        
PORTD=binary_pattern[a2]; // send 1000's place data to fourth digit
digit1=0;   //  turn on forth display unit
__delay_ms(3);
digit1=1;   //  turn off forth display unit
PORTD=display1[a3];  // send 100's place data to 3rd digit
digit2=0;    //  turn on 3rd display unit
__delay_ms(3);
digit2=1;  //  turn off 3rd display unit
PORTD=binary_pattern[a4];  // send 10th place data to 2nd digit
digit3 = 0;  //  turn on 2nd display unit
__delay_ms(3);
 digit3 = 1;   //  turn off 2nd display unit
PORTD=binary_pattern[a1];  // send unit place data to 1st digit
digit4 = 0;  //  turn on 1st display unit
__delay_ms(3);
digit4 = 1;  //  turn off 1st display unit


    }
    return ;
}

16 thoughts on “Digital DC Voltmeter using 7-Segment Display and Pic Microcontroller”

  1. Hi. Just very quickly sifted through this, but found it somewhat uninteresting since it has “only” 4 digits.
    Is there any tip of any micro processor that has higher resolution, as a build I mean, so that it might have, 5 or 6 digits instead?

    I think many would find such a build very interesting, but it’d, of course, had to be reliable too.
    I’d like to have one that can measure more finite than these cheap-o-meter out there, but I don’t feel like paying multiple digits, in cash that is.

    I noticed that the input is very rudimentary on this build, and I can only imagine that a real one would have, besides low tolerances on the components, also have more sophisticated setup of components.

    Could you put together a such build it’d be really super.
    Thanks.

    Reply
    • Hi

      It is just for a demo purpose. To make better and practical design, we can use a shift register like 74HC595 and I2C communication to display data. By using a shift register like 74HC595 we can achieve same result with microcontroller three pins only.

      Reply
  2. Yeah, I know that, but I was more interested in the micro controller and input-circuitry. I don’t know how high resolution this particular controller has, but I’d imagine 10 bit as most has.
    Sure you could use an external ADC with high resolution like, I dunno, 16-24 bit, but this is a bit over my knowledge when it comes to hooking the stuff up (Seem to always be some weird connections by some strange components, some filtering-caps and whatnot) as well as programming, I’m sort of semi-lost.

    I haven’t even used any PIC yet, only some small-time projects with ATMEGA, apart from my washing machine (re)build which incorporates a 2560 with tons of sensors and output to 3-phase motor-controller and lots of other stuff.
    Haven’t started programming that yet, but it’ll be an hell of a challenge!

    Reply
  3. Hi may i know for the mikroc code, can it be use for the pic16f887 ? If there any changes that are needed could you tell me.

    Reply

Leave a Comment