Path: Home => AVR-EN => Applications => LCD on an AVR => include file
LCD small AVR applications

LCD control with an AVR in assembler

Include file "lcd.inc"
Logo

Include file for accessing an LCD with an AVR

The assembler source code, as shown here, can be downloaded here in source format.

;
; *********************************
; * LCD include routines          *
; * (C)2018 avr-asm-tutorial.net  *
; *********************************
;
; ***********************************************
; *  L C D   I N T E R F A C E  R O U T I N E S *
; ***********************************************
;
; +-------+----------------+--------------+
; |Routine|Function        |Parameters    |
; +-------+----------------+--------------+
; |LcdInit|Inits the LCD   |Ports, pins   |
; |       |in the desired  |              |
; |       |mode            |              |
; +-------+----------------+--------------+
; |LcdText|Displays the    |Z=2*Table     |
; |       |text in flash   |  address     |
; |       |memory starting |0x0D: Next    |
; |       |with line 1     |      line    |
; |       |                |0xFF: Ignore  |
; |       |                |0xFE: Text end|
; +-------+----------------+--------------+
; |LcdSram|Display the text|Z=SRAM-Address|
; |       |in SRAM         |R16: number of|
; |       |                |    characters|
; +-------+----------------+--------------+
; |LcdChar|Display charac- |R16: Character|
; |       |ter on LCD      |              |
; +-------+----------------+--------------+
; |LcdCtrl|Output control  |R16: Control  |
; |       |byte to LCD     |     byte     |
; +-------+----------------+--------------+
; |LcdPos |Set position on |ZH: Line 0123 |
; |       |the LCD         |ZL: Col 0..   |
; +-------+----------------+--------------+
; |LcdSpec|Generate special|Z: 2*Table    |
; |       |characters      |   address    |
; +-------+----------------+--------------+
; |    S W I T C H   L C D D E C I M A L  |
; +-------+----------------+--------------+
; |LcdDec2|Convert to two  |R16: Binary   |
; |       |decimal digits  |     8 bit    |
; +-------+----------------+--------------+
; |LcdDec3|Convert to three|R16: Binary   |
; |       |decimal digits, |     8 bit    |
; |       |supp. leading 0s|              |
; +-------+----------------+--------------+
; |LcdDec5|Convert to five |Z: Binary     |
; |       |decimal digits, |   16 bit     |
; |       |supp. leading 0s|              |
; +-------+----------------+--------------+
; |      S W I T C H   L C D H E X        |
; +-------+----------------+--------------+
; |LcdHex2|Convert to two  |R16: Binary   |
; |       |digits in hex   |     8 bit    |
; +-------+----------------+--------------+
; |LcdHex4|Convert to four |Z: Binary     |
; |       |digits in hex   |   16 bit     |
; +-------+----------------+--------------+
;
; *************************************
;  P A R A M E T E R - T E M P L A T E
; *************************************
;
; (Copy to your source code, remove ; and adjust
;  parameters to fit your hardware)
;
; Standard parameter set of properties/definitions
;.equ clock = 1000000 ; Clock frequency of controller in Hz
; LCD size:
  ;.equ LcdLines = 1 ; Number of lines (1, 2, 4)
  ;.equ LcdCols = 8 ; Number of characters per line (8..24)
; LCD bus interface
  ;.equ LcdBits = 4 ; Bus size (4 or 8)
  ; If 4 bit bus:
    ;.equ Lcd4High = 1 ; Bus nibble (1=Upper, 0=Lower)
  ;.equ LcdWait = 0 ; Access mode (0 with busy, 1 with delay loops)
; LCD data ports
  ;.equ pLcdDO = PORTA ; Data output port
  ;.equ pLcdDD = DDRA ; Data direction port
; LCD control ports und pins
  ;.equ pLcdCEO = PORTB ; Control E output port
  ;.equ bLcdCEO = PORTB0 ; Control E output portpin
  ;.equ pLcdCED = DDRB ; Control E direction port
  ;.equ bLcdCED = DDB0 ; Control E direction portpin
  ;.equ pLcdCRSO = PORTB ; Control RS output port
  ;.equ bLcdCRSO = PORTB1 ; Control RS output portpin
  ;.equ pLcdCRSD = DDRB ; Control RS direction port
  ;.equ bLcdCRSD = DDB1 ; Control RS direction portpin
; If LcdWait = 0:
  ;.equ pLcdDI = PINA ; Data input port
  ;.equ pLcdCRWO = PORTB ; Control RW output port
  ;.equ bLcdCRWO = PORTB2 ; Control RW output portpin
  ;.equ pLcdCRWD = DDRB ; Control RW direction port
  ;.equ bLcdCRWD = DDB2 ; Control RW direction portpin
; If you need binary to decimal conversion:
  ;.equ LcdDecimal = 1 ; If defined: include those routines
; If you need binary to hexadecimal conversion:
  ;.equ LcdHex = 1 ; If defined: include those routines
; If simulation in the SRAM is desired:
  ;.equ avr_sim = 1 ; 1=Simulate, 0 or undefined=Do not simulate
