LCD440 LIBRARY- 40x4 LCD & DUAL-LCD DRIVERS/SOURCE CODE

General discussion on mikroBasic.
Post Reply
Author
Message
xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

LCD440 LIBRARY- 40x4 LCD & DUAL-LCD DRIVERS/SOURCE CODE

#1 Post by xor » 28 May 2006 21:10

  • This library can drive all HD44780 LCD's of any configuration up to 40 characters x 4 lines, including dual LCD configurations. It is primarily designed for easy printing to a 40x4 LCD. By using the LCD440_OUT() and LCD440_CHR() commands, the programmer can simply state the line and position to start printing without any address calculations or manual controller changes.

    The following LCD440 Source Code is actually 2 different libraries, one for P16 PIC's, and the other for P18 PIC's. Install the correct one in the same folder as your program and compile using the INCLUDE directive in your main program or another module:

    For P16:

    Code: Select all

    program myprog
    include "lcd440_p16"
    For P18:

    Code: Select all

    program myprog
    include "lcd440_p18"
    The prototypes are almost identical to the other mBASIC LCD routines. You can use all the same commands which are compatible with the HD44780 LCD controller. A couple differences to note is LCD440_CMD() and LCD440_CONFIG(). LCD440_CMD() requires a controller value, 1 or 2, in addition to the command byte. LCD440_CONFIG() requires that you choose the upper or lower 4 bits of the PORT used for the data lines. $0F or "LoNyb" are values that can be used for the lower nybble, and $F0 or "HiNyb" can be used for the high nybble.
    • LCD440_CONFIG(dim byref dataport as byte, dim nybble as byte, dim byref controlport as byte, dim rs, e1, e2 as byte)
      • Example: LCD440_CONFIG(PortB, HiNyb, PortC, 5, 0, 1)
        Comments: HiNyb and LoNyb are constants equal to $F0 and $0F, respectively. The data lines D7..D4 should be connected to PORTx<7..4> for the upper nybble, or PORTx<3..0> for the lower nybble.
      LCD440_CMD(dim controller, cmd as byte)
      • Example: LCD440_CMD(1, LCD_CURSOR_OFF)
        Comments: Valid controller values are 1 and 2.
      LCD440_OUT(dim row, column as byte, dim byref _string as string[40])
      • Example: LCD440_OUT(1, 4, "xor was here")
        Example: LCD440_OUT(4, 21, text)
        Comments: Valid Row values are 1 thru 4, and valid Column values are 1 thru 40. The maximum string length is 40 characters.
      LCD440_CHR(dim row, column as byte, dim _character as char)
      • Example: LCD440_CHR(2, 12, "A")
        Example: LCD440_CHR(3, 1, 65)
        Comments: Valid Row values are 1 thru 4, and valid Column values are 1 thru 40. Maximum of one character or byte.
      LCD440_OUT_CP(dim byref _string as string[40])
      • Example: LCD440_OUT_CP("Stop Here")
        Example: LCD440_OUT_CP(mystring)
        Comments: The maximum string length is 40 characters.
      LCD440_CHR_CP(dim _character as char)
      • Example: LCD440_CHR_CP("Hello")
        Example: LCD440_CHR_CP(mybyte)
        Comments: Maximum of one character or byte.
LIBRARY MODULE FOR P16 "lcd440_p16":

Code: Select all

module LCD440_P16

include "lcd_consts"
include "delays"

const HiNyb as byte = $F0
const LoNyb as byte = $0F
dim dport, cport as ^byte
dim _rs, _e1, _e2, _cc, dmask as byte

implements

sub procedure delay_40us
  delay_us(40)
end sub

sub procedure CLOCKIN(dim cont as byte)                      ' takes E1 or E2 high then low
  dim t0 as byte
    If cont = 1 Then
      t0 = _e1
    Else
      t0 = _e2
    End If
    cport^ = cport^ Or t0
    delay_1us
    cport^ = cport^ And Not(t0)
