Proton code to Drive a 2x16 LCD with a single pin via a 74HC595 shift register

Started by top204, Feb 04, 2021, 09:58 PM

Previous topic - Next topic

top204

I remember seeing this quite a few years ago on a different microcontroller type and thought it was a good idea, and a thread on this forum 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 PIC12F675 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:

keytapper

As usual a great work Mr Les!!!

The only think I may disagree it's about the pin layout. Because I made a small interface which mimic the byte assignment for the Arduino PCF8574. Just to keep a similar condition which would work just by changing the interface. But this is probably a different case. The only part that I would like to add is the way to control the backlight LED.

For some extent it would be necessary to add declares  for the pin assignment.
Ignorance comes with a cost

top204

QuoteI get compilation errors ?
Symbol not previously defined [print]

You need the latest compiler version. Quite a few versions ago, I had to rename the compiler's library subroutines so they were suitable for the new assembler program.

You can download it from here:
https://sites.google.com/view/rosetta-tech/home

FiremanTR

The best thing I know is that I know nothing
__SOCRATES__