# Fahrenheit to Celsius (Integer math for a PIC) help wanted.

Status
Not open for further replies.

#### blueroomelectronics

##### Well-Known Member
I’m building a thermostat kit using a LM34 temperature sensor.
I requre the results in C or F

An LM34DZ is a Fahrenheit sensor and has an excellent range for direct A/D input on the PIC. -32C to 100C are possible (0F to 212F) or 0V to 2.12V at the A/D pin. The LM34 also offers more resolution per degree in this application.

The PICs 10bit ADRES is right justified (MSB --- LSB) and uses GND for the zero reference and 2.5V for the +Vref.

The math I’d like to simplify and avoid floating point, it would be nice but not necessary to convert the fractional part at this point.

So for F the formula is… (ADRES is the 10bit right justfied result) the result is a single byte integer (decimal)

Now F itself is very simple, *125 is easy and divide by 4096 (2^12) is super easy in binary. It’s the Celsius math I’d like some help with.

#### eng1

##### New Member
blueroomelectronics said:
From my calculations °F = (ADRES*2.5)/1023*100 = ADRES * 250 / 1023

#### ericgibbs

##### Well-Known Member
hi Bill, [sorry!]

I have found using the PIC 10bit ADC with temperature ranges is the poor resolution, about +/-0.5deg.

I multiply the ADC value by 10 , so max is 10230 [0x26F6]
Use a little 16bit arithmetic.

eg: C = [F *10 -320]/18
.....C =[2120-320]/18 = 100
.....C =[984-320]/18 =368

eg: F = [C*18] +320
.....F = [100 *18] +320 =212
.....F = [37 * 18] +320 =986

The conversion results I have shown are after binary to ascii conversion, the decimal part is the lsd.

Modify the 18 constant to give you the scaling factor.

Hope this makes sense to you.

EDIT:
Summary:

Multiply the 16 bit value in the ADC registers by 10, I expect you have 16bit maths in the program.

The 32 and 1.8 are set as constants 320 and 18

Do the 16 bit maths operation for the conversion F to C or C to F.

This gives a 16 bit result.

Convert the 16 bit result to 4 digit ASCII, you need the conversion anyway in order to display it.

Use the lsd ASCII digit as the decimal part of the temperature.

.
If you look at the 320 and 18 constants, it is possible to adjust them to correct for the scaling factor in the ADC volts versus Temperature.

Last edited:

#### Pommie

##### Well-Known Member
If you want to get rid of any non shift divides then,

How are you going to handle negative values?

Mike.

#### blueroomelectronics

##### Well-Known Member
Thanks for all the responses, I noticed my math was wrong in the original post. I'm putting it all in a spreadsheet *google to see the results.

LM34 F = ADRES*125/512
LM35 C = ADRES*125/512

Pommie your formula is simple and it's so easy to /65536

8897 factors to 7 x 31 x 41

I'll use a separate formula for the negative values.

Last edited:

#### blueroomelectronics

##### Well-Known Member
And here's the code I used for testing. It displays the ADRES (in hex) on the 2.5 digit LCD.
The schematic is on my site. The LM34 and PIC A/D appear to be rock solild.
Code:
       list     p=16F917
include <p16F917.inc>
__CONFIG _DEBUG_ON & _WDT_OFF & _INTOSCIO
errorlevel      -302,-305
; LCD

#define Rst555  PORTB,5
org     0x000
goto    Init

; *** Enable LCD
; g7 f6 e5 _4 d3 c2 b1 a0
LCD     andlw   0x0F            ; mask off high byte
addwf   PCL             ; hex display table
dt      b'01101111'     ; 0
dt      b'00000110'     ; 1
dt      b'10101011'     ; 2
dt      b'10001111'     ; 3
dt      b'11000110'     ; 4
dt      b'11001101'     ; 5
dt      b'11101101'     ; 6
dt      b'00000111'     ; 7
dt      b'11101111'     ; 8
dt      b'11001111'     ; 9
dt      b'11100111'     ; A
dt      b'11101100'     ; b
dt      b'01101001'     ; C
dt      b'10101110'     ; d
dt      b'11101001'     ; E
dt      b'11100001'     ; F