;
; *****************************************
;      T E X T   T E M P L A T E S
; *****************************************
;
; Tables to copy for diverse sizes
;
; --------------------------
; Single line LCD
;   8 chars per line
; Text_1_8:
;   .db "        ",0xFE,0xFF
;        01234567
;
;   16 chars per line
; Text_1_16:
;   .db "                ",0xFE,0xFF
;        0123456789012345
;
;   20 chars per line
; Text_1_20:
;   .db "                    ",0xFE,0xFF
;        01234567890123456789
;
;   24 chars per line
; Text_1_24:
;   .db "                        ",0xFE,0xFF
;        012345678901234567890123
;
; --------------------------
; Two line LCD
;   16 chars per line
; Text_2_16:
;   .db "                ",0x0D,0xFF
;   .db "                ",0xFE,0xFF
;        0123456789012345
;
;   20 chars per line
; Text_2_20:
;   .db "                    ",0x0D,0xFF
;   .db "                    ",0xFE,0xFF
;        01234567890123456789
;
;   24 chars per line
; Text_2_24:
;   .db "                        ",0x0D,0xFF
;   .db "                        ",0xFE,0xFF
;        012345678901234567890123
;
; --------------------------
; Four line LCD
;   16 chars per line
; Text_4_16:
;   .db "                ",0x0D,0xFF
;   .db "                ",0x0D,0xFF
;   .db "                ",0x0D,0xFF
;   .db "                ",0xFE,0xFF
;        0123456789012345
;
;   20 chars per line
; Text_4_20:
;   .db "                    ",0x0D,0xFF
;   .db "                    ",0x0D,0xFF
;   .db "                    ",0x0D,0xFF
;   .db "                    ",0xFE,0xFF
;        01234567890123456789
;
;   24 chars per line
; Text_4_24:
;   .db "                        ",0x0D,0xFF
;   .db "                        ",0x0D,0xFF
;   .db "                        ",0x0D,0xFF
;   .db "                        ",0xFE,0xFF
;        012345678901234567890123
;
; *******************************
;  P A R A M E T E R   C H E C K
; *******************************
;
; Are all parameters correct?
;
; Size defined?
.ifndef LcdLines
  .error "LCD line size (LcdLines) undefined!"
  .else
  .if (LcdLines!=1)&&(LcdLines!=2)&&(LcdLines!=4)
    .error "LCD illegal line size (LcdLines)!"
    .endif
  .endif
.ifndef LcdCols
  .error "LCD column size (LcdCols) undefined!"
  else
  .if (LcdCols<8)||(LcdCols>24)
    .error "LCD illegal column size (LcdCols)!"
    .endif
  .endif
;
; Clock defined?
.ifndef clock
  .error "Clock frequency (clock) undefined!"
  .endif
;
; 4- or 8-bit interface selected?
.ifndef LcdBits
  .error "LCD data bus bits (LcdBits) undefined!"
  .else
  .if (LcdBits != 4) && (LcdBits != 8)
    .error "LCD data bus bits (LcdBits) not 4 or 8!"
    .endif
  .if LcdBits == 4
    .ifndef Lcd4High
      .error "LCD 4 bit data bus nibble (Lcd4High) undefined!"
      .else
      .if (Lcd4High != 0) && (Lcd4High != 1)
        .error "LCD 4 bit data bus nibble (Lcd4High) not 0 or 1!"
        .endif
      .endif
    .endif
  .endif
;
; LCD data ports
.ifndef pLcdDO
  .error "LCD data output port (pLcdDO) undefined!"
  .endif
.ifndef pLcdDD
  .error "LCD data direction port (pLcdDD) undefined!"
  .endif
.if LcdWait == 0
  .ifndef pLcdDI
    .error "LCD data input port (pLcdDI) undefined!"
    .endif
  .endif
;
; LCD control ports und pins
.ifndef pLcdCEO
  .error "LCD control E output port (pLcdCEO) undefined!"
  .endif
.ifndef pLcdCED
  .error "LCD control E direction port (pLcdCED) undefined!"
  .endif
.ifndef bLcdCEO
  .error "LCD control E output pin (bLcdCEO) undefined!"
  .endif
.ifndef bLcdCED
  .error "LCD control E direction pin (bLcdCED) undefined!"
  .endif
.ifndef pLcdCRSO
  .error "LCD control RS output port (pLcdCRSO) undefined!"
  .endif
.ifndef pLcdCRSD
  .error "LCD control RS direction port (pLcdCRSD) undefined!"
  .endif
.ifndef bLcdCRSO
  .error "LCD control RS output pin (bLcdCRSO) undefined!"
  .endif
.ifndef bLcdCRSD
  .error "LCD control RS direction pin (bLcdCRSD) undefined!"
  .endif
.ifndef LcdWait
  .error "LCD operating property (LcdWait) undefined!"
  .else
  .if LcdWait == 0
    .ifndef pLcdCRWO
      .error "LCD control RW output port (pLcdCRWO) undefined!"
      .endif
    .ifndef bLcdCRWO
      .error "LCD control RW output pin (bLcdCRWO) undefined!"
      .endif
    .ifndef pLcdCRWD
      .error "LCD control RW direction port (pLcdCRWD) undefined!"
      .endif
    .ifndef bLcdCRWD
      .error "LCD control RW direction pin (bLcdCRWD) undefined!"
      .endif
    .endif
  .endif
