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

PIC ASM LCD. Check out my code.

Status
Not open for further replies.
Hey everyone.

It's know it's been done 1000's of times before, BUT, I'm interested to know if the way I'm doing it is a good way.

So basically I'm driving a 4x16 LCD (44780 controlled) in 4-bit mode.
I set aside 64 bytes of ram (in bank 1) to use as LCD Character place holders.
Throughout the program if I want to write something to the display I simply move it to the RAM placeholders
I use TIMER0 to cause an interrupt and refresh the display every 0.0655 seconds/15.26Hz.
The ISR updates the display writing everything that is held in these RAM locations to the LCD.

Here are the code snippets pulled from my program.

My hardware setup
Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                                                       ;
;                                      PIC16F628A Microcontroller                                       ;
;                                               ____ ____                                               ;
; BUZZER                         VREF/AN2/RA2 -| 1  - 18 |- RA1/AN1               TEMPERATURE SENSOR 1  ;
; PUSH BUTTON 1                  CPM1/AN3/RA3 -| 2    17 |- RA0/AN0               TEMPERATURE SENSOR 2  ;
; PUSH BUTTON 2                CMP2/T0CKI/RA4 -| 3    16 |- RA7/OSC1/CLKIN                 CRYSTAL OSC  ;
; PUSH BUTTON 3                  VPP/MCLR/RA5 -| 4    15 |- RA6/OSC2/CLKOUT                CRYSTAL OSC  ;
;                                         VSS -| 5    14 |- VDD                                         ;
; LCD RS                              INT/RB0 -| 6    13 |- RB7/T1OSC1/PGD                      LCD D7  ;
; LCD RW                            DT/RX/RB1 -| 7    12 |- RB6/T1OSCO/T1CLKI/PGC               LCD D6  ;
; LCD E                             CK/TX/RB2 -| 8    11 |- RB5                                 LCD D5  ;
; PWM OUT                            CCP1/RB3 -|_9____10_|- RB4/PGM                             LCD D4  ;
;                                                                                                       ;
;                                                                                                       ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Definitions & RAM placeholders for 64 charaters of LCD (4x16)
Code:
#DEFINE LCD_PORT        PORTB 
#DEFINE LCD_TRIS        TRISB 
#DEFINE LCD_E           PORTB, 2
#DEFINE LCD_RW          PORTB, 1
#DEFINE LCD_RS          PORTB, 0

CBLOCK   H'20'  ;
LCD_DATA                ;
LOOKUP_TABLE_POINTER    ;
LCD_ADDRESS             ;
ENDC

CBLOCK   H'A0'  ; Place in Bank 1 (General Purpose Registers 80 Bytes)
LCD_DISPLAY_R1C1
LCD_DISPLAY_R1C2
LCD_DISPLAY_R1C3
LCD_DISPLAY_R1C4
LCD_DISPLAY_R1C5
LCD_DISPLAY_R1C6
LCD_DISPLAY_R1C7
LCD_DISPLAY_R1C8
LCD_DISPLAY_R1C9
LCD_DISPLAY_R1C10
LCD_DISPLAY_R1C11
LCD_DISPLAY_R1C12
LCD_DISPLAY_R1C13
LCD_DISPLAY_R1C14
LCD_DISPLAY_R1C15
LCD_DISPLAY_R1C16
LCD_DISPLAY_R2C1
LCD_DISPLAY_R2C2
LCD_DISPLAY_R2C3
LCD_DISPLAY_R2C4
LCD_DISPLAY_R2C5
LCD_DISPLAY_R2C6
LCD_DISPLAY_R2C7
LCD_DISPLAY_R2C8
LCD_DISPLAY_R2C9
LCD_DISPLAY_R2C10
LCD_DISPLAY_R2C11
LCD_DISPLAY_R2C12
LCD_DISPLAY_R2C13
LCD_DISPLAY_R2C14
LCD_DISPLAY_R2C15
LCD_DISPLAY_R2C16
LCD_DISPLAY_R3C1
LCD_DISPLAY_R3C2
LCD_DISPLAY_R3C3
LCD_DISPLAY_R3C4
LCD_DISPLAY_R3C5
LCD_DISPLAY_R3C6
LCD_DISPLAY_R3C7
LCD_DISPLAY_R3C8
LCD_DISPLAY_R3C9
LCD_DISPLAY_R3C10
LCD_DISPLAY_R3C11
LCD_DISPLAY_R3C12
LCD_DISPLAY_R3C13
LCD_DISPLAY_R3C14
LCD_DISPLAY_R3C15
LCD_DISPLAY_R3C16
LCD_DISPLAY_R4C1
LCD_DISPLAY_R4C2
LCD_DISPLAY_R4C3
LCD_DISPLAY_R4C4
LCD_DISPLAY_R4C5
LCD_DISPLAY_R4C6
LCD_DISPLAY_R4C7
LCD_DISPLAY_R4C8
LCD_DISPLAY_R4C9
LCD_DISPLAY_R4C10
LCD_DISPLAY_R4C11
LCD_DISPLAY_R4C12
LCD_DISPLAY_R4C13
LCD_DISPLAY_R4C14
LCD_DISPLAY_R4C15
LCD_DISPLAY_R4C16
ENDC

