Drive a 2x16 LCD with only one pin of the microcontroller

Started by Moscrash, Feb 03, 2021, 07:24 AM

Previous topic - Next topic

Moscrash

Hi everyone. Some time ago I had read in the old forum that it was possible to drive a 2x16 LCD with a single pin of the microcontroller through a shift register of the type 74hc595.Have any of you succeeded? If you can upload a sample file? Thank you in advance.

Moscrash

rick.curl

Hi Moscrash-
The info you are looking for was posted on the other forum by George Towler. I have a copy of it here but I haven't figured out how to upload to the new forum yet.  I did find a copy of it on GitHub.  Look here: https://github.com/HvandeVen/PCF8574-Display
-Rick

Moscrash

Thank you very much Rick. Have a nice day.

Moscrash

keytapper

For a 74xx595 exists a method and it's ported on GCBasic, but for Proton it would take some fiddling to get the same result.

In practice is just a matter to find the correct timing in order to trigger the data pin in the necessary period.
Ignorance comes with a cost

Moscrash

Thank you Keytapper. I'll try to do some experiments.

Have a nice day.
Moscrash.

See_Mos

Just added a copy of the information from the old forum to the download section

Moscrash


top204

I remember seeing this quite a few years ago on a different microcontroller type and thought it was a good idea, and this thread got my interest up again, so I have created a library routine that replaces the compiler's Print command with the mechanism to control a 2x16 Hitachi Alphanumeric LCD with a single pin via a 74HC595 shift register.

The code below is the library:
$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
'
' The circuit diagram can be found here:
' https://drive.google.com/file/d/1GZwvOt88bgCRtThvqMBkdJscaZrvgd_N/view?usp=sharing
'
' Written by Les Johnson for the Proton8 BASIC compiler.
' https://sites.google.com/view/rosetta-tech/home
'
    #disable Print                              ' Disable the compiler's Print library routine

$ifndef LCD_Pin                                 ' Has the LCD_Pin been defined in the main program?
    $define LCD_Pin PORTB.2                     ' No. So set a default pin
    $SendWarning "LCD_Pin is missing from the main program so using the default pin of PORTB.2"
$endif
'
' Create some compiler system 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_bChar          As PP3              ' Temporary byte for commands to send to the LCD
    Dim _LCD_bCharStore     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     : None
'
__Print_:
$if _CORE = 14                                          ' Is it a 14-bit core device?
    $ifdef _ecore                                       ' Is it an enhanced 14-bit core device?
        _LCD_bCharStore = WREG                          ' Transfer the byte to print from WREG to _LCD_bCharStore
    $else
        Wreg_Byte _LCD_bCharStore                       ' Transfer the byte to print from WREG to _LCD_bCharStore
    $endif
$else                                                   ' Otherwise... WREG is an SFR that is accessable
    _LCD_bCharStore = WREG                              ' Transfer the byte to print from WREG to _LCD_bCharStore
$endif
    If _LCD_tInitialised = 0 Then Goto _LCD_Init        ' Make sure the LCD was initialised
_Print_Continue1:
    If _LCD_bCharStore = $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_bChar = _LCD_bCharStore                        ' Get the byte to send back

_LCD_SendCommand:
    Goto _Print_ShiftOutByte
_Print_Continue2:
    If _LCD_tRS = 0 Then                                ' Is it a Command byte?
        If _LCD_bChar = 1 Then                          ' Yes. So is the command Cls?
            DelayUS 30                                  ' Yes. So delay for Clear Screen
        Else If _LCD_bChar = 2 Then                     ' Is the command Home?
            DelayUS 30                                  ' Yes. So a longer delay is required for Home
        EndIf
        DelayUs 1                                       ' A standard delay for LCD commands
    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_bCharStore                          ' Recover the byte sent back into WREG
    $else
        Byte_Wreg _LCD_bCharStore                       ' Recover the byte sent back into WREG
    $endif
$else                                                   ' Otherwise... WREG is an SFR that is accessable
    WREG = _LCD_bCharStore                              ' Recover the byte sent back into WREG
$endif
    Return
'
' Initialise the LCD
'
_LCD_Init:
    Output LCD_Pin
    _LCD_bShiftSteps = 1
    _LCD_bChar = $33
    Gosub _LCD_SendCommand
    DelayUS 5000
    Gosub _LCD_SendCommand
    DelayUS 100
    Gosub _LCD_SendCommand
    DelayUS 100
    _LCD_bChar = $22                                    ' \ Start 4-bit mode
    Gosub _LCD_SendCommand                              ' /
    DelayUS 100

    _LCD_bShiftSteps = 3
    _LCD_bChar = $28                                    ' \
    Gosub _LCD_SendCommand                              ' / Set for 4-bit mode, 2 line, 5x7
    _LCD_bChar = $0C                                    ' \
    Gosub _LCD_SendCommand                              ' / Display On
    _LCD_bChar = $06                                    ' \
    Gosub _LCD_SendCommand                              ' / Entry Mode
    _LCD_tInitialised = 1                               ' Indicate that the LCD has been initialised
    Goto _Print_Continue1
