Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

ADC value & temp NOT same display on LCD...

Status
Not open for further replies.

hhhsssmmm

New Member
Hello

My project is working partially. I have designed a temperature display on LCD project that involves
the following components...

PIC18F4423
ceramic 20Mhz
C18 compiler
Thermistor: MCP9701A
LCD: 16X2

Im using full 12bit ADC and +Vref == VDD and -Vref == VSS.

Im displaying both the ADC value and the corresponding temperature on the LCD display.
The problem is that my temperature display output is about 12deg higher than the actual corresponding
ADC value display output. For example....

LCD output:
Line 1 reads: 670 (ADC value)
Line 2 reads: 34.3 C

Now according to this formula Vin == [(670)x(5)] / (4096) .... gives '817mV' which is '21.4C'

21.4C is what my precision digital thermometer is also displaying. So that is how i know that my ADC
value is displaying correctly...and that its the displayed temperature value that is 12 deg higher.

Usally the offset value is adjusted by a few bits only....I on the other hand have had to increase the offset
value to 100 steps more and then my temp read out displays correctly. BUT this is not good since when i go
down to -2C....then the displayed temperature value drops to -9C....and that is WAY too much offset! Also
the offset value 328 is properly caluculated as follows...

For the thermistor used:

1degC == 19.5mV
so 0.0626degC == 1.2207mV

And 1 ADC bit corresponds to 5/4096 == 1.2207mV

So 0degC or 400mV (factory calibrated setting of thermistor) == 328 ADC bits


So please can some one have a look at my code below and kindly suggest why the error in temp display?

Thank you
Haseeb

Code:
#include <p18f4423.h>    
#include <delays.h> 
         
#pragma config OSC = HS
#pragma config WDT = OFF  
#pragma config LVP = OFF 
 
void SendLCD(unsigned char Byte, unsigned char type); //LCD display function
 
#define LCD LATD //LCD Latch PORT
#define LCD_RS LATEbits.LATE0 //Register Select (0: Instruction/ 1: Data)
#define LCD_E LATEbits.LATE2 //Enable (Starts data read/write)
#define LED LATCbits.LATC3  //power up LED

unsigned char minus = 0; //-ve flag...for -ve sign activation                          

unsigned int
              bin = 0, //temp buffer variable for ADC value for BCD conversion process                     

              binADC = 0, //buffer variable for showing direct ADC value on LCD

             //BCD coversion variables
             d0 = 0,       
             d1 = 0,
              d2 = 0;   
      
