Digital Voltmeters - PIC
User Rating: / 7
PoorBest 
Written by Bryce Ringwood   

This article was updated on 5 Dec, 2014

Many of you visiting this site will already own a digital multimeter and if you are older than about 55, you may own an analog one. I would guess that no matter which you own, the instrument will principally be used for measuring resistance, with the measurement of  voltage being in second place.  Third place would probably go to current measurement.

Here, we are interested in voltage measurement. In particular, we look at the conversion of the voltage being measured to a displayed value we can read directly or a digital value we can process to provide an indication of some other physical quantity.

A Vintage HP 3460B Digital Voltmeter Using a Nixie Tube Display

For example, a temperature probe might provide an output voltage proportional to temperature. A simple computer program contained in the instrument could provide a display in degrees Celsius, or degrees Fahrenheit. Another example would be a weighing system, where a load transducer would give an output voltage proportional to weight. Such a system would incorporate  a "tare weight" measurement at switch-on, so that the net weight would be displayed or printed.

The ICL710- chips

In 1977, a company called "Intersil" produced the ICL7106 chip which converts an input voltage of up to 200mV to a display of 1999 on an LCD. The companion ICL 7107 provides the same facility for an LED, and the ICL7109 does the same, but outputs digitally for a microprocessor chip. These chips have survived until the present time, even though the successions of companies that made them have not.

You can buy an ICL7106 kit from Mantech, or you can make your own from the circuit layout below. It might be useful having a few PCBs made, then any time you need a voltmeter, you can quickly put one together. If you are making one for the first time - rather go for the kit to gain experience.

small meter pcb
Small Panel Meter PC Layout. Red lines are jumpers
Bottom LCD side Top Component Side

There is also an ICL7129 that displays 19999 for an input of 200mV. (Available at RS electronics - but it needs a special LCD display, which seems very hard to find.) This is a very easy chip to use - you could build it on Veroboard.

Digital displays are often referred to as 3-1/2 digit (Max display 1999), 4-1/2 digit(Max display 19999), 3-3/4 digit (3999) - and so on. The accuracy of the voltmeter depends on:

  1.  How linear the circuitry is,
  2.  Roll-over error as the display changes from a positive to negative reading,
  3.  The accuracy of the internal (or external, if used) reference and,
  4.  How well you have adjusted the meter against an accurate voltage standard.

An AD584 voltage standard will set you back about R500-00 and will have a 0.3% accuracy.

ICL7106/7 Datasheet

Basic principle of operation

Unlike their analogue counterparts, digital meters (and analog to digital) converters work by comparing the input voltage to a known reference. The voltage reference can be an accurate Zener diode or 2-terminal voltage reference. The circuits mentioned contain their own voltage reference, with the exception of the ICL7129. (The modern equivalent is the Microchip TC7129).

An integrator (see analog computer section) is used to perform the comparison. Input is applied to the integrator for a set time period. At the end of this time a voltage will have built up on the reference capacitor. The next phase is de-integration. Now, the reference voltage is applied to the input to the integrator, in the opposite sense to make it ramp down. A count of the number of clock cyles for integrator output to reach zero provides the result.

To measure resistance directly, simply apply the same current to a known resistor and the unknown resistor and compare the voltage developed across them. The displayed result will be the resistance ratio.

These chips are great for projects where you want to make your own meters for various projects. I used one in a valve-tester, for example.

Now, I'm asking myself if that was a good idea - couldn't I have perhaps made a "smart valve tester" using a microprocessor, such as the PIC?

Using the PIC internal A to D convertor

The PIC has an internal analog to digital convertor. By default, this works to 8 bits, giving a resolution of one part in 256. The accuracy might be quite a bit less - say 5% ?. This is just as good as many analog meters, and I think would have been good enough for my valve tester. After all, do you really need to know a valve's gm to a vast accuracy? Admittedly, the votage gain could be a problem, since pentodes have much more gain than triodes. 

The PIC Trix board has a potentiometer that allows you to apply a variable voltage to one of the analog input pins. (The PIC16F877 has 8 analog inputs).

 

 

 

Here is the main routine:

#include <htc.h>
#include <stdlib.h>
#include <stdio.h>
#include "lcd.h"
#include "adc.h"
#include "numstr.h"
#ifndef _XTAL_FREQ
 // Unless specified elsewhere, 4MHz system frequency is assumed
 #define _XTAL_FREQ 4000000
#endif
#define line1 0x00
#define line2 0x40
#define line3 0x10
#define line4 0x50
 
 
// Configuration set using the IDE, not in code any more
//__CONFIG(XT & WDTDIS & PWRTDIS & BORDIS & LVPDIS &
// DEBUGDIS /*& DUNPROT*/ & UNPROTECT);
 
 
/* A voltmeter that works with the Pic Trix board.   */
 
//This is for the PIC16F887!!!
 
 
void main(void){ 
/****** Declare the variables you are going to use ********/
 unsigned input;  
 double var;
 char* buf;
 int status,i;
// float cfact = 0.0195; // Conversion to volts = 5.0 / 256.
 float cfact = 0.005069; // about 5/1023 - fiddled to be accurate using a DVM
// The supply voltage is used as Vref   
 float result;
 
  
  ANSEL  = 0x0C;              // Configure AN1 & AN2 pin as analog
  ANSELH = 0;                 // Configure other AN pins as digital I/O
  C1ON = 0;                   // Disable comparators
  C2ON = 0;
//  
/****** Variables declared ! ******************************/
/****** Now Output a greeting message *********************/
 
 
 lcd_init();
 lcd_goto(line2); // select 2nd line
 lcd_puts("Voltmeter");
 lcd_goto(line3); // Select third line
 lcd_puts("Program");
 init_a2d();
 
/************  This is the program loop   ******************/
 while(1){
/************  Put your repetitive loop code here **********/
    
       
         result=0.;
// There are better ways of doing this...but, then again, it works. 
         for(i=0;i<5;i++)
         {
              input = read_a2d(0);       // Schematic uses channel 0
              result = (float)input*cfact + result;
         }
         buf = num_to_str(result/5.,2);   
         lcd_clear(); 
         lcd_goto(line1);
// Stop that LCD from dropping characters
         __delay_ms(5);
         lcd_puts("Result = ");
         lcd_goto(line2);
         lcd_puts(buf);
       
 
/************ End of loop code *****************************/
     }
/************ The above braces signify end of the loop *****/
}
 