Another RAM byte used for flags. Flags represent which screen to display
Code:
LCD_SCREEN_SELECT       EQU     H'76'                   ;   -------0   
                                                        ;   ------1-    Welcome Screen
                                                        ;   -----2--    Temperatures Screen
                                                        ;   ----3---    Settings Screen a
                                                        ;   ---4----    Settings Screen b
                                                        ;   --5-----    Settings Screen c
                                                        ;   -6------    Manual Control Screen
                                                        ;   7-------

Text tables, used for printing strings of characters.
Code:
  ; Must be at top of program to attempt to keep Lookup Table in first 255 lines of the Program Memory
LOOKUP_TABLE_TEXT 
        ADDWF   PCL, F                  ;
        RETLW   A'E'                    ; EFFECTIVE
        RETLW   A'F'                    ;
        RETLW   A'F'                    ;
        RETLW   A'E'                    ;
        RETLW   A'C'                    ;
        RETLW   A'T'                    ;
        RETLW   A'I'                    ;
        RETLW   A'V'                    ;
        RETLW   A'E'                    ;
        RETLW   A' '                    ;
        CLRW                            ; Effects Z bit, used to indicate end of print sequence
        RETURN                          ;
        RETLW   A'W'                    ; WATER TEMP
        RETLW   A'A'                    ;
        RETLW   A'T'                    ;
        RETLW   A'E'                    ;
        RETLW   A'R'                    ;
        RETLW   A' '                    ;
        RETLW   A'T'                    ;
        RETLW   A'E'                    ;
        RETLW   A'M'                    ;
        RETLW   A'P'                    ;
        CLRW                            ; 
        RETURN                          ; 
        RETLW   A'C'                    ; CONTROLLER
        RETLW   A'O'                    ;
        RETLW   A'N'                    ;
        RETLW   A'T'                    ;
        RETLW   A'R'                    ;
        RETLW   A'O'                    ;
        RETLW   A'L'                    ;
        RETLW   A'L'                    ;
        RETLW   A'E'                    ;
        RETLW   A'R'                    ;
        CLRW                            ;
        RETURN                          ;
        RETLW   A'T'                    ; Temperatures:
        RETLW   A'e'                    ;
        RETLW   A'm'                    ;
        RETLW   A'p'                    ;
        RETLW   A'e'                    ;
        RETLW   A'r'                    ;
        RETLW   A'a'                    ;
        RETLW   A't'                    ;
        RETLW   A'u'                    ;
        RETLW   A'r'                    ;
        RETLW   A'e'                    ;
        RETLW   A's'                    ;
        RETLW   A':'                    ;
        CLRW                            ;
        RETURN                          ;
        RETLW   A'C'                    ; Coolant
        RETLW   A'o'                    ;
        RETLW   A'o'                    ;
        RETLW   A'l'                    ;
        RETLW   A'a'                    ;
        RETLW   A'n'                    ;
        RETLW   A't'                    ;
        CLRW                            ;
        RETURN                          ;
        RETLW   A'C'                    ; Column
        RETLW   A'o'                    ;
        RETLW   A'l'                    ;
        RETLW   A'u'                    ;
        RETLW   A'm'                    ;
        RETLW   A'n'                    ;
        CLRW                            ;
        RETURN                          ;
        RETLW   A'P'                    ; Pump Speed
        RETLW   A'u'                    ;
        RETLW   A'm'                    ;
        RETLW   A'p'                    ;
        RETLW   A' '                    ;
        RETLW   A'S'                    ;
        RETLW   A'p'                    ;
        RETLW   A'e'                    ;
        RETLW   A'e'                    ;
        RETLW   A'd'                    ;
        CLRW                            ;
        RETURN                          ;
        RETLW   B'11011111'             ; (Celcius Character)
        CLRW                            ;
        RETURN                          ;
        RETLW   A'%'                    ; %
        CLRW                            ;
        RETURN                          ;
        RETLW   A'S'                    ; Settings:
        RETLW   A'e'                    ;
        RETLW   A't'                    ;
        RETLW   A't'                    ;
        RETLW   A'i'                    ;
        RETLW   A'n'                    ;
        RETLW   A'g'                    ;
        RETLW   A's'                    ;
        RETLW   A':'                    ;
        CLRW                            ;
        RETURN                          ;
        RETLW   A'P'                    ; Pump Min
        RETLW   A'u'                    ;
        RETLW   A'm'                    ;
        RETLW   A'p'                    ;
        RETLW   A' '                    ;
        RETLW   A'M'                    ;
        RETLW   A'i'                    ;
        RETLW   A'n'                    ;
        CLRW                            ;
        RETURN                          ; 
        RETLW   A'C'                    ; Control
        RETLW   A'o'                    ;
        RETLW   A'n'                    ;
        RETLW   A't'                    ;
        RETLW   A'r'                    ;
        RETLW   A'o'                    ;
        RETLW   A'l'                    ;
        RETLW   A':'                    ;
        CLRW                            ;
        RETURN                          ;
        RETLW   A'M'                    ; Manual
        RETLW   A'a'                    ;
        RETLW   A'n'                    ;
        RETLW   A'u'                    ;
        RETLW   A'a'                    ;
        RETLW   A'l'                    ;
        CLRW                            ;
        RETURN                          ;
        RETLW   A'A'                    ; Automatic
        RETLW   A'u'                    ;
        RETLW   A't'                    ;
        RETLW   A'o'                    ;
        RETLW   A'm'                    ;
        RETLW   A'a'                    ;
        RETLW   A't'                    ;
        RETLW   A'i'                    ;
        RETLW   A'c'                    ;
        CLRW                            ;
        RETURN                          ;
      
      
