News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

How to drive two LCD displays...

Started by SCV, Nov 16, 2021, 11:10 AM

Previous topic - Next topic

SCV

With two character LCD modules on a 4bit bus with seperate E & RS lines, is there a way to flip control between the first and second display through LCDOUT?

Thanks,
Tim.

RGV250

Hi,
It is quite easy, all you need to do is have a logic IC to switch the enable line. I will see if I can find a schematic I used.

Regards,
Bob

Yasin

If you want to solve it in hardware. You can use 74HC4052 or similar multiplexer.

RGV250

Hi,
This is the schematic, should work with ALCD or GLCD. I should have the code if you need but it is very simple.
dual LCD.jpg

Bob

RGV250

Hi,
Code snippets, add this to the start of the code somewhere.
'****************************************************************
'* MACRO's for display switching                                *
'****************************************************************
 
        Symbol DISPLAY1 = PORTB.0       
        Symbol DISPLAY2 = PORTB.1       

       
DISPLAY_1 Macro
        DISPLAY1 = 1                  'Select screen 1 macro
        DISPLAY2 = 0                 
Endm         

DISPLAY_2 Macro
        DISPLAY1 = 0                  'Select screen 2 macro
        DISPLAY2 = 1                         
Endm                 


Just use the Macro DISPLAY_1 or DISPLAY_2 and then print as normal
TEMPERATURE_DISPLAY:
        DISPLAY_1
        Print Font NORMAL_NP
        Print At 0,1,Dec1 TEMPERATURE_1, " C "
        Print At 10,1,Dec1 TEMPERATURE_2, " C "       
        Print At 20,1,Dec1 TEMPERATURE_3, " C "   
           
        Return
                 
DISPLAY:
        DISPLAY_2               'Select screen 2
        Print Font NORMAL_NP
       
        Print At 0,1, "R1 ", Dec MESSAGE_0_DATA#0,"  "
        Print At 0,40, Dec MESSAGE_0_DATA#1,"  "
        Print At 0,65, Dec MESSAGE_0_DATA#2,"  "
        Print At 0,90, Dec MESSAGE_0_DATA#3,"  "   
   
        Return

Bob

SCV

Thanks for the suggestions, I was hoping for a software solution as the displays have seperate control lines.

RGV250

Hi,
I am not sure what you mean by separate control lines, why have they got to be separate? Can you give more details.

Regards,
Bob

top204

#7
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_:

The code can be placed in an include file and used as a library, but the demo part and the declares must be removed first. :-)

Below is a screenshot of the code above working in a simulation:
Two LCDs.jpg

SCV


chris_cb_uk

Les, would this also work for a compatible GLCD?  How would you treat the R/W line? Could you use compiler commands in the same way you've addressed the enable lines or is there a complication to this?

top204

#10
Hello Chris

Yes, the same type of mechanism can be used with graphic LCDs and the compiler's commands for them. However, it will require a re-write of the compiler's library routines in BASIC, so their EN lines are altered to suit the LCD being used, and the initialisation code within them altered as well.