I2CLCDPCF8574

The I2CLCD Module

This module allows you to use the I2C LCD Displays that are interfaced with the PCF8574 expander chip. It works with both the hardware and software I2C libraries. And supports both 16x2 line and 16x4 line displays with no configuration.

I have worked with the I2CLCD module to get it as close as possible to the original Swordfish LCD module for the HD44780 display. This allows you to use the same LCD instructions as you would with the Swordfish LCD library.

  • Note: This library will require you to use the latest version of Swordfish as of v2.2.3.3

About PCF8574

The PCF8574 module board is a breakout board for the I2C IO Expander chip PCF8574 designed for LCD interfacing via a 16-pin header. There is a jumper to turn on or off the LCD backlight. As well as a potentiometer to adjust the LCD screen contrast. And with solder pads, A0 A1 A2. You can solder any bit of them to make it a different address if needed. The board has a power led as well as a voltage regulator and most importantly a header to connect your I2C 2-wires (SCL, SDA).

The SCL and SDA lines will require the use of a pullup resistor since it is used as a bidirectional open-drain. Serial clock (SCL), serial data (SDA)) @ 100kHz.








Module Options

The modules default I2C address is set to $4E, this was the address that my test boards was set too, but you are able to change the address to suit. As the module is self Initialized you will need to disable the Initialize() options within the module, and add the Initialize() code to your main prog with the included address within the parenthesis.

Example: Change the following module options to false

#option I2CLCD_INITIALIZE = true

And add the following code to your main prog (with your desired address).

Initialize($4E)

Sample Code (Software SI2C)


This sample code shows you how to use the I2CLCD library with the software SI2C options. You don't need to include the I2C/S12C libraries as the module takes care of that for you. You just need to add the optional lines of code and set your SDA, SCL pins.




' Using software I2C to control the I2CLCD display.
Device = 18F2550
Clock = 20        

// optional configure SDA and SCL if using software I2C...
#option USE_SI2C = true
#option I2C_SDA = PORTA.4
#option I2C_SCL = PORTA.5

// import libraries...   
Include "I2CLCD.bas"

' write string to display at locations...
I2CLCD.WriteAt(1,2,"Swordfish") 
I2CLCD.WriteAt(2,4,"I2C LCD")
I2CLCD.WriteAt(3,6,"Library") 
I2CLCD.WriteAt(4,8,"by Bitfogav.") 

Sample Code (BarGraphs)


' Draws two graphs to the LCD display lines..

Device = 18F2550
Clock = 20        

// import libraries...
Include "I2CLCD.bas"   
Include "utils.bas"

// refresh speed...
Const UpdateMS = 50

// initialise bit patterns...
// programmable characters are available that use codes $00 to $07. 
// Create the bit patterns that make up the bars in the LCD's CGRAM.
// The vertical bars are made up of 8 identical bit patterns  
Const CGRAM(32) As Byte = ($00,$00,$00,$00,$00,$00,$00,$00,  // base bar
                           $10,$10,$10,$10,$10,$10,$10,$00,  // 8 x %10000 = |
                           $14,$14,$14,$14,$14,$14,$14,$00,  // 8 x %10100 = ||
                           $15,$15,$15,$15,$15,$15,$15,$00)  // 8 x %10101 = |||

// output byte pRepValue times...
NoInline Sub Rep(pValue, pRepValue As Byte)
   Dim Index As Byte
   Index = 0
   While Index < pRepValue
      I2CLCD.Write(pValue)
      Inc(Index)
   Wend
End Sub

// display the bar...
NoInline Sub Bargraph(pLine, pBarValue As Byte)
   Const BASE_BAR = 0                  // ASCII value of 0 bar (blank)
   Const FULL_BAR = 3                  // ASCII value of ||| bar
   Const BAR_WIDTH = 16                // Max width in characters of bar
   Const MAX_BAR_COUNT = BAR_WIDTH * 3 // Max bar counts 

   Dim NumberOfBars As Byte = pBarValue / 3
   Dim Balance As Byte = pBarValue Mod 3

   I2CLCD.MoveCursor(pLine,1)
   Rep(FULL_BAR,NumberOfBars)  
   I2CLCD.Write(Balance)
   Rep(BASE_BAR,BAR_WIDTH - (NumberOfBars + Min(Balance,1)))
End Sub

// loop index
Dim Index As Byte

// starting point of prog...   
   I2CLCD.Write(CGRAM)
   DelayMS(2000)

// display the bar
While true
   For Index = 0 To 48
      Bargraph(1,Index)
      Bargraph(2,48 - Index)
      DelayMS(UpdateMS)
   Next
   For Index = 48 To 0 Step -1
      Bargraph(1,Index)
      Bargraph(2,48 - Index)
      DelayMS(UpdateMS)
   Next
Wend 

I2CLCD Module