LOOKUP_TABLE_LOOP
  ; Execute loop and use lookup table to print text
        MOVF    LOOKUP_TABLE_POINTER, W ;
        CALL    LOOKUP_TABLE_TEXT       ;
        BTFSC   STATUS, Z               ; Z bit affected by CLRW instruction
        RETURN
        MOVWF   INDF                    ;
        INCF    FSR                     ;
        INCF    LOOKUP_TABLE_POINTER, F ;
        GOTO    LOOKUP_TABLE_LOOP       ;

LCD one off Setup
Code:
LCD_INITIALISE
        BCF     LCD_RS            ; LCD treats data as a Command
        BCF     LCD_RW            ; Write mode
      
        CALL    DELAY_100mS
      
        MOVF    LCD_PORT, W        ;
        ANDLW   B'00001111'     ; Keep lower nible of LCD_PORT unchanged, zero upper nibble
        IORLW   b'00110000'     ; lower nibble must be zero, upper nibble is lower nibble of LCD Command (Function Set; 8-bit mode)
        MOVWF   LCD_PORT
        CALL    TOGGLE_E
        CALL    DELAY_5mS
      
        MOVF    LCD_PORT, W        ;
        ANDLW   B'00001111'     ; Keep lower nible of LCD_PORT unchanged, zero upper nibble
        IORLW   b'00110000'     ; lower nibble must be zero, upper nibble is lower nibble of LCD Command (Function Set; 8-bit mode)
        MOVWF   LCD_PORT
        CALL    TOGGLE_E
        CALL    DELAY_5mS
      
        MOVF    LCD_PORT, W        ;
        ANDLW   B'00001111'     ; Keep lower nible of LCD_PORT unchanged, zero upper nibble
        IORLW   b'00110000'     ; lower nibble must be zero, upper nibble is lower nibble of LCD Command (Function Set; 8-bit mode)
        MOVWF   LCD_PORT
        CALL    TOGGLE_E
        CALL    DELAY_5mS
      
        MOVF    LCD_PORT, W        ;
        ANDLW   B'00001111'     ; Keep lower nible of LCD_PORT unchanged, zero upper nibble
        IORLW   b'00100000'     ; lower nibble must be zero, upper nibble is lower nibble of LCD Command (Function Set; 4-bit mode)
        MOVWF   LCD_PORT
        CALL    TOGGLE_E
        CALL    DELAY_5mS
      
  ; LCD now in 4-bit mode

        MOVLW   b'00101000'     ; FUNCTION SET (4-bit, 2 line, 5x7 matrix)
        CALL    LCD_WRITE
      
        MOVLW   b'00001000'     ; DISPLAY ON/OFF & CURSOR (Display off, underline off, blink off)
        CALL    LCD_WRITE
      
        MOVLW   b'00000001'     ; CLEAR DISPLAY
        CALL    LCD_WRITE
      
        MOVLW   b'00000110'     ; CHARACTER ENTYRY MODE (Increment, shift off)
        CALL    LCD_WRITE
      
        MOVLW   b'00001100'     ; DISPLAY ON/OFF & CURSOR (Display on, underline off, blink off)
        CALL    LCD_WRITE

TIMER0 interrupt
Code:
TMR0_INTERRUPT 
  ; Refresh display
UPDATE_LCD       
        BCF     STATUS, IRP             ; Indirect addressing Bank 0/1
        MOVLW   LCD_DISPLAY_R1C1        ; Initialise pointer to RAM
        MOVWF   FSR                     ; (File Select Register)

        BCF     LCD_RS                    ; LCD treats data as a COMMAND
        MOVLW   b'10000000'             ; DISPLAY ADDRESS R1C1
        CALL    LCD_WRITE
        CALL    WRITE_16_CHAR
      
        BCF     LCD_RS                    ; LCD treats data as a COMMAND
        MOVLW   b'11000000'             ; DISPLAY ADDRESS R2C1
        CALL    LCD_WRITE
        CALL    WRITE_16_CHAR
      
        BCF     LCD_RS                    ; LCD treats data as a COMMAND
        MOVLW   b'10010000'             ; DISPLAY ADDRESS R3C1
        CALL    LCD_WRITE
        CALL    WRITE_16_CHAR
      
        BCF     LCD_RS                    ; LCD treats data as a COMMAND
        MOVLW   b'11010000'             ; DISPLAY ADDRESS R4C1
        CALL    LCD_WRITE
        CALL    WRITE_16_CHAR
      
        BCF     INTCON, TMR0IF            ;   
        CLRF    TMR0
        GOTO    WHICH_INTERRUPT

