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.

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)

F=(ADRES*125)/4096

C=(ADRES*125-32)*(5/9)/4096

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.
 
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:
If you want to get rid of any non shift divides then,

C=(ADRES-131)*8897/65536

How are you going to handle negative values?

Mike.
 
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.

Here's the link to the spreadsheet
**broken link removed**
 
Last edited:
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'     ;
        movwf   ADCON0          ;B0
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
        bsf     ADCON0, GO
        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
 
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:
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.

New Articles From Microcontroller Tips

Back
Top