line

Jake's Electronics

a place to document

line

DS18B20 Digital Thermometer

Firstly I would like to thank Pommie and Mike K8LH from the Electro-Tech-Online Forum for their generous help. This has been the most challenging project to date and they have helped immensely. They have taught me many valuable lessons about programming in assembly language and have really assisted me in getting this project up and running. I would like to make a point that some snippets of their own code is used in this program. I have used it with their permission and please observe any copyrights or name recognition placed in the code. If you wish to use their snippets of code, please contact them via the Electro-Tech-Online forum.

What Is It:

This circuit is simply a digital thermometer. I use a Microchip PIC Microcontroller to communicate to a Dallas DS18B20 Digital Temperature Sensor, interpret the data, and display the temperature on a multi 7 segment LED display.

Breadboard:

Digital Temperature Sensor Breadboard

Video:

How it works:

The DS18B20 is a direct-to-digital temperature sensor using Maxim's exclusive 1-Wire bus protocol that implements bus communication using one control signal. In regards to hardware, this sensor is particularly easy to interface to. It only requires 1 external pull-up resistor to operate as opposed to an analogue sensor which possibly needs multiple external components such as resistors and op-amps. In regards to software, opposed to analogue sensors, the Dallas 1-wire digital sensors are arguably as easy to interface to. While an analogue sensor will need an Analogue to Digital conversion using a voltage reference and possibly using an op-amp, the Dallas 1-wire direct to digital sensors require precise timing when it comes to communication.

This program is fairly basic in principle as all it does is obtain temperature data from the DS18B20 sensor and display the temperature on a 4 digit, 7 segment display. But when it comes to actually doing this, as you will see from the .ASM file, it is a little more complicated than it sounds. In other words; the program first initialises the PIC16F628A Microcontroller by assigning the Inputs and Outputs, zeroing all bank 0 RAM, configuring TIMER 2 and setting the display column select bit. After the Microcontroller has been set up, it begins communication with the DS18B20 Temperature Sensor. When the temperature data has been gathered and stored in RAM, the Microcontroller takes the 16 bit signed integer and converts it into a decimal number then stores it in four general purpose registers in RAM. An example of how the temperature 95.8 is stored is shown below.

RAM Address RAM Address Pseudonym Stored Number
0x20 HUNS 0
0x21 TENS 9
0x22 ONES 5
0x23 DECI 8

The program communicates with the temperature sensor, gathers data and converts it continuously, updating the four RAM registers (HUNS, TENS, ONES & DECI) every time. While this is happening, TIMER 2 is used to interrupt the normal loop of the program to update the multi 7 segment LED display. However, due to the precise timing required to communicate with the DS18B20 Temperature Sensor, the interrupts that call for the main program loop to halt are disabled. Once the communication is finished the interrupts are re-enabled to enable the display to be updated. The four RAM registers along with the column select bit are used within the Interrupt Service Routine to call a table to obtain display data. The program runs continuously, updating the temperature on the display just over once per 1 second.

Source Code:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                                                  ;
;                        Program: DS18B20 Digital Celsius & Fahrenheit Thermometer                 ;
;                        Author:  Jake Sutherland                                                  ;
;                        Start Date: Friday 1st July 2011                                          ;
;                        Finish Date:                                                              ;
;                        Comments:                                                                 ;
;                                                                                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

        LIST            P=PIC16F628A
        INCLUDE         P16F628A.INC
        __config        _CP_OFF & DATA_CP_OFF & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT
        errorlevel      -302    ;Eliminate bank warning

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                                                  ;
;                                   PIC16F628A Microcontroller                                     ;
;                                            ____ ____                                             ;
; LED 7 Segment 10's CC       VREF/AN2/RA2 -| 1  - 18 |- RA1/AN1              LED 7 Segment 1's CC ;
; LED 7 Segment 100's CC      CPM1/AN3/RA3 -| 2    17 |- RA0/AN0             LED 7 Segment .1's CC ;
; DS18B20 I/O               CMP2/T0CKI/RA4 -| 3    16 |- RA7/OSC1/CLKIN                            ;
; Fahrenheit/Celsius Select   VPP/MCLR/RA5 -| 4    15 |- RA6/OSC2/CLKOUT  Temp Format (C/F) Select ;
;                                      VSS -| 5    14 |- VDD                                       ;
; Segment DP                       INT/RB0 -| 6    13 |- RB7/T1OSC1/ICSPDAT              Segment B ;
; Segment C                      DT/RX/RB1 -| 7    12 |- RB6/T1OSCO/T1CLKI/ICSPCLK       Segment A ;
; Segment D                      CK/TX/RB2 -| 8    11 |- RB5                             Segment F ;
; Segment E                       CCP1/RB3 -|_9____10_|- RB4/PGM                         Segment G ;
;                                                                                                  ;
;                                                                                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
CBLOCK  H'20'
HUNS                                    ; Jump pointer for Hundreds digit
TENS                                    ; Jump pointer for Tens digit
ONES                                    ; Jump pointer for Ones digit
DECI                                    ; Jump pointer for Decimal digit
THOU_COL                                ; 7 Segment Display Thousands Column, used within ISR
HUNS_COL                                ; 7 Segment Display Hundreds Column, used within ISR
TENS_COL                                ; 7 Segment Display Tens Column, used within ISR
ONES_COL                                ; 7 Segment Display Ones Column, used within ISR
COLSEL                                  ; Stores COLUMN SELECT information 
DIGIT                                   ; Used for Indirect Addressing in Interrupt Service Routine
TEMPLO_C                                ; BS18B20 Celsius Temperature Result Low Byte
TEMPHI_C                                ; BS18B20 Celsius Temperature Result High Byte
TEMPLO_F                                ; Fahrenheit temperature lower byte
TEMPHI_F                                ; Fahrenheit temperature upper byte
COUNT                                   ; For counting execution times in various routines
SHIFT                                   ; For storing data to shift into and out of the DS18B20
DELAYGPR1                               ; Used in delay subroutines
FLAGS                                   ;   -------0  Temperature Format Select (0 = Celsius 1 = Fahrenheit)
                                        ;   ------0-  Celsius/Fahrenheit Temperature Negative Flag (0 = False 1 = True)
                                        ;   -----X--  Unused
                                        ;   ----0---  Positive/Negative Flag after 'Add 4' routine
                                        ;   ---X----  Unused
                                        ;   --X-----  Unused
                                        ;   -X------  Unused
                                        ;   X-------  Unused