LCD Communication routines
Code:
CLEAR_LCD_DISPLAY_RAM
  ; Write ascii 'blank space' charater to ram used for holding display charaters as b'00000000' shows a weird charater
        MOVLW   D'64'                   ;
        MOVWF   COUNT                   ; Set COUNT to number of registers to write
        BCF     STATUS, IRP             ; Indirect addressing Bank 0/1
        MOVLW   LCD_DISPLAY_R1C1        ; Initialise pointer to RAM
        MOVWF   FSR                     ; (File Select Register)
BLANK_NEXT_GPR       
        MOVLW   A' '                    ; ASCII charater for blank space
        MOVWF   INDF                    ; Write  to GPR
        INCF    FSR, F                  ; Increment RAM address pointer
        DECFSZ  COUNT, F                ; Countdown, if zero; done
        GOTO    BLANK_NEXT_GPR          ; Not zero, do again
        RETURN
      
      
      
      
      
WRITE_16_CHAR
  ; This is a looping routine that writes 1 line (16 charaters at a time) to the display.
        BSF     LCD_RS                  ; LCD treats data as a character
        MOVLW   D'16'                   ;
        MOVWF   COUNT_ISR               ;
NEXT_CHARACTER       
        MOVF    INDF, W                 ;
        CALL    LCD_WRITE               ;
        INCF    FSR, F                  ; Increment pointer
        DECFSZ  COUNT_ISR, F            ;
        GOTO    NEXT_CHARACTER          ;
        RETURN
      
      
LCD_WRITE
  ; 4-bit data send routine
        MOVWF   LCD_DATA                ; Move contents of W (could be command or charater data) to GPR
      
        MOVLW   B'00001111'             ;
        ANDWF   LCD_PORT, F             ; Zero upper nibble of LCD_PORT while leaving lower nibble unchanged
        MOVF    LCD_DATA, W             ; Move contents of LCD_DATA to W. Original file register unchanged
        ANDLW   B'11110000'             ; Zero lower nibble of W
        IORWF   LCD_PORT, F             ; Move data to LCD Data Lines leaving lower nibble unchanged
        CALL    TOGGLE_E                ; Enter
        CALL    LCD_CHECK_BUSY          ; Wait while LCD complates exectuion
      
        MOVLW   B'00001111'             ;
        ANDWF   LCD_PORT, F             ; Zero upper nibble of LCD_PORT while leaving lower nibble unchanged
        SWAPF   LCD_DATA, W             ; Swap upper and lower nibble of LCD_DATA to send next nibble to LCD
        ANDLW   B'11110000'             ; Zero lower nibble of W
        IORWF   LCD_PORT, F             ; Move data to LCD Data Lines leaving lower nibble unchanged
        CALL    TOGGLE_E                ; Enter
        CALL    LCD_CHECK_BUSY          ; Wait while LCD complates exectuion
        RETURN
      
      
TOGGLE_E
        BSF     LCD_E                   ;
        BCF     LCD_E                   ;
        RETURN 
      
      
LCD_CHECK_BUSY ;
  ; Save LCD, RS state
        BCF     FLAGS, 2                ;
        BTFSC   LCD_RS                  ;
        BSF     FLAGS, 2                ;
        BCF     LCD_RS                  ;
      
        BSF     LCD_RW                  ;
  ; Switch to Bank 1 and change LCD comms IO to inputs while maintaining rest of port IO direction (set elsewhere in code)
        BSF     STATUS, RP0             ; Bank 1
        MOVF    LCD_TRIS, W             ;
        IORLW   B'11110000'             ; 0000 xxxx = LCD Comms Lines
        MOVWF   LCD_TRIS   ;
        BCF     STATUS, RP0             ; Bank 0
      
LCD_READ_STATUS
        BSF     LCD_E                   ; Data is avaiable when E line is high
        NOP                             ; Wait to make sure data is valid
        BTFSC   LCD_PORT, 7             ; Test busy flag of LCD
        GOTO    LCD_READ_STATUS         ; LCD is busy, check again
        BCF     LCD_E                   ; LCD ready, lower E line
  ; Switch to Bank 1 and change LCD comms IO to outputs while maintaining rest of port IO direction (set elsewhere in code)
        BSF     STATUS, RP0             ; Bank 1
        MOVF    LCD_TRIS, W             ;
        ANDLW   B'00001111'             ; 0000 xxxx = LCD Comms Lines
        MOVWF   LCD_TRIS                ;
        BCF     STATUS, RP0             ; Bank 0
  ; Reinstate RS     
        BTFSS   FLAGS, 2                ;
        GOTO    $+2                     ;
        BSF     LCD_RS                  ;
  ; Change back to write mode     
        BCF     LCD_RW                  ;
        RETURN