;
; *************************************
;  S I M U L A T I O N   A V R _ S I M
; *************************************
;
; Check if simulation desired
.ifdef avr_sim
  .equ simulation = avr_sim
  .else
  .equ simulation = 0
  .endif
.if simulation == 1
  .dseg
  SimStart:
  SimDisplayPos:
    .byte 1
  SimCtrlClear:
    .byte 1
  SimCtrlReset:
    .byte 1
  SimCtrlInputmode:
    .byte 1
  SimCtrlDisplay:
    .byte 1
  SimCtrlCursorShift:
    .byte 1
  SimCtrlFunctionset:
    .byte 1
  SimCtrlCharGenRamAdr:
    .byte 1
  SimCtrlDisplayRamAdr:
    .byte 1
  SimDataDisplay:
    .byte LcdLines*LcdCols
  SimEnd:
  .cseg
  .endif
;
; *********************************
;     L C D   R O U T I N E S
; *********************************
;
; LcdInit: Init LCD ports and pins
;          Wait cycle for LCD start-up
;          Function set
;          Clear LCD
LcdInit:
  ; Init the LCD control bits
  cbi pLcdCEO,bLcdCEO ; E pin low
  sbi pLcdCED,bLcdCED ; E pin output
  cbi pLcdCRSO,bLcdCRSO ; RS pin low
  sbi pLcdCRSD,bLcdCRSD ; RS pin output
  .ifdef pLcdCRWO
    .ifdef bLcdCRWO
      cbi pLcdCRWO,bLcdCRWO ; RW pin low
      .endif
    .endif
  .ifdef pLcdCRWD
    .ifdef pLcdCRWD
      sbi pLcdCRWD,bLcdCRWD ; RW pin output
      .endif
    .endif
  ; Init the LCD data bus ports
  .if LcdBits == 8
    clr R16 ; Data bus to low
    .else
    in R16,pLcdDO ; Read output bits data bus
    .if Lcd4High == 1
      andi R16,0x0F ; Clear upper nibble
      .else
      andi R16,0xF0 ; Clear lower nibble
      .endif
    .endif
  out pLcdDO,R16 ; Data bus output clear
  .if LcdBits == 8
    ldi R16,0xFF ; Set all direction bits high
    .else
    in R16,pLcdDD ; Read direction bits data bus
    .if Lcd4High == 1
      ori R16,0xF0 ; Set upper nibble
      .else
      ori R16,0x0F ; Set lower nibble
      .endif
    .endif
  out pLcdDD,R16 ; Set direction bits data bus
  ; LCD-Startphase
  .if simulation == 0
    rcall LcdWait50ms
    .endif
  ; LCD to 8 bit data bus
  ldi R16,0x30
  rcall Lcd8Ctrl ; In 8 bit mode to LCD control
  .if simulation == 0
    rcall LcdWait5ms ; Wait for 5 ms
    .endif
  ldi R16,0x30
  rcall Lcd8Ctrl ; In 8 bit mode to LCD control
  .if simulation == 0
    rcall LcdWait5ms ; Wait for 5 ms
    .endif
  ldi R16,0x30
  rcall Lcd8Ctrl ; In 8 bit mode to LCD control
  .if simulation == 0
    rcall LcdWait5ms ; Wait for 5 ms
    .endif
  ldi R16,0x30
  rcall Lcd8Ctrl ; In 8 bit mode to LCD control
  .if simulation == 0
    rcall LcdWait5ms ; Wait for 5 ms
    .endif
  ; If 4 bit data bus interface: switch to 4 bit mode
  .if LcdBits == 4
    ldi R16,0x20 ; 4 bit interface
    rcall Lcd8Ctrl ; In 8 bit mode to LCD control
    .if simulation == 0
      rcall LcdWait5ms
      .endif
    .endif
  ; Function set
  .if LcdBits == 8
    ldi R16,0x30 ; 8 bit data bus
    .else
    ldi R16,0x20 ; 4 bit data bus
    .endif
  .if LcdLines > 1
     ori R16,0x08 ; LCDs with more than one line
    .endif
  rcall LcdCtrl
  ; Display mode
  ldi R16,0x0C ; Display on, underline off, cursor blink off
  rcall LcdCtrl
  ; LCD entry mode set
  ldi R16,0x06 ; Cursor right, cursor move not display shift
  rcall LcdCtrl
  ; Clear LCD
  ldi R16,0x01 ; LCD clear
  rjmp LcdCtrl
;
; LcdText
;   Displays the text in flash memory on the LCD
;     Z points to 2*Text table address
;     0x0D: Line feed and carriage return
;     0xFF: Ignore (fill character, ignored)
;     0xFE: End of the text
LcdText:
  push R0 ; Save R0
  ldi R16,LcdLines ; Maximum line number
  mov R0,R16 ; to R0