{
*****************************************************************************
*  Name    : I2CLCD.BAS                                                     *
*  Author  : Gavin Wiggett (Bitfogav)                                       *
*  Notice  : Copyright (c) 2020                                             *
*          : All Rights Reserved                                            *
*  Date    : 14/06/2020                                                     *
*          :                                                                *
*  Version : 1.0                                                            *
*  Notes   : I2CLCD.bas Lib for use with the PCF8574 controlled LCD.        *
*          : It will work with I2C Hardward or Software Libraries.          *
*          : just include the optonal settings in your main prog.           *
*          :                                                                *
*          : Works with 2line and 4line LCD.                                *
*          :                                                                *
*          : Note: Lib doesn't support reading the PCF8574 or LCD busy flag.*
*          : (this maybe added in a future update).                         *
*****************************************************************************
}

Module I2CLCD

// default module options for software I2C if used - user options can override these values...
#option USE_SI2C = false
#option I2C_SDA = PORTB.2
#option I2C_SCL = PORTB.3

// Use hardware or software I2C, (default is hardware I2C)...
#if IsOption (USE_SI2C) And Not (USE_SI2C in (true, false))
   #error USE_SI2C, "Invalid Option, USE_SI2C must be true or false"
#endIf

// SCL option for software I2C only...
#if IsOption(I2C_SCL) And Not IsValidPortPin(I2C_SCL) 
   #error I2C_SCL, "Invalid option. I2C clock must be a valid port pin."
#endif

// SDA option for software I2C only...
#if IsOption(I2C_SDA) And Not IsValidPortPin(I2C_SDA) 
   #error I2C_SDA, "Invalid option. I2C data line must be a valid port pin."
#endif

// By default, the module includes a call to I2C lcd Initialize() at startup. 
// set this option false to exclude initializer... user MUST then call Initialize()
// you can then change the I2C address by calling Initialize() and adding your 
// desired address for example I2CLCD.Initialize($20).  
#option I2CLCD_INITIALIZE = true
#if Not(I2CLCD_INITIALIZE in (true, false))
   #error I2CLCD_INITIALIZE, "Invalid option"
#endif

// Depending on the #option USE_SI2C setting, will select Hardware or Software I2C... 
#if (_mssp > 0) And Not USE_SI2C
   Include "I2C.bas"
#else
   Include "SI2C.bas"
#endif

// public LCD control commands...
Public Const 
   cmdCGRAM           = $40,
   cmdDDRAM           = $60,
   cmdClear           = $01,
   cmdHome            = $02,
   cmdEntryMode       = $04,
   cmdUnderLineON     = $0E,  
   cmdCursorOFF       = $0C,
   cmdCursorON        = $0F,
   cmdBackLightON     = $08,
   cmdBackLightOFF    = $00,
   cmdMoveCursorLeft  = $10,
   cmdMoveCursorRight = $14,
   cmdDisplayON       = $0C,
   cmdDisplayOFF      = $08,
   cmdMoveLeft        = $18,
   cmdMoveRight       = $1E      

// Bring LCD controls into the module...
Private Dim 
   RS,                          // LCD register select pin. 
   I2C_Addrs As Byte,           // I2C device address (default $4E).
   I2CInitialize As Initialize  // Bring Hardware or Software I2C into the module...
Private Dim
   FPosX As Byte = 1,           // local x, y coordinates.
   FPosY As Byte = 1            // initialized as per compiler update 2.2.3.3. 

// Bring LCD backlight into the module...      
Private Dim 
   BackLight_State As Byte = cmdBackLightON   

'****************************************************************************
'* Name    : ExpanderWrite                                                  *
'* Purpose : Write data to PCF8574 I2C                                      *
'****************************************************************************
Private Sub ExpanderWrite(pData As Byte) 
   Start()
   WriteByte(I2C_Addrs)
   WriteByte(pData Or BackLight_State)
   Stop() 
End Sub

'****************************************************************************
'* Name    : SetData                                                        *
'* Purpose : Write 4 bit data                 (4 bit interface)             *
'****************************************************************************
Private Sub SetData(pNibble As Byte)   
   pNibble = pNibble Or RS  // Include RS Value To LSB OF Data     
   ExpanderWrite(pNibble Or $04) 
   ExpanderWrite(pNibble And $FB) 
   DelayUS(50) 
End Sub

'****************************************************************************
'* Name    : Command                                                        *
'* Purpose : Write command data to LCD        (4 bit interface)             *
'****************************************************************************
Public Sub Command(pCommand As Byte) 
   RS = 0   
   SetData(pCommand And $F0)
   SetData((pCommand << 4) And $F0)
   DelayUS(50)   
End Sub

'****************************************************************************
'* Name    : WriteItem (OVERLOAD)                                           *
'* Purpose : Write a single byte to the LCD   (4 bit interface)             *
'****************************************************************************
Private Sub WriteItem(pData As Byte)
   RS = 1
   SetData(pData And $F0)
   SetData((pData << 4) And $F0)
   DelayUS(50)   
End Sub