LCD Screens
Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                                                       ;
;                                               SCREEN 1                                                ;
;                                                                                                       ;
;                                           +----------------+                                          ;
;                                           |   EFFECTIVE    |                                          ;
;                                           |                |                                          ;
;                                           |   WATER TEMP   |                                          ;
;                                           |   CONTROLLER   |                                          ;
;                                           +----------------+                                          ;
;                                                                                                       ;
;                                                                                                       ;
;                                               SCREEN 2                                                ;
;                                                                                                       ;
;                                           +----------------+                                          ;
;                                           |Temperatures:   |                                          ;
;                                           |  Coolant  23.9*|                                          ;
;                                           |  Column   25.0*|                                          ;
;                                           |Pump Speed 99.9%|                                          ;
;                                           +----------------+                                          ;
;                                                                                                       ;
;                                                                                                       ;
;                                               SCREEN 3a                                               ;
;                                                                                                       ;
;                                           +----------------+                                          ;
;                                           |Settings:       |                                          ;
;                                           | >Coolant  55.0*|                                          ;
;                                           |  Column   45.0*|                                          ;
;                                           |  Pump Min 60.0%|                                          ;
;                                           +----------------+                                          ;
;                                                                                                       ;
;                                                                                                       ;
;                                               SCREEN 3b                                               ;
;                                                                                                       ;
;                                           +----------------+                                          ;
;                                           |Settings:       |                                          ;
;                                           |  Coolant  55.0*|                                          ;
;                                           | >Column   45.0*|                                          ;
;                                           |  Pump Min 60.0%|                                          ;
;                                           +----------------+                                          ;
;                                                                                                       ;
;                                                                                                       ;
;                                               SCREEN 3c                                               ;
;                                                                                                       ;
;                                           +----------------+                                          ;
;                                           |Settings:       |                                          ;
;                                           |  Coolant  55.0*|                                          ;
;                                           |  Column   45.0*|                                          ;
;                                           | >Pump Min 60.0%|                                          ;
;                                           +----------------+                                          ;
;                                                                                                       ;
;                                                                                                       ;
;                                                                                                       ;
;                                               SCREEN 4                                                ;
;                                                                                                       ;
;                                           +----------------+                                          ;
;                                           |                |                                          ;
;                                           |                |                                          ;
;                                           |                |                                          ;
;                                           |                |                                          ;
;                                           +----------------+                                          ;
;                                                                                                       ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


LCD_PRINT_ACTIVE_SCREEN
        MOVWF   LCD_SCREEN_SELECT       ;
      
        BTFSC   LCD_SCREEN_SELECT, 1    ; Check LCD Screen Select flag bit
        CALL    LCD_SCREEN_1            ; Print Screen 1
        BTFSC   LCD_SCREEN_SELECT, 2    ; Check LCD Screen Select flag bit
        CALL    LCD_SCREEN_2            ; Print Screen 2
        BTFSC   LCD_SCREEN_SELECT, 3    ; Check LCD Screen Select flag bit
        CALL    LCD_SCREEN_3            ; Print Screen 3a
        BTFSC   LCD_SCREEN_SELECT, 4    ; Check LCD Screen Select flag bit
        CALL    LCD_SCREEN_3            ; Print Screen 3b
        BTFSC   LCD_SCREEN_SELECT, 5    ; Check LCD Screen Select flag bit
        CALL    LCD_SCREEN_3            ; Print Screen 3c
        BTFSC   LCD_SCREEN_SELECT, 6    ; Check LCD Screen Select flag bit
        CALL    LCD_SCREEN_4            ; Print Screen 4
        RETURN


LCD_SCREEN_1
  ; Clear LCD & wipe RAM
        MOVLW   b'00000001'             ; LCD 'Clear Display' Command
        CALL    LCD_WRITE               ; Execute command
        CALL    CLEAR_LCD_DISPLAY_RAM   ; Set all Display Character RAM to blank (Ascii charater ' ')
  ; Effective     
        MOVLW   LCD_DISPLAY_R1C4        ; Set position on the LCD to start printing text,
        MOVWF   FSR                     ;
        MOVLW   D'0'                    ; and at which point of lookup table to print from
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; Delay to give line by line scroll effect
        CALL    DELAY_100mS             ;
        CALL    DELAY_100mS             ;
        CALL    DELAY_100mS             ;
  ; WATER TEMP     
        MOVLW   LCD_DISPLAY_R3C4        ;
        MOVWF   FSR                     ;
        MOVLW   D'12'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; CONTROLLER     
        MOVLW   LCD_DISPLAY_R4C4        ;
        MOVWF   FSR                     ;
        MOVLW   D'24'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ;
        CALL    DELAY_1S
        RETURN

      
      
LCD_SCREEN_2
  ; Clear LCD & wipe RAM
        MOVLW   b'00000001'             ;
        CALL    LCD_WRITE               ;
        CALL    CLEAR_LCD_DISPLAY_RAM   ;
  ; Temperatures:     
        MOVLW   LCD_DISPLAY_R1C1        ;
        MOVWF   FSR                     ;
        MOVLW   D'36'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; Coolant
        MOVLW   LCD_DISPLAY_R2C3        ;
        MOVWF   FSR                     ;
        MOVLW   D'51'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; (Celcius Charater)     
        MOVLW   LCD_DISPLAY_R2C16       ;
        MOVWF   FSR                     ;
        MOVLW   D'80'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ; 
  ; Column
        MOVLW   LCD_DISPLAY_R3C3        ;
        MOVWF   FSR                     ;
        MOVLW   D'60'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; (Celcius Charater)     
        MOVLW   LCD_DISPLAY_R3C16       ;
        MOVWF   FSR                     ;
        MOVLW   D'80'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ; 
  ; Pump Speed
        MOVLW   LCD_DISPLAY_R4C1        ;
        MOVWF   FSR                     ;
        MOVLW   D'68'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; %     
        MOVLW   LCD_DISPLAY_R4C16       ;
        MOVWF   FSR                     ;
        MOVLW   D'83'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;       

        RETURN

      
      