ENDC
  
W_ISR   EQU     H'70'                   ; For context saving. Ensures GPR is accessible from any Bank. See Memory Map of 628A
S_ISR   EQU     H'71'                   ; For context saving. Ensures GPR is accessible from any Bank. See Memory Map of 628A
P_ISR   EQU     H'72'                   ; For context saving. Ensures GPR is accessible from any Bank. See Memory Map of 628A
F_ISR   EQU     H'73'                   ; For context saving. Ensures GPR is accessible from any Bank. See Memory Map of 628A
  
  
        ORG     H'000'                  ; Processor reset vector location. On power up, the program jumps here
        GOTO    SETUP                   ; 
  
        ORG     H'004'                  ; Interrupt vector location. When an Interrupt occurs, the program jumps here
  ; (Thanks Mike, K8LH for help with this ISR routine. Contact via Electro-Tech-Online.com)
  ; Save
        MOVWF   W_ISR                   ; Save W to W_ISR
        SWAPF   STATUS, W               ; Use SWAPF instruction so status bits don't change
        MOVWF   S_ISR                   ; Save Status to S_ISR
        CLRF    STATUS                  ; Switch to Bank 0
        MOVF    PCLATH, W               ; Move PCLATH to W register
        MOVWF   P_ISR                   ; Save PCLATH to P_ISR
        CLRF    PCLATH                  ; Force page 0
        MOVF    FSR, W                  ; Move FSR to W register
        MOVWF   F_ISR                   ; Save FSR to F_ISR
  
  ; Refresh display
        CLRF    PORTB                   ; Blank the display
        MOVF    PORTA, W                ; Move PORTA's current state to W
        ANDLW   B'11100000'             ; Clear ONLY Column Select Bits & leave upper nibble unchanged
        IORWF   COLSEL, W               ; Inclusive OR above with COLSEL register
        MOVWF   PORTA                   ; Leave upper nibble of PORTA unchanged & select new column
  
        MOVF    DIGIT, W                ; Column number (DIGIT is initialised to 0. Used for Indirect Addressing)
        ADDLW   THOU_COL                ; Add 'DIGIT' to 'THOU_COL' GPR address
        MOVWF   FSR                     ; FSR = THOU_COL + (0forTHOUS, 1forHUNS, 2forTENS & 3forONES) as they are in sequential order in RAM
        MOVF    INDF, W                 ; W register now holds the jump pointer for the corresponding digit to be displayed
        CALL    SEGMENT_TABLE           ; Get 7 segment arrangement from SEGMENT_TABLE for corresponding digit
  
        BTFSC   COLSEL, 1               ; Is TENS Column active
        IORLW   B'00000001'             ; Yes, so activate TEN's column decimal point
        MOVWF   PORTB                   ; NO, hide decimal point and display new column
  
  ; Prepare for next column interrupt
        INCF    DIGIT, F                ; Increment DIGIT jump pointer (used when indirect addressing above)
        BCF     STATUS, C               ; Clear C bit of STATUS register
        RRF     COLSEL, F               ; Advance column select bit       
        BTFSS   STATUS,C                ; Was that the last column? (or, Is Carry Flag Set?)  
        GOTO    $+D'3'                  ; NO, do not reset DIGIT and COLumnSELect
        CLRF    DIGIT                   ; Reset DIGIT jump pointer
        BSF     COLSEL, 3               ; Reset column select to THOU_COL (1XXX) (00001000)
  
  ; RESTORE
        BCF     PIR1, TMR2IF            ; Clear TMR2 interrupt flag while still in Bank 0        
        MOVF    F_ISR, W                ; MOVE F_ISR to W
        MOVWF   FSR                     ; Restore FSR
        MOVF    P_ISR, W                ; MOVE P_ISR to W
        MOVWF   PCLATH                  ; Restore PCLATH
        SWAPF   S_ISR, W                ; Undo previous SWAPF, place result in W
        MOVWF   STATUS                  ; Restore STATUS
        SWAPF   W_ISR, F                ; Use SWAPF instruction so status bits don't change
        SWAPF   W_ISR, W                ; Undo previous SWAPF and restore W register
        RETFIE                          ; Return From Interrupt
  
  ; 7 Segment Display Arrangement Lookup