Init    movlw   0x0F            ; 1:2 prescaler (4 sec clock)
movwf   T1CON           ; enable Timer1 ext 32768 crystal
banksel LCDCON
movlw   .6              ; 1:7 postscale 31Hz refresh
movwf   LCDPS           ; static bias COM0 (RB4)
movlw   b'10011000'     ; LCD, VLCD (RC2) & Sleep on
movwf   LCDCON          ; clock INTRC/32
movlw   b'11101111'     ;
movwf    LCDSE0
movwf   LCDDATA0
movlw   b'11111111'
movwf    LCDSE2
movwf   LCDDATA2
clrf    STATUS          ;Bank0
movlw   b'10100001'     ;
Main    bcf     PIR1, TMR1IF
banksel ANSEL
movlw   b'00001001'     ; RA0 & RA3 digital
movwf   ANSEL           ;B1
movlw   b'11011111'     ; 555 Control
movwf   TRISB
movlw   0xFF            ; enable TMR0 on RA4
movwf   OPTION_REG
clrf    STATUS          ;Bank0
btfsc   ADCON0, GO      ;B0 wait for A/D done
goto    $-1 rrf ADRESH,W ;B0 put high bit in Carry bsf STATUS,RP0 ;Bank1 movf ADRESL,W call LCD banksel LCDDATA2 movwf LCDDATA2 ; skpc bsf LCDDATA2,4 ; skpnc bcf LCDDATA2,4 ; banksel ADRESL swapf ADRESL,W call LCD banksel LCDDATA0 movwf LCDDATA0 ; clrf STATUS ;B0 bsf Rst555 ;B0 enable humidity btfss PIR1, TMR1IF goto$-1
goto    Main            ; not TMR1 IF
END

#### Mike - K8LH

##### Well-Known Member
Is it too late to contribute an example?

I usually flag a negative result from the sign extended 16x16 multiply routine and turn it into a positive number by two's complimenting it. If you guys have a better way to do it, I'd love to study your method.

My example is similar to Mike's (Pommie's) but I use an ADC reference voltage of 2.56 volts. I hope this example helps.

Have fun. Mike
Code:
;******************************************************************
;
;  Convert °F*4 (ADRES) to °C*1000 (3 decimal places)
;
;  ADRES values are °F * 4 (10mv/degree Fahrenheit or 2.5mv/step)
;  using a 2.56v ADC reference voltage
;
;  °C*1000 = ABS(35556 * ( °F*4 - 32*4 )) / 256
;          = ABS(35556 * ( ADRES - 128 )) / 256
;
;  NegFlag = '1' for '-' result or '0' for '+' result
;
;                 ADRES    Output   Display
;      =====================================
;        0.00 °F      0    017778  -17.77 °C
;        5.00 °F     20    015000  -15.00 °C
;       10.00 °F     40    012222  -12.22 °C
;       15.00 °F     60    009444  - 9.44 °C
;       20.00 °F     80    006666  - 6.66 °C
;       25.00 °F    100    003888  - 3.88 °C
;       25.25 °F    101    003750  - 3.75 °C
;       25.50 °F    102    003611  - 3.61 °C
;       25.75 °F    103    003472  - 3.47 °C
;       26.00 °F    104    003333  - 3.33 °C
;       32.00 °F    128    000000    0.00 °C
;       60.00 °F    240    015555   15.55 °C
;       70.00 °F    280    021111   21.11 °C
;       77.00 °F    308    025000   25.00 °C
;       80.00 °F    320    026667   26.66 °C
;       90.00 °F    360    032222   32.22 °C
;      100.00 °F    400    037778   37.77 °C
;      212.00 °F    848    100001  100.00 °C
;
;  Digit6:1 (msb:lsb) contain the bcd/decimal output
;