LCD_SCREEN_3
  ; Clear LCD & wipe RAM
        MOVLW   b'00000001'             ;
        CALL    LCD_WRITE               ;
        CALL    CLEAR_LCD_DISPLAY_RAM   ;
  ; Settings:     
        MOVLW   LCD_DISPLAY_R1C1        ;
        MOVWF   FSR                     ;
        MOVLW   D'86'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; Coolant
        MOVLW   LCD_DISPLAY_R2C3        ;
        MOVWF   FSR                     ;
        MOVLW   D'51'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; (Celcius Charater)     
        MOVLW   LCD_DISPLAY_R2C16       ;
        MOVWF   FSR                     ;
        MOVLW   D'80'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ; 
  ; Column
        MOVLW   LCD_DISPLAY_R3C3        ;
        MOVWF   FSR                     ;
        MOVLW   D'60'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; (Celcius Charater)     
        MOVLW   LCD_DISPLAY_R3C16       ;
        MOVWF   FSR                     ;
        MOVLW   D'80'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ; 
  ; Pump Min
        MOVLW   LCD_DISPLAY_R4C3        ;
        MOVWF   FSR                     ;
        MOVLW   D'97'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; %     
        MOVLW   LCD_DISPLAY_R4C16       ;
        MOVWF   FSR                     ;
        MOVLW   D'83'                   ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;             

  ; Display Coolant Set Point
  ; Rearrange Temperature data and convert to BCD   
        MOVF    COOLANT_SET_L, W        ;
        MOVWF   DS18B20_DATA_ADJUST_L   ;
        MOVF    COOLANT_SET_H, W        ;
        MOVWF   DS18B20_DATA_ADJUST_H   ;   
        CALL    DS18B20_DATA_ADJUST     ;
      
        MOVLW   LCD_DISPLAY_R2C11       ; Initialise pointer to RAM
        MOVWF   FSR                     ; (File Select Register)
  ; Convert to BCD result to ASCII charaters     
        CALL    BCD2ASCII 
 
  ; Display Column Set Point 
  ; Rearrange Temperature data and convert to BCD   
        MOVF    COLUMN_SET_L, W         ;
        MOVWF   DS18B20_DATA_ADJUST_L   ;
        MOVF    COLUMN_SET_H, W         ;
        MOVWF   DS18B20_DATA_ADJUST_H   ;   
        CALL    DS18B20_DATA_ADJUST     ;
      
        MOVLW   LCD_DISPLAY_R3C11       ; Initialise pointer to RAM
        MOVWF   FSR                     ; (File Select Register)
  ; Convert to BCD result to ASCII charaters     
        CALL    BCD2ASCII 
 
  ; Display Column Set Point 
        CALL    DISPLAY_PWM_DC 
    
  ; Place arrow next to currently active setting
        BSF     STATUS, RP0             ; Bank 1
        MOVLW   B'01111110'             ; Right Arrow Character
        BTFSC   LCD_SCREEN_SELECT, 3    ; GPR in universal bank
        MOVWF   LCD_DISPLAY_R2C2        ; GPR in bank 2
        BTFSC   LCD_SCREEN_SELECT, 4    ;
        MOVWF   LCD_DISPLAY_R3C2        ;
        BTFSC   LCD_SCREEN_SELECT, 5    ;
        MOVWF   LCD_DISPLAY_R4C2        ;
      
        MOVLW   A' '                    ; Blank Character
        BTFSS   LCD_SCREEN_SELECT, 3    ; GPR in universal bank
        MOVWF   LCD_DISPLAY_R2C2        ; GPR in bank 2
        BTFSS   LCD_SCREEN_SELECT, 4    ;
        MOVWF   LCD_DISPLAY_R3C2        ;
        BTFSS   LCD_SCREEN_SELECT, 5    ;
        MOVWF   LCD_DISPLAY_R4C2        ;
        BCF     STATUS, RP0             ; Bank 0
                
        RETURN
      
      
      
  
      
LCD_SCREEN_4
  ; Clear LCD & wipe RAM
        MOVLW   b'00000001'             ;
        CALL    LCD_WRITE               ;
        CALL    CLEAR_LCD_DISPLAY_RAM   ;
  ; Control:     
        MOVLW   LCD_DISPLAY_R1C1        ;
        MOVWF   FSR                     ;
        MOVLW   D'107'                  ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; Manual     
        MOVLW   LCD_DISPLAY_R2C3        ;
        MOVWF   FSR                     ;
        MOVLW   D'117'                  ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;
  ; Austomatic     
        MOVLW   LCD_DISPLAY_R3C3        ;
        MOVWF   FSR                     ;
        MOVLW   D'125'                  ;
        MOVWF   LOOKUP_TABLE_POINTER    ;
        CALL    LOOKUP_TABLE_LOOP       ;       

        RETURN

So, for example, in my main code, I do this.
Code:
  ; Set LCD Screen with screen 1     
  ; This method of setting a flag instead of just calling the 'LCD_SCREEN_1' routine is used so
  ; various pushbutton functions can be differentiated depending on which screen is active.
        MOVLW   B'00000010'             ; Set Screen 1 Flag
        CALL    LCD_PRINT_ACTIVE_SCREEN
      
  ; Set LCD Screen with screen 2
        MOVLW   B'00000100'             ; Set Screen 2 Flag
        CALL    LCD_PRINT_ACTIVE_SCREEN