SEGMENT_TABLE  ; This routine assigns on/off arrangement to the 7 Segment Display
        ADDWF   PCL, F
             ;    BAFGEDCp    JUMP   B|A|F|G|E|D|C|p   DISPLAY
        RETLW   B'11101110' ;   0    B|A|F|-|E|D|C|-      0
        RETLW   B'10000010' ;   1    B|-|-|-|-|-|C|-      1
        RETLW   B'11011100' ;   2    B|A|-|G|E|D|-|-      2
        RETLW   B'11010110' ;   3    B|A|-|G|-|D|C|-      3
        RETLW   B'10110010' ;   4    B|-|F|G|-|-|C|-      4
        RETLW   B'01110110' ;   5    -|A|F|G|-|D|C|-      5
        RETLW   B'01111110' ;   6    -|A|F|G|E|D|C|-      6
        RETLW   B'11000010' ;   7    B|A|-|-|-|-|C|-      7
        RETLW   B'11111110' ;   8    B|A|F|G|E|D|C|-      8
        RETLW   B'11110110' ;   9    B|A|F|G|-|D|C|-      9
        RETLW   B'00000000' ;  10    -|-|-|-|-|-|-|-    BLANK
        RETLW   B'00010000' ;  11    -|-|-|G|-|-|-|-      -
        RETURN
  
  ; Program Originates Here
SETUP  ; This routine sets up the Microcontroller's Inputs and Outputs
        MOVLW   H'07'                   ; Turn Comparators off and enable pins for I/O functions
        MOVWF   CMCON                   ;
        BSF     STATUS, RP0             ; Bank 1
        MOVLW   B'01110000'             ; RA<7><3:0> Output, RA<5:4> Input
        MOVWF   TRISA                   ;
        CLRF    TRISB                   ; RB<7:0> Outputs
        BCF     STATUS, RP0             ; Bank 0
  
        CLRF    PORTA
        CLRF    PORTB
  
ONE_OFF_INITIALISE
  ; Clear ALL Bank 0 RAM from 0X20 - 0X7F (96 bytes)
        BCF     STATUS, IRP             ; Indirect addressing Bank 0/1
        MOVLW   H'20'                   ; Initialise pointer to RAM
        MOVWF   FSR                     ; (File Select Register)
        CLRF    INDF                    ; Clear Register indirectly
        INCF    FSR, F                  ; Increment pointer
        BTFSS   FSR, 7                  ; All done?
        GOTO    $-D'3'                  ; No, Clear next byte
  
  ; Initialise Column Select
        BSF     COLSEL, 3               ; Set column select to THOU_COL (1XXX) (00001000)
  
  ; Setup TMR2 for 0.0005s or 500uS Interrupts
        CLRF    TMR2                    ; Clear TMR2 register
        BSF     STATUS, RP0             ; Bank 1
        MOVLW   B'00000010'             ;   00000010
        MOVWF   PIE1                    ;   -------0  Disable TMR1IE - TMR1 Overflow Interrupt Enable bit
                                        ;   ------1-  Enable  TMR2IE - TMR2 to PR2 Match Interrupt Enable bit
                                        ;   -----0--  Disable CCP1IE - CCP1 Interrupt Enable bit
                                        ;   ----X---  Unused
                                        ;   ---0----  Disable TXIE - USART Transmit Interrupt Enable bit
                                        ;   --0-----  Disable RCIE - USART Receive Interrupt Enable bit
                                        ;   -0------  Disable CMIE - Comparator Interrupt Enable bit
                                        ;   0-------  Disable EEIE - EE Write Complete Interrupt Enable bit
        BCF     STATUS, RP0             ; Bank 0
        CLRF    PIR1                    ; Clear Peripheral Interrupt Flags
        MOVLW   B'00000001'             ;   00000001
        MOVWF   T2CON                   ;   ------01  Pre-scale 1:4
                                        ;   -----0--  TMR2 OFF
                                        ;   -0000---  Post-scale 1:1
                                        ;   0-------  Unused
        BSF     STATUS, RP0             ; Bank 1
        MOVLW   D'250'                  ; 
        MOVWF   PR2                     ; Interrupt every 0.001s or 1mS
        BCF     STATUS, RP0             ; Bank 0
        BSF     INTCON, GIE             ; Enable Global Interrupts
        BSF     INTCON, PEIE            ; Enable Peripheral Interrupts
        BSF     T2CON, TMR2ON           ; Start TMR2
  
        GOTO    GET_TEMP
  
  ; Dallas DS18B20 Temperature Sensor 'One Wire' Communication Routines
