CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

LM35 Driver - UPDATED

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
Gabriel



Joined: 03 Aug 2009
Posts: 1067
Location: Panama

View user's profile Send private message

LM35 Driver - UPDATED
PostPosted: Wed Jun 27, 2012 11:06 pm     Reply with quote

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 Smile



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

View user's profile Send private message

PostPosted: Sat Jun 30, 2012 9:42 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Jul 02, 2012 10:23 pm     Reply with quote

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

View user's profile Send private message

Excellent
PostPosted: Fri Jul 13, 2012 10:16 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Jul 13, 2012 12:29 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 4:15 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Jul 23, 2012 7:01 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Dec 12, 2012 11:38 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Dec 13, 2012 10:35 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group