end sub
'****************************************************************************************
'         **** LCD440_CMD accepts CONTROLLER VALUE (1 or 2), COMMAND BYTE ****
'****************************************************************************************
sub procedure LCD440_CMD(dim controller, cmd as byte)        ' must pass the controller# (1 or 2) & byte
  dim t as byte[2]
    _cc = controller
    If dmask = $0F Then
      t[0] = $F0 And cmd                                     ' get high nybble and store high
      t[1] = cmd << 4                                        ' get low nybble and store high
    Else
      t[0] = cmd >> 4                                        ' get high nybble and store low
      t[1] = $0F and cmd                                     ' get low nybble and store low
    End If
    dport^ = (dport^ And dmask) Or t[0]                      ' clear dataport and output cmd high nybble
    CLOCKIN(controller)
    dport^ = (dport^ And dmask) Or t[1]                      ' clear dataport and output cmd low nybble
    CLOCKIN(controller)
    If cmd > 2 Then
      delay_40us                                             ' this delay is required for most commands
    Else
      delay_5500us                                           ' this delay is for LCD_CLEAR & LCD_CURSOR_HOME
    End If
end sub
'****************************************************************************************
'  **** LCD440_CONFIG accepts DATAPORT, HiNyb or LoNyb, CONTROLPORT, RS, E1, & E2 ****
'****************************************************************************************
sub procedure LCD440_CONFIG(dim byref dataport as byte,
                            dim nybble as byte,
                            dim byref controlport as byte,
                            dim rs, e1, e2 as byte)
  dim cmask as byte

'**** setup global variables ****

    _rs = 1 << rs
    _e1 = 1 << e1
    _e2 = 1 << e2
    dmask = Not(nybble)
    cmask = 255
    clearbit(cmask, rs)
    clearbit(cmask, e1)
    clearbit(cmask, e2)
    dport = @dataport
    cport = @controlport

'**** clear data lines & do TRIS setup ****

    dport^ = dport^ And dmask                                ' clear data lines
    setbit(dport, 7)                                         ' for P18's: dport = dport + 18
    dport^ = dport^ And dmask                                ' TRIS data lines as outputs
    clearbit(dport, 7)                                       ' for P18's: dport = dport - 18

'**** clear control lines & do TRIS setup ****

    cport^ = cport^ And cmask                                ' clear control lines
    setbit(cport, 7)                                         ' for P18's: cport = cport + 18
    cport^ = cport^ And cmask                                ' TRIS control lines as outputs
    clearbit(cport, 7)                                       ' for P18's: cport = cport - 18

'**** intialize the LCD ****

    delay_5500us                                             ' min 15ms delay at LCD powerup
    delay_5500us
    delay_5500us
    dport^ = dport^ Or (nybble And $33)                      ' clock-in $03 3 times with min 5ms, 160us, & 160us delays
    CLOCKIN(1)
    CLOCKIN(2)
    delay_5500us
    CLOCKIN(1)
    CLOCKIN(2)
    delay_40us
    delay_40us
    delay_40us
    delay_40us
    CLOCKIN(1)
    CLOCKIN(2)
    delay_40us
    delay_40us
    delay_40us
    delay_40us
    dport^ = dport^ And dmask                                ' clear data
    dport^ = dport^ Or (nybble And $22)                      ' clock-in $02 for 4-bit mode
    CLOCKIN(1)
    CLOCKIN(2)
    LCD440_CMD(1, $28)                                       ' set for 2-line
    LCD440_CMD(2, $28)
    LCD440_CMD(1, $10)                                       ' turn display off
    LCD440_CMD(2, $10)                                       ' turn display off
    LCD440_CMD(1, $01)                                       ' clear display
    LCD440_CMD(2, $01)                                       ' clear display
    LCD440_CMD(1, $14)                                       ' set cursor direction
    LCD440_CMD(2, $14)                                       ' set cursor direction
    LCD440_CMD(1, $0F)                                       ' turn display on, cursor on, blink on
    LCD440_CMD(2, $0F)                                       ' turn display on, cursor on, blink on
    _cc = 1                                                  ' current controller is 1
