News:

Let's find out together what makes a PIC Tick!

Main Menu

LCD Display 40x4

Started by charliecoutas, Dec 24, 2023, 02:48 PM

Previous topic - Next topic

charliecoutas

Happy Christmas everybody.

I bought a 40x4 LCD display for our new project at the museum. I (wrongly) assumed that it would be more or less the same as a 40x2 display. Wrong! Its pinout is different (18 pins not 16) and it has two CE pins. It seems that each pair of lines (40x2) has its own CE.

Can anybody offer any suggestions of how I can interface it to Positron please?

The datasheet is here:  https://www.farnell.com/datasheets/3257187.pdf

Charlie

Frizie

There have been examples here of controlling 2 LC-displays.
Maybe you can manage it this way (I have never studied those examples).
By the way, it only has an extra Enable line, the other extra pin is n.c.
Ohm sweet Ohm | www.picbasic.nl

rick.curl

I encountered the same problem several years back, and Tim was kind enough to provide a solution.  include this code:
'setup alternative system for 4 line LCD

    #Disable Print
    '#disable Cursor
    #Disable Cls
    #Enable DelayUS

    Dim BPF As Byte System
    Dim BPFH As Byte System
    Dim GEN As Byte System
    Dim PP0 As Byte System
    Dim PP0H As Byte System
    Dim PP3 As Byte System
    Dim PP3H As Byte System
    Dim PP4 As Byte System
    Dim PP5 As Byte System

    Dim LCD#ENPINSELECT1 As Bit
    Dim LCD#ENPINSELECT2 As Bit

    Symbol LCD#DATAUS = 50
    Symbol LCD#COMMANDUS = 2000
    Symbol LCD_ENPORT2 = PORTB
    Symbol LCD_ENPIN2= 0

    Declare LCD_DTPin = PORTB.4
    Declare LCD_RSPin = PORTB.3
    Declare LCD_ENPin = PORTB.1
    Declare LCD_Interface = 4           '4-bit Interface
    Declare LCD_Lines = 2               'in this case 2 lots of 2 lines
    Declare LCD_Type = 0

'------------------------------------------------------------------------------------
        Declare Watchdog Off
        Declare Bootloader off
        Declare Optimiser_Level = 0
        Declare Float_Display_Type = large

        Reminders off
        Clear
        Reminders On

        $define LCD_ena_12   '
        LCD#ENPINSELECT1 = 1 '
        LCD#ENPINSELECT2 = 0

        $define LCD_ena_34   '
        LCD#ENPINSELECT1 = 0 '
        LCD#ENPINSELECT2 = 1


        $define LCD_ena_1234 '
        LCD#ENPINSELECT1 = 1 '
        LCD#ENPINSELECT2 = 1

...and then before each print command you can use:
LCD_ena_1234 (for all 4 lines)  (good for initializing the display)
-or-
LCD_ena_12  (for the first two lines)
-or-
LCD_ena_34 (for the second two lines)

...works a treat!! (thanks to Tim)

-Rick

charliecoutas

Many, many thanks Rick, and Tim.

Charlie

AlbertoFS

See this Les's Code:
(*
The hardware solution is a good way of interfacing to two seperate LCDs, but it does suffer from a lack of initialisation, because the first time an LCD is interfaced with, the compiler's library routine initialises it and sets a flag bit, so it will not be initialised again, because it doesn't need to be. So calling the Print or Cls command for a second LCD will not initialise it because the library routine has already done that. Clearing the compiler's BPF system variable will force an initialisation, so if using hardware switching, create a procedure or meta-macro to clear the BPF system variable when an LCD is first interfaced with.

Another way of interfacing to two LCDs independently is through software, but it has to be created so it initialises either LCD only once, otherwise, it wastes time setting pins to outputs and sending the init commands to the LCD etc...

The code listing below replaces the compiler's Print and Cls commands so two seperate LCDs can be interfaced with independently, using a single standard 18F device. It chooses the pin to use for an LCD's EN line depending on the setting of a flag bit, so it is very efficient.
*)
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Interface to two Hitachi alphanumeric LCDs independently with an 18F device.
'
' The listing's __print_ subroutine replaces the compiler's Print command's library subroutine
' But allows two EN lines to be established, so two LCDs are interfaced seperately
'
' Written for the Positron8 compiler by Les Johnson
'
    Device = 18F25K20                           ' This code is for an 18F device
    Declare Xtal = 16
    #Disable Print                              ' Disable the compiler's print library routine