LcdText1:
  lpm R16,Z+ ; Read next char from flash
  cpi R16,0xFE ; End of text?
  breq LcdText3 ; Yes
  brcc LcdText1 ; Ignore fill characters
  cpi R16,0x0D ; Line feed and carriage return?
  brne LcdText2 ; No
  dec R0 ; Decrease line counter
  breq LcdText1 ; If zero, continue
  .if simulation == 1
    ; CR/LF in SRAM memory
    push ZH ; Save Z
    push ZL
    push R16 ; Save character
    ldi ZH,High(SimDataDisplay) ; Perform newline
    ldi ZL,Low(SimDataDisplay)
    ldi R16,LcdLines
    LcdText1a:
      adiw ZL,LcdCols
      dec R16
      cp R16,R0
      brne LcdText1a
    ldi R16,Low(SimDataDisplay)
    sub ZL,R16
    ldi R16,High(SimDataDisplay)
    sbc ZH,R16
    sts SimDisplayPos,ZL
    pop R16 ; Restore character
    pop ZL ; Restore Z
    pop ZH
    .endif
  ; CR/LF on LCD
  push ZH ; Save Z
  push ZL
  ldi ZH,LcdLines ; Calculate line
  sub ZH,R0
  clr ZL ; To line start
  rcall LcdPos ; Set LCD position
  pop ZL ; Restore Z
  pop ZH
  rjmp LcdText1 ; Next character in flash
LcdText2:
  rcall LcdChar ; Character in R16 to LCD
  rjmp LcdText1 ; Next character
LcdText3:
  pop R0 ; Restore R0
  ret
;
; LcdSRam displays text in SRAM at the current LCD position
;   Z points to SRAM address
;   R16: Number of characters
LcdSRam:
  push R16 ; Save R16
  ld R16,Z+ ; Read character
  rcall LcdChar ; Display on LCD
  pop R16 ; Restore R16
  dec R16 ; Downcount number of characters
  brne LcdSRam ; Further characters to display
  ret
;
; LcdChar displays character in R16 at the current position
;   R16: Character
LcdChar:
.if simulation == 1
  ; Simulation, write character to SRAM
  push ZH ; Save Z
  push ZL
  push R16 ; Save character
  ldi ZH,High(SimDataDisplay) ; SRAM position
  ldi ZL,Low(SimDataDisplay)
  lds R16,SimDisplayPos
  inc R16
  sts SimDisplayPos,R16
  dec R16
  add ZL,R16
  ldi R16,0
  adc ZH,R16
  pop R16
  st Z,R16 ; Write to SRAM
  pop ZL
  pop ZH
  .endif
  rjmp LcdData ; Write as data to LCD