DS18B20_RESET  ; This routine Resets the DS18B20
        CALL    DQ_LL                   ; Force the DQ Line to Logic Low
        MOVLW   (D'480'-D'5')/D'5'      ; Reset pulse must be held a minimum of 480uS. 
        CALL    DELAY                   ; For delays from 10uS to 1285uS. Example, MOVLW D'10' for 10uS, MOVLW D'500' for 500uS. (D'n'-D'5')/D'5'; n must be divisible by 5
        CALL    DQ_HIZ                  ; Release DQ Line
        MOVLW   (D'60'-D'5')/D'5'       ; Wait for recovery
        CALL    DELAY                   ; 
        BTFSC   PORTA, 4                ; Test for 'Presence Pulse'
        GOTO    DS18B20_RESET           ; If not present, Reset
        MOVLW   (D'420'-D'5')/D'5'      ; Must wait a minimum of 480uS from when DQ line is released before moving on
        CALL    DELAY                   ; 
        RETURN                          ;
  
DS18B20_READ_BYTE  ; This routine reads a byte of data from the DS18B20
        MOVLW   H'08'                   ; Amount of bits to shift out
        MOVWF   COUNT                   ; Store in COUNT GPR
        CALL    DS18B20_READ_BIT        ; Read bit
        RRF     SHIFT, F                ; Rotate bit out of carry into SHIFT GPR
        DECFSZ  COUNT, F                ; Decrement COUNT
        GOTO    $-D'3'                  ; If not zero, read next bit
        MOVF    SHIFT, W                ; If zero, move SHIFT GPR to W
        RETURN                          ;
  
DS18B20_READ_BIT  ; This routine reads one bit of data from the DS18B20
        BCF     INTCON, GIE             ; Disable Global Interrupts
        CALL    DQ_LL                   ; Force the DQ Line to Logic Low
        CALL    DQ_HIZ                  ; Release DQ Line
        BSF     STATUS, C               ; Pre-set Carry Flag
        BTFSS   PORTA, 4                ; Test DQ Line
        BCF     STATUS, C               ; If zero, Clear Carry Flag
        BSF     INTCON, GIE             ; Enable Global Interrupts
        MOVLW   (D'60'-D'5')/D'5'       ; Wait for Time Slot to end
        CALL    DELAY                   ; 
        RETURN                          ;
  
DS18B20_WRITE_BYTE  ; This routine writes a byte of data to the DS18B20
        MOVWF   SHIFT                   ; Move data to shift into DS18B20 to SHIFT GPR
        MOVLW   D'08'                   ; Amount of bits to shift in
        MOVWF   COUNT                   ; Store in COUNT GPR
        RRF     SHIFT, F                ; Rotate valid data into Carry Flag
        CALL    DS18B20_WRITE_BIT       ; Write bit
        DECFSZ  COUNT, F                ; Decrement COUNT
        GOTO    $-D'3'                  ; If not zero, read next bit
        RETURN                          ; If zero, RETURN
  
DS18B20_WRITE_BIT  ; This routine writes one bit of data to the DS18B20
        BCF     INTCON, GIE             ; Disable Global Interrupts
        CALL    DQ_LL                   ; Force the DQ Line to Logic Low
        BTFSS   STATUS, C               ; Test Carry Flag
        GOTO    $+D'2'                  ; If zero, leave DQ Line Logic Low
        CALL    DQ_HIZ                  ; If not zero, release DQ Line to Logic High
        MOVLW   (D'60'-D'5')/D'5'       ; Hold Logic Low, write 0
        CALL    DELAY                   ; 
        CALL    DQ_HIZ                  ; If not zero, release DQ Line to Logic High, write 1
        BSF     INTCON, GIE             ; Enable Global Interrupts
        RETURN                          ; 
  
DQ_HIZ  ; This routine forces the DQ Line to an Input / High Impedance state
        BSF     STATUS, RP0             ; Bank 1
        BSF     TRISA, 4                ; Make Pin 3 an input, Pull-up resistor forces line to logic 1, unless DS18B20 pulls it low
        BCF     STATUS, RP0             ; Bank 0
        RETURN
  
DQ_LL  ; This routine forces the DQ Line to Logic Low
        BCF     PORTA, 4                ; Clear output latch
        BSF     STATUS, RP0             ; Bank 1
        BCF     TRISA, 4                ; Make Pin 3 an output
        BCF     STATUS, RP0             ; Bank 0
        RETURN
  
DELAY  ; This routine can provide delays from 10uS to 1285uS depending on the number moved to the Working register prior to calling the delay
        MOVWF   DELAYGPR1               ; Move integer to GPR
        NOP                             ; No Operation
        NOP                             ; No Operation
        DECFSZ  DELAYGPR1, F            ; Decrement GPR and place back in itself
        GOTO    $-D'3'                  ; Not finished, GOTO here - 3 instructions
        RETURN                          ; Finished, Return
  