'
' Setup the alphanumeric LCD
'   
    Declare LCD_DTPin = PORTB.4                 ' LCD lines D4 to D7 connect to the PIC's PORTB.4 to PORTB,.7 pins
    Declare LCD_RSPin = PORTB.3                 ' Connects to the LCD's RS line
    Declare LCD_Interface = 4                   ' Using a 4-bit interface
    Declare LCD_Lines = 2                       ' Using a 2 line LCD
    Declare LCD_Type = Alpha                    ' Using an Hitachi Alphanumeric LCD
    Declare LCD_CommandUs = 2000                ' The delay after commands are sent to the LCD
    Declare LCD_DataUs = 50                     ' The delay after data is sent to the LCD
'
' Setup the pins to use for the LCD EN lines
'
    Symbol LCD_EN1_Pin = PORTB.1                ' Connects to LCD1's EN line
    Symbol LCD_EN2_Pin = PORTB.2                ' Connects to LCD2's EN line
'
' Create variables for the __print_ routine
'
    Dim PP0               As Byte System
    Dim PP0H              As Byte System
    Dim BPF               As Byte System
    Dim PP3               As Byte System
    Dim PP3H              As Byte System
    Dim LCD_bFlags        As Byte Access = 0    ' Flags used by the LCD routine
    Dim LCD_tInitialised  As LCD_bFlags.0       ' Holds 1 if an LCD is initialised
    Dim LCD1_tInitialised As LCD_bFlags.1       ' Holds 1 if LCD1 is initialised
    Dim LCD2_tInitialised As LCD_bFlags.2       ' Holds 1 if LCD2 is initialised
    Dim LCD_tNumber       As LCD_bFlags.3       ' Holds 0 for LCD1, 1 for LCD2
'
' Create some aliases to make the code's operation clearer
'
    Dim LCD_bByteOut      As PP3
    Dim LCD_bWREGStore    As PP3H
    Dim LCD_tComOrData    As BPF.0
'
' Create two variables for the demo
'
    Dim Demo_wCounter1 As Word
    Dim Demo_wCounter2 As Word
   
'------------------------------------------------------------------------------------------------
' Clear an LCD
' Input     : pLCD is 1 for LCD1, 2 for LCD2
' Output    : None
' Notes     : None
'
$define LCD_Cls(pLCD) '
    $if pLCD = 1      '
    LCD_tNumber = 0   '
    Cls               '
    $elseif pLCD = 2  '
    LCD_tNumber = 1   '
    Cls               '
    $else             '
        $error "Incorrect LCD number. 1 or 2 only" '
    $endif         
   
'------------------------------------------------------------------------------------------------
' Choose an LCD
' Input     : pLCD is 1 for LCD1, 2 for LCD2
' Output    : None
' Notes     : None

$define LCD_Number(pLCD) '
    $if pLCD = 1         '
    LCD_tNumber = 0      '
    $elseif pLCD = 2     '
    LCD_tNumber = 1      '
    $else                '
        $error "Incorrect LCD number. 1 or 2 only" '
    $endif
   
'------------------------------------------------------------------------------------------------
' The main program starts here
' Interface to two seperate LCDs and display a count on them
'
Main:
    LCD_Cls(1)                                                  ' Clear the display of LCD1
    LCD_Cls(2)                                                  ' Clear the display of LCD2
   
    Do                                                          ' Create a loop
        LCD_Number(1)                                           ' Choose LCD1 for the Print command
        Print At 1, 1, "LCD1: ", Dec Demo_wCounter1, "     "    ' Display Demo_wCounter1 on LCD1
       
        LCD_Number(2)                                           ' Choose LCD2 for the Print command
        Print At 1, 1, "LCD2: ", Dec Demo_wCounter2, "     "    ' Display Demo_wCounter2 on LCD2
       
        Demo_wCounter1 = Demo_wCounter1 + 1                     ' Increment Demo_wCounter1
        Demo_wCounter2 = Demo_wCounter2 + 10                    ' Increment Demo_wCounter2 differently
        DelayMS 100                                             ' A delay so we can see the counts happening
    Loop                                                        ' Do it forever
   