Main Routine for the Voltmeter - main.c

The adc routines are supplied by Microchip. The way the system works is by having 8 channels multiplexed on to 1 analog to digital converter., so a call to read_a2d(2) would read the input on RA2. Here we use pin2, RA0 and channel 0. The instructions on how to read to 10 bits are fairly clear by comparison.  Having read the result, it must be scaled to a range between 0.0 and 5.0 volts, but in order to display the value, it has to be converted to an alpha string. 

ftoa() does the job - but you will have to include "stdio.h", and for some arcane reason, you will have to redefine the ftoa function somewhere. I provided the function numstr( .. ) to do the same job, but the code is a bit less heavyweight  - and less comprehensive. Note that the Arduino has a lightweight AND comprehensive function to do the job. (dtostrf() .. I think.).  Don't use sprintf .. it just doesn't seem to work.

Now, - a style thing. Quite a few of the variables have been declared, but have not been given an initial value. This can be construed as sloppy and indeed dangeous.

The code for the LCD is the same as the LCD example given earlier and that leaves us with the number to string conversion routine:

extern char * num_to_str(float num  , int numberofdecplaces);
numstr.h

and

#include "numstr.h"
char * num_to_str(float num  , int numberofdecplaces)
{
 /********************************************************************
 ** Very limited number to string conversion routine                **
 ** num = input floating point number                               **
 ** numberofdecplaces = number of characters after the decimal point**
 ** Character string returned in buf.                               **
 ** Intended use is for a simple Voltmeter program for a PIC CPU    **
 ********************************************************************/

 /******         Declare all variables up front  *********/

 static char  buf[20];    // output buffer
 int idec = 0;            // decimal point flag
 int i;                   // loop counter
 int bufptr=0;            // character pointer in the output buffer
 int nr=0;                // number of characters after the decimal
 float anum;              // holds the current most significant digit
 float div=1.0f;          // divisor
 /******* check if a negative number            **********/
      if(num < 0.0)
      {
            *(buf+bufptr) = '-';
            bufptr++;
            num=num*-1.0f;   
      }
 /****** Calculate the starting divisor - we don't want leading zeroes*/
      if(num > 1.0f)
      {
           for(;;)
           {
                if(num >= 10*div)
                {
                     div*=10.f;
                }
                else
                {
                     break;
                }
            }
      }
 /******* Fill the string by successively calculating the remainder after division by .10^n all the way down to the last turtle ...*/
      for(i = 0 ; i < 20 ;i++)
      { 
            anum=(float)((int)(num/div));  
            *(buf+bufptr) = (int)anum + '0'; 
            num = num -(anum*div);  
            bufptr++;
            /**  Check if we need to insert a decimal point **/
            if(div == 1.0)
            {
                  *(buf + bufptr) = '.';
                  bufptr++;
                  idec=1;  
            }
            div = div * .1f;
            if((idec==1) && (++nr > numberofdecplaces))break;  
      }
      buf[bufptr]='\0'; 
      return buf;
}

numstr.c

 

Rough Circuit Diagram

Circuit Diagram of PIC16f877 Voltmeter

Circuit Diagram of Voltmeter  - You should also refer to the PIC datasheet

Update: Pin 1 Needs to be connected to +5V via a 10k Resistor for the PIC 16F877

Follow the Code

The numstr routine works by first of all determining the sign of the input floating point number. The routine works by successively dividing the float by powers of 10 and calculating the remainder after division. This remainder is converted to a character and added to the string. Finally, the string is terminated with a null('\0'). When the divisor is 1.0 - its time to insert a decimal point. The routine can be shortened if you know the input string is positive, for example, or a whole number (integer).

The analog to digital converter code supplied by Microchip is something of a "given".  If you need more accuracy, you will have to write your own code to interface an ICL7109 (12 bits) ot ICL7135 (16 bits). There are analog to digital converters that will give you 24 or more bits of resolution, but these often work in "serial mode".

Update: Will provide a PIC16F887 (successor to the 877) project download for MPLAB 8.92. This uses the 10 bit ADC configuration. I'm struggling a bit with MPLAB version 10 and the PICKIT2. This has a rather silly averaging routine, which I'm sure you can improve on (or omit altogether). This is the link. The project is for the PIC16F887, for which you may not need that pull-up resistor I omitted in the circuit diagram. 

Applications

The nice thing about using a microprocessor is that a whole world of ideas opens up. You can now make a power consumption meter by measuring current and voltage and multiplying the results before displaying the power. Probably the most common applications are numerous temperature controller designs used in environmental projects. A "radio" project might be a magnetometer to measure solar flux. I would use the micro to process the signal and send the result to a PC via a serial cable. I bought the magnetometer chip some time ago and really should get this project under way. 

tube tester
One use for a digital voltmeter. This instrument is the same size and weight as a house brick. I made the mistake of trying to make it in too small a space, which contributed to the difficulty of construction.

 

 

 
Joomla template by a4joomla