end sub
'****************************************************************************************
' **** LCD440_OUT accepts Row Value, Column Value, and a string up to 40 character ****
'****************************************************************************************
sub procedure LCD440_OUT(dim row, column as byte, dim byref _string as string[40])
  dim cp, c0, tmp as byte
    dec(column)
    Select Case row                                          ' determine controller and cursor position
      Case 1
        cp = 128
        c0 = 1
      Case 2
        cp = 192
        c0 = 1
      Case 3
        cp = 128
        c0 = 2
      Case 4
        cp = 192
        c0 = 2
    End Select
    LCD440_CMD(c0, cp + column)                              ' set the cursor position
    cport^ = cport^ Or _rs                                   ' set RS for LCD text
    tmp = 0
    While _string[tmp] <> 0                                  ' send string...stop at end of string
      LCD440_CMD(c0, _string[tmp])                           ' print character at cursor position and auto-increment position
      inc(tmp)                                               ' next character in string
    Wend
    cport^ = cport^ And Not(_rs)                             ' reset RS for LCD commands
end sub
'****************************************************************************************
'    **** LCD440_CHR accepts a ROW VALUE, COLUMN VALUE, and a single character ****
'****************************************************************************************
sub procedure LCD440_CHR(dim row, column as byte, dim  _character as char)
  dim cp, c0, tmp as byte
    dec(column)
    Select Case row                                          ' determine controller and cursor position
      Case 1
        cp = 128
        c0 = 1
      Case 2
        cp = 192
        c0 = 1
      Case 3
        cp = 128
        c0 = 2
      Case 4
        cp = 192
        c0 = 2
    End Select
    LCD440_CMD(c0, cp + column)                              ' set cursor position
    cport^ = cport^ Or _rs                                   ' set RS for LCD text
    LCD440_CMD(c0, _character)                               ' print character at cursor position and auto-increment position
    cport^ = cport^ And Not(_rs)                             ' reset RS for LCD commands
end sub
'**************************************************************************************************
'**** LCD440_OUT_CP accepts a string up to 40 character and print to current cursor position ****
'**************************************************************************************************
sub procedure LCD440_OUT_CP(dim byref _string as string[40])
  dim tmp as byte
    cport^ = cport^ Or _rs                                   ' set RS for LCD text
    tmp = 0
    While _string[tmp] <> 0                                  ' send string...stop at end of string
      LCD440_CMD(_cc, _string[tmp])                          ' print character at cursor position and auto-increment position
      inc(tmp)                                               ' next character in string
    Wend
    cport^ = cport^ And Not(_rs)                             ' reset RS for LCD commands
end sub
'**************************************************************************************************
'  **** LCD440_CHR_CP accepts a single character and prints at the current cursor position ****
'**************************************************************************************************
sub procedure LCD440_CHR_CP(dim  _character as char)
    cport^ = cport^ Or _rs                                   ' set RS for LCD text
    LCD440_CMD(_cc, _character)                              ' print character at cursor position and auto-increment position
    cport^ = cport^ And Not(_rs)                             ' reset RS for LCD commands
end sub

end.
LIBRARY MODULE FOR P18 "lcd440_p18":

Code: Select all

module LCD440_P18

include "lcd_consts"
include "delays"

const HiNyb as byte = $F0
const LoNyb as byte = $0F
dim dport, cport as ^byte
dim _rs, _e1, _e2, _cc, dmask as byte

implements

sub procedure delay_40us
  delay_us(40)
end sub

sub procedure CLOCKIN(dim cont as byte)                      ' takes E1 or E2 high then low
  dim t0 as byte
    If cont = 1 Then
      t0 = _e1
    Else
      t0 = _e2
    End If
    cport^ = cport^ Or t0
    delay_1us
    cport^ = cport^ And Not(t0)
