How to read LM34 temperature sensor negative voltage with atmega32?

I'm trying to read the temperature witr LM34 but it only reads from 0 to 50 F and ut's rage knows as from -50 to +300 F, so i asked a lot and i found that my circuit below can't read a negative voltage,

so what should i do to read negative voltage. also i found because i use single pin to read the temperature this makes me only measure a couple degrees not all the degrees, so what should i do to read all of the degrees and what would the code be. my code

#include <util/delay.h>
#include <stdio.h>
#include <avr/io.h>

// define port C
#define LCD_DDDR   DDRC   /*LCD DATA DDR*/
#define LCD_DPRT   PORTC   /*LCD DATA PORT*/
#define LCD_DPIN   PINC   /*LCD DATA PIN*/
// define port D
#define LCD_CDDR  DDRD   /*LCD COMMAND DDR*/
#define LCD_CPRT  PORTD    /*LCD COMMAND PORT*/
#define LCD_CPIN  PIND    /*LCD COMMAND PIN*/

#define LCD_RS 0      //LCD RS
#define LCD_RW 1      //LCD RW
#define LCD_EN 2      //LCD EN

void delay_us(unsigned int d)
{
while(d) {
_delay_us(1);
d--;
}
}

void lcdCommand(unsigned char cmnd)
{
LCD_DPRT = cmnd;                //send cmnd to data port
LCD_CPRT &= ~(1<<LCD_RS);       //RS = 0 for command
LCD_CPRT &= ~(1<<LCD_RW);       //RW = 0 for write
LCD_CPRT |= (1<<LCD_EN);        //EN = 1 for H-to-L pulse
delay_us(1);                    //wait to make enable wide
LCD_CPRT &= ~(1<<LCD_EN);       //EN = 0 for H-to-T pulse
delay_us(100);                  //wait to make enable wide
}

void lcdData(unsigned char data)
{
LCD_DPRT = data;                //send data to data port
LCD_CPRT |= (1<<LCD_RS);        //RS = 1 for data
LCD_CPRT &= ~(1<<LCD_RW);       //RW = 0 for write
LCD_CPRT |= (1<<LCD_EN);        //EN = 1 for H-to-L pulse
delay_us(1);                    //wait to make enable wide
LCD_CPRT &= ~(1<<LCD_EN);       //EN = 0 for H-to-l pulse
delay_us(100);                  //wait to make enable wide
}

void lcd_init()
{
LCD_DDDR = 0xff;                //all pins in port C output
LCD_CDDR = 0xff;                //all pins in port D output

LCD_CPRT &= ~(1<<LCD_EN);       //LCD_EN = 0
delay_us(2000);                 //wait for init
lcdCommand(0x38);               //init. lcd 2 line, 5 x 7 matrix
lcdCommand(0x0e);               //display on, cursor on
lcdCommand(0x01);               //clear LCD
delay_us(2000);                 //wait
lcdCommand(0x06);               //shift cursor right
}

void lcd_gotoxy(unsigned char x, unsigned char y)
{
unsigned char firstCharAdr[] = {0x80, 0xc0, 0x94, 0xd4};
lcdCommand(firstCharAdr[y - 1] + x - 1);
delay_us(100);
}

void lcd_print(char* str)
{
while (*str)
{
lcdData(*str++);
}
}

int main(void)
{

char buffer[3];

DDRA |= 0b00000111;             //pin 0,1,2 output & pin 3,4,5,6,7 input.
DDRB |= 0xFF;                   // make port B output.
ADMUX |= 0xE6;                  //2.56 v vref, select ADC6 and make left justified.

lcd_init();
lcd_gotoxy(1, 1);
lcd_print("temperature:");

while(1)
{

{
sprintf(buffer, "%d", PORTB);
lcd_gotoxy(1, 2);
lcd_print(buffer);
}
}
return 0;
}

• Your LM34 doesn't give out negative voltages. It only has a positive supply. 0°F isn't 0 V. Check the datasheet and see what the output voltage will be for the range of temperatures you are interested in. Put the additional information in your question. – Transistor Jun 13 '17 at 19:16
• Your schematic shows the LM34 connected to ADC7, but the comment in your code says you are reading ADC6. – Peter Bennett Jun 13 '17 at 19:22
• its a mistake. consider it as it's connected to ADC6 – Ahmed Rifaat Jun 13 '17 at 19:27
• You are only reading the high byte of the ADC result - you need to read ADCL as well, and combine them into a 16 bit int variable. What are you doing with PORTB? – Peter Bennett Jun 13 '17 at 20:07
• i'm reading the high byte to read the real temperature because it gives me for every degree 10 mv and my step size is 2.56/1024 = 2.5 mv this means that the value will be 4 times bigger than the real value. – Ahmed Rifaat Jun 13 '17 at 20:23

As noted in the comments, the LM34 will only be able to read temperatures in the range $[5^\circ F, 300^\circ F]$ configured as you have it. In this confiugration, the output voltage will never be negative so you don't have to do anything special to connect it to your ADC.

However, if you want the full temperature range, you will need to provide a negative supply rail. (See the datasheet for how to choose the resistor $R_1$):

simulate this circuit – Schematic created using CircuitLab

One possible solution is to simply move what you designate "ground" as, that is use a split supply for the LM34, but designate the negative rail as ground.

simulate this circuit

Note that this solution will signifcantly reduce your ADC range since now the neutral point is at $V_s$ rather than ground, and because the output voltage range is skewed positive ($V_{out} \in [-0.5, 3] + V_s$).

Another option is to add a constant voltage to the output using an op-amp circuit:

simulate this circuit

Note that $V_{off}$ was chosen to be 500mV since that's the lowest output voltage the LM34 can output. This type of design can be made to get the full range of your ADC by changing $R_f$ and $V_{off}$, but comes at the cost of requiring more components.

I added a unity gain inverting amplifier as the last stage since the design of the summing amplifier inverts the voltage so it's output is strictly non positive. You can omit this stage by changing what ground is defined as and doing the invert in your code.

You can also add a differential output amplifier stage instead of the summing amplifier stage, though this is usually best tied to an ADC which accepts a differential input. I'm not familiar enough with the ATMEGA32 to know if the ADC's can be configured in differential input mode or not. Since the output voltage range is skewed towards the positive rail, you're losing some of the resolution of your ADC. Differential output amplifiers usually let you set the common mode voltage level so you can set this to 1.25V to get the full ADC range, but this again adds having to compensate for the offset in code. In theory you can use two single input ADC's as a pseudo differential input ADC, though this isn't that ideal.