GET_TEMP
        CALL    DS18B20_RESET           ; Reset
        MOVLW   H'CC'                   ; Skiprom
        CALL    DS18B20_WRITE_BYTE      ; Skiprom
        MOVLW   H'44'                   ; Convert T
        CALL    DS18B20_WRITE_BYTE      ; Convert T
  
        CALL    DS18B20_READ_BIT        ; Initiate Read Time Slots
        BTFSS   STATUS, C               ; DS18B20 transmits a 0 while the temperature conversion is in progress and a 1 when the conversion is done
        GOTO    $-D'2'                  ; Transmitting 0 therefore NOT done
  
        CALL    DS18B20_RESET           ; Reset
        MOVLW   H'CC'                   ; Skiprom
        CALL    DS18B20_WRITE_BYTE      ; Skiprom
        MOVLW   H'BE'                   ; Read Scratchpad
        CALL    DS18B20_WRITE_BYTE      ; Read Scratchpad
  
        CALL    DS18B20_READ_BYTE       ; Move Scrachpad Byte 0 to W
        MOVWF   TEMPLO_C                ; Store in Celsius TEMPLO GPR
        CALL    DS18B20_READ_BYTE       ; Move Scrachpad Byte 1 to W
        MOVWF   TEMPHI_C                ; Store in Celsius TEMPHI GPR
  
  ; Test Temperature Format Select pin and display corresponding temperature type (C or F)
        BTFSS   PORTA, 5                ; Test Select pin for high or low
        GOTO    DISPLAY_CELSIUS         ; Clear, display Celsius
                                        ; Set, display Fahrenheit
  
  ; Begin converting 16 bit Celsius data into Fahrenheit