end sub
'****************************************************************************************
'         **** LCD440_CMD accepts CONTROLLER VALUE (1 or 2), COMMAND BYTE ****
'****************************************************************************************
sub procedure LCD440_CMD(dim controller, cmd as byte)        ' must pass the controller# (1 or 2) & byte
  dim t as byte[2]
    _cc = controller
    If dmask = $0F Then
      t[0] = $F0 And cmd                                     ' get high nybble and store high
      t[1] = cmd << 4                                        ' get low nybble and store high
    Else
      t[0] = cmd >> 4                                        ' get high nybble and store low
      t[1] = $0F and cmd                                     ' get low nybble and store low
    End If
    dport^ = (dport^ And dmask) Or t[0]                      ' clear dataport and output cmd high nybble
    CLOCKIN(controller)
    dport^ = (dport^ And dmask) Or t[1]                      ' clear dataport and output cmd low nybble
    CLOCKIN(controller)
    If cmd > 2 Then
      delay_40us                                             ' this delay is required for most commands
    Else
      delay_5500us                                           ' this delay is for LCD_CLEAR & LCD_CURSOR_HOME
    End If
end sub
'****************************************************************************************
'  **** LCD440_CONFIG accepts DATAPORT, HiNyb or LoNyb, CONTROLPORT, RS, E1, & E2 ****
'****************************************************************************************
sub procedure LCD440_CONFIG(dim byref dataport as byte,
                            dim nybble as byte,
                            dim byref controlport as byte,
                            dim rs, e1, e2 as byte)
  dim cmask as byte

'**** setup global variables ****

    _rs = 1 << rs
    _e1 = 1 << e1
    _e2 = 1 << e2
    dmask = Not(nybble)
    cmask = 255
    clearbit(cmask, rs)
    clearbit(cmask, e1)
    clearbit(cmask, e2)
    dport = @dataport
    cport = @controlport

'**** clear data lines & do TRIS setup ****

    dport^ = dport^ And dmask                                ' clears data lines
    dport = dport + 18                                       ' for P16's: setbit(dport, 7)
    dport^ = dport^ And dmask                                ' TRIS data lines as outputs
    dport = dport - 18                                       ' for P16's: clearbit(dport, 7)

'**** clear control lines & do TRIS setup ****

    cport^ = cport^ And cmask                                ' clears control lines
    cport = cport + 18                                       ' for P16's: setbit(cport, 7)
    cport^ = cport^ And cmask                                ' TRIS control lines as outputs
    cport = cport - 18                                       ' for P16's: clearbit(cport, 7)

'**** intialize the LCD ****

    delay_5500us                                             ' min 15ms delay at LCD powerup
    delay_5500us
    delay_5500us
    dport^ = dport^ Or (nybble And $33)                      ' clock-in $03 3 times with min 5ms, 160us, & 160us delays
    CLOCKIN(1)
    CLOCKIN(2)
    delay_5500us
    CLOCKIN(1)
    CLOCKIN(2)
    delay_40us
    delay_40us
    delay_40us
    delay_40us
    CLOCKIN(1)
    CLOCKIN(2)
    delay_40us
    delay_40us
    delay_40us
    delay_40us
    dport^ = dport^ And dmask                                ' clear data
    dport^ = dport^ Or (nybble And $22)                      ' clock-in $02 for 4-bit mode
    CLOCKIN(1)
    CLOCKIN(2)
    LCD440_CMD(1, $28)                                       ' set for 2-line
    LCD440_CMD(2, $28)
    LCD440_CMD(1, $10)                                       ' turn display off
    LCD440_CMD(2, $10)                                       ' turn display off
    LCD440_CMD(1, $01)                                       ' clear display
    LCD440_CMD(2, $01)                                       ' clear display
    LCD440_CMD(1, $14)                                       ' set cursor direction
    LCD440_CMD(2, $14)                                       ' set cursor direction
    LCD440_CMD(1, $0F)                                       ' turn display on, cursor on, blink on
    LCD440_CMD(2, $0F)                                       ' turn display on, cursor on, blink on
    _cc = 1                                                  ' current controller is 1
end sub
'****************************************************************************************
' **** LCD440_OUT accepts Row Value, Column Value, and a string up to 40 character ****
'****************************************************************************************
sub procedure LCD440_OUT(dim row, column as byte, dim byref _string as string[40])
  dim cp, c0, tmp as byte
    dec(column)
    Select Case row                                          ' determine controller and cursor position
      Case 1
        cp = 128
        c0 = 1
      Case 2
        cp = 192
        c0 = 1
      Case 3
        cp = 128
        c0 = 2
      Case 4
        cp = 192
        c0 = 2
    End Select
    LCD440_CMD(c0, cp + column)                              ' set the cursor position
    cport^ = cport^ Or _rs                                   ' set RS for LCD text
    tmp = 0
    While _string[tmp] <> 0                                  ' send string...stop at end of string
      LCD440_CMD(c0, _string[tmp])                           ' print character at cursor position and auto-increment position
      inc(tmp)                                               ' next character in string
    Wend
    cport^ = cport^ And Not(_rs)                             ' reset RS for LCD commands