'------------------------------------------------------------------------------------------------
' Replacement library routine for the Print command that allows two LCDs to be interfaced independently
' Input     : WREG holds the byte to send to the LCD
'           : LCD_tNumber holds the LCD to access. 0 is LCD1, 1 is LCD2
'           : LCD1_tInitialised is 1 if LCD1 has been initialised, else 0
'           : LCD2_tInitialised is 1 if LCD2 has been initialised, else 0
' Output    : WREG still holds the byte that was sent
' Notes     : Uses two Symbols for the two seperate LCD EN pins: LCD_EN1_Pin and LCD_EN2_Pin
'           : The subroutine is for a 4-bit interface on a standard 18F device, and uses the compiler's LCD Declares
'
    GoTo _LCD_Over_                             ' Jump over the compiler's replacement library subroutine
__print_:
    LCD_bWREGStore = WREG                       ' Save the byte sent
    If LCD_tNumber = 0 Then                     ' Is it LCD1 that is being accessed?
        PinLow LCD_EN1_Pin                      ' Yes. So pull the EN pin to output low
    Else                                        ' Otherwise... It is LCD2 that is being accessed
        PinLow LCD_EN2_Pin                      ' So. Pull the EN pin to output low
    EndIf
    PinLow __LCD_RSPORT.__LCD_RSPIN             ' Pull the RS pin to output low
    #if(__LCD_DTPIN == 0)
        WREG = $F0
    #else
        WREG = $0F
    #endif
    Andwf (__LCD_DTPORT + _TRIS_offset)         ' Set the correct half of the port to output on a standard 18F device
    If LCD_tNumber = 0 Then                     ' Is it LCD1 that is being accessed?
        LCD_tInitialised = LCD1_tInitialised    ' Yes. So transfer LCD1_tInitialised to LCD_tInitialised
    Else                                        ' Otherwise... It is LCD2 that is being accessed
        LCD_tInitialised = LCD2_tInitialised    ' So. Transfer LCD2_tInitialised to LCD_tInitialised
    EndIf
    WREG = LCD_bWREGStore                       ' Get back the byte that is to be sent to the LCD
    If LCD_tInitialised = 0 Then                ' Has the LCD been initialised?
        DelayUS 15000                           ' No. So wait 15ms
        LCD_bByteOut = $33                      ' Initialise the LCD
        GoSub _LCD_NibbleSend_                  ' Send init
        DelayUS 5000                            ' Wait 5ms
        GoSub _LCD_NibbleSend_                  ' Send init again
        DelayUS 100                             ' Wait 100us
        GoSub _LCD_NibbleSend_                  ' Send init one more time
        DelayUS 100                             ' Wait 100us
        LCD_bByteOut = $22                      ' Set 4-bit mode
        GoSub _LCD_NibbleSend_                  ' Set the interface to 4-bit mode
        #ifdef __LCD_LINES                      ' Has the LCD_Lines declare been used in the main program?
            #if(__LCD_LINES == 1)               ' Yes. So...
                WREG = $20                      ' Set for 4-bit mode, 1 line, 5x7 font
            #else                               ' Otherwise...
                WREG = $28                      ' Set for 4-bit mode, 2+ lines, 5x7 font
            #endif
        #else                                   ' Otherwise... Default to 2 lines
            WREG = $28                          ' Set for 4-bit mode, 2+ lines, 5x7 font
        #endif
        GoSub _LCD1_SetForCommand_              ' Send a command
        WREG = $0C                              ' Set for display on, no cursor, no blink
        GoSub _LCD1_SetForCommand_
        WREG = $06                              ' LCD entry mode set, increment, no shift
        GoSub _LCD1_SetForCommand_
        If LCD_tNumber = 0 Then                 ' Is it LCD1 that is being accessed?
            LCD1_tInitialised = 1               ' Yes. So indicate that LCD1 is initialised
        Else                                    ' Otherwise... It is LCD2 that is being accessed
            LCD2_tInitialised = 1               ' So. Indicate that LCD2 is initialised
        EndIf
        WREG = LCD_bWREGStore                   ' Get the saved byte back
        GoTo _LCD_Send_