;
; LcdCtrl writes control byte in R16 on the LCD
LcdCtrl:
.if simulation == 1
    cpi R16,0x80 ; Display RAM address write?
    brcs LcdCtrlSim1
    sts SimCtrlDisplayRamAdr,R16
    push ZH
    push ZL
    mov ZH,R16
    andi R16,0x40
    brne LcdCtrlSim0a
    clr ZL ; Line 1 or 3
    rjmp LcdCtrlSim0b
  LcdCtrlSim0a:
    ldi ZL,LcdCols ; Line 2 or 4
  LcdCtrlSim0b:
    mov R16,ZH
    andi R16,0x3F
    subi R16,LcdCols
    brcc LcdCtrlSim0c ; Line 3 or 4
    subi R16,-LcdCols
    rjmp LcdCtrlSim0d
  LcdCtrlSim0c:
    sbrc ZH,6 ; Line 4?
    subi R16,-LcdCols ; Add column length
  LcdCtrlSim0d:
    add ZL,R16
    sts SimDisplayPos,ZL
    pop ZL
    pop ZH
    rjmp LcdCtrlSim9
  LcdCtrlSim1:
    cpi R16,0x40 ; Character generator RAM address set?
    brcs LcdCtrlSim2
    sts SimCtrlCharGenRamAdr, R16
    rjmp LcdCtrlSim9
  LcdCtrlSim2:
    cpi R16,0x20 ; Function set?
    brcs LcdCtrlSim3
    sts SimCtrlFunctionSet, R16
    rjmp LcdCtrlSim9
  LcdCtrlSim3:
    cpi R16,0x10 ; Cursor shift?
    brcs LcdCtrlSim4
    sts SimCtrlCursorShift, R16
    rjmp LcdCtrlSim9
  LcdCtrlSim4:
    cpi R16,0x08 ; Display control set?
    brcs LcdCtrlSim5
    sts SimCtrlDisplay, R16
    rjmp LcdCtrlSim9
  LcdCtrlSim5:
    cpi R16,0x04 ; Display control?
    brcs LcdCtrlSim6
    sts SimCtrlInputmode, R16
    rjmp LcdCtrlSim9
  LcdCtrlSim6:
    cpi R16,0x02 ; Reset?
    brcs LcdCtrlSim7
    sts SimCtrlReset,R16
    rjmp LcdCtrlSim9
  LcdCtrlSim7:
    cpi R16,0x01 ; Clear?
    brcs LcdCtrlSim8
    sts SimCtrlClear,R16
    ; LCD clear display
    push ZH
    push ZL
    ldi ZH,High(SimEnd)
    ldi ZL,Low(SimEnd)
    clr R16
  LcdCtrl7a:
    st -Z,R16 ; Fill with zeroes backwards
    cpi ZL,Low(SimStart)
    brne LcdCtrl7a
    cpi ZH,High(SimStart)
    brne LcdCtrl7a
    ldi R16,0x01
    pop ZL
    pop ZH
    rjmp LcdCtrlSim9
  LcdCtrlSim8: ; 00 command
  LcdCtrlSim9:
  .endif
  .if LcdWait == 0
    rcall LcdBusy ; Wait for busy flag clear
    .endif
  cbi pLcdCRSO,bLcdCRSO ; RS bit low
  .if LcdBits == 4
    push ZL
    push R16
    in ZL,pLcdDO ; Read data output port
    .if Lcd4High == 1
      andi ZL,0x0F ; Clear upper nibble port
      andi R16,0xF0 ; Clear lower nibble R16
      .else
      andi ZL,0xF0 ; Clear lower nibble port
      swap R16 ; Upper to lower nibble
      andi R16,0x0F ; Clear upper nibble R16
      .endif
    or R16,ZL ; Combine
    out pLcdDO,R16 ; and output
    rcall LcdPulseE ; Activate E
    pop R16 ; R16 restore
    push R16 ; and save again
    in ZL,pLcdDO ; Read data output port
    .if Lcd4High == 1
      andi ZL,0x0F ; Preserve lower nibble of port
      swap R16 ; Lower to upper nibble R16
      andi R16,0xF0 ; Preserve upper nibble R16
      .else
      andi ZL,0xF0 ; Preserve upper nibble of port
      andi R16,0x0F ; Preserve lower nibble R16
      .endif
    out pLcdDO,R16 ; To data bus
    rcall LcdPulseE ; Activate E
    pop R16 ; Restore R16
    pop ZL ; Restore ZL
    .else
    out pLcdDO,R16 ; Input on data bus
    rcall LcdPulseE ; Activate E
    .endif
  .if LcdWait == 1
    andi R16,0xFC ; The six upper bits of control command
    brne LcdCtrl1 ; are not zero, short delay
    rjmp LcdWait1640us ; Wait 1,64 ms
  LcdCtrl1:
    rjmp LcdWait40us ; Wait 40 us
    .else
    ret
    .endif
;
; LcdPos position the LCD cursor to the position in Z
;   ZH: Line (0 to number of lines - 1)
;   ZL: Column (0 to number of colums per line - 1)
LcdPos:
  cpi ZH,1 ; Line = 1?
  ldi R16,0x80 ; Address line 0
  .if LcdLines < 2 ; LCD has only one line
    rjmp LcdPos1
    .endif
  brcs LcdPos1 ; Line 1
  ldi R16,0xC0 ; Line 2 address
  .if LcdLines == 2
    rjmp LcdPos1 ; Lcd has only two lines
    .endif
  breq LcdPos1 ; Line 2 selected
  ldi R16,0x80+LcdCols ; Address line 3
  cpi ZH,2 ; Line = 3
  breq LcdPos1 ; Line 3
  ldi R16,0xC0+LcdCols ; Address line 4
LcdPos1:
  add R16,ZL ; Add column
  rjmp LcdCtrl ; Send as control to LCD
;
; LcdSpec generates special characters on the LCD
;   Z points to 2*Table address
;   Table format:
;     1. byte: Address of character, 0b01zzz000,
;        0: End of the table
;     2. byte: Dummy character, ignored
;     3. to 10 th byte: Pixel data of the lines 1 to 8
LcdSpec:
  push R0 ; R0 is counter
LcdSpec1:
  lpm R16,Z+ ; Read address of character
  tst R16 ; End of the table?
  breq LcdSpec3 ; Yes
  rcall LcdCtrl ; Write address
  adiw ZL,1 ; Overread dummy
  ldi R16,8 ; 8 byte per character
  mov R0,R16 ; R0 is counter
LcdSpec2:
  lpm R16,Z+ ; Read data byte from table
  rcall LcdData ; Output as data byte
  dec R0 ; Count down
  brne LcdSpec2 ; Further data bytes
  rjmp LcdSpec1 ; Next character
LcdSpec3:
  pop R0 ; Restore R0
  ldi ZH,0 ; Cursor to home position
  ldi ZL,0
  rjmp LcdPos
