Drive a 2x16 or 4x20 Hitachi LCD with a single pin via a 74HC595 Shift Register

Started by top204, Feb 06, 2021, 03:58 PM

Previous topic - Next topic

top204

I've made some changes to the LCD controlling Shift Register library code I posted earlier so it operates more like the LCD's Print command, and is a bit more efficient. The library uses some of the compiler's Print Declares to set the pin to use, and the amount of lines on the LCD, and the delay required after commands are sent to the LCD. Here is the library code:

$ifndef _Shift_Print_inc_
$define _Shift_Print_inc_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Control an Hitachi Alphanumeric LCD with a single I/O pin using a 74HC595 Shift Register chip.
' This include file replaces the compiler's Print command with the routine that will control the LCD via a single pin
'
' Written by Les Johnson for the Positron8 BASIC compiler.
' https://sites.google.com/view/rosetta-tech/home
'
' Version 2.0
'
' This library uses some of the standard Print Declares to set the pin for the Shift register,
' and the amount of lines, and the delay after commands are sent to the LCD
'
' The Declares it uses are:
'    Declare LCD_DTPin = Port.Pin               ' The pin to use for the interface to the 74HC595 Shift Register
'    Declare LCD_Lines = 2 or 4                 ' The amount of lines on the LCD for the compiler's Cursor and Print At to set calculations by
'    Declare LCD_CommandUs = Integer Value      ' The amount of time (in uS) after a command has been sent to the LCD
'
    #Disable Print                              ' Disable the compiler's Print library routine
'
' Create some compiler system variables and user variables for the replacement Print subroutine
'
    Dim BPF  As Byte System
    Dim PP3  As Byte System
    Dim PP3H As Byte System
    Dim PP4  As Byte System
    Dim PP4H As Byte System

    Dim _LCD_bValue         As PP3              ' Temporary byte for commands to send to the LCD
    Dim _LCD_bByteStore     As PP3H             ' Keeps the byte sent to the the Print routine safe so WREG can be restored before exiting
    Dim _LCD_tInitialised   As BPF.1            ' 1 if the LCD has been initialised
    Dim _LCD_tRS            As BPF.0            ' 0 if a command is to be sent to the LCD

    Dim _LCD_bTemp          As PP4              ' Temporary byte to hold the pin states for the LCD
    Dim _LCD_bShiftSteps    As PP4H             ' Holds the sequence of events required to send out a byte to the LCD
    Dim _LCD_bBitsOut       As Byte Access      ' Holds the value to shift to the LCD
    Dim _LCD_bStepsCounter  As Byte Access      ' Holds the counter for the events required to send out a byte to the LCD
    Dim _LCD_bBitShiftIndex As Byte Access      ' Holds the amount of bits to send in the loop

'--------------------------------------------------------------------
    GoTo _Shift_Print_Main_                             ' Jump over the replacement Print subroutine

'--------------------------------------------------------------------
' Replacement library routine for the compiler's Print command
' The LCD connects to the PIC via a 74HC595 shift register
' And this routine interfaces to the 74HC595 via a single pin
' Input     : WREG holds the byte to send to the LCD
' Output    : WREG still holds the byte sent to the LCD
' Notes     : Uses the compiler's Declare LCD_DTPin setting for the pin to use to communicate to the 74HC595
'
__Print_:
$if _core = 14                                          ' Is it a 14-bit core device?   
    $ifdef _ecore                                       ' Is it an enhanced 14-bit core device?
        _LCD_bByteStore = WREG                          ' Transfer the byte to print from WREG to _LCD_bByteStore
    $else                                               ' Otherwise... Use a macro to transfer WREG
        Wreg_Byte _LCD_bByteStore                       ' Transfer the byte to print from WREG to _LCD_bByteStore
    $endif
$else                                                   ' Otherwise... WREG is an SFR that is accessable
    _LCD_bByteStore = WREG                              ' Transfer the byte to print from WREG to _LCD_bByteStore
$endif
    If _LCD_tInitialised = 0 Then                       ' Has the LCD been initialised?
        PinOutput __LCD_DTPORT.__LCD_DTPIN              ' No. So initialise the LCD
        _LCD_bShiftSteps = 1
        _LCD_bValue = $33
        GoSub _Print_ShiftOutByte
        DelayUS 5000
        GoSub _Print_ShiftOutByte
        DelayUS 100
        GoSub _Print_ShiftOutByte
        DelayUS 100
        _LCD_bValue = $22                               ' \ Start 4-bit mode
        GoSub _Print_ShiftOutByte                       ' /
        DelayUS 100

        _LCD_bShiftSteps = 3
        _LCD_bValue = $28                               ' \
        GoSub _Print_ShiftOutByte                       ' / Set the LCD for 4-bit mode, 5x7
        _LCD_bValue = $0C                               ' \
        GoSub _Print_ShiftOutByte                       ' / Display On
        _LCD_bValue = $06                               ' \
        GoSub _Print_ShiftOutByte                       ' / Entry Mode
        _LCD_tInitialised = 1                           ' Indicate that the LCD has been initialised
    EndIf
   
    If _LCD_bByteStore = $FE Then                       ' Is the value to send $FE?
        _LCD_tRS = 0                                    ' Yes. So the next byte is a command so clear the RS flag bit
        GoTo _Print_Exit                                ' Exit Print for now
    EndIf
    _LCD_bValue = _LCD_bByteStore                       ' Get the byte to send back and load it into _LCD_bValue
    GoTo _Print_ShiftOutByte
   