'
' Send a byte to the LCD via the 74HC595
'
_Print_ShiftOutByte:
    For _LCD_bStepsCounter = 0 to _LCD_bShiftSteps      ' Create a loop for teh steps required for the byte to be sent
        Select _LCD_bStepsCounter
            Case 0                                      ' Is _LCD_bStepsCounter 0?
                _LCD_bTemp = _LCD_bChar & %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_bChar & %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_Pin
                DelayUS 1                               ' Delay 1 µs
                PinSet LCD_Pin
                DelayUS 15                              ' Delay 15 µs
            Else
                PinClear LCD_Pin
                DelayUS 15                              ' Delay 15 µs
                PinSet LCD_Pin
                DelayUS 30                              ' Delay 30 µs
            EndIf
        Next
        '
        ' Alter the Latch pin
        '
        PinClear LCD_Pin
        DelayUS 200                                     ' Delay 200 µs for Latch
        PinSet LCD_Pin
        DelayUS 300                                     ' Delay 300 µs
    Next
    Goto _Print_Continue2

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

$endif  ' _Shift_Print_inc_

And the code below is a demo program showing it working on an 8-pin 12F675 device:
'
' Demonstrate the single pin LCD control using a standard 14-bit core PIC12F675 device
' Setup for a 2x16 LCD
'
' The circuit diagram can be found here:
' https://drive.google.com/file/d/1GZwvOt88bgCRtThvqMBkdJscaZrvgd_N/view?usp=sharing
'
' Written by Les Johnson for the Proton8 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

$define LCD_Pin PORTB.2                 ' Set the pin to use for the communication with the 74HC595 shift register

    Include "Shift_Print.inc"           ' Load the LCD Shift Print library routine into the program

    Dim MyByte As Byte = 0
    Dim MyFloat As Float = 3.14

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

    Do
        Print At 1, 1, "MyFloat = ", Dec1 MyFloat
        Print At 2, 1, "MyByte = ", Dec MyByte, "  "
        MyFloat = MyFloat + 0.1
        MyByte = MyByte + 1
        DelayMs 100
    Loop

The circuit diagram is below set in a simulation of the code in Isis, and a zip file containing the Proton8 program firmware and circuit diagram:

Moscrash

Thank you very much Les. You are always there to solve all our problems. Thanks again for your time, your dedication and your availability. You are always the best.

Have a nice day.

Moscrash.

P.S. Sorry for my bad english ;D

keytapper

As mentioned, there should be a pin for backlight.
Ignorance comes with a cost

joesaliba

 @top204 That is absolutely fantastic Les. I always wondered how to use an LCD from a single pin.

Thank you

top204

QuoteAs mentioned, there should be a pin for backlight.

Getting the LCD's backlight to work will be very straightforward and one of the unused pins on the 74HC595 device will need to be attached to a MOSFET or transistor to enable/disable the backlight.

Then a procedure can be created to toggle the 74HC595 pins in the correct sequence for the output bit to to be set or cleared. I'll leave that up to the users, and look forward to seeing the code.

joesaliba

Altering this code will be good to drive 7-segments without doing a table using a CD4094. However I do not know if timings for latch etc... are same as for 74HC595.

Also, the include file is for a 2-line LCD. Am I correct to say that by changing: -

_LCD-bChar from $28 to $50

I can drive a 4-line LCD?

Thank you

keytapper

Quote from: top204 on Feb 06, 2021, 10:14 AMI'll leave that up to the users, and look forward to seeing the code.
I'll study that case, but currently I'm a bit out of resources. BTW,  is the shifting only for 7 bits, as Roman Black mentioning?
Ignorance comes with a cost

Giuseppe MPO

I have tried to make the circuit and try it, I have inserted the include and I must say that it works really well.
Thanks to Mr Les a truly exceptional job.

dr-zin

I downloaded and tried this demo program, and I can't get it to compile (about 20 errors saying something like "ASM ERROR: Error[113].......Symbol not previously defined (Print)").  Obviously I'm doing something wrong or it doesn't like me?  If I comment out the Include file line, it compiles correctly.  Can someone please tell me the dumb error I'm making?
BTW, was there a time when Include files ended in ".int" instead of ".inc"?  I have some ancient programs that include things like:  Include "PROTON_G4.INT".  Obviously, my knowledge of the file structure here is deeply flawed.  Please elucidate me if you can.  --Gracias

Pepe

In line 62 replace __Print_: with @Print: in  Shift_Print.Inc

Pepe

Example with backlight in Q1 and simulation in proteus

Giuseppe


dr-zin

Still nothing but compile-time errors. :(   Guess I'll have to continue on using the conventional technique?  Thanks anyway.