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


    • #3
      Hello swr999
      Thanks for your article

      Related to I2C communication connecting LCD display,
      Based on your code, I compiled the error so I can't understand the problem,
      I am working on a relay product to turn the controller over to the motor
      You can look at my code and support me a little idea how I can display the information as in my code using I2C communication.
      Please give me some advice,
      Attached Files

      Comment


      • #4
        Hi thehoang,

        It has been a few years since I wrote this LCD-I2C code, but I'll look at your code and reply later.
        I suggest that to begin, you should just try the code I posted as-is and see if it works OK. Maybe initially use a 16F877 and be sure it compiles OK. If it works OK, then you should be able to make changes to display the data you want to see.

        Comment


        • #5
          Originally posted by swr999 View Post
          Hi thehoang,

          It has been a few years since I wrote this LCD-I2C code, but I'll look at your code and reply later.
          I suggest that to begin, you should just try the code I posted as-is and see if it works OK. Maybe initially use a 16F877 and be sure it compiles OK. If it works OK, then you should be able to make changes to display the data you want to see.
          Hello swr999,
          I have try your code to see if it works Ok, but when i "Compile" then show errors results,
          Can you advice for me about this error?

          Attached Files

          Comment


          • #6
            Hello thehoang,

            I'll try compiling my original code again, and make sure it compiles OK for me, and I will let you know. Sorry for my late reply.

            Comment


            • #7
              ^^^

              I see the problem.
              You are trying to compile LCD_I2C.inc, which is an INCLUDE file.You do not compile INCLUDE files, you will get errors.

              If you look at the SteveLCD_I2Ctest.pbp program, you will see the following statement:

              INCLUDE "LCD.I2C.inc"

              This statement loads in the LCD_I2C.inc file into your program at compile time, and then compiles everything together.

              So I would say you should first compile just the SteveLCD_I2C.pbp program to make sure it compiles OK. It should compile OK with no errors.

              Then in your program code be sure to include the same INCLUDE "LCD_I2C.inc" statement. That will bring in the LCD_I2C.inc file into your program at compile time.

              I hope this helps.

              Comment


              • #8
                HI swr999,
                I also tried one of the two codes above, SteveLCD_I2Ctest.pbp
                When I compile, the software reported an error "syntax error",
                I am struggling and there is no next solution when working with LCD.I2C, i have record a video but can not upload.
                Oh my God!
                Attached Files

                Comment


                • #9
                  ^^^

                  OK, here's the problem: Look at the first 'WARNING' . It says "Unable to open INCLUDE file LCD_I2C.INC".
                  You need to have the include file LCD_I2C.INC saved in the same folder as the program file SteveLCD_I2Ctest.pbp.

                  You probably have the include file stored in a different folder from where the SteveLCD_I2Ctest.pbp is stored.
                  They both have to be in the same folder.

                  If you make these changes everything _should_ compile OK.
                  Once you get that working you can see how my program interacts with the include file LCD_I2C.INC code so you can do the same thing with your custom program.

                  I suggest that you read about how to use Include files in PIC Basic Pro. Are you using PBP3?

                  More generally, when you have compiler error messages and warnings, it's always best to start at the top of the error list and fix the top error first. Often you may have only one error, but that one error can trigger many subsequent errors. Start at the one top error/warning and fix that first. Then try re-compiling. You may often find all the other errors disappear once you correct the first error.

                  Let me know how it goes.
                  Last edited by swr999; 2 weeks ago.

                  Comment


                  • #10
                    Hi swr999,
                    Thanks for your quickly reply,
                    I did as you guide that put the LCD_I2C.INC file in the same folder as the SteveLCD_I2Ctest.pbp file, but when I compiled it, the error was on the LCD_I2C.INC side
                    Is there any problem with my software?
                    The error appears at the command line " Nyb.0(LCD_Ebit)=1:I2CWRITE SDA,SCL,LCD_addr,[LCD_Nyb] 'send with E=1"
                    now, i am using MCS with version 4.0.0.0 and PICBASIC PRO 2.60.
                    do you have any suggest?
                    Attached Files

                    Comment


                    • #11
                      The code I wrote was written for PBP3. PBP3 added the ARRAYREAD and ARRAYWRITE instructions which are not part of PBP 2.60. So the 2.60 compiler can't handle the ARRAYWRITE instruction, and this may be producing the compiler error you are seeing. ARRAYWRITE is used to fill an array with characters.

                      In PBP2 since you don't have ARRAYWRITE you will have to fill the L1 and L2 arrays in some other way. For a simple test you could just put some ASCII characters in L1 and L2. You could fill L1 and L2 doing something like:

                      FOR i=0 TO 15
                      L1[i] = 49 'create line 1 with 16 ASCII characters. 49 is an ASCII 1
                      L2[i] = 50 'create line 2 with 16 ASCII characters. 50 is an ASCII 2
                      NEXT i


                      This should put the first line on the LCD as a line of 16 1's, and the second line as 16 2's.

                      So the LCD display should show:

                      1111111111111111
                      2222222222222222

                      So, commment out the two ARRAYWRITE statements and instead use the FOR/NEXT loop to create the values for the arrays L1 and L2.
                      See if that compiles OK.

                      Best would be to move to PBP3, but try this as a workaround.


                      Comment


                      • #12
                        Thanks swr999,
                        I think, i will buy new software with PBP3 to continue my project and will respond to you if any problems arise
                        Thank you so much for your timely support.
                        I think this is very good, so there are more examples for people to approach and make more programs for people to refer to.

                        Comment

                        Working...
                        X