_Print_Continue:
    If _LCD_tRS = 0 Then                                ' Is it a Command byte to send?
        #ifdef __LCD_COMMANDUS                          ' Has Declare LCD_CommandUs been used in the main program?
            DelayUS __LCD_COMMANDUS                     ' Yes. So use the value in it for the delay for LCD commands
        #endif
        #ifndef __LCD_COMMANDUS                         ' Has Declare LCD_CommandUs been used in the main program?
            DelayUS 30                                  ' No. So use a default delay for LCD commands
        #endif
    EndIf
    _LCD_tRS = _LCD_tInitialised                        ' Set RS to Data next time
_Print_Exit:
$if _core = 14                                          ' Is it a 14-bit core device?
    $ifdef _ecore                                       ' Is it an enhanced 14-bit core device?
        WREG = _LCD_bByteStore                          ' Yes. So recover the byte sent back into WREG
    $else                                               ' Otherwise... Use a macro to transfer into WREG
        Byte_Wreg _LCD_bByteStore                       ' Recover the byte sent back into WREG
    $endif
$else                                                   ' Otherwise... WREG is an SFR that is accessable
    WREG = _LCD_bByteStore                              ' Recover the byte sent back into WREG
$endif
    Return
'
' Send a byte to the LCD via the 74HC595
'
_Print_ShiftOutByte:
    For _LCD_bStepsCounter = 0 To _LCD_bShiftSteps      ' Create a loop for the steps required for the byte to be sent
        Select _LCD_bStepsCounter
            Case 0                                      ' Is _LCD_bStepsCounter 0?
                _LCD_bTemp = _LCD_bValue & %11110000    ' Yes. So isolate the upper nibble
                _LCD_bTemp.2 = _LCD_tRS                 ' Set the RS Bit
                _LCD_bTemp.3 = 1                        ' Set the Enable Bit

            Case 1, 3                                   ' Is _LCD_bStepsCounter 1 or 3?
                _LCD_bTemp.3 = 0                        ' Yes. So clear the Enable Bit

            Case 2                                      ' Is _LCD_bStepsCounter 3?
                _LCD_bTemp = _LCD_bValue & %00001111    ' Yes. So isolate the lower nibble
                Swap _LCD_bTemp, _LCD_bTemp             ' Swap nibbles
                _LCD_bTemp.2 = _LCD_tRS                 ' Set the RS Bit
                _LCD_bTemp.3 = 1                        ' Set the Enable Bit
        EndSelect
        _LCD_bBitsOut = _LCD_bTemp                      ' Transfer the byte into the shifter byte
        '
        ' Shift the bits held in _LCD_bBitsOut to the 74HC595
        '
        For _LCD_bBitShiftIndex = 6 To 0 Step -1        ' Create a loop for the 7-bits to shift
            Rol _LCD_bBitsOut                           ' Shift the bits left
            If STATUSbits_C = 1 Then                    ' Is the Carry Flag set?
                PinClear __LCD_DTPORT.__LCD_DTPIN
                DelayUS 1                               ' Delay 1 µs
                PinSet __LCD_DTPORT.__LCD_DTPIN
                DelayUS 15                              ' Delay 15 µs
            Else
                PinClear __LCD_DTPORT.__LCD_DTPIN
                DelayUS 15                              ' Delay 15 µs
                PinSet __LCD_DTPORT.__LCD_DTPIN
                DelayUS 30                              ' Delay 30 µs
            EndIf
        Next
        '
        ' Alter the Latch pin
        '
        PinClear __LCD_DTPORT.__LCD_DTPIN
        DelayUS 200                                     ' Delay 200 µs for Latch
        PinSet __LCD_DTPORT.__LCD_DTPIN
        DelayUS 300                                     ' Delay 300 µs
    Next
    GoTo _Print_Continue

'-----------------------------------------------------------------------
_Shift_Print_Main_:

$endif  ' _Shift_Print_inc_
Name the library file: "Shift_Print.inc" and copy it to the compiler's shared "Includes" folder: "C:\Users\User Name\PDS\Includes". This will allow all BASIC programs to see it and use it. The shared Includes folder is where the compiler's external libraries are stored.