end sub
'****************************************************************************************
'    **** LCD440_CHR accepts a ROW VALUE, COLUMN VALUE, and a single character ****
'****************************************************************************************
sub procedure LCD440_CHR(dim row, column as byte, dim _character as char)
  dim cp, c0, tmp as byte
    dec(column)
    Select Case row                                          ' determine controller and cursor position
      Case 1
        cp = 128
        c0 = 1
      Case 2
        cp = 192
        c0 = 1
      Case 3
        cp = 128
        c0 = 2
      Case 4
        cp = 192
        c0 = 2
    End Select
    LCD440_CMD(c0, cp + column)                              ' set cursor position
    cport^ = cport^ Or _rs                                   ' set RS for LCD text
    LCD440_CMD(c0, _character)                               ' print character at cursor position and auto-increment position
    cport^ = cport^ And Not(_rs)                             ' reset RS for LCD commands
end sub
'**************************************************************************************************
'**** LCD440_OUT_CP accepts a string up to 40 character and print to current cursor position ****
'**************************************************************************************************
sub procedure LCD440_OUT_CP(dim byref _string as string[40])
  dim tmp as byte
    cport^ = cport^ Or _rs                                   ' set RS for LCD text
    tmp = 0
    While _string[tmp] <> 0                                  ' send string...stop at end of string
      LCD440_CMD(_cc, _string[tmp])                          ' print character at cursor position and auto-increment position
      inc(tmp)                                               ' next character in string
    Wend
    cport^ = cport^ And Not(_rs)                             ' reset RS for LCD commands
end sub
'**************************************************************************************************
'  **** LCD440_CHR_CP accepts a single character and prints at the current cursor position ****
'**************************************************************************************************
sub procedure LCD440_CHR_CP(dim _character as char)
    cport^ = cport^ Or _rs                                   ' set RS for LCD text
    LCD440_CMD(_cc, _character)                              ' print character at cursor position and auto-increment position
    cport^ = cport^ And Not(_rs)                             ' reset RS for LCD commands
end sub

end.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

zkt
Posts: 365
Joined: 03 Jan 2006 22:05

#2 Post by zkt » 29 May 2006 00:53

By George he`s done it again ! :D
You`re what this is all about man
Thanks
zkt
gotta be something stupid
May the Repair Angel smile upon you

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#3 Post by xor » 29 May 2006 02:18

  • Thanks zkt :D .

    Here's a 4 channel horizontal bar-graph demo using random numbers to try on a 40x4 LCD.

    If you are interested in a new 40x4 LCD, here's some for $7.00 USD: CHEAP 40x4 LCD. Don't know if this will work with the new library but might be worth a try.

    Code: Select all

    ' tested on 18F452
    ' 40x4 Horizontal Bargraph Demo
    
    include "lcd440_p18"
    
    dim t0, t1 as byte
    
    sub procedure customchar
    dim t1 as byte
      LCD440_CMD(1, 64)
      For t1 = 0 to 7
        LCD440_CHR_CP($18)
      Next t1
      For t1 = 0 to 7
        LCD440_CHR_CP($1B)
      Next t1
      LCD440_CMD(2, 64)
      For t1 = 0 to 7
        LCD440_CHR_CP($18)
      Next t1
      For t1 = 0 to 7
        LCD440_CHR_CP($1B)
      Next t1
    end sub
    
    sub function rnd8_18F(dim min, max as byte) as byte
    dim f0 as boolean
    f0 = true
    While f0
        asm
          clrf    STATUS
          rlcf    TMR2, 1
          swapf   TMR2, 0
          andlw   0xE0
          rrcf    TMR2, 1
          addwf   TMR2, 0
          addwf   TMR2, 0
          addwf   TMR2, 0
          sublw   0x35
          movwf   TMR2
        end asm
        If (TMR2 <= max) And (TMR2 >= min) Then f0 = false End If
    Wend
    result = TMR2
    end sub
    
    sub procedure outstr
      dim cnt, lr as integer
      dim xlr as boolean
        xlr = false
        lr = rnd8_18F(0, 80)
        If lr.0 <> 0 Then xlr = true End If
        lr = lr >> 1
        For cnt = 1 to lr
          LCD440_CHR_CP(1)
        Next cnt
        cnt = 40 - lr
        If xlr = true Then
          LCD440_CHR_CP(0)
          dec(cnt)
        End If
        For lr = 1 to cnt
          LCD440_CHR_CP(32)
        Next lr
    end sub
    
    main:
    PORTB = 0
    TRISB = 0
    LCD440_CONFIG(PORTB, HiNyb, PORTB, 2, 3, 1)
    LCD440_CMD(1, LCD_CURSOR_OFF)
    LCD440_CMD(2, LCD_CURSOR_OFF)
    customchar
    While true
      For t0 = 1 to 2
        LCD440_CMD(t0, 128)
        outstr
        LCD440_CMD(t0, 192)
        outstr
      Next t0
    Wend
    end.
    
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

