![]() |
![]() |
![]() |
|
|
|||||||
| Math and Physics Discuss the complex nature of mathmatics and physics relating to electronic circuitry. |
|
|
Thread Tools | Display Modes |
|
|
(permalink) |
|
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. |
|
|
|
|
|
|
(permalink) | |
|
Quote:
|
||
|
|
|
|
|
(permalink) |
|
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.
__________________
Eric "Good enough is Perfect" PIC tutorials: Gramo's: www.digital-diy.net/ Bill's: www.blueroomelectronics.com/ Last edited by ericgibbs; 25th September 2007 at 06:15 PM. |
|
|
|
|
|
|
(permalink) |
|
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. |
|
|
|
|
|
|
(permalink) |
|
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 http://spreadsheets.google.com/pub?k...rBSgFiYuMz18LA Last edited by blueroomelectronics; 26th September 2007 at 03:02 AM. |
|
|
|
|
|
|
(permalink) |
|
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
|
|
|
|
|
|
|
(permalink) |
|
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 by Mike, K8LH; 7th October 2007 at 09:19 AM. |
|
|
|
|
|
|
(permalink) |
|
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 by blueroomelectronics; 3rd October 2007 at 03:02 PM. |
|
|
|
|