View previous topic :: View next topic |
Author |
Message |
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
LM35 Driver - UPDATED |
Posted: Wed Jun 27, 2012 11:06 pm |
|
|
Hi All;
Just finished coding a little "driver" for the LM35 Temp Sensor.
I know there are onemillionfivehundredthousandandfifteen better sensors out there but, this one is simple and CHEAP.
The Code takes 18 samples of Vout+ and 18 of Vout- and does an olimpic average. Might look strange at first, but i tried to save a bit of code space by ordering my math and looking at the LST files.
The output I get is as per the datasheet +/-0.5C
(tested at 5V, powered via Pickit3)
Quote: |
New Temp: 32.208 Difference: 0.000
DATE: 00/00/00 TIME: 18:53:45
New Temp: 32.696 Difference: 0.488
DATE: 00/00/00 TIME: 18:53:45
New Temp: 32.696 Difference: 0.000
DATE: 00/00/00 TIME: 18:53:45
New Temp: 32.696 Difference: 0.000
DATE: 00/00/00 TIME: 18:53:46
New Temp: 32.208 Difference: 0.488
DATE: 00/00/00 TIME: 18:53:46
|
(sample taken while touching the sensor with my fingers)
As you can see, the variation is in 0.5C steps up or down.
Code: |
// AUTHOR: GABRIEL BARRIOS
// DATE: 26/06/2012
// Panama, Rep. Panama.
/*****************************************************************************************
/ Driver for the LM35
/ Basically a "fancy" ADC reading Function that uses Channels AN0 & AN1.
/ The LM35 circuit attached is as per the datasheet -Figure7-Page7-(Nov 2000)
/ Its a "fancy" ADC reading function because the LM35 is driven by a
/ charge pump (2xVin) attached to an I/O Pin. LM35 says Vin=(4-20V).
/ The I/O Pin drives the flying capacitor by toggling in TIMER0's ISR.
/ The function samples each of the inputs 18 times, and averages.
/ The Average is actually the "Olimpic Average" kind, this means it
/ removes the highest and lowest values
/ Carefull ordering of operations saves a _few_ extra lines of code when complied
/ The whole thing takes 59.6ms or 9.6ms @8Mhz if you remove the 50ms Chg Pump
/ stabilizing period (tested with O-scope)
/*****************************************************************************************/
//******************************** Function declarations *********************************
int16 GET_TEMP(); //returns the current temperature value.
//******************************** Variable declarations *********************************
// NONE - ALL LOCAL
//************************************** CODE ********************************************
//****************************************************************************************
// Returns a 16bit HEX temperature value
//****************************************************************************************
int16 GET_TEMP()
{
ENABLE_INTERRUPTS(INT_TIMER0); // enable timer interrupts (for charge pump)
SETUP_TIMER_0(RTCC_DIV_2|RTCC_INTERNAL); // Start timer (ISR drives a Charge Pump)
SETUP_ADC_PORTS(sAN0|sAN1); // Select Analog channels on PIC
SETUP_ADC(ADC_CLOCK_DIV_16); // Select ADC conversion Clock
delay_ms(50); // give some time for charge pump to stabilize
int counter=0; // temporary counter
int16 temperature=0; // temporary Temperaure variable
int16 highest=0; // saves highest reading for filtering
int16 lowest=0xFFFF; // saves lowest reading for filtering
int16 temporary=0;
SET_ADC_CHANNEL(0); // select ADC Channel (Vout+ on LM35)
delay_us(15); // small delay to settle
counter=0; // clear counter
while(counter<18) // Take 18 readings and average (16+highest+lowest)
{
temporary=READ_ADC(); // take an ADC reading
if(temporary<lowest) // check if smaller than lowest
lowest=temporary; // save value if smaller
if(temporary>highest) // check if larger than highest
highest=temporary; // save value if larger
temperature+=temporary; // add the ADC value for averaging (Vout+ on LM35)
counter++;
}
temperature-=highest; // remove highest value
temperature-=lowest; // remove lowest value
SET_ADC_CHANNEL(1); // select ADC channel (Vout- on LM35)
delay_us(15); // small delay to settle
counter=0; // clear counter
lowest=0xFFFF; // reset lowest
highest=0; // reset highest
while(counter<18) // Take 18 readings and average (16+highest+lowest)
{
temporary=READ_ADC(); // take an ADC reading
if(temporary<lowest) // check if smaller than lowest
lowest=temporary; // save value if smaller
if(temporary>highest) // check if larger than highest
highest=temporary; // save value if larger
temperature-=temporary; // substract the ADC values for averaging (Vout- on LM35)
counter++;
}
temperature+=highest; // remove highest value
temperature+=lowest; // remove lowest value
temperature/=16; // average by 16
temperature*=0x1E8; // multiply by ADC constant.
SETUP_ADC(ADC_OFF); // turn off ADC module
DISABLE_INTERRUPTS(INT_TIMER0); // disable interrupt (Charge pump off)
return(temperature);
}
|
(Sorry for the ugly formating of the comments - Copy/Paste issue)
Save the above code in "LM35.c", include it in your main code and then:
Code: | Temperature=GET_TEMP(); |
or
Code: | Printf("New Temp: %2.3w \n\r", GET_TEMP()); |
Simple.
Enjoy,
Gabriel
Thanks forum for your previous help
Ps: this code *should* work on any other sensor that has a 2 (+&-) outputs... or diferential outputs.
EDIT: i tested this on a 16F88... it does not handle Negative Temperatures .. YET. _________________ CCS PCM 5.078 & CCS PCH 5.093
Last edited by Gabriel on Mon Jul 02, 2012 10:23 pm; edited 1 time in total |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Sat Jun 30, 2012 9:42 am |
|
|
Finished testing the function last night over the full range of the LM35... including negative temperatures.
I'll upload the updated driver on monday.
It now returns a signed int16, and only 2 decimals.... its pointless to have more. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Mon Jul 02, 2012 10:23 pm |
|
|
As Promised:
Updated code LM35 - Full range - Negative temperatures - Tested in full (using trimpots)
Now I believe it is truly also applicable for Differential Sensors as well.
Code is tested at 5V on a 16F88 using internal OSC@8MHz and VDD-VSS as ADC references.
Code: | // AUTHOR: GABRIEL BARRIOS
// DATE: 26/06/2012
// Panama, Rep. Panama.
/*****************************************************************************************
/ Driver for the LM35
/ Basically a "fancy" ADC reading Function that uses Channels AN0 & AN1.
/ The LM35 circuit attached is as per the datasheet -Figure7-Page7-(Nov 2000)
/ Its a "fancy" ADC reading function because the LM35 is driven by a
/ charge pump (2xVin) attached to an I/O Pin. LM35 says Vin=(4-20V).
/ The I/O Pin drives the flying capacitor by toggling in TIMER0's ISR.
/ The function samples each of the inputs 18 times, and averages.
/ The Average is actually the "Olympic Average" kind, this means it
/ removes the highest and lowest values
/ Careful ordering of operations saves a _few_ extra lines of code when complied
/ The whole thing takes 59.6ms or 9.6ms @8Mhz if you remove the 50ms Chg Pump
/ stabilizing period (tested with O-scope)
/*****************************************************************************************
/ EDIT 29/06/2012:
/ Code modified to handle negative temperatures and returned signed values.
/ Tested over the Full Range of values for the LM35 (using trimpots)
/ Reduced number of Decimals used (modified ADC constant)
/*****************************************************************************************/
//******************************** Function declarations *********************************
signed int16 GET_TEMP(); //returns the current temperature value.
//******************************** Variable declarations *********************************
// NONE - ALL LOCAL
//************************************** CODE ********************************************
//****************************************************************************************
// Returns a 16bit signed temperature value
//****************************************************************************************
signed int16 GET_TEMP()
{
ENABLE_INTERRUPTS(INT_TIMER0); // enable timer interrupts (for charge pump)
SETUP_TIMER_0(RTCC_DIV_2|RTCC_INTERNAL); // Start timer (ISR drives a Charge Pump)
SETUP_ADC_PORTS(sAN0|sAN1); // Select Analog channels on PIC
SETUP_ADC(ADC_CLOCK_DIV_16); // Select ADC conversion Clock
delay_ms(50); // give some time for charge pump to stabilize
int counter=0; // temporary counter
signed int16 temperature=0; // temporary Temperaure variable
int16 highest=0; // saves highest reading for filtering
int16 lowest=0xFFFF; // saves lowest reading for filtering
int16 temporary=0;
SET_ADC_CHANNEL(0); // select ADC Channel (Vout+ on LM35)
delay_us(15); // small delay to settle
counter=0; // clear counter
while(counter<18) // Take 18 readings and average (16+highest+lowest)
{
temporary=READ_ADC(); // take an ADC reading
if(temporary<lowest) // check if smaller than lowest
lowest=temporary; // save value if smaller
if(temporary>highest) // check if larger than highest
highest=temporary; // save value if larger
temperature+=temporary; // add the ADC value for averaging (Vout+ on LM35)
counter++;
}
temperature-=highest; // remove highest value
temperature-=lowest; // remove lowest value
SET_ADC_CHANNEL(1); // select ADC channel (Vout- on LM35)
delay_us(15); // small delay to settle
counter=0; // clear counter
lowest=0xFFFF; // reset lowest
highest=0; // reset highest
while(counter<18) // Take 18 readings and average (16+highest+lowest)
{
temporary=READ_ADC(); // take an ADC reading
if(temporary<lowest) // check if smaller than lowest
lowest=temporary; // save value if smaller
if(temporary>highest) // check if larger than highest
highest=temporary; // save value if larger
temperature-=temporary; // substract the ADC values for averaging (Vout- on LM35)
counter++;
}
temperature+=highest; // remove highest value
temperature+=lowest; // remove lowest value
temperature/=16; // average by 16
temperature*=0x31; // multiply by ADC constant.
SETUP_ADC(ADC_OFF); // turn off ADC module
DISABLE_INTERRUPTS(INT_TIMER0); // disable interrupt (Charge pump off)
return(temperature);
} |
This is my timer ISR to drive the Charge Pump.
"PUMP" is the defined as the pin that drives the flying capacitor
Code: | #INT_TIMER0
void Timer0Int()
{
output_toggle(PUMP);
} |
To get a temperature reading:
Code: | Printf("New Temp: %2.2w \n\r",GET_TEMP()); |
Regards,
Gabriel. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
codyfinden
Joined: 13 Jul 2012 Posts: 2
|
Excellent |
Posted: Fri Jul 13, 2012 10:16 am |
|
|
Excellent work! Even though the LM35 is very simple, it's nice to have a well written driver to use with it. It is a reliable temp. sensor. Just wanted to say thanks for the driver! |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Fri Jul 13, 2012 12:29 pm |
|
|
Hey!
Glad to hear you find this usefull.
Have you used the code?
note: that if your circuit runs on 5V you need not to use the charge pump im using in my code... i put it inplace since my application at the moment was battery operated....3.3V, so i would be placing the LM35 out of spec.
the charge pump compensates for this..
Code: | D1
5V o--------->|---o
|
|
C1 | D2
PUMP o------]|----o-->|-----o
PIN |
C2 |
o------]|----o---------o LM35 V+
|
_|_
GND |
_________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
Teriyaki
Joined: 07 Sep 2006 Posts: 7 Location: Munich, Germany
|
|
Posted: Mon Jul 23, 2012 4:15 am |
|
|
Hi Gabriel,
the LM35 in TO220 actually provides the best results because of the high thermal conductivity of the mounting connection. Just a pity that the mounting surface is connected to pin2. So with the proposed circuit the electronic ground never can be connected to the object ground where the temperature shall be measured, if somebody wants to measure the negative temperatures. Do you have any ideas how to work around this problem? |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Mon Jul 23, 2012 7:01 am |
|
|
Hi,
Yeah.. the TO-220 has that problem with the circuit I'm using.
My application used a TO-92 so it was not a problem for me.
You can still use the TO-220 package, just make sure its electrically isolated... if you want to use it in a car for example you could still use it for Positive temperatures....not with this code however.
G _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
freesat
Joined: 08 Feb 2011 Posts: 32
|
|
Posted: Wed Dec 12, 2012 11:38 pm |
|
|
Hi Gabriel, can you draw a better schematic circuit? I'm a bit confused with it. Maybe upload a picture.
Thanks for lm35 driver. |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Thu Dec 13, 2012 10:35 am |
|
|
If you will be powering your circuit from 5V just use the circuit shown on FIG.7 of the LM35 Datasheet, no charge pump required.
if you will be powering your circuit from ~3V (like i was) then you need to use a charge pump as described in my previous post... and used that to power the circuit as described in FIG.7 of the Datasheet of the LM35.
what is it that you dont understand?
http://en.wikipedia.org/wiki/Charge_pump
Its a simple doubler ch/pump... 2 diodes and a "flying capacitor"...
EDIT: Caps are electrolytic 10V of a few uF... 0.47 seems to work fine use a larger one on C2 (check with Oscope for ripple)... Diodes: any small shottky will work fine.
"PUMP PIN" is any pin of your PIC and its driven in the TMR0 ISR as defined in the post.
br,
G. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
|