_LCD1_SetForCommand_:
        LCD_tComOrData = 1                      ' Set for command
    EndIf

_LCD_Send_:
    LCD_bByteOut = WREG                         ' Get the byte to send from WREG
    If LCD_tComOrData = 1 Then                  ' Is it a command this time?
        PinClear __LCD_RSPORT.__LCD_RSPIN       ' Yes. So set command register select
        WREG = 3 - WREG
        Bnc _LCD_SetForData_                    ' Short delay
        GoSub _LCD_SetForData_                  ' Long delay
        DelayUS __LCD_COMMANDUS                 ' Wait for the command to complete
        Return
    EndIf
    LCD_tComOrData = 1                          ' Indicate first nibble, or command next time
    WREG = $FE - WREG                           ' Command next time?
    Bz _LCD_Exit_
    PinSet __LCD_RSPORT.__LCD_RSPIN             ' Set the RS pin high

_LCD_SetForData_:
    #if(__LCD_DTPIN == 0)
        Swap LCD_bByteOut, LCD_bByteOut         ' Swap top and bottom nibbles
    #endif
    Btfss LCD_tComOrData                        ' First time through only
_LCD_NibbleSend_:
    LCD_tComOrData = 0                          ' Indicate second nibble, and data next time
    If LCD_tNumber = 0 Then                     ' Is it LCD1 that is being accessed?
        PinSet LCD_EN1_Pin                      ' Yes. So enable LCD1
    Else                                        ' Otherwise... It is LCD2 that is being accessed
        PinSet LCD_EN2_Pin                      ' So. Enable LCD2
    EndIf
    #if(__LCD_DTPIN == 0)
        WREG = $F0                              ' Clear the bits of the LCD data port
    #else
        WREG = $0F                              ' Clear the bits of the LCD data port
    #endif
    __LCD_DTPORT = __LCD_DTPORT & WREG          ' \
    WREG = LCD_bByteOut                         ' / Isolate the correct nibble of the byte
    #if(__LCD_DTPIN == 0)
        WREG = WREG & $0F
    #else
        WREG = WREG & $F0
    #endif
    __LCD_DTPORT = __LCD_DTPORT | WREG          ' Write the byte to the data port
    DelayCS 6
    If LCD_tNumber = 0 Then                     ' Is it LCD1 that is being accessed?
        PinClear LCD_EN1_Pin                    ' Yes. So disable LCD1
    Else                                        ' Otherwise... It is LCD2 that is being accessed
        PinClear LCD_EN2_Pin                    ' So. Disable LCD2
    EndIf
    Swap LCD_bByteOut, LCD_bByteOut             ' Move to the other nibble
    DelayCS 6
    Btfsc LCD_tComOrData
    GoTo _LCD_NibbleSend_                       ' Send the lower 4-bits to the LCD
    DelayUS __LCD_DATAUS                        ' Wait for the data operation to complete
_LCD_Exit_:
    WREG = LCD_bWREGStore                       ' Return the original byte in WREG
    Return
_LCD_Over_:
73's de EA3AGV

charliecoutas

Thanks Alberto, I now have enough to tackle the job.

Charlie

John Drew

G'day Charlie, I hope you're well.
I used Tim's code for many years and then recently I struck trouble. A change to Les's recent code sorted things nicely.
John

charliecoutas

G'Day John

Just got back from an offline week in the sun in Tenerife. Will get back to you about this when I have sorted myself out.

Charlie