Digital Voltmeters are most common devices for Embedded and Hardware Engineers and are used on daily purpose for measuring voltage values in their circuits. In this post i will show you how to make digital voltmeter using Microchip PIC micro-controller and then display voltage values on multiplexed seven segment display (I hope you all are aware of the seven segments and if not then you can click here for that).
In this project, i will just use the Proteus Simulation environment, and we will measure the DC voltage (0-100V) using PIC Analog to Digital Converter. One thing is important, as PIC is a 5V device and directly connecting such a high voltage (100V) on the micro-controller, will definitely (i am not using probably word, as it will definitely blow the device) damage the micro-controller. So to overcome this situation we have to scale down the Maximum Voltage value which is 100V to the Maximum Voltage value which the micro-controller can sense, in this case it is 5V.
For scaling down the voltage we will use the voltage divider circuit, which is shown below.
Now a question arises that, how i selected these values of resistor, for that see the following calculations.
So i think it is clear now, how to choose the resistor values. But there is point, you will not find 19k resistor in market, but as i am working in simulation so i can tune the resistor value to any value, so in practical use, fine tune the values, with the equation above and with the resistors you have.
Now lets build the circuit using PIC16F676 micro-controller, the schematic used is given below, as our maximum voltage (100V) value is of 3-digits, so we need only three seven segments but in the schematic, four seven segments are used, because in the Proteus i didn't find the multiplexed seven segments having only three seven segments, so i used the one with four seven segments but as you can see in the schematic the fourth one is disabled.
The program is as follow:
The above program is written in MPLAB-X 3.40 with XC8 v1.38 Compiler.
Calculation Logic Explanation:
The Voltage which needs to be measured is first scaled down by 20 times with the help of voltage divider circuit, so whatever voltage PIC micro-controller's analog to digital converter will measure, have to be multiplied by 20 times to get back the exact value.
But if you see the code given below, all the above calculation is done in a single line i.e. by dividing the adc value by 10.
Now i will explain, why i just divided the adc value by 10 instead of doing what i have written in the first line of this section. So lets understand all this, step by step:
Have a look at this video for the complete project demo:
Download Source From GitHub
Digital Voltmeter |
For scaling down the voltage we will use the voltage divider circuit, which is shown below.
Voltage Divider |
Calculations |
Now lets build the circuit using PIC16F676 micro-controller, the schematic used is given below, as our maximum voltage (100V) value is of 3-digits, so we need only three seven segments but in the schematic, four seven segments are used, because in the Proteus i didn't find the multiplexed seven segments having only three seven segments, so i used the one with four seven segments but as you can see in the schematic the fourth one is disabled.
Schematic Diagram |
#include "config.h"
#define REFRESH_INTERVAL 50ul
#define DIGIT_0_Indx 0x40
#define DIGIT_1_Indx 0x79
#define DIGIT_2_Indx 0x24
#define DIGIT_3_Indx 0x30
#define DIGIT_4_Indx 0x19
#define DIGIT_5_Indx 0x12
#define DIGIT_6_Indx 0x02
#define DIGIT_7_Indx 0x78
#define DIGIT_8_Indx 0x00
#define DIGIT_9_Indx 0x10
#define BLANK_Indx 0x7F
uint8_t SevenSegment[] = { DIGIT_0_Indx, DIGIT_1_Indx, DIGIT_2_Indx,
DIGIT_3_Indx, DIGIT_4_Indx, DIGIT_5_Indx,
DIGIT_6_Indx, DIGIT_7_Indx, DIGIT_8_Indx,
DIGIT_9_Indx };
void Initialize_Display( void );
void Display_Value( uint8_t code );
void main()
{
CMCON = 0x07;
ANSEL = 0x08;
TRISA |= 0x10; // RA4/AN3 as Input
ADCON0 = 0x8D; // Right Justified, VDD, AN3 Selected, A/D Power-Up
ADCON1 = 0x10;
InitTimer0 ();
Initialize_Display ();
while(1)
{
uint16_t adc_data = 0;
ADCON0 = 0x8D;
ADCON0bits.GO_DONE = 1; // Start Conversion
while( ADCON0bits.GO_DONE ); // Wait Here
adc_data = ADRESH<<8;
adc_data |= ADRESL;
adc_data = adc_data & 0x3FF;
adc_data = adc_data/10;
uint8_t d1, d2, d3;
d1 = adc_data % 10;
d2 = (adc_data/10) % 10;
d3 = (adc_data/100) % 10;
PORTA &= 0xF8;
PORTA |= 0x04;
Display_Value (SevenSegment[d1]);
__delay_ms(10);
PORTA &= 0xF8;
PORTA |= 0x02;
Display_Value (SevenSegment[d2]);
__delay_ms(10);
PORTA &= 0xF8;
PORTA |= 0x01;
Display_Value (SevenSegment[d3]);
__delay_ms(10);
}
}
void Initialize_Display( void )
{
TRISA &= 0xD8; // RA0,RA1,RA2, RA5 as Output
TRISC = 0x00;
PORTA &= 0xD8;
PORTC = 0x00;
}
void Display_Value( uint8_t code )
{
/*
* a -> RC0 (bit-0)
* b -> RC3 (bit-1)
* c -> RC5 (bit-2)
* d -> RC4 (bit-3)
* e -> RC2 (bit-4)
* f -> RC1 (bit-5)
* g -> RA5 (bit-6)
*/
PORTC = 0;
PORTA &= 0xDF;
if ( code == BLANK_Indx )
{
PORTC = 0xFF;
PORTA |= 0x20;
}
else
{
// RC0
if( code & 0x01 )
PORTC |= 0x01;
// RC3
if( code & 0x02 )
PORTC |= 0x08;
// RC5
if( code & 0x04 )
PORTC |= 0x20;
// RC4
if( code & 0x08 )
PORTC |= 0x10;
// RC2
if( code & 0x10 )
PORTC |= 0x04;
// RC1
if( code & 0x20 )
PORTC |= 0x02;
// RA5
if( code & 0x40 )
PORTA |= 0x20;
}
}
The above program is written in MPLAB-X 3.40 with XC8 v1.38 Compiler.
Calculation Logic Explanation:
The Voltage which needs to be measured is first scaled down by 20 times with the help of voltage divider circuit, so whatever voltage PIC micro-controller's analog to digital converter will measure, have to be multiplied by 20 times to get back the exact value.
But if you see the code given below, all the above calculation is done in a single line i.e. by dividing the adc value by 10.
Calculation part in Source Code |
- We get digital value of the analog signal and let's say it is stored in ADC_DATA variable.
- Next step is to convert this digital value in voltage.
- Analog Value Sensed by PIC, ANALOG_VALUE_PIC = (Reference Voltage*ADC_DATA)/(2^10)
- ANALOG_VALUE_PIC = (5*ADC_DATA)/(1024)
Reference Voltage in our case is 5V and we are using 10-bit ADC of PIC micro-controller, that's why 2^10 = 1024 is used. - ANALOG_VALUE = ANALOG_VALUE_PIC * 20
We multiplied by 20 because, we scale down the voltage by 20 as explained above. - ANALOG_VALUE = (5*20/1024)*ADC_DATA
- So it's clear from the above expression, that we have (5*10/1024) as constant value, which is equivalent to = 0.098 and i rounded off this to 0.01 or (1/10), to make the calculation simple, with some error in the system.
- Final Expression, ANALOG_VALUE = ADC_DATA/10
Sample-1 |
Sample-2 |
Thanks for your great work.... Seven Segment Common Cathode or common anode??
ReplyDeleteThanks for appreciation.
DeleteIts Common Anode.
You can download the code, which also has Proteus Design file, after opening the design you can see that this part has -CA written at last, which means Common Anode, similarly if -CC is written then that seven segment is Common Cathode.
I want to further use equation:
ReplyDelete(dB) = 20xLog{to the base 10} x (V in/V out)
& change the range to -16 to 16 (dBm)
How far it is possible?
Please let me know fast
It looks a simple formula to me, but I doubt that small micro will be able to handle this.
DeleteWhat is the problem you are facing in applying this formula, as it is not very clear from your comment.
I have analog input from 0V to 1.3V. I want to convert it into dBm using (dB) = 20xLog{to the base 10} x (V in/V out) equation. Can you please suggest me what changes i have to do it in the program ?
DeleteHi,
DeleteThanks for the tutorial.
What changes I have to make it to work with common cathode display
Sir, please guide me how convert this meter upto 300v.
ReplyDeletehi,
ReplyDeleteThanks for the tutorial.
What changes I have to make inorder to work it with Common Cathode in program.
Hi send this project dineshbdinesh@gmail.com
ReplyDeleteEverything is already there on this page. What else is needed?
DeleteHi sir
ReplyDeleteiam a new bee in programming while compiling i got the following errors
make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory 'C:/Users/Manoj/Documents/psmcu676.X'
make -f nbproject/Makefile-default.mk dist/default/production/psmcu676.X.production.hex
make[2]: Entering directory 'C:/Users/Manoj/Documents/psmcu676.X'
"C:\Program Files (x86)\Microchip\xc8\v2.05\bin\xc8-cc.exe" -mcpu=16F676 -c -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -Wa,-a -DXPRJ_default=default -msummary=-psect,-class,+mem,-hex,-file -ginhx032 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -std=c99 -gdwarf-3 -mstack=compiled:auto:auto -o build/default/production/psmcu676.p1 psmcu676.c
::: advisory: (2049) C99 compliant libraries are currently not available for baseline or mid-range devices, or for enhanced mid-range devices using a reentrant stack; using C90 libraries
psmcu676.c:26:2: error: unknown type name 'uint8_t'
uint8_t SevenSegment[] = { DIGIT_0_Indx, DIGIT_1_Indx, DIGIT_2_Indx,
^
psmcu676.c:31:22: error: unknown type name 'uint8_t'
void Display_Value( uint8_t code );
^
psmcu676.c:39:3: warning: implicit declaration of function 'InitTimer0' is invalid in C99 [-Wimplicit-function-declaration]
InitTimer0 ();