And then when I want to print something to the LCD, such as temperatures on the temperatures screen, i do this:
Preload the LCD character RAM placeholder into the FSR, call BCD2ASCII where it converts the temperature data from BCD and places the ASCII equivelent into the RAM placeholders stating at RAM address "LCD_DISPLAY_R4C11"
Code:
  ; BCD2ASCII Pre call instructions
        MOVLW   LCD_DISPLAY_R4C11       ; Initialise pointer to RAM
        MOVWF   FSR                     ; (File Select Register)

        CALL    BCD2ASCII

Or, if i simply wanted to print a 'H' on the 3rd line in the first column, I could do this and the interrupt and update routines take care of the rest.
Code:
        BSF     STATUS, RP0             ; Bank 0
        MOVLW   A'H'                    ; Letter 'H' Character
        MOVWF   LCD_DISPLAY_R3C1        ; RAM in bank 1
        BCF     STATUS, RP0             ; Bank 0
So, what do you think?
Is this a good way of driving an LCD?
How do-you/would-you do it?
 

Mike - K8LH

Well-Known Member
Greetings, Jake:

I played with this several years ago. I was trying to simplify writing to the LCD and eliminate the ~50-us LCD inter-write delay by writing to the display in my ISR. Basically, I would write one character to the display from a buffer during each 250-us (500 cycle) interrupt for a ~117-Hz refresh rate on my 2x16 display. The method works well but there's no way to use the LCD 'cursor' so if you need a blinking cursor you have to "roll your own". Anyway, check out the code and video...

Have fun... Cheerful regards, Mike