DISPLAY_FAHRENHEIT  ; This routine Clear all Flags and copies Celsius data into Fahrenheit GPRs for conversion
        CLRF    FLAGS                   ; Clear Flags GPR
        MOVF    TEMPLO_C, W             ; Copy Celsius Temperature Data lower byte
        MOVWF   TEMPLO_F                ; Move to Fahrenheit GPR lower byte
        MOVF    TEMPHI_C, W             ; Copy Celsius Temperature Data upper byte
        MOVWF   TEMPHI_F                ; Move to Fahrenheit GPR upper byte
  
 ; The following group of routines convert the Celsius Temperature Data into Fahrenheit x 10.
 ; This is acheived by dividing by 8 and adding the original number, which gives 18 times the value. 
 ; If you now add 320 you will have the temperature in Fahrenheit x 10.
 ; After the conversion it is simply a matter of displaying the data with the decimal shifted one place to the right.
 ; Example 
 ; 25 Degrees C  =  B'00000001 10010000'  =  D'400' 
 ; 400 / 8   =  50
 ;  50 + 400 = 450
 ; 450 + 320 = 770
 ; 770 /  10 = 77 Degrees Fahrenheit
  
  ; Add 4, this ensures number is correctly rounded when using the shift method below to divide
        MOVLW   B'00000100'             ; Move D'4' to W
        ADDWF   TEMPLO_F, F             ; Add W to TEMPLO_F and place result back in File Register TEMPLO_F
        BTFSC   STATUS, C               ; Check if TEMPLO_F overflowed
        INCF    TEMPHI_F, F             ; Overflow occurred, increment upper register 
  
  ; Flag if result positive or negative after add 4 (needed for mask routine)
        BTFSC   TEMPHI_F, 7             ; Test bit 7 of TEMPHI_F
        BSF     FLAGS, 3                ; Set flag if negative, skip if clear
  
  ; Divide by 8 using shift method
        RRF     TEMPHI_F, F             ; Rotate Right File Register 'F' (TEMPHI_F) into carry
        RRF     TEMPLO_F, F             ; Rotate Right File Register 'F' (TEMPLO_F) out of carry;  / 2
        RRF     TEMPHI_F, F             ; Repeat...
        RRF     TEMPLO_F, F             ;                                                          / 4
        RRF     TEMPHI_F, F             ; 
        RRF     TEMPLO_F, F             ;                                                          / 8
  
  ; Mask routine. Adjust upper 3 bits of TEMPHI_F due to shift. Set if negative, clear if positive
        BTFSS   FLAGS, 3                ; Test if result after 'Add 4' is positive or negative
        GOTO    $+D'4'                  ; Positive, GOTO here + D'4'
        MOVLW   B'11100000'             ; Negative,
        IORWF   TEMPHI_F, F             ; Set upper 3 bits
        GOTO    $+D'3'                  ; Done, Skip
        MOVLW   B'00011111'             ; Positive,
        ANDWF   TEMPHI_F, F             ; Clear upper 3 bits
  
  ; Add original Celsius Temperature to obtain C x 18 
        MOVF    TEMPLO_C, W             ; Move TEMPLO_C to W
        ADDWF   TEMPLO_F, F             ; Add TEMPLO_C and TEMPLO_F and place result back in File Register TEMPLO_F
        MOVF    TEMPHI_C, W             ; Pre-load TEMPHI_C to W (doesn't effect carry)
        BTFSC   STATUS, C               ; Did Add cause overflow?
        INCF    TEMPHI_C, W             ; Yes, increment TEMPHI_C store in W
        ADDWF   TEMPHI_F, F             ; Add TEMPHI_C and TEMPHI_F. Result in now C x 18
  
  ; Add 320 to obtain F x 10
        MOVLW   B'01000000'             ; 320 lower byte
        ADDWF   TEMPLO_F, F             ; Add 320 lower byte and TEMPLO_F and place result back in File Register TEMPLO_F
        BTFSC   STATUS, C               ; Did Add cause overflow?
        INCF    TEMPHI_F, F             ; Yes, increment TEMPHI_F
        MOVLW   B'00000001'             ; 320 upper byte
        ADDWF   TEMPHI_F, F             ; Add 320 upper byte and TEMPHI_F. Result is now C x 18 + 320 (= F x 10)
  
        BTFSS   TEMPHI_F, 7             ; Test Sign Bits for Fahrenheit Temperature negative (2's complemented)
        GOTO    BIN_TO_DEC_F            ; No, skip undo 2's complement
  
        COMF    TEMPLO_F, F             ; Invert TEMPLO 
        COMF    TEMPHI_F, F             ; Invert TEMPHI
        INCFSZ  TEMPLO_F, F             ; Increment TEMPLO once; After inverting the data, add one to achieve equivalent positive number (see 2's complement)
        GOTO    $+D'2'                  ; No overflow skip next instruction
        INCF    TEMPHI_F, F             ; Overflow occurred, increment upper byte
        BSF     FLAGS, 1                ; Set Negative Flag for Fahrenheit Temperature
  
BIN_TO_DEC_F  ; This routine converts a 16-bit number held in TEMPHI_F:TEMPLO_F to Decimal and stores it in 4 GPR's; HUNS, TENS, ONES & DECI 
 ; !!!!! Tweaked for this program due to Fahrenheit x 10. GPR Labels adjusted for this program and would normally be THOUS, HUNS, TENS & ONES instead of HUNS, TENS, ONES & DECI !!!!!
 ; Example 1225;
 ; THOUS holds amount of thousands (i.e. 0000 0001 = 1x1000)
 ; HUNS holds amount of hundreds (i.e. 0000 0010 = 2x100)
 ; TENS holds amount of tens (i.e. 0000 0010 = 2x10)
 ; ONES holds amount of ones (i.e. 0000 0101 = 5x1)
 ; These registers are then used as jump pointers when calling digits to display
  
        INCF    TEMPLO_F                ; Pre-load TEMPHI + 1
        INCF    TEMPHI_F                ; Pre-load TEMPHI + 1
        CLRF    HUNS                    ; THOUS = 0000 0000                                                                         GPR changed from THOUS for this program
  
        MOVLW   D'246'                  ; Move decimal '246' to W
        MOVWF   TENS                    ; HUNS GPR = 1111 0101                                                                      GPR changed from HUNS for this program
        MOVWF   ONES                    ; TENS GPR = 1111 0101                                                                      GPR changed from TENS for this program
        MOVWF   DECI                    ; ONES GPR = 1111 0101                                                                      GPR changed from ONES for this program
        DECFSZ  TEMPLO_F, F             ; Decrement TEMPLO_F register
        GOTO    $+D'4'                  ; Not 0, GOTO here + 4 instructions
        DECFSZ  TEMPHI_F, F             ; Decrement TEMPHI_F register
        GOTO    $+D'2'                  ; Not 0, skip next instruction
        GOTO    $+D'9'                  ; Is 0, done, GOTO here + 9 instructions
        INCFSZ  DECI, F                 ; Increment ONES register, skip if 0                                                        GPR changed from ONES for this program
        GOTO    $-D'6'                  ; Not 0, GOTO here - 6 instructions
        INCFSZ  ONES, F                 ; Is 0, Increment TENS register skip if 0                                                   GPR changed from TENS for this program
        GOTO    $-D'9'                  ; Not 0, GOTO here - 9 instructions & reset the ONES register to D'246'
        INCFSZ  TENS, F                 ; TENS overflowed, Increment HUNS skip if 0                                                 GPR changed from HUNS for this program
        GOTO    $-D'12'                 ; Not 0, GOTO here - 12 instructions & reset the ONES and TENS registers to D'246'
        INCF    HUNS, F                 ; HUNS overflowed, Increment THOUS                                                          GPR changed from THOUS for this program
        GOTO    $-D'15'                 ; GOTO here - 15 instructions & reset the ONES, TENS & HUNS registers to D'246'
  
        SUBWF   TENS, F                 ; W still holds D'246 so subtract it from HUNS register to determine how many 'HUNS' (0-9)   GPR changed from HUNS for this program
        SUBWF   ONES, F                 ; W still holds D'246 so subtract it from TENS register to determine how many 'TENS' (0-9)   GPR changed from TENS for this program
        SUBWF   DECI, F                 ; W still holds D'246 so subtract it from ONES register to determine how many 'ONES' (0-9)   GPR changed from ONES for this program
  
        GOTO    ZERO_SUPPRESS_AND_NEG_SIGN
  
  
DISPLAY_CELSIUS
        BCF     FLAGS, 1                ; Clear Negative Flag
        BTFSS   TEMPHI_C, 7             ; Test if Temperature is negative (2's complement format)
        GOTO    REARRANGE_DATA          ; NOT Negative, skip 'Undo 2's Complement'
        BSF     FLAGS, 1                ; Set Negative Flag; used later
  
 ;  Undo 2's Complement. When the Temperature is Negative, the DS18B20 outputs the 2's Complement. This routine converts the negative data into its positive equivalent
                                        ; Example Negative Temperature            TEMPHI = 1111 1110  TEMPLO = 0110 1110 = -25.1250 (see datasheet)
        COMF    TEMPLO_C, F             ; Invert TEMPLO 
        COMF    TEMPHI_C, F             ; Invert TEMPHI
        INCFSZ  TEMPLO_C, F             ; Increment TEMPLO once; After inverting the data, add one to achieve equivalent positive number (see 2's complement)
        GOTO    $+D'2'                  ; No overflow skip next instruction
        INCF    TEMPHI_C, F             ; Overflow occurred, increment upper byte
                                        ; Example Negative Temperature AFTER undo TEMPHI = 0000 0001  TEMPLO = 1001 0010 = +25.1250
  
REARRANGE_DATA ; This routine rearranges the 2 Temperature result bytes into usable data.
 ; EXPLANATION:
 ; * After the DS18B20 finishes a Temperature Conversion, the 12-bit result is stored in two 8-bit 
 ;   registers as a 16-bit sign-extended two?s complement number.
 ; * The 5 MSb's of the MSB (TEMPHI_C) indicate a negative or positive temperature.
 ; * The output data follows this format (12-bit resolution; 0.0625 degrees Celsius increments);
 ;
 ;         SSSS SNNN : NNNN FFFF      Where S = Sign (0 = Positive 1 = Negative)
 ;                                          N = Number, &
 ;                                          F = Fraction (16 X 0.0625 increments = 1)
 ;
 ;   After communication with the DS18B20 the output data is held in two 8 bit registers called 
 ;   TEMPHI_C & TEMPLO_C in the following format TEMPHI_C = SSSS SNNN : TEMPLO_C = NNNN FFFF
 ;         
 ;             Example;  0000 0011  1001 0001  =  (D'913' X 0.0625)  =  57.0625 Degrees Celsius
 ;
 ; * This 6 instruction routine below rearranges the 4 nibbles of TEMPHI_C & TEMPLO_C so the data 
 ;   is easy to work with. 
 ; * The MSB (TEMPHI_C) will be used for the 'whole' number i.e. 1's, 10's & 100's digits (57);
 ;   and the LSB (TEMPLO_C) will be used for the decimal digit (.1's) with the upper nibble of the 
 ;   LSB masked. (These are in fact the sign bits, but we have already handled negative data and 
 ;   created a Flag (FLAGS, 1) for negative temperatures.
 ;
 ;     Example above, after rearrangement 0011 1001  0000 0001  (see below)
 ;
                                        ; Example before; TEMPHI_C = 0000 0011  TEMPLO_C = 1001 0001   = 57.0625 Degrees Celsius
  
        MOVLW   B'11110000'             ; W = 1111 0000
        ANDWF   TEMPLO_C, W             ; F = 1001 0001  W = 1001 0000
        IORWF   TEMPHI_C, F             ;                F = 0000 0011   F = 1001 0011
        SWAPF   TEMPHI_C, F             ;                                F = 1001 0011   F = 0011 1001 (New TEMPHI_C)
        MOVLW   B'00001111'             ; W = 0000 1111
        ANDWF   TEMPLO_C, F             ; F = 1001 0001  F = 0000 0001 (New TEMPLO_C)
  
                                        ; Example after;  TEMPHI_C = 0011 1001  TEMPLO_C = 0000 0001
                                        ;                 = D'57' (X 1.0) = 57  = D'1' (X 0.0625) = 0.0625  = 57.0625 Degrees Celsius
  
BIN_TO_DEC_C  ; This routine converts the 8-bit number held in TEMPHI_C to Decimal and stores it in 3 GPR's; HUNS, TENS & ONES
 ; Example 225;
 ; HUNS holds amount of hundreds (i.e. 0000 0010 = 2x100)
 ; TENS holds amount of tens (i.e. 0000 0010 = 2x10)
 ; ONES holds amount of ones (i.e. 0000 0101 = 5x1)
 ; These registers are then used as jump pointers when calling digits to display
  
        INCF    TEMPHI_C                ; Pre-load TEMPHI + 1
        CLRF    HUNS                    ; HUNS = 0000 0000
  
        MOVLW   D'246'                  ; MOVE Decimal'246' to W
        MOVWF   TENS                    ; TENS GPR = 1111 0101
        MOVWF   ONES                    ; ONES GPR = 1111 0101
        DECFSZ  TEMPHI_C, F             ; Decrement TEMPHI register
        GOTO    $+D'2'                  ; NOT 0, skip next instruction
        GOTO    $+D'7'                  ; IS 0, TEMPHI = 0000 0000, skip next 6 instructions and calculate how many tens and hundreds
        INCFSZ  ONES, F                 ; Increment ONES register, skip if 0
        GOTO    $-D'4'                  ; NOT 0, GOTO here - 4 instructions
        INCFSZ  TENS, F                 ; IS 0, Increment TENS register skip if 0
        GOTO    $-D'7'                  ; GOTO here - 7 instructions & reset the ONES register to D'246'
        INCF    HUNS, F                 ; TENS overflowed, Increment HUNS
        GOTO    $-D'10'                 ; GOTO here - 10 instructions & reset the ONES and TENS registers to D'246'
  
        SUBWF   TENS, F                 ; W still holds D'246 so subtract it from TENS register to determine how many 'TENS'
        SUBWF   ONES, F                 ; W still holds D'246 so subtract it from ONES register to determine how many 'ONES'
  
FRACTION_ROUND  ; This routine rounds the fraction portion of the Temperature data to the nearest .1
; Provided by & used with his permission from 'Pommy'. Contact via Electro-Tech-Online.com
; Examples
; TENTHS = Fraction                           * 10 / 16
; TENTHS = 00000100 ( D'4' * 0.0625 = 0.2500 Degrees C) * 10 / 16 = 3
; TENTHS = 00001001 ( D'9' * 0.0625 = 0.5625 Degrees C) * 10 / 16 = 6
; TENTHS = 00001101 (D'13' * 0.0625 = 0.8125 Degrees C) * 10 / 16 = 8
        MOVLW   B'00001111'             ;
        ANDWF   TEMPLO_C, F             ;
        MOVF    TEMPLO_C, W             ;
        ADDWF   TEMPLO_C, F             ; *2, C=0
        RLF     TEMPLO_C, F             ; *4, C=0
        ADDWF   TEMPLO_C, F             ; *5, C=0
        RLF     TEMPLO_C, F             ; *10
        MOVLW   B'00001000'             ; 
        ADDWF   TEMPLO_C, F             ; Rounding
        SWAPF   TEMPLO_C, W             ; Pseudo divide by 16
        ANDLW   B'00001111'             ; 
        MOVWF   DECI                    ; 
  
  
ZERO_SUPPRESS_AND_NEG_SIGN        
  ; Leading zero suppression & minus (-) sign if Temperature Negative
        MOVF    HUNS, W                 ; MOVF instruction affects Z bit of STATUS register 
        BTFSC   STATUS, Z               ; Does HUNS = 0? 
        MOVLW   B'00001010'             ; Yes, jump Pointer for blank arrangement
  
        BTFSC   FLAGS, 1                ; Is Temperature Negative?
        MOVLW   B'00001011'             ; Yes, jump Pointer for '-' arrangement
        MOVWF   HUNS                    ; 
  
        BTFSS   STATUS, Z               ; Does HUNS = not 0?
        GOTO    $+D'5'                  ; Yes, do not blank TENS
        MOVF    TENS, W                 ; MOVF instruction affects Z bit of STATUS register 
        BTFSC   STATUS, Z               ; Does TENS = 0? 
        MOVLW   B'00001010'             ; Yes, jump Pointer for blank arrangement
        MOVWF   TENS                    ; 
  
  ; This routine would not be needed if interrupts were disabled for the whole BIN_TO_DEC routine. So this is added to minimise the amount of time interrupts are disabled to minimise flicker. 
        BCF     INTCON, GIE             ; Disable Interrupts
        MOVF    HUNS, W
        MOVWF   THOU_COL
        MOVF    TENS, W
        MOVWF   HUNS_COL
        MOVF    ONES, W
        MOVWF   TENS_COL
        MOVF    DECI, W
        MOVWF   ONES_COL
        BSF     INTCON, GIE             ; Enable Interrupts
  
        GOTO    GET_TEMP
END                                     ; Directive 'end of program'
.asm File Icon .hex File Icon

Schematic:

Digital Temperature Sensor Schematic

Parts List:

Part Quantity Schematic Designator
Semiconductors    
PIC16F628A Microcontroller 1 U1
Dallas DS18B20 Digital Temperature Sensor 1 U2
LED 7 Segment (Common Cathode) 4 D1 - D4
BC548 NPN Transistor 4 Q1 - Q4
     
Resistors    
330Ω 1 R12
470Ω 7 R5 - R11
4.7KΩ 1 R14
10KΩ 5 R1 - R4, R13
     
Capacitors    
.1μF Multilayer Ceramic 2 C1 - C2
     
Hardware    
Breadboard 1  
Jumper Wire    
     
Miscellaneous    
-    
     
Power Requirements    
5 Volt DC Regulated, 100mAh    
     

line