Announcement

Collapse
No announcement yet.

Example code I2C-LCD character displays

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Example code I2C-LCD character displays

    Hi All!

    Over the years I've seen forum posts where some people have had problems to successfully communicate to LCD character displays which use an I2C interface. So I thought I would post my approach to this task hoping it may help others in dealing with these devices.

    Of course, the gold standard for PBP coding to deal with I2C-LCD character display devices is the elegant code posted several years ago by Darryl Taylor. See for example post #3 in:
    http://support.melabs.com/forum/picb...f8574-20x4-lcd

    My code is clunkier but I've had good success using it and it gets the job done. It is set up to handle transfers in 4-bit mode, where the 4 data/command bits can reside in the low nibble or the hi nibble of the PCF8574x PortP. It also handles variable bit positions for RS, RW, and E. The approach is that the user declares if the least significant data bit is bit 4 or bit 0, and declares which bit positions the HD44780 RS, RW, and E occupy in the PCF8574. My code does not handle the backlight through the I2C interface.

    I am posting both the interface include file LCD_I2C.inc, and a test program LCD_I2Ctest.pbp

    Here's the interface include file:
    Code:
    '******************************************************************************
    '*  Name    : LCD_I2C.inc,  written for 16F877                                                 
    '*  Author  : Steve Roberts                                      
    '*  Notice  : Copyright (c) 2017                                
    '*  Date    : 8/16/2017                                         
    '*  Notes   : This is an include file to use with an I2C-controlled LCD module.
    '             Initializes a 2x16 I2C LCD, and contains routines to write
    '             commands and data to an I2C LCD in 4-bit mode. Assumes a Hitachi
    '             HD44780 or ST7066U LCD controller and a PCF8574 I2C port expander.             
    '******************************************************************************
    '=====================================================
    'PCF8574 pinout:     P7  P6  P5  P4  P3  P2  P1  P0  |
    '----------------------------------------------------|
    'LCD pinout:         D7  D6  D5  D4  --  E   RW  RS  |
    '=====================================================
    'RS:   Register Select. 1 =  sends to data reg; 0 = sends to command reg
    'RW:   Read/Write select. 1 = read from LCD; 0 = write to LCD (cmd or data)
    'E:    LCD enable. data or command is strobed into the data or cmd register
    '      by from a hi to lo transition.
    '      The data or cmd nybble is strobed on the falling edge of the E bit.    
    'D7,D6,D5,D4: data or commands are sent to the 8574/LCD in 4-bit nybbles.
    'SENDING:
    '      Bytes (commands or data) must be sent as two sequential nybbles. Put the
    '      most significant nybble into D7-D4 (here it's P7-P4 on the PCF8574),
    '      set RS=0, RW=0, then do two I2CWRITEs with E=1 in the first write and
    '      then with E=0 in the second I2CWRITE. It's the falling edge of the E bit
    '      which strobes the nybble into the HD44780. Next, put least significant
    '      nybble into D7-D4 and do the same hi-to-lo sequence for the E bit.
    'NOTE: Depending on the LCD you have, SCL and SDA may already have onboard
    '      pullups. If not, you will have to add 4.7k pullups to SCL and SDA.
    'BACKLIGHT: Often the LED backlight anode pin 15 already has 5V supplied.
    '      Thus, the backlight can be controlled with a spare I/O pin connected to
    '      the LED cathode "K" (LCD pin 16). On = 0 (Lo), Off = 1 (Hi). Current is
    '      usually ~20 mA.
    'COMMANDS: Look at the data sheet for the Hitachi HD44780 LCD controller   
    '      for information on additional commands available.
    '==============================================================================
    '      User code must include the following declarations:        
    '      TRISB.3=0:TRISB.4=0                     'SCL and SDA pins
    '      SCL       VAR PORTB.3                   'I2C clock line
    '      SDA       VAR PORTB.4                   'I2C data line
    '      LCD_addr  CON $7F                       'I2C address for LCD
    '      LCD_Byte  VAR byte                      'byte to be sent to the LCD
    '      LCD_RS    VAR Byte                      'register select: cmd=0, data=1
    '      LCD_RW    VAR BYTE:LCD_RW=0             'read/write bit: 1=read, 0=write
    '      LCD_RSbit CON 0-7                       'bit location of RS on PCF8574
    '      LCD_RWbit CON 0-7                       '                RW
    '      LCD_Ebit  CON 0-7                       '                E
    '      LCD_Dbits CON 4 or 0                    'least significant D bit location
    '      
    '      L1        Var BYTE [16]                 'data for line 1
    '      L2        Var BYTE [16]                 'data for line 2
    '      i         var byte                      'reuseable byte
    '      INCLUDE "LCD_I2C.inc"                   'utilities for an I2C LCD display
    '      pause 500                               'let the LCD power up
    '      GOSUB LCD_Init                          'initialize the I2C LCD module
    '==============================================================================
          LCD_Nyb   VAR BYTE                       'reuseable nybble
    Goto JumpOver
    
    LCD_Send: '-----  send a byte (two nybbles) as data or as command  ------------
          Nyb.0(LCD_Ebit)=1:I2CWRITE SDA,SCL,LCD_addr,[LCD_Nyb]  'send with E=1
          Nyb.0(LCD_Ebit)=0:I2CWRITE SDA,SCL,LCD_addr,[LCD_Nyb]  'send with E=0
    Return
    
    LCD_I2C: '--------  break down the byte to send into two nybbles  -------------
          IF LCD_Dbits=4 then                 'send nybble goes in bits 7-4 or 3-0?
             LCD_Nyb=LCD_Byte                 'hi nybble already in 8574 bits P7-P4
             LCD_Nyb.0(LCD_RSbit)=LCD_RS:LCD_Nyb.0(LCD_RWbit)=LCD_RW)'control bits
             gosub LCD_Send                   'Nyb = hi nyb of 'LCD_Byte'
             LCD_Nyb=LCD_Byte << 4            'shift lo nyb up to 8574 bits P7-P4
             LCD_Nyb.0(LCD_RSbit)=LCD_RS:LCD_Nyb.0(LCD_RWbit)=LCD_RW)'control bits
             gosub LCD_Send                   'Nyb = lo nyb of 'LCD_Byte'
          ELSE
             LCD_Nyb=LCD_Byte >> 4            'shift hi nyb down to 8574 bits P3-P0
             LCD_Nyb.0(LCD_RSbit)=LCD_RS:LCD_Nyb.0(LCD_RWbit)=LCD_RW)'control bits
             gosub LCD_Send                   'Nyb = hi nyb of 'LCD_Byte'
             LCD_Nyb=LCD_Byte                 'lo nybble already in 8574 bits P3-P0
             LCD_Nyb.0(LCD_RSbit)=LCD_RS:LCD_Nyb.0(LCD_RWbit)=LCD_RW)'control bits
             gosub LCD_Send                   'Nyb = lo nyb of 'LCD_Byte'
          ENDIF
    Return
    
    LCD_Init '---------------- Initializes the I2C LCD  ---------------------------
          LCD_RS=0                                 'select command mode, RS=0
          LCD_Byte=$30:Gosub LCD_I2C:pause 5       'send $30
          LCD_Byte=$30:Gosub LCD_I2C:pauseUS 100   'and again
          LCD_Byte=$30:Gosub LCD_I2C:pauseUS 100   'and again
                                                   'LCD setup
          LCD_Byte=$33:gosub LCD_I2C:pauseUS 100   'we're in 8-bit mode
          LCD_Byte=$32:gosub LCD_I2C:pauseUS 100   'get into 4-bit mode  
          LCD_Byte=$28:gosub LCD_I2C:pauseUS 100   '4-bits, 2 lines, 5x7 pixels
          LCD_Byte=$0C:gosub LCD_I2C:pauseUS 100   'disp on, curs off, curs no blink
          LCD_Byte=$06:gosub LCD_I2C:pauseUS 100   'incr cursor, shift display   
          LCD_Byte=$01:gosub LCD_I2C:pause 4       'clear the screen
          Pause 500
    Return
    
    JumpOver:



  • #2
    Here is a test program for the code in the previous post:

    Code:
    '  Name: SteveLCD_I2Ctest.pbp  Date: 08/16/2017
    '         written for 16F877                        
    '==================================================
    'PCF8574 pinout:     P7  P6  P5  P4  P3  P2  P1  P0
    '--------------------------------------------------
    'LCD pinout:         D7  D6  D5  D4  --  E   RW  RS
    '==================================================
    
    'Usage: First, you need to initialize the LCD module by doing a 'GOSUB LCD_Init'
    '      Then you put the byte you want to print into the variable 'LCD_Byte',
    '      and then do a 'GOSUB LCD_I2C'. The byte you send could be a printable
    '      character (referred to as a data byte) or it could be a command byte.
    '      If you are sending a data byte, set 'LCD_RS=1' prior to doing the
    '      'GOSUB LCD_I2C' instruction. If the byte to be sent is a command,
    '      set 'LCD_RS=0' prior to doing the 'GOSUB LCD_I2C' instruction. You will
    '      also need to change the I2C address 'LCD_addr' to agree with the settings
    '      of A0,A1,A2 address pins on your PCF8574.
    
    '      You also need to discover which PCF8574 port pins (P7-P0) are mapped
    '      into the HD44780 control bits RS, RW, and E bits. The data or command
    '      bits will always be in PCF8574 bits either 7-4, or 3-0. Thus, the
    '      HD44780 control bits will be in the 4 bits not used for data or command.        
    '      The bit positions shown above are how my LCD display was set up, but
    '      other systems may be different. Trace your connections by ohming the pins
    '      on the 8574 port to the LCD pins.
    
    '      Use the ARRAYWRITE instruction to build a 16-character line of printable
    '      characters, then send the whole line. I used a FOR loop with the index
    '      running from 0-15.
    
    '      The usual disclaimer: I am offering this software as-is simply as an
    '      example of what worked for me, with no guarantee that it will work for
    '      you.
    
          DEFINE LOADER_USED 1                     'if using the bootloader
          TRISB.3=0:TRISB.4=0                      'SCL and SDA pins
          SCL       VAR PORTB.3                    'I2C clock line
          SDA       VAR PORTB.4                    'I2C data line
          LCD_addr  CON $7F                        'I2C address for LCD
          LCD_Byte  VAR BYTE                       'byte to be sent to the LCD
          LCD_RS    VAR BYTE                       'register select: cmd=0, data=1
          LCD_RW    VAR BYTE:LCD_RW=0              'RW=0 means we're writing to LCD
          LCD_RSbit CON 0                          'location of RS on PCF8574 PortP
          LCD_RWbit CON 1                          '            RW
          LCD_Ebit  CON 2                          '            E
          LCD_Dbits CON 4                          'least significant D bit location
          L1        VAR BYTE [16]                  'data for line 1
          L2        VAR BYTE [16]                  'data for line 2
          i         VAR BYTE                       'reuseable byte
          INCLUDE "LCD_I2C.inc"                    'utilities for an I2C LCD display
          PAUSE 500                                'let the LCD power up
          GOSUB LCD_Init                           'initialize the I2C LCD module
    
          'print something      
          Arraywrite L1,["0123456789ABCDEF"]          'create line 1 for display
          Arraywrite L2,["Here is line #",dec 2,"!"]  'create line 2
    
          LCD_RS=0                                 'RS=0 to select command mode
          LCD_Byte=$01:GOSUB LCD_I2C               'clear the screen
          LCD_Byte=$80:GOSUB LCD_I2C               'goto line 1, col 1
    
          LCD_RS=1                                 'RS=1 to select data mode
          FOR i=0 to 15                            'write one line to LCD
             LCD_BYTE=L1[i]:GOSUB LCD_I2C          'send a byte
          NEXT i   
    
          LCD_RS=0                                 'select command mode
          LCD_Byte=$C0:GOSUB LCD_I2C               'goto line 2, col 1
    
          LCD_RS=1                                 'select data mode
          FOR i=0 to 15                            'write one line to LCD
             LCD_BYTE=L2[i]:GOSUB LCD_I2C          'send a byte
          NEXT i   
    
    STOP
    I'll be happy to answer questions. Hope this may be useful to someone.

    Cheers!
    -Steve

    Comment

    Working...
    X