LGR
Posts: 3204
Joined: 23 Sep 2004 20:07

#4 Post by LGR » 29 May 2006 05:52

$7 is a very good price for those. If you want one (or more) I suggest you order while you can.

Of course, with the introduction of the T6963c library to C/dsPIC, and soon to all the compilers, you can do 8x40. :D Those are a bit more money, but I've seen some deals.....
If you know what you're doing, you're not learning anything.

munchy
Posts: 20
Joined: 19 Mar 2009 19:49

#5 Post by munchy » 17 Apr 2009 16:13

Sorry to drag up a very old thread - but I'm having a strange issue with my 40x4 display.

Everything seems to work fine with xor's code, except when I try and 'overwrite' on the 3rd and 4th row. It shifts the 3rd and 4th row left, the number of characters I'm writing!

I'm trying to set up a display that takes in ADC inputs and displays them on a 'static' background showing channel number, units etc. I write the 4 lines of static background, then in a loop I am trying to display the ADC channels. The first two lines work fine (first controller) and at first I thought the second wasn;t displaying at all, except I noticed that it's just continously scrolling left!

Any ideas if I'm doing anything wrong?

Code: Select all

program ion_pump_controller_v0_1

include "lcd4400_P16.pbas"

dim t as word
    ch, n as byte
    tlong as longint

main:
  TRISB=0
  TRISA=1
  Lcd440_CONFIG(PortB, HiNyb, PortB, 2, 3, 1)                  ' Initialize LCD connected to PORTB
  Lcd440_Cmd(1, LCD_CURSOR_OFF)          ' Send command cursor off
  Lcd440_Cmd(2, LCD_CURSOR_OFF)

   LCD440_OUT(1,1,"CH1:  .  kV     uA    CH5:  .  kV     uA")
   LCD440_OUT(2,1,"CH2:  .  kV     uA    CH6:  .  kV     uA")
   LCD440_OUT(3,1,"CH3:  .  kV     uA    CH7:  .  kV     uA")
   LCD440_OUT(4,1,"CH4:  .  kV     uA    CH8:  .  kV     uA")

while true

    PORTC = %00001000

    for n = 0 to 3

    PORTC = n + 8

    t = ADC_read(1)
    tlong = t*5000
    t     = tlong >>10
    ch    = t div 1000
    LCD440_CHR(3,6,48+ch)

    ch    = (t div 100) mod 10
    LCD440_CHR(3,8,48+ch)

    ch    = (t div 10) mod 10
    LCD440_CHR(3,9,48+ch)
 
    delay_ms(100)

    next n

wend

end.
The display is fine on the first two lines (I can change where the ADC displays to either line 1 or 2 ). The above code will send the 3rd and 4th lines scrolling to the left...

Thanks for looking, Andrew

Post Reply

Return to “mikroBasic General”