Celsius
movlw   low(d'35556')   ; setup multiplier Mult1H:L       |B0
movwf   Mult1L          ;                                 |B0
movlw   high(d'35556')  ;                                 |B0
movwf   Mult1H          ;                                 |B0
bsf     STATUS,RP0      ; bank 1                          |B1
movlw   d'128'          ; setup multiplicand Mult2H:L     |B1
subwf   ADRESL,W        ;                                 |B1
bcf     STATUS,RP0      ; bank 0                          |B0
movwf   Mult2L          ;                                 |B0
movf    ADRESH,W        ;                                 |B0
skpc                    ; borrow? no, skip, else          |B0
decf    ADRESH,W        ;                                 |B0
movwf   Mult2H          ;                                 |B0
call    Multiply        ; sign extended 16x16 multiply    |B0
;
;  check for a negative or 'minus' product
;
AbsProd32
bcf     NegFlag         ; assume a positive result        |B0
btfss   Prod4,7         ; negative? yes, skip, else       |B0
goto    Bin2Dec24       ; branch, it's already positive   |B0
;
;  flag and "two's complement" the negative 32 bit product
;
bsf     NegFlag         ; indicate negative result        |B0
comf    Prod1,F         ; two's complement Prod4:Prod1    |B0
comf    Prod2,F         ;                                 |B0
comf    Prod3,F         ;                                 |B0
comf    Prod4,F         ;                                 |B0
incf    Prod1,F         ;                                 |B0
skpnz                   ;                                 |B0
incf    Prod2,F         ;                                 |B0
skpnz                   ;                                 |B0
incf    Prod3,F         ;                                 |B0
skpnz                   ;                                 |B0
incf    Prod4,F         ;                                 |B0
;
;  effect the /256 portion of the formula by ignoring LSB 'Prod1'
;
Bin2Dec24
clrf    Digit1          ; clear output array              |B0
clrf    Digit2          ;                                 |B0
clrf    Digit3          ;                                 |B0
clrf    Digit4          ;                                 |B0
clrf    Digit5          ;                                 |B0
clrf    Digit6          ;                                 |B0
;       clrf    Digit7          ;                                 |B0
;       clrf    Digit8          ;                                 |B0
movlw   24              ; 24 bits to do                   |B0
movwf   BitCtr          ; set bit loop counter            |B0
BitLp   rlf     Prod2,F         ; 24 bit single shift left        |B0
rlf     Prod3,F         ;                                 |B0
rlf     Prod4,F         ;                                 |B0
movlw   Digit1          ;                                 |B0
movwf   FSR             ; setup indirect access           |B0
;       movlw   8               ; use this for 8 digit output     |B0
movlw   6               ; use this for 6 digit output     |B0
movwf   DigCtr          ; set digit loop counter          |B0
AdjLp   rlf     INDF,F          ; shift digit left 1 bit          |B0
movlw   -10             ;                                 |B0
addwf   INDF,W          ; chk/adj for decimal overflow    |B0
skpnc                   ;                                 |B0
movwf   INDF            ;                                 |B0
incf    FSR,F           ; next digit                      |B0
decfsz  DigCtr,F        ;                                 |B0
goto    AdjLp           ;                                 |B0
decfsz  BitCtr,F        ; next bit                        |B0
goto    BitLp           ;                                 |B0
return                  ;                                 |B0

;******************************************************************
Code:
;******************************************************************
;
;  Sign Extended 16 x 16 Multiply
;
;  Mult1H:L * Mult2H:L = sign extended Prod4:1 (32 bit product)
;  Multiplier Mult1H:L destroyed, Multiplicand Mult2H:L unaltered
;
;  23 words, 231 to 312 cycles (PICLIST, modified by Mike, K8LH)
;
Multiply
clrf    Prod4           ;                                 |B0
clrf    Prod3           ;                                 |B0
clrf    Prod2           ;                                 |B0
clrf    Prod1           ;                                 |B0
bsf     Prod2,7         ;                                 |B0
M1      rrf     Mult1H,F        ;                                 |B0
rrf     Mult1L,F        ;                                 |B0
skpc                    ;                                 |B0
goto    M2              ;                                 |B0
movf    Mult2L,W        ;                                 |B0
addwf   Prod3,F         ;                                 |B0
movf    Mult2H,W        ;                                 |B0
skpnc                   ;                                 |B0
incf    Mult2H,W        ;                                 |B0
addwf   Prod4,F         ;                                 |B0
M2      rlf     Prod4,W         ; extend the sign bit             |B0
rrf     Prod4,F         ;                                 |B0
rrf     Prod3,F         ;                                 |B0
rrf     Prod2,F         ;                                 |B0
rrf     Prod1,F         ;                                 |B0
skpc                    ;                                 |B0
goto    M1              ;                                 |B0
return                  ;                                 |B0

;******************************************************************

Last edited:

#### blueroomelectronics

##### Well-Known Member
Never too late for an example, thanks Mike.

Is there a 2.56V reference IC? I use a TL431 2.5V reference as it's easy to obtain.

I guess since the TL431 is programmable it's possible with 1% resistors.

Last edited:
Status
Not open for further replies.