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.

How to correctly use Interrupts

Status
Not open for further replies.
I'm working on a project for a battery charge station for my other hobby (flying RC helis)
I want to be able to monitor the temperature of the power supplys and also run a count up timer for a very rough guide to fuel usage of a generator that will power the charge station at the flying field).

I am deep in coding and have the timer and temp (single sensor for now) displayed on the LCD.
It is running, however very inefficiently.

I am using Timer 1 with a 32.768 crytal to generate a 1 second interrupt for the timer, and also Timer 2 to generate an interrupt at about 15hz to refresh the display.

What I want to know is, what should the interrupt be used for and how should i best update the display.

At the moment i have 64 bytes of RAM to represent each character on my 16x4 LCD (LCD_DISP_R1_C1, LCD_DISP_R1_C2 and so on). The Timer 2 interrupt simply loads the RAM contents into the LCD during the interrupt using indirect addressing and if i want to write something to the display i just write it to the RAM address during normal program flow and the interrupt does the rest.
but is this the best way to do it?

Here is what i am doing in the interrupt with timer 2.
Code:
TIMER2_ISR
  ; Refresh all (visible) LCD display address locations
        MOVLW   LCD_DISP_R1_C1          ; Initialise pointer to RAM address
        MOVWF   FSR                     ; (File Select Register)
      
  ; Print first 32 address locations (LCD display address' 0x00 to 0x1F, lines 1 & 3)
        BSF     LCD_RS                  ; Place LCD in character entry mode
        CALL    PRINT_32_CHAR           ;
      
  ; Move cursor to address 0x40
        BCF     LCD_RS                  ; Place LCD in command entry mode
        MOVLW   B'11000000'             ; Set display address at 0x40 (Line 2, Column 1)
        CALL    LCD_WRITE               ; Send data to LCD
      
  ; Print second 32 address locations (LCD display address' 0x40 to 0x5F, lines 2 & 4)
        BSF     LCD_RS                  ; Place LCD in character entry mode
        CALL    PRINT_32_CHAR           ;
      
  ; Display & Cursor home
        BCF     LCD_RS                  ; Place LCD in command entry mode
        MOVLW   B'00000010'             ; LCD command for Display & Cursor home
        CALL    LCD_WRITE               ; Send data to LCD
      
        BCF     PIR1, TMR2IF            ; Clear TMR2 interrupt flag
        GOTO    ISR_END
      
PRINT_32_CHAR      
        CLRF    COUNT                   ; Reset counter
        MOVF    INDF, W                 ; Move indirectly addressed registers contents to W
        CALL    LCD_WRITE               ; Send data to LCD
        INCF    FSR, F                  ; Increment indirectly addressed register pointer
        INCF    COUNT, F                ; Increment count
        BTFSS   COUNT, 5                ; Count 32?
        GOTO    $-D'5'                  ; No? Do again. Yes? Skip..
        RETURN
 
Whatever works for you!

LCD panels with on-board chipsets ( Intelligent displays ) do their own housekeeping, so maintaining a refresh rate is not required, the information will still be displayed if there is no interaction.... I, personally, only use a 17 character ram buffer which is all I need to store a single line of data... I build a string of text and blit it to the line I need to..

If you where working on led arrays, then you would need refresh the screen periodically... You are wasting valuable ram resources and a timer doing it this way... Just my opinion...
 
Just my opinion...
No I like your opinion. Keep them coming.
By refreshing the display constantly I know I am wasting RAM as well as precious program cycles.
So although I understand there are better ways of doing things, I am struggling to piece a complete idea together.

Is any part of the LCD driven by an interrupt in your instance?
Or do you just send your RAM buffer to the LCD in normal program if it has been updated?
 
No I like your opinion. Keep them coming.
By refreshing the display constantly I know I am wasting RAM as well as precious program cycles.
So although I understand there are better ways of doing things, I am struggling to piece a complete idea together.

Is any part of the LCD driven by an interrupt in your instance?
Or do you just send your RAM buffer to the LCD in normal program if it has been updated?

You need to be more explicit about what you're doing, if you're using a normal LCD text module then there's no refreshing or buffering required. Just write the text to the LCD and it's done and finished - nothing else to do unless it changes.

If you're using a bare LCD then you need to provide AC drive for it (and everything else).
 
I use the slightly smaller 16x2 for my products.... The display does not need updating very much as my data acquisition is quite slow... I try to minimize display updates because it helps with the averaging... When digits are constantly changing and to update the screen more than 3~5 times a second the digits will flicker too fast and make it nearly unreadable...

I oversample to the hilt and then then update the display twice a second ad I get favorable results... If your displayed data doesn't change much, you can update the display faster....
 
You need to be more explicit about what you're doing,
Ok.
I should mention it's a standard 4x16 44780 character LCD.
On the top line in the center I am displaying 00:00:00 for hours, minutes, seconds of the count up timer.
On the third line I am displaying the temperature _27.2 , one of the left, and in future one on the right to represent power supply intake and outtake air temp.

I am using the timer 1 interupt to increment the counter and the result is stored in the SECONDS MINUTES and HOURS RAM registers.
Code:
TIMER1_ISR
  ; Timer 1 interrupt used for incrementing the real time clock registers
        MOVLW   B'10000000'             ; Load number into upper byte of TMR1H:TMR1L so Timer 1 overflows once per second
        MOVWF   TMR1H                   ;

  ; Clock maths
    ; Increment Seconds  
        INCF    SECONDS, F              ; At 1 second intervals increment SECONDS register

    ; Check if seconds reached 60. Yes; Reset and increment Minutes. No; Done
        MOVF    SECONDS, W              ;
        SUBLW   D'60'                   ;
        BTFSS   STATUS, Z               ;
        GOTO    TIMER1_ISR_DONE         ;
        CLRF    SECONDS                 ;
     
    ; Increment Minutes
        INCF    MINUTES, F              ; At 60 second intervals increment MINUTES register
     
    ; Check if minutes reached 60. Yes; Reset and increment hours No; Done
        MOVF    MINUTES, W              ;
        SUBLW   D'60'                   ;
        BTFSS   STATUS, Z               ;
        GOTO    TIMER1_ISR_DONE         ;
        CLRF    MINUTES                 ;
 
    ; Increment Hours
        INCF    HOURS, F                ; At 60 minute intervals increment HOURS register

TIMER1_ISR_DONE 
        BCF     PIR1, TMR1IF            ; Clear TMR1 interrupt flag
        GOTO    ISR_END

I am also obtaining temperature data from the DS18B20 digital temp sensor and the data is stored in 16 bit TEMPHI:TEMPLO.
I rearrange the data, covert it to BCD and round the decimal and have the result stored in HUNS TENS ONES and DECI.

I am currently, in normal program, using these registers, looking up table which simply has 0-9 ASCII digits, and returning the corresponding ASCII digit to the one of 64 RAM address's used for the display, then Timer 2 cycles through the display writing whatever is in the RAM address's.
This is the part that needs to change.

So I have the data, convert it, BCD it, ASCII it, but when it comes to the display effectively I need an idea.
Sure, I can write it to the display, but creating a effective and efficient subroutine i can call to write characters to a specific place on the LCD is where I want help.

Ian's one line buffer gave me an idea.
How about:
16 RAM buffer.
After data change/obtained, write to buffer,
then call (and only call after data change) subroutine which writes data to LCD.
Maybe I can use a 2 or 4 bit flag to indicate which line of LCD to write the buffer to?
Well TIMER1 interrupts every 1 second to increment the count, can I implement an update routine in there?

Hope that helps give an idea as to what exactly I need help with.
 
You've got a one second timer interrupt to update the RTC, why not simply update the display during the interrupt routine? (by writing the entire changeable display - no need to change any fixed text parts) - you're making much harder work of this than I would have thought possible :D

If you look at Ian's C versions of my tutorials he added a C function to position the cursor on the line and position required, simply position it where you need, then display what you want.
 
I think by "refreshing the display" he means updating the displayed data. Not the display driving refresh rate.

That said, I would just code the program to collect the data, convert to ASCII, then parse the characters to the RAM buffer. Then use a timer interrupt to parse the RAM buffer contents to the LCD every 1/2 second or so. This way the data is being collected and updated to the RAM buffer much faster than the displayed data so that the displayed data always has the latest collected data.

As Nigel stated, you only need to update the displayed dynamic data. Static data (i.e. symbology such as the words "Temp", "Fuel", and any other words which label the displayed dynamic data) don't need to be refreshed every time. Just make sure your code sends the move cursor instruction to the correct position on the LCD prior to parsing the RAM buffer to the display.
 
I tend to write code using a 20mS main loop time I.E. it runs 50 times per second. Each loop I read all sensors, keypad etc and update the display. Note that a 4*20 display can be fully updated in 4mS. Seems pointless using a ram buffer and doing this on interrupt.

Mike.
 
That said, I would just code the program to collect the data, convert to ASCII, then parse the characters to the RAM buffer. Then use a timer interrupt to parse the RAM buffer contents to the LCD every 1/2 second or so.

From his vague description that appears top be 'pretty well' what he;s doing (but more often) - and neither is required.

The display doesn't need 'refreshing' - simply update the display if and when the vales change.

For his particular application (monitoring a battery?) I would suggest something like reading every tenth of a second, making an average of ten readings, and then updating the display - or if you want 1/2 second updates then read every 20th of a second. If you really wanted?, you could also compare the new reading to the old one, and only update if it's changed.
 
General opinion is that the ISR code should be as short as possible, plus using Calls, partic to outside the ISR is not good practice as your stack could overflow and loose the ISR return location.

Why cannot you send out your lcd data at the end of each program loop or simply set a flag every second or so in your ISR for the main program loop to test and output to the lcd if set.

Also , your DS1820 routine, from the little I have done with them , believe you need to disable the Interrupts while its lengthy routine completes, otherwise it will timeout if disturbed by a ISR.

Your block method of sending out your data is a good as any method, but this little routine of lcd examples might be of interest.
 

Attachments

  • lcd18f.asm
    13.7 KB · Views: 181
Got something working...
There is a minor glitch, I saw a 1 flash up in front of 24 (124) so thatll need looking into.
I'm reading the sensor and updating the display for the temperature way more than I have to. But ill worry about that when I have ather things for the program to do.

Need to now learn how to properly use the READ ROM command for the one wire to get more than 1 temp sensor going.

Count up timer with 2 temperature readings..
1zd562w.jpg


Code:
        LIST            P=PIC16F628A
        INCLUDE         P16F628A.INC
        __CONFIG        _CP_OFF & _CPD_OFF & _LVP_OFF & _BOREN_OFF & _MCLRE_OFF & _PWRTE_ON & _WDTE_OFF & _INTOSC_OSC_NOCLKOUT
        ERRORLEVEL      -302    ;Eliminate bank warning

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                                                       ;
;                                      PIC16F628A Microcontroller                                       ;
;                                               ____ ____                                               ;
; LCD ENABLE                     VREF/AN2/RA2 -| 1  - 18 |- RA1/AN1                      LCD READ/WRITE ;
; LED Indicator                  CPM1/AN3/RA3 -| 2    17 |- RA0/AN0                 LCD REGISTER SELECT ;
; 1-Wire Devices               CMP2/T0CKI/RA4 -| 3    16 |- RA7/OSC1/CLKIN                              ;
; Power Down Detect              VPP/MCLR/RA5 -| 4    15 |- RA6/OSC2/CLKOUT                             ;
;                                         VSS -| 5    14 |- VDD                                         ;
; LCD Data 4                          INT/RB0 -| 6    13 |- RB7/T1OSC1/PGD          TMR1 32.768KHz Xtal ;
; LCD Data 5                        DT/RX/RB1 -| 7    12 |- RB6/T1OSCO/T1CLKI/PGC   TMR1 32.768KHz Xtal ;
; LCD Data 6                        CK/TX/RB2 -| 8    11 |- RB5                          Push Button #2 ;
; LCD Data 7, LCD Busy Status        CCP1/RB3 -|_9____10_|- RB4/PGM                      Push Button #1 ;
;                                                                                                       ;
;                                                                                                       ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


  ; Notes
; If writing to LCD in main program loop, interrupts should be disabled as communication routines will be interrupted.
;   RW  RS 
;   L   L   Write Command Mode
;   L   H   Write Character Data
;   H   L   Read Busy Flag and Current Cursor Address
;   H   H   Read Current Cursor Address Character Data 

CBLOCK  0x20                            ; Commence defining following constant block at RAM address 0x20 (LIMIT OF 80 BYTES IN BANK 1)
DELAYGPR1                               ; Delay general purpose register
DELAYGPR2                               ; Delay general purpose register
DELAYGPR3                               ; Delay general purpose register

COUNT                                   ; Loop counter
COUNT_ISR                               ; Loop counter within ISR

FLAGS                                   ;   -------0  LCD_RS_FLAG
                                        ;   ------0-  LCD_RW_FLAG
                                        ;   -----0--  Temperature Negative Flag (0 = False 1 = True)
                                        ;   ----0--- 
                                        ;   ---0----  LCD display line 1 write flag
                                        ;   --0-----  LCD display line 2 write flag
                                        ;   -0------  LCD display line 3 write flag
                                        ;   0-------  LCD display line 4 write flag

BIN_2_BCD_LSB                           ; Binary to BCD conversion
HUNS                                    ; BCD result register
TENS                                    ; BCD result register
ONES                                    ; BCD result register
DECI                                    ; Jump pointer for Decimal digit
                                       
SECONDS                                 ; Clock (Hour/Minute/Second Counter)
MINUTES                                 ; Clock (Hour/Minute/Second Counter)
HOURS                                   ; Clock (Hour/Minute/Second Counter)

SHIFT                                   ; For storing data to shift into and out of the DS18B20
TEMPLO                                  ; BS18B20 Celsius Temperature Result Low Byte
TEMPHI                                  ; BS18B20 Celsius Temperature Result High Byte

LCD_DATA_TEMP                           ; Temporary storage for data to write to LCD

LCD_DISP_BUFFER_C1                          ; LCD display buffer register
LCD_DISP_BUFFER_C2                          ; LCD display buffer register
LCD_DISP_BUFFER_C3                          ; LCD display buffer register
LCD_DISP_BUFFER_C4                          ; LCD display buffer register
LCD_DISP_BUFFER_C5                          ; LCD display buffer register
LCD_DISP_BUFFER_C6                          ; LCD display buffer register
LCD_DISP_BUFFER_C7                          ; LCD display buffer register
LCD_DISP_BUFFER_C8                          ; LCD display buffer register
LCD_DISP_BUFFER_C9                          ; LCD display buffer register
LCD_DISP_BUFFER_C10                         ; LCD display buffer register
LCD_DISP_BUFFER_C11                         ; LCD display buffer register
LCD_DISP_BUFFER_C12                         ; LCD display buffer register
LCD_DISP_BUFFER_C13                         ; LCD display buffer register
LCD_DISP_BUFFER_C14                         ; LCD display buffer register
LCD_DISP_BUFFER_C15                         ; LCD display buffer register
LCD_DISP_BUFFER_C16                         ; LCD display buffer register
ENDC

CBLOCK  0x70                            ; Ensures GPR is accessible from any Bank. See Memory Map of 628A. (LIMIT OF 16 BYTES)
W_ISR                                   ; For context saving
S_ISR                                   ; For context saving
P_ISR                                   ; For context saving
F_ISR                                   ; For context saving

EEPROM_ADDRESS                          ; Temporary storage for EEPROM Address
EEPROM_DATA                             ; Temporary storage for EEPROM Data
ENDC

LCD_DATA        EQU     PORTB           ;

#DEFINE LCD_RS          PORTA, 0        ;
#DEFINE LCD_RW          PORTA, 1        ;
#DEFINE LCD_E           PORTA, 2        ;
#DEFINE LCD_BUSY        PORTB, 3        ; LCD busy flag is read on LCD DB7.  
#DEFINE LCD_RS_FLAG     FLAGS, 0        ;
#DEFINE LCD_RW_FLAG     FLAGS, 1        ;
#DEFINE LCD_LINE_1      FLAGS, 4        ;
#DEFINE LCD_LINE_2      FLAGS, 5        ;
#DEFINE LCD_LINE_3      FLAGS, 6        ;
#DEFINE LCD_LINE_4      FLAGS, 7        ;
#DEFINE LED             PORTA, 3        ;
#DEFINE PUSHBUTTON1     PORTB, 4        ;
#DEFINE PUSHBUTTON2     PORTB, 5        ;
  
  
        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
       
  ; 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
        

TIMER1_ISR
FLASH_INDICATION_LED  ; Place this code snippet in code to give led indication of code execution
        BSF     LED
        CALL    DELAY_50MS
        BCF     LED
       
  ; Timer 1 interrupt used for incrementing the real time clock registers
        MOVLW   B'10000000'             ; Load number into upper byte of TMR1H:TMR1L so Timer 1 overflows once per second
        MOVWF   TMR1H                   ;
       
  ; Clock maths
    ; Increment Seconds    
        INCF    SECONDS, F              ; At 1 second intervals increment SECONDS register
  
    ; Check if seconds reached 60. Yes; Reset and increment Minutes. No; Done
        MOVF    SECONDS, W              ;
        SUBLW   D'60'                   ;
        BTFSS   STATUS, Z               ;
        GOTO    UPDATE_TIME_BUFFER         ;
        CLRF    SECONDS                 ;
       
    ; Increment Minutes
        INCF    MINUTES, F              ; At 60 second intervals increment MINUTES register
   
    ; Check if minutes reached 60. Yes; Reset and increment hours No; Done
        MOVF    MINUTES, W              ;
        SUBLW   D'60'                   ;
        BTFSS   STATUS, Z               ;
        GOTO    UPDATE_TIME_BUFFER         ;
        CLRF    MINUTES                 ;
   
    ; Increment Hours
        INCF    HOURS, F                ; At 60 minute intervals increment HOURS register

       
UPDATE_TIME_BUFFER       
    ; Clear LCD buffer to clear address locations that aren't altered below
        CALL    LCD_CLEAR_BUFFER
       
    ; Update LCD buffer with seconds data
        MOVF    SECONDS, W
        CALL    BIN_2_BCD
       
        MOVF    ONES, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C12
        MOVF    TENS, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C11

    ; Update LCD buffer with minutes data
        MOVF    MINUTES, W
        CALL    BIN_2_BCD
       
        MOVF    ONES, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C9
        MOVF    TENS, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C8
       
    ; Update LCD buffer with hours data
        MOVF    HOURS, W
        CALL    BIN_2_BCD
       
        MOVF    ONES, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C6
        MOVF    TENS, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C5 
       
    ; Update LCD buffer with other data
        MOVLW   A':'                    ; Load ASCII character of : to W
        MOVWF   LCD_DISP_BUFFER_C7      ; Store in Display general purpose registers and the display will be refreshed every time timer 2 triggers an interruts.
        MOVWF   LCD_DISP_BUFFER_C10     ;
        BSF     LCD_LINE_1              ; Tell program to write buffer contents to line 1 of the LCD display
        CALL    LCD_PRINT
       
TIMER1_ISR_DONE
        BCF     PIR1, TMR1IF            ; Clear TMR1 interrupt flag
        GOTO    ISR_END
       
ISR_END
  ; RESTORE     
        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
ASCII_DIGIT  ; This table return the ASCII equivalent for a number
        ADDWF   PCL, F
        RETLW   A'0'    ; B'00110000'
        RETLW   A'1'    ; B'00110001'
        RETLW   A'2'    ; B'00110010'
        RETLW   A'3'    ; B'00110011'
        RETLW   A'4'    ; B'00110100'
        RETLW   A'5'    ; B'00110101'
        RETLW   A'6'    ; B'00110110'
        RETLW   A'7'    ; B'00110111'
        RETLW   A'8'    ; B'00111000'
        RETLW   A'9'    ; B'00111001'
        RETLW   A' '    ; B'00100000'
        RETLW   A'-'    ; B'00101101'
        RETURN
  
  ; Program Originates Here
SETUP  ; This routine sets up the chips Inputs and Outputs
        CLRF    PORTA                   ; Initialise PORT A by setting output data latches
        MOVLW   H'07'                   ; Turn Comparators off and enable pins for I/O functions
        MOVWF   CMCON                   ;
       
        BCF     STATUS, RP1             ; Bank 1
        BSF     STATUS, RP0             ; Bank 1
       
        MOVLW   B'00110000'             ; PORTA Inputs and Outputs
        MOVWF   TRISA                   ;
        MOVLW   B'11110000'             ;
        MOVWF   TRISB                   ; PORTB Inputs and Outputs
        BCF     STATUS, RP1             ; Bank 0
        BCF     STATUS, RP0             ; Bank 0
       
       
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_LCD                          ;; START OF RECOMMENDED INITIALISATION FOR 4 BIT MODE (as per datasheet:)
        CALL    DELAY_5mS               ;; Wait time > 15 ms after VDD > 4.5V
        CALL    DELAY_5mS               ;;
        CALL    DELAY_5mS               ;;
        MOVLW   b'00000011'             ;; BF cannot be checked before this instruction. Function set (Interface is 8 bits length.)
        MOVWF   LCD_DATA                ;;
        CALL    LCD_ENABLE              ;;
        CALL    DELAY_5mS               ;; Wait time > 4.1 ms
        MOVLW   B'00000011'             ;; BF cannot be checked before this instruction. Function set (Interface is 8 bits length.)
        MOVWF   LCD_DATA                ;;
        CALL    LCD_ENABLE              ;;
        CALL    DELAY_5mS               ;; Wait time > 100 us
        MOVLW   B'00000011'             ;; BF cannot be checked before this instruction. Function set (Interface is 8 bits length.)
        MOVWF   LCD_DATA                ;;
        CALL    LCD_ENABLE              ;;
        CALL    LCD_CHECK_BUSY          ;;
        MOVLW   B'00000010'             ;; Function set ( Set interface to be 4 bits length) Interface is 8 bits length . BF can be checked after the following instructions.
        MOVWF   LCD_DATA                ;;
        CALL    LCD_ENABLE              ;;
        CALL    LCD_CHECK_BUSY          ;;
        MOVLW   B'00101000'             ;; Function set ( Interface is 4 bits length .Specify the number of the display lines and character font . )
        CALL    LCD_WRITE               ;;
        MOVLW   B'00001100'             ;; Display On/Off & Cursor
        CALL    LCD_WRITE               ;;
        MOVLW   B'00000001'             ;; Display clear
        CALL    LCD_WRITE               ;; END OF RECOMMENDED INITIALISATION
        MOVLW   B'00000010'             ;; Display & Cursor home
        CALL    LCD_WRITE               ;;
       
       
INTERRUPT_CONFIG
  ; Setup TMR1 for 1s interrupt (32.768KHz crystal on RB6:7)
        MOVLW   B'00001110'             ;               R/W-POR State   Bit Name: Bit Description - Setting
        MOVWF   T1CON                   ;   -------0    R/W-0           TMR1ON: Timer1 On bit (0 = Stops Timer1)
                                        ;   ------1-    R/W-0           TMR1CS: Timer1 Clock Source Select bit (1 = External clock from pin RB6/T1OSO/T1CKI/PGC (on the rising edge))
                                        ;   -----1--    R/W-0           T1SYNC: Timer1 External Clock Input Synchronization Control bit (1 = Do not synchronize external clock input)
                                        ;   ----1---    R/W-0           T1OSCEN: Timer1 Oscillator Enable Control bit (1 = Oscillator is enabled)                                       
                                        ;   --00----    R/W-0           T1CKPS<1:0>: Timer1 Input Clock Prescale Select bits (00 = 1:1 Prescale value)
                                        ;   00------    R/W-0           Unimplemented: Read as ‘0’
        BSF     T1CON, TMR1ON           ; Start TMR1

 
       
  ; Finalise interrupt configuration
        BSF     STATUS, RP0             ; Bank 1
        BSF     PIE1, TMR1IE            ; -------1  Enable TMR1IE - TMR1 Overflow Interrupt Enable bit
        BCF     STATUS, RP0             ; Bank 0
       
        CLRF    PIR1                    ; Clear all Peripheral Interrupt Flags
        BSF     INTCON, PEIE            ; Enable Peripheral Interrupts
       
       
        BSF     INTCON, GIE             ; Enable Global Interrupts
       
MAIN
       
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                  ; Store in Celsius TEMPLO GPR
        CALL    DS18B20_READ_BYTE       ; Move Scrachpad Byte 1 to W
        MOVWF   TEMPHI                  ; Store in Celsius TEMPHI GPR
       
        GOTO    PROCESS_TEMPERATURE_DATA;
        CALL    DELAY_50mS
       
        GOTO    MAIN
       
       
       
BIN_2_BCD  ; This routine converts the 8-bit number held in BCD_2_DEC_LSB to Decimal and stores it in 3 GPR's; HUNS, TENS & ONES
; These registers are then used as jump pointers for use with the display
        MOVWF   BIN_2_BCD_LSB
        INCF    BIN_2_BCD_LSB           ; Pre-load low byte register + 1
        CLRF    HUNS                    ; HUNS = 0000 0000
        MOVLW   D'246'                  ; Move decimal '246' to W
        
RESET_TENS
        MOVWF   TENS                    ; TENS GPR = 1111 0101
RESET_ONES
        MOVWF   ONES                    ; ONES GPR = 1111 0101
DEC_ORIGINAL       
        DECFSZ  BIN_2_BCD_LSB, F        ; Decrement BIN_TO_BCD_LO register
        GOTO    INC_RESULT              ; Not 0, GOTO INC_RESULT
        GOTO    ADJUST_RESULT           ; Is 0, done, GOTO ADJUST_RESULT
INC_RESULT       
        INCFSZ  ONES, F                 ; Increment ONES register, skip if 0 (overflow)
        GOTO    DEC_ORIGINAL            ; Not 0, repeat decrement of original number
        INCFSZ  TENS, F                 ; Is 0, Increment TENS register skip if 0
        GOTO    RESET_ONES              ; Not 0, repeat decrement of original number & reset the ONES register to D'246'
        INCF    HUNS, F                 ; TENS overflowed, Increment HUNS skip if 0
        GOTO    RESET_TENS              ; Not 0, repeat decrement of original number & reset the ONES and TENS registers to D'246'
        
ADJUST_RESULT       
        SUBWF   TENS, F                 ; W still holds D'246 so subtract it from TENS register to determine how many 'TENS' (0-9)
        SUBWF   ONES, F                 ; W still holds D'246 so subtract it from ONES register to determine how many 'ONES' (0-9)
        RETURN
           

       
LCD_PRINT
        BCF     INTCON, GIE             ; Prevent LCD communication from being interrupted
        CALL    LCD_CURSOR_SHIFT
        BTFSS   PIR1, TMR1IF            ; Sneaky trick to check if currently in ISR or not as this routine is called in normal program as well as inside the ISR.
        BSF     INTCON, GIE             ; Re-enable Global Interrupts if not already in ISR
        RETURN

LCD_CURSOR_SHIFT
  ; Test which line of the LCD to write to by checking flags
        MOVLW   B'10000000'     ; Line 1 address
        BTFSC   LCD_LINE_2     ; Flag
        MOVLW   B'11000000'    ; Line 2 address
        BTFSC   LCD_LINE_3     ; Flag
        MOVLW   B'10010000'    ; Line 3 address
        BTFSC   LCD_LINE_4     ; Flag
        MOVLW   B'11010000'    ; Line 4 address
       
        BCF     LCD_RS                  ; Place LCD in command entry mode
        CALL    LCD_WRITE               ; Send data to LCD
       
        MOVLW   B'00001111'      ; Clear LCD line pointer flags
        ANDWF   FLAGS, F         ;
       
                   
LCD_WRITE_BUFFER
  ; Write 16 character buffer to LCD
        MOVLW   LCD_DISP_BUFFER_C1      ; Initialise pointer to RAM address
        MOVWF   FSR                     ; (File Select Register)
        BSF     LCD_RS                  ; Place LCD in character entry mode       
        CLRF    COUNT                   ; Reset counter
        MOVF    INDF, W                 ; Move indirectly addressed registers contents to W
        CALL    LCD_WRITE               ; Send data to LCD
        INCF    FSR, F                  ; Increment indirectly addressed register pointer
        INCF    COUNT, F                ; Increment count
        BTFSS   COUNT, 4                ; Count 32?
        GOTO    $-D'5'                  ; No? Do again. Yes? Skip..
        RETURN


       
LCD_CLEAR_BUFFER       
  ; Initialise all 16x LCD_DISP_BUFFER_xx RAM with blank character
        MOVLW   LCD_DISP_BUFFER_C1      ; Initialise pointer to RAM
        MOVWF   FSR                     ; (File Select Register)
        CLRF    COUNT                   ; Reset counter
        MOVLW   A' '
        MOVWF   INDF                    ; Move indirectly addressed registers contents to W
        INCF    FSR, F                  ; Increment indirectly addressed register pointer
        INCF    COUNT, F                ; Increment count
        BTFSS   COUNT, 4                ; Count 16?
        GOTO    $-D'5'                  ; No? Do again. Yes? Skip..
        RETURN
       
       
LCD_WRITE
  ; 4 bit mode data write while maintaining state of upper 4 bits of PORTB
        MOVWF   LCD_DATA_TEMP           ; Store data in temporary register for manipulation.
       
        MOVLW   B'11110000'             ; Used to clear lower bits of PORTB (LCD Data lines)
        ANDWF   LCD_DATA, F             ; Clear LCD Data lines leaving upper nibble unchanged
        SWAPF   LCD_DATA_TEMP, W        ; Upper nibble of data to be sent first, so swap nibbles to upper is in lower
        ANDLW   B'00001111'             ; Clear upper nibble 
        IORWF   LCD_DATA, F             ; Sent to PORTB0-3 (LCD Data lines) without changing upper half of PORTB
        CALL    LCD_ENABLE              ; Latch data into LCD. 
       
        MOVLW   B'11110000'             ; Used to clear lower bits of PORTB (LCD Data lines)
        ANDWF   LCD_DATA, F             ; Clear LCD Data lines leaving upper nibble unchanged
        MOVF    LCD_DATA_TEMP, W        ; Lower nibble of data sent second, so first move data to W
        ANDLW   B'00001111'             ; Mask upper nibble
        IORWF   LCD_DATA, F             ; Sent to PORTB0-3 (LCD Data lines) without changing upper half of PORTB
        CALL    LCD_ENABLE              ; Latch data into LCD.
           
        CALL    LCD_CHECK_BUSY          ; Wait for LCD to
       
        RETURN                          ;
       
       
LCD_ENABLE
        BSF     LCD_E                   ;
        NOP                             ; Wait for tDA (320ns)
        BCF     LCD_E                   ;
        RETURN        
       
       
LCD_CHECK_BUSY   
  ; PORTB, 3 / LCD DB7 to input
        BSF     STATUS, RP0             ; Bank 1
        BSF     LCD_BUSY                ; TRISB, 7 make input
        BCF     STATUS, RP0             ; Bank 0

  ; Save RS & RW state       
        BTFSS   LCD_RS                  ;
        GOTO    $+3                     ;
        BSF     LCD_RS_FLAG             ;
        GOTO    $+2                     ;
        BCF     LCD_RS_FLAG             ;
       
        BTFSS   LCD_RW                  ;
        GOTO    $+3                     ;
        BSF     LCD_RW_FLAG             ;
        GOTO    $+2                     ;
        BCF     LCD_RW_FLAG             ;
       
  ; Configure LCD to 'Read Status' mode and read busy flag
        BCF     LCD_RS                  ;
        BSF     LCD_RW                  ;
        NOP                             ; Wait for tAS (140ns)
        BSF     LCD_E                   ;
        NOP                             ; Wait for tDA (320ns)
        BTFSC   LCD_BUSY                ;
        GOTO    $-1                     ;
        BCF     LCD_E                   ;
       
  ; Restore RS & RW state       
        BTFSS   LCD_RS_FLAG             ;
        GOTO    $+3                     ;
        BSF     LCD_RS                  ;
        GOTO    $+2                     ;
        BCF     LCD_RS                  ;
       
        BTFSS   LCD_RW_FLAG             ;
        GOTO    $+3                     ;
        BSF     LCD_RW                  ;
        GOTO    $+2                     ;
        BCF     LCD_RW                  ;
       
  ; PORTB, 3 / LCD DB7 back to output
        BSF     STATUS, RP0             ;
        BCF     LCD_BUSY                ; 
        BCF     STATUS, RP0             ;
        RETURN 
       
       
  
  ; 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
       
PROCESS_TEMPERATURE_DATA
        BCF     FLAGS, 2                ; Clear Negative Flag
        BTFSS   TEMPHI, 7               ; Test if Temperature is negative (2's complement format)
        GOTO    REARRANGE_DATA          ; NOT Negative, skip 'Undo 2's Complement'
        BSF     FLAGS, 2                ; 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, F               ; Invert TEMPLO
        COMF    TEMPHI, F               ; Invert TEMPHI
        INCFSZ  TEMPLO, 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               ; 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, 2) 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, W               ; F = 1001 0001  W = 1001 0000
        IORWF   TEMPHI, F               ;                F = 0000 0011   F = 1001 0011
        SWAPF   TEMPHI, F               ;                                F = 1001 0011   F = 0011 1001 (New TEMPHI_C)
        MOVLW   B'00001111'             ; W = 0000 1111
        ANDWF   TEMPLO, 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
       
REARRANGED_DATA_2_BCD
        BCF     INTCON, GIE             ; Disable Interrupts
       
        MOVF    TEMPHI, W
        MOVWF   BIN_2_BCD_LSB
        CALL    BIN_2_BCD
       
       
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, F               ;
        MOVF    TEMPLO, W               ;
        ADDWF   TEMPLO, F               ; *2, C=0
        RLF     TEMPLO, F               ; *4, C=0
        ADDWF   TEMPLO, F               ; *5, C=0
        RLF     TEMPLO, F               ; *10
        MOVLW   B'00001000'             ;
        ADDWF   TEMPLO, F               ; Rounding
        SWAPF   TEMPLO, 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   D'10'                   ; Yes, jump Pointer for blank arrangement
  
        BTFSC   FLAGS, 2                ; Is Temperature Negative?
        MOVLW   D'11'                   ; 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   D'10'                   ; Yes, jump Pointer for blank arrangement
        MOVWF   TENS                    ;
       
        CALL    LCD_CLEAR_BUFFER
       
        MOVF    HUNS, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C1                          ; LCD_DISP_ADDR_40
        MOVF    TENS, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C2                          ; LCD_DISP_ADDR_41
        MOVF    ONES, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C3                          ; LCD_DISP_ADDR_42
        MOVF    DECI, W
        CALL    ASCII_DIGIT
        MOVWF   LCD_DISP_BUFFER_C5                          ; LCD_DISP_ADDR_44
       
  ; Write temperature to LCD
        MOVLW   A'.'                    ;
        MOVWF   LCD_DISP_BUFFER_C4      ;
        MOVWF   LCD_DISP_BUFFER_C12      ;
        MOVLW   H'DF'                    ;
        MOVWF   LCD_DISP_BUFFER_C6      ;
        MOVWF   LCD_DISP_BUFFER_C14      ;
        MOVLW   A'C'                    ;
        MOVWF   LCD_DISP_BUFFER_C7      ;
        MOVWF   LCD_DISP_BUFFER_C15      ;
        BSF     LCD_LINE_3              ; Set flag to print on line 1 of display
        CALL    LCD_PRINT
       
        BSF     INTCON, GIE             ; Enable Interrupts
        GOTO    MAIN
       
       
DELAY_1S ; Actual delay = 1 seconds = 1000000 cycles                    1/1 Second
        MOVLW   0X07                    ;
        MOVWF   DELAYGPR1               ;
        MOVLW   0X2F                    ;
        MOVWF   DELAYGPR2               ;
        MOVLW   0X03                    ;
        MOVWF   DELAYGPR3               ;
DELAY_1S_0
        DECFSZ  DELAYGPR1, F            ;
        GOTO    $+2                     ;
        DECFSZ  DELAYGPR2, F            ;
        GOTO    $+2                     ;
        DECFSZ  DELAYGPR3, F            ;
        GOTO    DELAY_1S_0              ; 999990 cycles
        GOTO    $+1                     ;
        GOTO    $+1                     ;
        GOTO    $+1                     ; 6 cycles
        RETURN                          ; 4 cycles (including call)
       
DELAY_50mS  ; Actual delay = 0.05 seconds = 50,000 cycles               1/20 Second
        MOVLW   0X0E                    ;
        MOVWF   DELAYGPR1               ;
        MOVLW   0X28                    ;
        MOVWF   DELAYGPR2               ;
DELAY_50mS_0                            ;
        DECFSZ  DELAYGPR1, F            ;
        GOTO    $+2                     ;
        DECFSZ  DELAYGPR2, F            ;
        GOTO    DELAY_50MS_0            ; 49993 cycles
        GOTO    $+1                     ;
        NOP                             ; 3 cycles
        RETURN                          ; 4 cycles (including call)    

       
DELAY_5mS ; Actual delay = 0.005 seconds = 5000 cycles                  1/200 Second
        MOVLW   0XE6                    ;
        MOVWF   DELAYGPR1               ;
        MOVLW   0X04                    ;
        MOVWF   DELAYGPR2               ;
DELAY_5mS_0                             ;
        DECFSZ  DELAYGPR1, F            ;
        GOTO    $+2                     ;
        DECFSZ  DELAYGPR2, F            ;
        GOTO    DELAY_5mS_0             ; 4993 cycles
        GOTO    $+1                     ;
        NOP                             ; 3 cycles
        RETURN                          ; 4 cycles (including call)
       

STOP    GOTO    STOP       
END
 
Your code impresses me... Very well laid out.... I
Thanks Ian. I'm not the greatest coder so keeping things neat and understandable helps a lot.

I have code for the One wire senso
I checked out your link, I found the one wire stuff, but couldn't see anything using the READ ROM and SEARCH ROM commands for using more than 1 sensor.
I'm being lazy.. I haven't re-visited the data sheet and had a go myself yet.. Shouldn't be too tricky. I already have the comms routines working.
 
You only need the read rom routines if your system is going to read multiple devices
Yes that is what I would like to do. I'd like to have 2 temperature sensors measuring air temp for a power supply cooling fan. One for incoming air and one on the outgoing.
I'll get doing some research. I remember seeing Mike K8LH had some good routines, I'll try ind them for some inspiration.
 
000139.jpg Just looking at the code within your ISR and I count 12 Calls, I you then follow all those Calls they lead to subsequent routine that also contain Calls, i lost count at 20.

Have a read of the datasheet as show which explains how the program counter is stored for the Return location when a Call is executed, and how on that chip you only 8 levels in the 628 stack before it overwrites itself.

Because your program is a relatively small, it appears to work ok, put large programs will probably fail.
 
You can easily overcome the Stack problem by moving your UPDATE_TIME_BUFFER out of the ISR and calling it in the Main loop as shown in the attathed file.

The problem is that because of the length of the ds1820 routine your Seconds display may be uneven; however that problem was already there with your original code as disabling the interrupts always means you could miss one .

Also noticed you are disabling the interrupts all over the whole code, you do not have to disable it while outputting to the lcd or most other things; its just things like the ds1820 where it uses a non standard but very precise timing routine for its data, that if interrupted, would fail that read.
So remove all all other bcf interrupts otherwise your seconds will be even more out.

The way you can get an accurate seconds display, when using the revised ISR is, based on the ds1820 taking about 750ms , then every time you want to start a ds1820 read , you must first wait and test to see when the Timer1 IF is changed from off to on so that you have a clear 1 second to read the ds1820 before updating your display.
 

Attachments

  • jjake.zip
    8.8 KB · Views: 171
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top