void main(void)
{     

  //Configure ADC
  ADCON1 = 0b00001110; //VREF- = VSS,
                        //VREF+ = VDD,
                       //Enable AN0 only...rest all Digital
   
  ADCON0 = 0b00000000;  //Select channel 0 (AN0),
                         //ADC Idle,
                        //ADC Dissabled
              
  ADCON2 = 0b10001000;    //Right Justified for 12bit ADC scaling
                        //ADC Acquisition Time 2TAD,
                        //ADC Conversion Clock FOSC/2               

  PORTA = 0; //initiallize PORTA
  TRISAbits.TRISA0 = 1; // Analog Channel 0 (AN0 input) 
 
  PORTB = 0; //intialize PORTB 
  TRISBbits.TRISB3 = 1;    //swtich 1 input
  TRISBbits.TRISB4 = 1;    //switch 2 input
  TRISBbits.TRISB5 = 1;    //switch 3 input   

  PORTC = 0; //intialize PORTC
  TRISCbits.TRISC3 = 0;  //LED      
 
  PORTD = 0; //initiallize PORTD 
  TRISD = 0x00; //LCD output   

  PORTE = 0; //initiallize PORTE   
  TRISEbits.TRISE0 = 0;  //LCD Register Select (to LCD Pin 4)
  TRISEbits.TRISE2 = 0;  //LCD Enable (to LCD Pin 6)
 

    INTCON = 0; //dissable all interrupts


    Delay10KTCYx(10); //20ms wait for LCD power up


    //Initialize the LCD
    LCD = 0x00; //clear LCD PORTD pins
    Delay1KTCYx(25); //delay 5mS
    SendLCD(0x03,0); //Initialization command
    Delay1KTCYx(25); //delay 5mS
    SendLCD(0x03,0); //Initialization command
    Delay100TCYx(8); //delay 160uS
    SendLCD(0x03,0); //Initialization command
    Delay100TCYx(8); //delay 160uS
    SendLCD(0x3C,0); //Interface lenght is 8 bits long, 2-lines, 5x10 dots
    Delay100TCYx(8); //delay 160uS
    SendLCD(0x10,0); //Turn off LCD
    Delay100TCYx(8); //delay 160uS
    SendLCD(0x01,0); //Clear LCD   
    Delay1KTCYx(25); //delay 5mS
    SendLCD(0x06,0); //Increment the cursor after each byte written
    Delay100TCYx(8); //delay 160uS
    SendLCD(0x0C,0); //Turn on LCD, cursor off, cursor blinking off
    Delay100TCYx(8); //delay 160uS


    //Power up flashing LED
    LED = 1;   
    Delay10KTCYx(125); //250ms delay            
    LED = 0;
    Delay10KTCYx(125); //250ms delay
    LED = 1;
    Delay10KTCYx(125); //250ms delay
    LED = 0;




    ADCON0bits.ADON = 1; //Enable ADC


    while(1) //loop forever
    {         
   


        /********************** READING 12bit ADC value ************************************/

        ADCON0bits.GO_DONE = 1; //Start ADC Conversion
                                           
        while (ADCON0bits.GO_DONE == 1);  //Wait until Conversion finishes
 
        bin = (ADRESH * 256) + ADRESL; //12bit ADC read...Merging High byte with low byte       

        /***********************************************************************************/





   



        /*********** Below steps are for displaying the ADC value on LCD ***************/

        binADC = bin; //assigning ADC value to buffer variable

        //convert ADC value to BCD
        d2 = binADC % 10;                   
        d1 = (binADC / 10) % 10;          
        d0 = ((binADC / 10) / 10) % 10;  
       
        //convert BCD digit numbers to ASCII text characters for LCD           
        d0 += '0';
        d1 += '0';
        d2 += '0';        

        SendLCD(0x80,0); //activate LCD line 1           

        //print the ADC value on LCD
        SendLCD(d0,1);    
        SendLCD(d1,1);    
        SendLCD(d2,1);
       
        /*******************************************************************************/
       








        /********************* Below are the steps to display the temperature on LCD ********************/   
   
        if(bin < 328) //below 0 deg C ... (0 deg C @ 400mV)                   
        {

            minus = 1;    //activate -ve sign   
   
            bin = 328 - bin; //offset adjustment ... (0 deg C @ 400mV)       
 
        }
       
        else

            bin = bin - 328; //offset adjustment ... (0 deg C @ 400mV)               

        //convert ADC value to BCD
        d2 = bin % 10;                   
        d1 = (bin / 10) % 10;          
        d0 = ((bin / 10) / 10) % 10;  
       
        //convert BCD digit numbers to ASCII text characters for LCD           
        d0 += '0';
        d1 += '0';
        d2 += '0';            

        SendLCD(0xC0,0); //activate LCD line 2   
           
        //if temperature is in -ve ... then print -ve sign on LCD
        if(minus == 1)
        {

            SendLCD('-',1);    

            minus = 0; //reset minus sign flag

        }   

        else //temperature is in +ve so erase minus sign (blank space print)
       
            SendLCD(' ',1);
       
        //print the temperature on LCD
        SendLCD(d0,1);    
        SendLCD(d1,1);
        SendLCD('.',1);
        SendLCD(d2,1);
        SendLCD('C',1);                            
       
        /********************************************************************************************/



        Delay10KTCYx(250); //500ms cycle delay                    


    }// end of main while loop




} //end of main()



void SendLCD(unsigned char Byte, unsigned char type)
{
   
    LCD_RS = type; //Set whether it is a Command (0) or Data/Char (1)   

    Delay100TCYx(8); //delay 160uS

    LCD = Byte; //assign the new data to the LCD PORTD
   
    Delay100TCYx(8); //delay 160uS

    LCD_E = 1; //Set Enable High   
   
    Delay100TCYx(8); //delay 160uS
   
    LCD_E = 0; //Set Enable Low

}
 
Dear Haseeb

I believe that you have made a mistake when calculating the temperature. You correctly do the subtraction of/from 328 and store the result to bin. So far so good. But now think a little bit. What does this value represent? It is obviously, the absolute number of bits, in reference to zero degrees C. This value needs to be converted to voltage first, using the formula that you also describe at your post, in order to find its representation in temperature. So, lets take your example. You read 670 from your ADC. Then bin=670-328=342. Vin=(342x5.0)/4096=417mV. This means that you are 417mV above the zero degrees value of 400mV. By dividing 417mV with 19.5mV/degrees C, you find 21.4 degrees C.

Your mistake is that you take the value of bin and you directly convert it in BCD etc. etc. and you display it in the LCD. This is wrong, because the bin value does not represent temperature. I hope that you have understood me!!
 
ok thank you very much for your thorough explanation....

this is what i understand now in terms of coding of what u said....

plz take a look at my ammended code section below.....however im still not getting the
temperature correctly displayed...it shows temperature as all zeros only...

thank you

haseeb

Code:
unsigned int
           bin = 0, //temp buffer variable for ADC value for BCD conversion process                          
         

unsigned long temp_result = 0, //variable to hold the actual temperature value (volt to                                    degree conversion)

          //BCD coversion variables
                 d0 = 0,      
              d1 = 0,
                d2 = 0,


