Author |
Topic |
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 04 2010 : 09:34:18 AM
|
So a small project of mine. I want to read a voltage (CPU vCore in fact) between about 0.8v and 2.3v into a PIC 16F819 and display it on an LCD (I'm fine with the LCD part).
As for reading the voltage, as I understand, I will get approx. 0.005v accuracy (10 bit ADC, so 2^10 values, 5 / 1023 = ~0.005) using VDD (+5v) as Vref+ and Gnd as Vref-. However, I would like to get 1mv accuracy if at all possible.
Can I use a differential amplifier to scale the 0.8-2.3v to 0-5v for the PIC? Is there any other way of increasing the accuracy to 1mv without using a different ADC (oversampling?, not looked into it yet)?
And one more thing while I'm here. I would also like to read the temp. off a K-Type probe on the same LCD through a range of about -200c to +50c. I came across the AD595, is this a suitable interface device? Again, I will have to scale the output voltage from this IC to 0-5v for the PIC.
Sorry for the long post. Thanks for any input :)
AJ. |
|
wasssup1990
Nobel Prize Winner
A Land Down Under
2261 Posts |
Posted - Jun 04 2010 : 12:24:48 PM
|
I can only help you a little bit. I was once familiar with that PIC but no longer. Does the data sheet say that you can Make Vref+ as low as 2.3V? If yes then try that. You can simply use a tight voltage divider... Use lower resistor values and low tolerance for higher accuracy, making sure that that 5V is well regulated.
As for the temp probe I am not familiar and don't have the brain power left. Good night from AUS. |
When one person suffers from a delusion it is called insanity. When many people suffer from a delusion it is called religion. |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 04 2010 : 1:45:25 PM
|
Hey Johnny,
Your suggestion of using 2.3v Vref is a good one. I did think of it but for some reason I discarded the idea. Looking at the 16F819 datasheet it seems that min vref+ would be 2.5v (AVdd-2.5, where Vdd would be 5v). That should give me approx 2.5mv precision which I think I can live with :D.
The voltage divider idea: were you referring to this as the source of vref? For 2.5v obviously I want 1:1 resistor ratio, but what is the best value? 1K?
Temp probe: If it works as I think it does, it outputs roughly 10mv/°c. So the range would be about -2v to +0.5v. How would be the best way to go about translating this into the PIC? |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 04 2010 : 3:52:54 PM
|
Ok, I've setup the PIC with the 2.5v ref (I used 2 off 1K resistors) and I'm very pleased with the results. The reading on the LCD is always within 3mv of my multimeter, usually closer. So I think that's that taken care of :)
Now, I need some kind of positive/negative voltage limiter/cut off for my PIC input. Ideas anyone? I just need to keep the input voltage to the PIC no lower than ground and no higher than 2.5v (probably 5v is suitable also, don't think it matters). Would something like a zener diode be appropriate to limit the upper voltage?
Also, the readout on the LCD tends to flicker a lot between two values. What can I do to stop this? I already have a 1uF cap in parallel with my input voltage - that helped a bit. Anything else I can do? Think software related too.
Thanks, AJ. |
|
|
wasssup1990
Nobel Prize Winner
A Land Down Under
2261 Posts |
Posted - Jun 04 2010 : 8:49:16 PM
|
For voltage limiting don't worry about it too much as long as they don't go out of the maximum limits specified in the datasheet. As for the values flickering on the LCD try a running average filter. Increase the number of averaging samples until the flicker goes away. |
When one person suffers from a delusion it is called insanity. When many people suffer from a delusion it is called religion. |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 05 2010 : 06:20:02 AM
|
Hmm yes, I suppose I don't need a voltage limiter, it was a "just in case" thing.
An averaging filter sounds like what I need. Just gotta do some googling to find out what it is :D
Thanks :)
Edit: I think i know what you mean (;)) now. To implement averaging in software right? |
Edited by - codingplanet on Jun 05 2010 06:57:24 AM |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 05 2010 : 09:21:10 AM
|
Ok, here's some code I put together in C. The idea is that the current ADC value is put into an array, an array average is calculated, then the result is converted to a voltage value and displayed. Then it starts again by overwriting one of the array values.
The calculation of actually voltage works fine, as does displaying the value. However, the averaging bit does not. The value on the screen starts at about 2.500 then rapidly decreases to zero and does not change again until I reset the PIC.
The relevant code:
unsigned long temp_res;
char temp_char[11];
unsigned long temp_int;
unsigned long multi = (2490L);
unsigned long avgarr[10];
int count = 0;
int i;
void readadc(int channel, int LCDline){
temp_res = Adc_Read(channel); // Get results of AD conversion
temp_int = 0;
if(count == 11)count = 0;
avgarr[count] = temp_res;
for(i = 0; i < 11; i++){
temp_int += avgarr[i];
}
temp_res = temp_int / 10;
count++;
// Works fine from here onwards
temp_res = (temp_res * multi) >> 10;
ulong2str(temp_res, temp_char, 0);
LCD_Chr(LCDline,6,temp_char[6]);
LCD_Chr(LCDline,7,'.');
LCD_Out(LCDline,8,&temp_char[7]);
LCD_Chr(LCDline,11,'v');
} |
Edited by - codingplanet on Jun 05 2010 09:28:35 AM |
|
|
wasssup1990
Nobel Prize Winner
A Land Down Under
2261 Posts |
Posted - Jun 05 2010 : 10:55:40 AM
|
That is not correct. Firstly, if you can sacrifice the CPU power use floating point variables, not integer types. When you divide by 10 you'll truncate your result. Example: 19/10 = 1 Large errors in other words.
Secondly your running averager won't work because you are not following the algorithm. You have kind of got the idea but what you need to do is shift the entire array so that the last value in it is gone and a new value is inserted at the beginning. Then the for loop and the temp_res = temp_int / 10; works its magic to find the average value of elements in the array. You can if you want start presenting readings even before your array is full but the algorithm states that you can only start to show the running average after you have got the first N samples, 10 in your case.
|
When one person suffers from a delusion it is called insanity. When many people suffer from a delusion it is called religion. |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 05 2010 : 11:08:06 AM
|
About the accuracy. Thanks for pointing that out. I can change that later after I have it working correctly, which brings me on to your second point.
As I understand it, the array "avgarr" starts off as {0,0,0,0,0,0,0,0,0,0}. When the first value is received, it is stores at position 0. Let's say that the value is 10. The array will now be {10,0,0,0,0,0,0,0,0,0}. (Obviously calculating an average from this is quite useless, as you say, but we'll continue never-the-less). Then another value (9) is received and added leaving the array to look as follows: {10,9,0,0,0,0,0,0,0,0}. When var count == 11 (I reach the end of the array), it is reset to 0. So let's say my array is as follows: {10,9,8,7,6,5,4,3,2,1} when count == 11. On the next iteration, the next incoming value (say 15) will be added to the array at position 0 leaving the array to look like this: {15,9,8,7,6,5,4,3,2,1}.
Is what I am saying correct? If so then I'm not sure why it won't work. |
|
|
wasssup1990
Nobel Prize Winner
A Land Down Under
2261 Posts |
Posted - Jun 05 2010 : 11:09:13 AM
|
Yes I found it. Hopefully this helps you out.
Make bi = 1/N Where N would be 10 in your case. |
When one person suffers from a delusion it is called insanity. When many people suffer from a delusion it is called religion. |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 05 2010 : 11:20:04 AM
|
Oooook...
I don't really understand this. What is x meant to be? And what is the whole equation for?
Edit: And why should bi be 1/N? |
Edited by - codingplanet on Jun 05 2010 11:20:43 AM |
|
|
wasssup1990
Nobel Prize Winner
A Land Down Under
2261 Posts |
Posted - Jun 05 2010 : 11:29:58 AM
|
Okay maybe the notation form was a bit too University level. Here it is in the form you're familiar with.
{0,0,0,0,0,0,0,0,0,0} {10,0,0,0,0,0,0,0,0,0} {10,9,0,0,0,0,0,0,0,0} {10,9,8,0,0,0,0,0,0,0} {10,9,8,7,0,0,0,0,0,0} {10,9,8,7,6,0,0,0,0,0} {10,9,8,7,6,5,0,0,0,0} {10,9,8,7,6,5,4,0,0,0} {10,9,8,7,6,5,4,3,0,0} {10,9,8,7,6,5,4,3,2,0} {10,9,8,7,6,5,4,3,2,1} {9,8,7,6,5,4,3,2,1,15} {8,7,6,5,4,3,2,1,15,16} {7,6,5,4,3,2,1,15,16,17} . . .
Your for loop will add up each element in the array for each line above and after that, will divide that total by 10 in your case. |
When one person suffers from a delusion it is called insanity. When many people suffer from a delusion it is called religion. |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 05 2010 : 11:50:30 AM
|
Yes, Im not even in college yet haha.
So with my code the array goes from: {10,9,8,7,6,5,4,3,2,1}
to: {9,8,7,6,5,4,3,2,1,15}
?? Strange. In that case what do I need to change to make it work? |
|
|
wasssup1990
Nobel Prize Winner
A Land Down Under
2261 Posts |
Posted - Jun 05 2010 : 12:06:47 PM
|
It's not hard. I'm sure you'll figure it out. I'm going to sleep. See ya. |
When one person suffers from a delusion it is called insanity. When many people suffer from a delusion it is called religion. |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 05 2010 : 12:10:49 PM
|
But I don't understand why that happens because if I set the value at position zero it should stay at position zero. Evidently it does not. I need some kind of first in last out array right?
Edit: Maybe I can just keep setting the value at position 0? Imma try it now... |
Edited by - codingplanet on Jun 05 2010 12:18:06 PM |
|
|
codingplanet
Mad Scientist
United Kingdom
195 Posts |
Posted - Jun 05 2010 : 1:51:36 PM
|
I've scrapped the array all together and now I'm using a stationary average (if that's the correct term).
unsigned long temp_res;
char temp_char[11];
unsigned long multi = (2490L);
int i;
void readadc(int channel, int LCDline){
temp_res = 0;
for(i = 0; i < 16; i++){
temp_res += Adc_Read(channel);
}
temp_res /= 16;
temp_res = (temp_res * multi) >> 10;
ulong2str(temp_res, temp_char, 0);
LCD_Chr(LCDline,6,temp_char[6]);
LCD_Chr(LCDline,7,'.');
LCD_Out(LCDline,8,&temp_char[7]);
LCD_Chr(LCDline,11,'v');
}
Works very well. No side effects that I have come across yet :)
Thanks for the help Johnny. |
Edited by - codingplanet on Jun 05 2010 1:54:21 PM |
|
|
Topic |
|
|
|