'****************************************************************************
'* Name    : WriteItem (OVERLOAD)                                           *
'* Purpose : Write a string to the LCD                                      *
'****************************************************************************
Private Sub WriteItem(pText As String)
   Dim TextPtr As POSTINC0
   Dim Text As INDF0
   FSR0 = AddressOf(pText)
   While Text <> 0
      WriteItem(TextPtr)
   Wend
End Sub

'****************************************************************************
'* Name    : WriteItem (OVERLOAD)                                           *
'* Purpose : Initialise the CGRAM with constant data array. Eight           *
'*         : programmable characters are available (0..7).                  *
'****************************************************************************
Private Sub WriteItem(ByRefConst pBitmap() As Byte)
   Dim Index As Byte
   Command(cmdCGRAM)
   For Index = 0 To Bound(pBitmap)
      WriteItem(pBitmap(Index))              
   Next
   Command(cmdDDRAM)
End Sub

'****************************************************************************
'* Name    : Write (COMPOUND)                                               *
'* Purpose : Calls one or more WriteItem() subroutines                      *
'****************************************************************************
Public Compound Sub Write(WriteItem)

'****************************************************************************
'* Name    : MoveCursor                                                     *
'* Purpose : Move the cursor to line and column                             *
'****************************************************************************
Public Sub MoveCursor(pLine, pCol As Byte) 
   Dec(pCol)   
   Select pLine
      Case 1 : Command($80 + pCol)
      Case 2 : Command($C0 + pCol)
      Case 3 : Command($94 + pCol)
      Case 4 : Command($D4 + pCol)  
   EndSelect 
End Sub

'****************************************************************************
'* Name    : MoveTo (PRIVATE)                                               *
'* Purpose : Moves cursor to module private x, y location                   *
'****************************************************************************
Private Inline Sub MoveTo()
   MoveCursor(FPosY, FPosX)
End Sub 

'****************************************************************************
'* Name    : SetLocationX (PRIVATE)                                         *
'* Purpose : Set cursor x                                                   *
'**************************************************************************** 
Private Inline Sub SetLocationX(pX As FPosX)
End Sub

'****************************************************************************
'* Name    : SetLocationY (PRIVATE)                                         *
'* Purpose : Set cursor y                                                   *
'****************************************************************************
Private Inline Sub SetLocationY(pY As FPosY)
End Sub

'****************************************************************************
'* Name    : WriteAt (COMPOUND)                                             *
'* Purpose : Calls one or more WriteItem() subroutines at location x, y     *
'****************************************************************************
Public Compound Sub WriteAt(SetLocationY, SetLocationX, MoveTo, WriteItem) 

'****************************************************************************
'* Name    : Cls                                                            *
'* Purpose : Clear the LCD screen                                           *
'****************************************************************************
Public Sub Cls()
   Command($01)  
   DelayMS(30) 
End Sub

'****************************************************************************
'* Name    : Backlight                                                      *
'* Purpose : Set LCD backlight on/off                                       *
'****************************************************************************
Public Sub Backlight(pSet As Boolean = true)
   If pSet = true Then
      BackLight_State = cmdBackLightON   
   Else 
      BackLight_State = cmdBackLightOFF
   End If 
   ExpanderWrite($00) 
End Sub

'****************************************************************************
'* Name    : ShiftLeft                                                      *
'* Purpose : Shift the LCD display left                                     *
'****************************************************************************
Public Sub ShiftLeft()
   Command($18)    
   DelayUS(40) 
End Sub

'****************************************************************************
'* Name    : ShiftRight                                                     *
'* Purpose : Shift the LCD display right                                    *
'****************************************************************************
Public Sub ShiftRight()
   Command($1C) 
   DelayUS(40)    
End Sub

'****************************************************************************
'* Name    : Initialize (Public)                                            *
'* Purpose : Initialize the LCD - Optional User can define i2c device addr  *
'****************************************************************************
Public Sub Initialize(pAddr As Byte = $4E)
   DelayMS(100)   // wait for LCD to stabilise... 
   I2C_Addrs = pAddr
   ExpanderWrite($00) 
   DelayMS(30) 
   Command($03) 
   DelayMS(5) 
   Command($03) 
   DelayMS(5) 
   Command($03) 
   DelayMS(5) 
   Command(cmdHome) 
   DelayMS(5) 
   Command($20 Or (2 << 2))  ' function set (4-bit interface, 2 lines, 5*7 Pixels) 
   DelayMS(50) 
   Command(cmdDisplayON) 
   DelayMS(50) 
   Command(cmdClear) 
   DelayMS(50) 
   Command(cmdEntryMode Or cmdHome) 
   DelayMS(50) 
End Sub

// initialise the I2C module (HW or SW)...  
I2CInitialize()

// initialise the I2CLCD module, you must disable the option above
// if you wish to use your own I2C address, see the option above...
#if I2CLCD_INITIALIZE
Initialize()
#endif          

End Module

Download Library

Download I2CLCD Library V1.0
I2CLCDv1.zip