Code:
/********************************************************************
 *                                                                  *
 *  Project: 12F1822 LCD Buffer Experiment                          *
 *   Source: 12F1822_LCD_Buffer_Experiment.c                        *
 *   Author: Mike McLaren, K8LH                                     *
 *  (C)2012: Micro Application Consultants, All Rights Reserved     *
 *     Date: 26-Jun-12                                              *
 *  Revised: 26-Jun-12                                              *
 *                                                                  *
 *  12F1822 Interrupt Driven Buffered LCD Experiment                *
 *                                                                  *
 *                                                                  *
 *      IDE: MPLAB 8.84 (tabs = 4)                                  *
 *     Lang: SourceBoost BoostC v7.05, Lite/Free version            *
 *                                                                  *
 ********************************************************************/

   #include <system.h>

   #pragma DATA _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _MCLRE_OFF
   #pragma DATA _CONFIG2, _LVP_OFF & _PLLEN_OFF

   #pragma CLOCK_FREQ 8000000   // 8-MHz INTOSC

  /******************************************************************
   *  function prototypes                                           *
   ******************************************************************/
  /******************************************************************
   *  type definitions                                              *
   ******************************************************************/

   typedef unsigned char u08;
   typedef unsigned int u16;

   #define r08 rom unsigned char

  /******************************************************************
   *  variables and constants                                       *
   ******************************************************************/

      char temp;                //
      char count = 0;           //
      char huns;                //
      char tens;                //
      char ones;                //

      char lcd[33] = { "        HH:MM:SS                " };

      #define line1 0
      #define line2 16

      #define D4 0              // RA0
      #define D5 1              // RA1
      #define D6 2              // RA2
      #define D7 4              // RA4
      #define E  5              // RA5
      #define RS 0              // RA0(RC output)

  /*
   *  assembly language helpers
   */
      #define clrc  bcf   _status,C
      #define setc  bsf   _status,C
      #define skpz  btfss _status,Z
      #define skpnz btfsc _status,Z
      #define skpc  btfss _status,C
      #define skpnc btfsc _status,C

  /******************************************************************
   *  functions                                                     *
   ******************************************************************/

   void PutNyb()                //
   { asm movlw   1<<E           //
     asm btfsc   _temp,0        // b0 = 0?  yes, skip, else
     asm iorlw   1<<D4          //
     asm btfsc   _temp,1        // b1 = 0?  yes, skip, else
     asm iorlw   1<<D5          //
     asm btfsc   _temp,2        // b2 = 0?  yes, skip, else
     asm iorlw   1<<D6          //
     asm btfsc   _temp,3        // b3 = 0?  yes, skip, else
     asm iorlw   1<<D7          //
     asm movwf   _porta         // LCD D4..D7 = data1, E = 1
     asm bcf     _porta,E       // LCD E = 0
   }                            //

   void PutLCD()
   { asm movwf   _temp          // save WREG data byte
     asm swapf   _temp,F        //
     PutNyb();                  //
     asm swapf   _temp,F        //
     PutNyb();                  //
   }                            //

   void PutCMD(char pdata)      //
   { porta.RS = 0; PutLCD();    //
   }                            //

   void PutDAT(char pdata)      //
   { porta.RS = 1; PutLCD();    //
   }                            //


  /******************************************************************
   *  main init                                                     *
   ******************************************************************/

   void main()
   { ansela = 0;                // make pins digital
     trisa = 0b00000000;        // porta all outputs
     porta = 0;                 // all output latches low
     osccon = 0b01110010;       // initialize 8-MHz INTOSC
     while(!oscstat.HFIOFS);    // wait until OSC stable

    /*                                                              *
     *  HD44780 4-bit "initialize by instruction" procedure         *
     *                                                              */
     delay_ms(100);             //
     porta.RS = 0;              // RS=0
     temp = 0x03;               // hi nibble of "8-bit" command
     PutNyb(); delay_ms(4);     // step 1 (send nibble only)
     PutNyb(); delay_us(160);   // step 2 (send nibble only)
     PutNyb(); delay_us(160);   // step 3 (send nibble only)
     temp = 0x02;               // hi nibble of "4-bit" command
     PutNyb(); delay_us(160);   // select 4-bit interface mode

     PutCMD(0x28);              // 4-bit, 2-lines, 5x7 font
     delay_us(50);              //
     PutCMD(0x0C);              // display on, currsor & blink off
     delay_us(50);              //
     PutCMD(0x06);              // cursor inc, shift off
     delay_us(50);              //
     PutCMD(0x01);              // clear display
     delay_ms(2);               // required delay

    /*                                                              *
     *  setup TMR2 for 250-usec interrupts (8-MHz clock)            *
     *                                                              */
     tmr2 = 0;                  //
     t2con = 0b00000001;        // 00000001
                                // -0000--- TOUTPS<3:0>, postscale 1
                                // -----0-- TMR2ON, timer off
                                // ------01 T2CKPS<1:0>, prescale 4
                                // produces 2-usec 'ticks'
     pr2 = 125-1;               // 125 'ticks' = 250-usec interrupts
     pie1 = 1<<TMR2IE;          // enable Timer 2 interrupts
     pir1 = 0;                  // clear peripheral interrupt flags
     intcon.PEIE = 1;           // enable peripheral interrupts
     intcon.GIE = 1;            // enable global interrupts
     t2con.TMR2ON = 1;          // turn TMR2 on  

    /****************************************************************
     *  main loop                                                   *
     ****************************************************************/

     while(1)                   //
     {                          //
       delay_ms(250);           //
       delay_ms(250);           //

       lcd[line1+10] ^= (':'^' ');
       lcd[line1+13] ^= (':'^' ');

       delay_ms(250);           //

       count++;                 //
       huns = count/100;        //
       tens = (count/10)%10;    //
       ones = count%10;         //
      /*
       *  updating the display takes 9 cycles (4.5 us)
       */
       lcd[line1+2] = huns|'0'; //
       lcd[line1+3] = tens|'0'; //
       lcd[line1+4] = ones|'0'; //

       delay_ms(240);           //

       lcd[line1+10] ^= (':'^' ');
       lcd[line1+13] ^= (':'^' ');
     }                          //
   }

  /******************************************************************
   *                                                                *
   ******************************************************************/

   void interrupt()             // 250 usec (500 cycle) interrupts
   { static char line = 0xC0;   // sync buffer index & DDRAM address
     static char bndx = 0;      //  "

     pir1.TMR2IF = 0;           // clear TMR2 interrupt flag

    /*                                                              *
     *  write 32 data characters and two "DDRAM address" commands,  *
     *  one write per 250-usec interrupt, to refresh the buffered   *
     *  display once every 8.5-msecs (~117-Hz).                     *
     *                                                              */

     if(line & 0x80)            // if "new line"
       PutCMD(line ^= 0x40);    // set DDRAM address (0x80 or 0xC0)
     else                       // not "new line" so
       PutDAT(lcd[bndx++]);     // refresh display, bump index
  // if(bndx == 80)             // for 4x20 display
  //   bndx = 0;                // 
  // if(bndx % 40 == 0)         //
  //   line ^= 0x80             // toggle b7 pseudo "new line" flag
     bndx &= 31;                // pseudo %32, 0..31 inclusive
     if((bndx & 15) == 0)       // if new line (0 or 16)
       line ^= 0x80;            // toggle b7 pseudo "new line" flag
   }
 
Hey Mike!
Sorry, I can't read C. But I sort of understand from your worded description.
And by trying to read your C code and reading the line descriptions it looks very similar to what I'm doing except instead of refreshing the display completely in the interrupt, you have a shorter interrupt cycle where you update each character 1 by 1.

So your buffer, are you using a ram byte for each character?
Is that what this is?
Code:
char lcd[33] = { "       HH:MM:SS               " };
 
Arr cool. Well, our versions are quite similar then, all bar the interrupt.
Onto my next question then. I'll start a new thread as it's regarding a different topic.
 

Rich D.

Active Member
That's the same method I've used on several projects. At the time I didn't realize I couldn't use the automatic cursor on the LCD. Instead I used a "<" character next to the desired cursor position, but that wasn't ideal.

If I had to do it again I would code it basically the same way, but instead of consistent updates every fraction of a second, I would have the routine that writes out the data to the LCD check a flag for any updates first. If no updates have been made to the character data and the update flag was not set, the update would not occur, allowing for the cursor to flash. Since my application doesn't require constant updates, the cursor has plenty of time to flash while the user is doing other things. My projects were done in assembly. The LCD refresh routine was not interrupt driven since there were more important timing uses for the interrupts, but done once every N-number of loops of the main code. Works well except for the cursor is a bit odd.
 
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top