Below is a demo for the above library controlling an Hitachi 4x20 LCD:
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Demonstrate the single pin LCD control on an 8-pin, standard 14-bit core PIC12F675 device
' Setup for a 4x20 LCD
'
' Written by Les Johnson for the Positron8 BASIC compiler.
' https://sites.google.com/view/rosetta-tech/home
'
    Device = 12F675                     ' Tell the compiler what device is being compiled for
    Declare Xtal = 20                   ' Tell the compiler what speed the device will be operating at
'
' Setup the LCD configuration

    Declare LCD_DTPin = GPIO.2          ' The pin to use for the interface to the 74HC595 Shift Register
    Declare LCD_Lines = 4               ' The amount of lines on the LCD for the compiler's Cursor and Print At to set calculations by
    Declare LCD_CommandUs = 200         ' The amount of time (in uS) after a command has been sent to the LCD
 
    Include "Shift_Print.inc"           ' Load the LCD Shift Print library routine into the program
 
    Dim MyByte As Byte = 0

'-----------------------------------------------------------------------
' Create a loop and display the values on all the lines of the LCD
'
Main:
    DelayMS 100                     ' Wait for the LCD to stabilise
    Cls                             ' Clear the LCD's display

    Do
        Print At 1, 1, "Line1: MyByte = ", At 1, 18, Dec MyByte
        Print At 2, 1, "Line2: MyByte = ", At 2, 18, Dec MyByte
        Print At 3, 1, "Line3: MyByte = ", At 3, 18, Dec MyByte
        Print At 4, 1, "Line4: MyByte = ", At 4, 18, Dec MyByte
        MyByte = MyByte + 1
        DelayMS 100
    Loop

The circuit for the above demo is shown below:
LCD_4x20_Through_ShiftRegiser.jpg

userrus

Hello!
Unfortunately, this code is compiled with errors.
Guys, posting such codes is not respecting yourself!
Best regards, from Belarus.

basiclover


Dompie


top204

A comma was hanging off the end of one of the Print command lines in the demo program, that made the program look for more parameters on the next line. Now removed.

By all means state that there is a problem with a piece of code, but please do not use "rather offensive" comments, and try and look at the error message because it will tell you where the tiny issue is.

John Drew

@userrus
We are all human and make mistakes.
This forum is built around helping and supporting each other.
We are very fortunate that the developer of Positron is so helpful by posting possible solutions, the fact a comma was left behind reminds me of how often I have done something similar.
Best wishes from Australia,
John

Gamboa

Thank you Les.

There are always people who try to improve the world and help those around them and then people who protest at everything without doing anything. Again thank you Les.

Regards,
Gamboa
Long live for you

Craig

Thank You Les for all your Kindness and Endless Hard work and always been willing to help and Share, It is VERY Much appreciated!

Kind Regards

Craig


Giuseppe MPO

One of our great poets (Dante Alighieri) said: do not care about them but look and pass. Nothing more appropriate at this time. Les, congratulations and thanks for the work you have done and are doing

charliecoutas

Les, if there was a gold medal for "Compiler Creation and Support" at the Tokyo Olympics, you would walk away with it!

basiclover


top204

QuoteOne of our great poets (Dante Alighieri) said: do not care about them but look and pass.

Wonderful, profound, words and I truly wish I could just "look and pass". However, since my brain hemorage, I now suffer badly from anxiety because of where the damage is. It's so strange and upsetting because before the injury I was a laid back person who didn't give a damn about anxiety and did not actually know what it felt like. :-(

I know I have always had a bit of an inferiority complex with myself, which is why I have been taken for granted all of my working life, and "other people" have made money because of my knowledge and creativity, and not me!. But I never see myself as "clever", just normal with a very curious mind and I never ask a question if I can learn the answer myself with a bit of study.

Giuseppe MPO

Les, the work you have done is remarkable, very difficult to believe that it was done by one person.
You have to believe more in yourself, continue to do this job, even with all the problems that derive
from it (new PIC models, registers and always different functions etc. etc.) denotes an out of the ordinary and very creative mind,
then if it also considers the problems you have had it is even more important than the work you have done.
Believe in yourself and do not care about the people who criticize you, in my real experience, they are the people who
criticize others the most, they are the ones with low intelligence, the ones who can't do anything in life.
Les courage ....

JonW

Really clever and elegant use of the shift register if you are short of I/O.

Keep them coming Les!

JonW

Les what's the ACCESS statement do?

    Dim _LCD_bBitsOut      As Byte Access      ' Holds the value to shift to the LCD

JonW

Thought I had seen this before many yrs ago by Roman Black.  Simplification and brilliance


top204

I originally saw it implemented on a BASIC Stamp2 device in the early 2000s. Then I saw that Roman Black has also implemented it much later.

It is an excellent mechanism that takes advantage of the shift register.

GDeSantis

I built a one wire LCD interface circuit using Les' circuit and software and it worked great when driving a 2x16 alphanumeric display.

Since previous posts mentioned Roman Black, a link to his comments regarding the one wire LCD interface is noted below:

https://www.romanblack.com/shift1.htm