Background
You cannot feed 20V directly to a PIC I/O pin, you need a resistor divider network that converts 0-20V range into 0-5V. The figure below shows how it will be achieved.
At any instant, the voltage Va will be 1/4th of the input voltage, Vin. So, for maximum input voltage of 20V, the Va will be 5V. A 5.1V Zener diode in the figure is to prevent Va to rise above 5.1V if the input voltage goes much above 20V. This will protect the microcontroller port. The analog voltage Va is read through AN0 port and is converted to 10-bit digital number (0-1023) by PIC12F683.
ADC Math
0-5V Analog I/P ---> 0-1023 Digital Count
=> Resolution = (5-0)/(1023-0) = 0.00489 V/Count
=> I/P voltage = 4*Va = 4* Digital Count * 0.00489 = 0.01956 * Digital Count
To avoid floating point, use I/P voltage = 196*Digital Count. This number is a long integer.
Example, suppose
Vin = 13.6V. Then,
Va = 0.25*Vin = 3.4V
=> Digital Count = 3.4/0.00489 = 695
=> Calculated I/P Voltage = 196*695 = 136220 = 13.6V (First 3 digits of 6 digit product)
Sources of error
The above math looks pretty easy but when you implement it, you will not errors in output measurements because the above calculations are based on following ideal conditions:
- Vcc supply voltage to PIC12F683 is exactly 5V,
- R1 and R2 are exact 1.3K and 3.9K respectively
So, let's revise the math above with real figures. I measured the supply voltage to PIC and it is 5.02V. R1 and R2 are measured to be 1267 and 3890 Ohms. So this gives,
0 - 5.02 V Analog I/P ---> 0-1023 Digital Count
=> Resolution = (5.02-0)/(1023-0) = 0.004907 V/Count
Va = 1267*Vin/(1267+3890) = 0.2457*Vin
=> I/P voltage = 4.07*Va = 4.07* Digital Count * 0.004907
= 0.01997 * Digital Count
= 0.02*Digital Count (Approx.)
To avoid floating point, use I/P voltage = 2*Digital Count. No need for Long integer.
Example, suppose
Vin = 4.6V. Then,
Va = 0.2457*Vin = 1.13V
=> Digital Count = 1.13/0.004907 = 230
=> Calculated I/P Voltage = 2*230 = 0460 = 04.6V (First 3 digits of 4 digit product)
Circuit Setup
Connect the Va terminal in the above resistor network to AN0 (GP0) input of PIC12F683. Also connect the serial LCD the same way as we did in our 3-wire serial LCD project. Just maintain the same setup for display as shown below.
Software
/* Digital Voltmeter and
3-wire Serial LCD using 74HC595
Rajendra Bhatt, Oct 3, 2010
*/
sbit Data_Pin at GP5_bit;
sbit Clk_Pin at GP1_bit;
sbit Enable_Pin at GP2_bit;
// Always mention this definition statement
unsigned short Low_Nibble, High_Nibble, p, q, Mask, N,t, RS, Flag, temp;
void Delay_50ms(){
Delay_ms(50);
}
void Write_LCD_Nibble(unsigned short N){
Enable_Pin = 0;
// ****** Write RS *********
Clk_Pin = 0;
Data_Pin = RS;
Clk_Pin = 1;
Clk_Pin = 0;
// ****** End RS Write
// Shift in 4 bits
Mask = 8;
for (t=0; t<4; t++){
Flag = N & Mask;
if(Flag==0) Data_Pin = 0;
else Data_Pin = 1;
Clk_Pin = 1;
Clk_Pin = 0;
Mask = Mask >> 1;
}
// One more clock because SC and ST clks are tied
Clk_Pin = 1;
Clk_Pin = 0;
Data_Pin = 0;
Enable_Pin = 1;
Enable_Pin = 0;
}
// ******* Write Nibble Ends
void Write_LCD_Data(unsigned short D){
RS = 1; // It is Data, not command
Low_Nibble = D & 15;
High_Nibble = D/16;
Write_LCD_Nibble(High_Nibble);
Write_LCD_Nibble(Low_Nibble);
}
void Write_LCD_Cmd(unsigned short C){
RS = 0; // It is command, not data
Low_Nibble = C & 15;
High_Nibble = C/16;
Write_LCD_Nibble(High_Nibble);
Write_LCD_Nibble(Low_Nibble);
}
void Initialize_LCD(){
Delay_50ms();
Write_LCD_Cmd(0x20); // Wake-Up Sequence
Delay_50ms();
Write_LCD_Cmd(0x20);
Delay_50ms();
Write_LCD_Cmd(0x20);
Delay_50ms();
Write_LCD_Cmd(0x28); // 4-bits, 2 lines, 5x7 font
Delay_50ms();
Write_LCD_Cmd(0x0C); // Display ON, No cursors
Delay_50ms();
Write_LCD_Cmd(0x06); // Entry mode- Auto-increment, No Display shifting
Delay_50ms();
Write_LCD_Cmd(0x01);
Delay_50ms();
}
void Position_LCD(unsigned short x, unsigned short y){
temp = 127 + y;
if (x == 2) temp = temp + 64;
Write_LCD_Cmd(temp);
}
void Write_LCD_Text(char *StrData){
q = strlen(StrData);
for (p = 0; p < q; p++){
temp = StrData[p];
Write_LCD_Data(temp);
}
}
char Message1[] = "DVM Project";
unsigned int ADC_Value, DisplayVolt;
char *volt = "00.00";
void main() {
CMCON0 = 7; // Disable Comparators
TRISIO = 0b00001001; // All Outputs, except GP0 and GP3
ANSEL = 0x01; // GP0 analog i/p
Initialize_LCD();
Position_LCD(1,3);
Write_LCD_Text(Message1);
Position_LCD(2,10);
Write_LCD_Data('V');
do {
ADC_Value = ADC_Read(0);
DisplayVolt = ADC_Value * 2;
volt[0] = DisplayVolt/1000 + 48;
volt[1] = (DisplayVolt/100)%10 + 48;
volt[3] = (DisplayVolt/10)%10 + 48;
volt[4] = DisplayVolt%10 + 48;
Position_LCD(2,4);
Write_LCD_Text(volt);
delay_ms(100);
} while(1);
}
Testing
I tested my DVM with my variable power supply and also verified with another digital multimeter.
I hope you liked this project.
I have another version of this project that uses PIC16F688 and no serial LCD driver. If you are interested, find here.
37 comments:
The circuit diagram and the coding is really helpful to make a such great device and i want to say thanks to this post owner and tell him to post more interesting thing continuously.
-industrial lighting fixtures
Great work Mr. Raj.
I am very thankful with your blog.
It is an excellent source of knowledge.
Thank you.
From Brazil.
Wow this is awesome. Keep up the great work and keep posting more stuff if you get time
why when I use the source code in CCS MPLAB compilator I have eror ?
Can you give me .HEX file from this project ?
Compile it with mikroC. Download it free @ http://www.mikroe.com/eng/downloads/get/29/mikroc_pro_pic_2010_v415_setup.zip
I have simulated in ISIS from Proteus but it don't work fine.Anyone try it too?
hi frend,
i am a final year student of e/e engineering.i am workin on a final year project title multiple measurement system like resistor,voltage,current,temperature and pressure using lcd at the output.i need u to give me ideal on how to go about it...i went thru ur multimeter project and it seens almost same....pls i am waitin 4 ur reply.cheerio
This is not a multimeter project, it is just a voltmeter. You can do some research and extend it to your desired project. I would suggest you to use a bigger PIC like PIC16F877A that has enough I/O pins.
Check other similar projects:
http://embedded-lab.com/blog/?p=11
http://embedded-lab.com/blog/?p=396
Great project, i was looking for something like this. I have a couple of questions for you: Have you considered connecting an external clock and a precision reference to your unused pins to improve the accuracy? I guess it would only be usefull for measuring really low currents. Also, have you considered using an op-amp before gp0 to lower the impedance?
Yes, you are right. I am going to use an opamp to lower the source impedance in my next ohmmeter project that would measure a wide range of resistance values. I liked the idea of using a precision reference voltage too. Thanks.
where the 4.07 com from..can u explain more..??
Va = 0.2457*Vin
Therefore, Vin = Va/0.2457 = 4.07*Va
hi raj ...im from bangalore.... and unfortunately im not finding pic12f683 microcontroller ic in my place,,,,can i pls know any alternatives for this....
Try this,
http://embedded-lab.com/blog/?p=396
It uses PIC16F688 microcontroller and does not require the shift register IC, 74HC595.
This is great. Amazing project.
discounted digital board
even when compling with mikroC i get a bunch of errors, what am i doing wrong?
What errors are you getting? Can you mention them here?
HI frnd, i thnk u r using mikroc instead of mikroc pro, dats y u r getting errors, i thnk most of the errors are from sbit and lcd initialization, dis program is for mikroc pro.
Even i'm getting some of the errors ,wen still building the above program using microC PRO for PIC ....
I see the errors as below and all of these errors are associated with the for loop in the void write_lcd_text
; expected,but 'temp' found
invalid expression
')' expected,but ';' found
'}'expected ';' found
specifier needed
invalid declarator expected '('or identifier
undeclared identifier 'Message1'in expression
implicit conversionof int to ptr
@Raj
{
q = strlen(StrData);
for (p = 0; p
temp = StrData[p];
Write_LCD_Data(temp);
}
}
In the above loop after intialising p=0;...why is p coming again ? ,The for loop braces are not closed and why have you closed two curly braces...and wat does the last closed curly braces for?
Praveen,
It was format problem with blogger. Try the new code now. It should work.
Hi Raj, I'm a newby trying to build the digital voltmeter, do I need to build first the development board to program the PIC?, how do I connect the board to the PC?
@ egomez_mx
You need an external PIC programmer to load the firmware inside the microcontroller even if you build the development board. I would suggest you to build this one instead, it is my another DVM project.
http://embedded-lab.com/blog/?p=396
Hi Raj! Nice tutorial!
In fact I am writing this post because I have a question about the polarity of the AN0.
If you switch by mistake the polarity of the analog input, i.e., connect the +5V into the GND and 0V into the AN0. My guess is that you'll get a +10V into AN0, right? OR to be more precise you will power the AN0 input with a -5Voltage to +5Voltage.
I tried switching the polarity, without the zener, and the PIC popped like popcorn :D
How do you suggest to protect the PIC against polarity changes?
Problem solved. The zener protects against polarity changes.
i dont understand this completely
volt[0] = DisplayVolt/1000 + 48;
volt[1] = (DisplayVolt/100)%10 + 48;
volt[3] = (DisplayVolt/10)%10 + 48;
volt[4] = DisplayVolt%10 + 48;
It is extraction of individual digit from a 4-digit decimal number. 48 is added to individual number to convert it to its ASCII equivalent, that is required to display it as a character on LCD. ASCII value of character 0 is 48, 1 is 49, 2 is 50, 3 is 51 and so on.
TO: Mr.Raj
I am new to this blog but would like to thank you for posting / sharing a very good project.
Regards
A. Durrani
to mr raj.. i had a problem on my project..im doing a rpm meter driven by voltage project..it use a lm2917(frequency to voltage)..it will produce a voltage instead of frequency and the problem is i dont know how to joint the circuit with the lcd..can you guide me what type of pic that i can use to joint from the lm2927 to lcd display..can i use the type of pic that you use in the project above?
Thank you raj for this well documented project can we use fixed voltage reference 16F chip for more accuracy
can u give me a detail file.hope u can help me.thanks..please email to farlin1987@gmail.com
hello mr raj,
im quite impressive with this project
i want to ask a question
can we use EASY 68K for the software to run the coding?
Hi,
Can someone please tell me, why the comment:
CMCON0 = 7; // Disable Comparators
is needed?
I compiled for the '683, no problem. I compiled for the '675, of which also has the same/ similar specs, but it gives an error due to that.
Also, using 4 MHZ, for INTOSC, is that ok? (8 MHz for '684, 4 MHz for the '675).
Another question I have, is if I wanted to count up to 30v, what would the differences be? Like resistors and programming values.
If an amp counter was added, would that still be below the 2K free compiler limit?
I am just more comforatable using mikroBasic, since I do not know C or mikroC at all.
Thank you.
Hi.Friend,
Can you give me .HEX file from this DVM project ?please email to subho254@gmail.com
volt[0] = DisplayVolt/1000 + 48;
volt[1] = (DisplayVolt/100)%10 + 48;
volt[3] = (DisplayVolt/10)%10 + 48;
volt[4] = DisplayVolt%10 + 48;
what this mean...?
Hello Raj..
Please tell me the coding for a 0 to 2000 mVolt (0-2000 mV)? Without change the resistors.. Thank you
You should remove the resistors (both) and put instead a non-inverting opamp and adjust gain for amplify the 2000mV to reach 5V, then the gain should be 2.5
Post a Comment