;
; ************************************
;  D E C I M A L  C O N V E R S I O N
; ************************************
;
; Routines for conversion and display decimals
;
.ifdef LcdDecimal
  ;
  ; LcdDec2 converts binary in R16 to a two digit decimal
  ;   without suppression of leading zeroes
  LcdDec2:
    mov ZL,R16 ; Copy number
    ldi R16,'0'-1 ; Tens counter
    cpi ZL,100 ; Larger that two digits?
    brcs LcdDec2a ; No
    ldi ZL,99 ; three digits, limit to max
  LcdDec2a:
    inc R16
    subi ZL,10
    brcc LcdDec2a
    rcall LcdData ; Display tens
    ldi R16,10+'0'
    add R16,ZL
    rcall LcdData ; Display ones
    ret
  ;
  ; LcdDec3 converts binary in R16 to three digits decimal
  ;   blanks leading zeroes
  LcdDec3:
    ldi ZH,1 ; Flag leading zeroes
  LcdDec3null: ; without changing leading zero flag
    push R0 ; Save R0
    mov ZL,R16 ; Binary in ZL
    ldi R16,100 ; Hundreds
    rcall LcdDec3a ; Convert to decimal
    rcall LcdData ; Display digit
    ldi R16,10 ; Tens
    rcall LcdDec3a ; Convert to decimal
    rcall LcdData ; Display digit
    ldi R16,'0'
    add R16,ZL ; Convert ones to ASCII
    rcall LcdData ; Display digit
    pop R0 ; Restore R0
    ret
  ;
  LcdDec3a: ; Calculate decimal digit
    clr R0 ; Counter
    dec R0 ; to - 1
  LcdDec3b:
    inc R0 ; Increase counter
    sub ZL,R16 ; Subtract 100 or 10
    brcc LcdDec3b ; No overflow, continue
    add ZL,R16 ; Recover last subtraction
    tst R0 ; Digit zero?
    breq LcdDec3c ; Yes, check blanking
    clr ZH ; No blanking any more
    ldi R16,'0' ; ASCII-0
    add R16,R0 ; Add counter
    ret
  LcdDec3c:
    tst ZH ; Zero blanking?
    breq LcdDec3d ; No
    ldi R16,' ' ; Blank char
    ret
  LcdDec3d:
    ldi R16,'0' ; Zero char
    ret
  ;
  ; LcdDec5 converts binary Z to five decimal digits
  ;   blank leading zeroes
  LcdDec5:
    push R2 ; Save registers
    push R1
    push R0
    clr R2 ; Leading zeroes
    inc R2 ; Blanking on
    mov R1,ZH ; Copy binary to R1:R0
    mov R0,ZL
    ldi ZH,High(10000) ; First decimal digit
    ldi ZL,Low(10000)
    rcall LcdDec5a ; Convert first digit
    ldi ZH,High(1000) ; Second decimal digit
    ldi ZL,Low(1000)
    rcall LcdDec5a ; Convert second digit
    ldi ZH,High(100) ; Third decimal digit
    ldi ZL,Low(100)
    rcall LcdDec5a ; Convert decimal digit
    ldi ZH,High(10) ; Fourth decimal digit
    ldi ZL,Low(10)
    rcall LcdDec5a ; Convert decimal digit
    ldi R16,'0' ; Convert fifth digit to ASCII
    add R16,R0
    rcall LcdChar ; Display digit
    pop R0 ; Restore registers
    pop R1
    pop R2
    ret
  ;
  LcdDec5a: ; Convert to decimal digit
    ldi R16,'0'-1 ; To ASCII-0 - 1
  LcdDec5b:
    inc R16 ; Increase counter
    sub R0,ZL ; Subtract decimal digit LSB
    sbc R1,ZH ; and MSB with Carry
    brcc LcdDec5b ; Repeat until carry
    add R0,ZL ; Take back last subtraction
    adc R1,ZH
    cpi R16,'0' ; Zero?
    breq LcdDec5c ; Yes, check blanking
    clr R2 ; End blanking
    rjmp LcdChar ; Display char in R16
  LcdDec5c:
    tst R2 ; Blanking active?
    breq LcdDec5d ; No
    ldi R16,' ' ; Blank character
  LcdDec5d:
    rjmp LcdChar ; Display char in R16
  ;
  .endif
;
; ****************************************
;  H E X A D E C I M A L  A U S G E B E N
; ****************************************
;
; Routines for conversion and displaying hexadecimals
;
.ifdef LcdHex
  ;
  ; LcdHex2 converts binary in R16 to hexadecimal
  ;   on the LCD
  LcdHex2:
    push R16 ; Is further needed
    swap R16 ; Upper nibble to lower nibble
    rcall LcdNibble ; Display lower nibble
    pop R16 ; Restore binary
  LcdNibble:
    andi R16,0x0F ; Clear upper nibble
    subi R16,-'0' ; Add ASCII-0
    cpi R16,'9'+1 ; A to F?
    brcs LcdNibble1 ; No
    subi R16,-7 ; Add 7
  LcdNibble1:
    rjmp LcdChar ; Display char on LCD
  ;
  ; LcdHex4 converts binary in Z to hexadecimal
  LcdHex4:
    mov R16,ZH ; MSB to R16
    rcall LcdHex2 ; Display byte hexadecimal
    mov R16,ZL ; LSB to R16
    rjmp LcdHex2 ; Display byte hexadecimal
  ;
  .endif