bin = bin - 328; //offset adjustment ... (0 deg C @ 400mV)            

      //Volt to degree conversion
   
      temp_result = (bin*10000) * 626; //0.0626 deg C per ADC division

      //convert degree value to BCD
      d9 =  temp_result % 10;                   

      d8 = (temp_result / 10) % 10;       
   
      d7 = ((temp_result / 10) / 10 ) % 10;   
      
      d6 = (((temp_result / 10) / 10) / 10 ) % 10;

      d5 = ((((temp_result / 10) / 10) / 10 ) / 10 ) % 10;
   
      d4 = (((((temp_result / 10) / 10) / 10 ) / 10 ) / 10 ) % 10;
         
      d3 = ((((((temp_result / 10) / 10) / 10 ) / 10 ) / 10 ) / 10) % 10;

      d2 = (((((((temp_result / 10) / 10) / 10 ) / 10 ) / 10 ) / 10) / 10) % 10;

      d1 = ((((((((temp_result / 10) / 10) / 10 ) / 10 ) / 10 ) / 10) / 10) / 10) % 10;

      d0 = (((((((((temp_result / 10) / 10) / 10 ) / 10 ) / 10 ) / 10) / 10) / 10) / 10) % 10;

      //convert BCD digit numbers to ASCII text characters for LCD         
      d0 += '0';
      d1 += '0';
      d2 += '0';    

   SendLCD(0xC0,0); //activate LCD line 2   

   //print the temperature on LCD
      SendLCD(d0,1);    
      SendLCD(d1,1);
      SendLCD('.',1);
      SendLCD(d2,1);
      SendLCD('C',1);
 
Again you seem to miss sth.

At your first post you have written that

Code:
Now according to this formula Vin == [(670)x(5)] / (4096) .... gives '817mV' which is '21.4C'

Do you understand this formula? Why have you used it? What does it mean? And why do you say that 817mV is 21.4C ?

I mean, it is the correct way to calculate the temperature, but I believe that you do not understand the meaning of it. This is the formula that you have to use to convert the "bin" value into temperature.
 
Dear nickagian

thank you for your reply.

I understand the formula well. And i understand the logic behind it all as well.

Can you please explain how can i place this equation in my code with out using floating point calculations...i know i must use integer maths....but i can not seem to work it out how.
once again, placing the equation in the code is not a problem for me its getting to work with integer maths and to avoid floating point..

please can u suggest how?

Another way i read elsewhere that in order to do 'volt to degree' conversion after the ADC offset adjustment, all i have to do is multiply my result with 0.0626 since thats what 1 ADC bit equates to in my setup. Again the integer maths needs to take place....i have tried some coding approach for this method and below is my code but im not getting the desired temp reading. Please can you help.


thank you

Haseeb

Code:
unsigned int 
                 bin;


unsigned char                
			  
             //BCD coversion variables
             d0 = 0,       
             d1 = 0,
             d2 = 0;   

unsigned long 

              temp_result = 0;



//volt to degree conversion
temp_result = (bin * 626) * 10000;


		//convert ADC value to BCD
		d2 = temp_result % 10;                   
        d1 = (temp_result / 10) % 10;         
        d0 = ((temp_result / 10) / 10) % 10;         
        
       
        //convert BCD digit numbers to ASCII text characters for LCD           
        d0 += '0';
        d1 += '0';
        d2 += '0';
 
Last edited:
Dear friend

First of all I apologize if you have been offended :-( I was in a hurry when I wrote my last post and maybe some points seem bad to you. But I didn't want to say anything against you! I'm sorry, I didn't mean to say that you do not know the formula. Perhaps I did not read your previous code very carefully.

Ok then, now I -think at least :) -have understood what your problem is. So let me tell you this :

You have the "bin" value (after the subtraction). This value has to be multiplied with 1.2207 mV/bit. And the result of the multiplication has to be divided with 19.5 mV/oC

All in all, you have :

Code:
temp_result = (bin * 1.2207 mV/bit) / (19.5 mV/oC) = bin * 0.0626 (in oC)

If you want to avoid floating point maths, you can multiply the 0.0626 constant with 10000. In the end you have :

Code:
temp_result = bin * 0.0626 * 10000 = bin * 626 (in 10^4 oC)

In the result, the decimal point will be between d3 and d4 digits of the number in BCD format (d6_d5_d4_d3_d2_d1_d0)

I think that in your code you multiply bin with 626*10000. This maybe the reason for the false reading.

With this implementation, however, you have to make sure that the result of this multiplication will never exceed the limit of the unsigned long variable. You can check this if you take the max possible value of bin and multiply it with 626.

I hope that this time it will work! :)
 
Last edited:
hello

my dear friend....thank you so much for your replies and your suggestions.

i have cracked the ice...and have now got a very nice accuratly working digital thermometer PIC based and using ADC 12bit and Vref == +5V.

The solution was the below lines of code....they worked perfectly....

thank you so much for all your help

Haseeb

Code:
//Volt to degree conversion using 0.0626 deg C per ADC division
       //Shifting all digits to MSB location (extreme left) in the 32bit variable 'temp_result'
       temp_result = (0.0626 * 100000000) * bin;

       //converting 'temp_result' variable to to BCD
         d2 = (((((((temp_result / 10) / 10) / 10 ) / 10 ) / 10 ) / 10) / 10) % 10;
         d1 = ((((((((temp_result / 10) / 10) / 10 ) / 10 ) / 10 ) / 10) / 10) / 10) % 10;
         d0 = (((((((((temp_result / 10) / 10) / 10 ) / 10 ) / 10 ) / 10) / 10) / 10) / 10) % 10;
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top