;
; *******************************
;  L C D   S U B R O U T I N E S
; *******************************
;
; Wait until LCD busy flag clear
;   needed only if wait mode is off
.if LcdWait == 0
  LcdBusy:
    push R16 ; Save R16
    .if LcdBits == 8
      clr R16 ; Data bus direction to input
      .else
      in R16,pLcdDD ; Read direction bits
      .if Lcd4High == 1
        andi R16,0x0F ; Clear upper nibble
        .else
        andi R16,0xF0 ; Clear lower nibble
        .endif
      .endif
    out pLcdDD,R16 ; Write to direction port
    .if LcdBits == 8
      clr R16 ; All output pins to low
      .else
      in R16,pLcdDO ; Read output port
      .if Lcd4High == 1
        andi R16,0x0F ; Clear upper nibble
        .else
        andi R16,0xF0 ; Clear lower nibble
        .endif
      .endif
    out pLcdDO,R16 ; Clear pull-Ups
    cbi pLcdCRSO,bLcdCRSO ; RS pin to low
    sbi pLcdCRWO,bLcdCRWO ; RW pin to high
  LcdBusyWait:
    rcall LcdIn ; Activate E, read data port, deactivate E
    .if LcdBits == 4
      rcall LcdPulseE ; Dummy for lower nibble
      .endif
    lsl R16 ; Busy flag to carry
    brcs LcdBusyWait ; Flag = 1, wait on
    cbi pLcdCRWO,bLcdCRWO ; RW pin to low
    .if LcdBits == 8
      ldi R16,0xFF ;  Data to output
      .else
      in R16,pLcdDD ; Read direction data port
      .if Lcd4High == 1
        ori R16,0xF0 ; Upper nibble high
        .else
        ori R16,0x0F ; Lower nibble high
        .endif
      .endif
    out pLcdDD,R16 ; Set direction port
    pop R16 ; Restore R16
    ret ; Done
  ;
  ; Read busy flag to R16
  ;   Needed only if wait mode off
  LcdIn:
    cbi pLcdCRSO,bLcdCRSO ; LCD RS pin to low
    sbi pLcdCEO,bLcdCEO ; Set bit bLcdCEO on LCD control port
    nop ; Wait at least one clock cycle
    .if clock>1000000 ; Insert further NOPs for higher clocks
      nop
      .endif
    .if clock>2000000
      nop
      .endif
    .if clock>3000000
      nop
      .endif
    .if clock>3000000
      nop
      .endif
    .if clock>4000000
      nop
      .endif
    .if clock>5000000
      nop
      .endif
    .if clock>6000000
      nop
      .endif
    .if clock>7000000
      nop
      .endif
    .if clock>8000000
      nop
      .endif
    .if clock>9000000
      nop
      .endif
    .if clock>10000000
      nop
      .endif
    .if clock>11000000
      nop
      .endif
    .if clock>12000000
      nop
      .endif
    .if clock>13000000
      nop
      .endif
    .if clock>14000000
      nop
      .endif
    .if clock>15000000
      nop
      .endif
    .if clock>16000000
      nop
      .endif
    .if clock>17000000
      nop
      .endif
    .if clock>18000000
      nop
      .endif
    .if clock>19000000
      nop
      .endif
    in R16,pLcdDI ; Read data bus to R16
    cbi pLcdCEO,bLcdCEO ; Clear bit bLcdCEO
    ret
  .endif
;
; 1 us pulse on LCD E pin
LcdPulseE:
  sbi pLcdCEO,bLcdCEO ; Set bit bLcdCEO in LCD control port
  .if clock>1000000 ; Add further NOPs for higher clocks
    nop
    .endif
  .if clock>2000000
    nop
    .endif
  .if clock>3000000
    nop
    .endif
  .if clock>3000000
    nop
    .endif
  .if clock>4000000
    nop
    .endif
  .if clock>5000000
    nop
    .endif
  .if clock>6000000
    nop
    .endif
  .if clock>7000000
    nop
    .endif
  .if clock>8000000
    nop
    .endif
  .if clock>9000000
    nop
    .endif
  .if clock>10000000
    nop
    .endif
  .if clock>11000000
    nop
    .endif
  .if clock>12000000
    nop
    .endif
  .if clock>13000000
    nop
    .endif
  .if clock>14000000
    nop
    .endif
  .if clock>15000000
    nop
    .endif
  .if clock>16000000
    nop
    .endif
  .if clock>17000000
    nop
    .endif
  .if clock>18000000
    nop
    .endif
  .if clock>19000000
    nop
    .endif
  cbi pLcdCEO,bLcdCEO ; Clear bit bLcdCEO
  ret
;
; Write R16 in 8 bit mode to LCD control
Lcd8Ctrl:
  .if simulation == 1
    push R16
    clr R16
    sts SimDisplayPos,R16
    pop R16
    .endif
  .if LcdBits == 4
    push ZL ; Save ZL
    in ZL,pLcdDO ; Read data output port
    .if Lcd4High == 1
      andi ZL,0x0F ; Clear upper nibble output
      andi R16,0xF0 ; Clear lower nibble R16
      .else
      andi ZL,0xF0 ; Clear lower nibble output
      swap R16 ; Swap upper and lower nibble
      andi R16,0x0F ; Clear upper nibble R16
      .endif
    or R16,ZL ; Combine
    pop ZL
    .endif
  out pLcdDO,R16 ; Data to LCD output port
  cbi pLcdCRSO,bLcdCRSO ; RS bit low
  rjmp LcdPulseE ; Activate E
;
; LcdData: Write data in R16 to LCD
;   R16: Character or data
LcdData:
  .if LcdWait == 0
    rcall LcdBusy ; Wait for busy flag of the LCD
    .endif
  sbi pLcdCRSO,bLcdCRSO ; Set RS bit
  .if LcdBits == 4
    push ZL
    push R16
    in ZL,pLcdDO ; Read data output port
    .if Lcd4High == 1
      andi ZL,0x0F ; Clear upper nibble port
      andi R16,0xF0 ; Clear lower nibble R16
      .else
      andi ZL,0xF0 ; Clear lower nibble port
      swap R16 ; Oberes und unteres Nibble vertauschen
      andi R16,0x0F ; Clear upper nibble R16
      .endif
    or R16,ZL ; Combine
    out pLcdDO,R16
    rcall LcdPulseE ; Activate E
    pop R16 ; Restore R16
    push R16 ; and save again
    in ZL,pLcdDO ; Read data output port
    .if Lcd4High == 1
      andi ZL,0x0F ; Preserve lower nibble of port
      swap R16 ; Upper to lower nibble
      andi R16,0xF0 ; Preserve upper nibble R16
      .else
      andi ZL,0xF0 ; Preserve upper nibble port
      andi R16,0x0F ; Preserve lower nibble R16
      .endif
    or R16,ZL ; Combine
    out pLcdDO,R16 ; To data bus
    rcall LcdPulseE ; Activate E
    pop R16 ; Restore R16
    pop ZL ; Restore ZL
    .else
    out pLcdDO,R16 ; Byte to data bus
    rcall LcdPulseE ; Activate E
    .endif
  .if LcdWait == 1
    rjmp LcdWait40us ; Wait for 40 us
    .endif
  ret
;
; ********************************
;    W A I T   R O U T I N E S
; ********************************
;
; Wait for 50 ms
;   At clock frequencies above 3.1 MHz the maximum
;   counting capability of Z exceeds its limit,
;   therefore the 50 ms calls the 5 ms delay 10
;   times
LcdWait50ms:
  push R16 ; Save R16
  ldi R16,10 ; 10 times 5 ms
LcdWait50ms1:
  rcall LcdWait5ms ; Call 5 ms delay
  dec R16 ; Decrease counter
  brne LcdWait50ms1 ; Repeat
  pop R16 ; Restore R16
  ret
;
LcdWait5ms:
  ; Wait for 5 ms, RCALL 3 clock cycles
  .equ cLcdZ5ms = (5*clock/1000 - 18 + 2) / 4
  push ZH ; Save ZH, + 2 cycles
  push ZL ; Save ZL, + 2 cycles
  ldi ZH,High(cLcdZ5ms) ; + 1 cycle
  ldi ZL,Low(cLcdZ5ms) ; + 1 cycle
  rjmp LcdWaitZ ; + 2 cycles
  ; Total: 11 clock cycles
;
.if LcdWait == 1 ;
  ; Wait routines necessary for wait mode
  ; Wait 1.64 ms
  .equ cLcdZ1640us = (164*(clock/1000)/100 - 18 + 2) / 4
  LcdWait1640us:
  push ZH ; Save ZH
  push ZL ; Save ZL
  ldi ZH,High(cLcdZ1640us)
  ldi ZL,Low(cLcdZ1640us)
  rjmp LcdWaitZ
  ;
  ; Wait 40 us
  .equ cLcdZ40us = (40*clock/1000000 - 18 + 2) / 4
  LcdWait40us:
  push ZH ; Save ZH
  push ZL ; Save ZL
  ldi ZH,High(cLcdZ40us)
  ldi ZL,Low(cLcdZ40us)
  rjmp LcdWaitZ
  .endif
;
; Wait routine for Z cycles
LcdWaitZ: ; 11 clock cycles
  sbiw ZL,1 ; Count down, +2 cycles
  brne LcdWaitZ ; If not zero: go on, + 2 cycles; if zero: 1 cycle
  pop ZL ; Restore ZL, +2 cycles
  pop ZH ; Restore ZH, +2 cycles
  ret ; Back, 4 cycles
  ; Total cycles: 11 + 4 * (Z - 1) + 3 + 8
  ;
  ; Number cycles= 11 + 4 * (Z - 1) + 3 + 8
  ;                11: RCALL, PUSH, PUSH, LDI, LDI, RJMP
  ;                4: 4 cycles per Z count (minus 1)
  ;                3: 3 cycles for last count
  ;                8: POP, RET
  ;              = 4 * Z + 11 - 4 + 3 + 8
  ;              = 4 * Z + 18
  ; Z = (Clock cycles - 18 + 2) / 4
  ;                +2: Rounding up during division by 4
;
; End of include file
;



Praise, error reports, scolding and spam please via the comment page to me.

To the top of that page

©2018 by http://www.avr-asm-tutorial.net