Shop OBEX P1 Docs P2 Docs Learn Events
BS2p driving I2C display problem — Parallax Forums

BS2p driving I2C display problem

I have this I2C 16x2 LCD (at 0x27) that I'm trying to interface to BS2p.
Plenty of samples of how to use bit-banging or LCDOUT commands but I plan on using I2COUT instead.
I have no luck with my code (see attached) so I hope that somebody can easily spot what am I doing wrong. Thanks
«1

Comments

  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    Without knowing what the LCD is expecting it would be impossible to tell if the code had any errors. Do you have a link to a datasheet for the display? Connection information? How is it wired to the BASIC Stamp 2p?
  • A PCF8574 is just an interface chip that converts a byte transmitted via I2C from a serial byte to a parallel byte and conversely takes an 8 bit byte and sends it out in serial form over an I2c bus. With respect to being hooked up to an LCD, that's all it does, it just presents the serial data received from the I2C bus as an 8-bit byte. How the 8574 is connected to the LCD is very important to know before you start sending characters down the line. There are at least 6 bits that have to be provided from the 8574 to the LCD. 7 if you wish to read data back from the LCD. The six are E, RS, DB4 - DB7. If you want to read data from the LCD back to the BS2P then you will also need the R/W signal, which you will tie to ground if not used. It is up to your program to manipulate those 6 (7) bits according to the 44780 datasheet to initialize the LCD and then send data to display. Mind you, it is not a trivial task because there are strict timing requirements. With this arrangement each 8 bit character is sent first with the upper nibble, RS = 1, E = 0, 2nd with upper nibble, RS = 1, E = 1, 3rd with lower nibble, RS = 1, E =0, 4th with lower nibble, RS = 1, E =1. Then on to the next character. RS = 1 for data to display or 0 for control operations.
  • Without knowing what the LCD is expecting it would be impossible to tell if the code had any errors. Do you have a link to a datasheet for the display? Connection information? How is it wired to the BASIC Stamp 2p?

    Chris, here's the schematic. The SDA is connected to pin 8 of BS2p, the SCL goes to pin 9 of BS2.

    1236 x 600 - 42K
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2017-02-10 16:13
    As Hal said, you're just converting from an I2C I/O expander (PCF8574) to the parallel interface of the LCD. But you still have to meet the timing and interface signals required by the parallel interface in 4-bit mode, which you're using. So everything you send needs to be sent in two chunks with the enable (called CS in this case) triggered for each transfer. That's also assuming RW and RS are in the correct position when you're sending your command data.

    Honestly with this type of interface you're saving I/O pins at the cost of more/complex code to drive it. I have seen some examples of driving a parallel display from such a device so you might be able to find some existing code. If you do I would recommend creating a subroutine that handles all the heavy lifting for you so that you can just send commands/data by setting a couple of variables and calling a subroutine (GOSUB).
  • Hal Albach wrote: »
    A PCF8574 is just an interface chip that converts a byte transmitted via I2C from a serial byte to a parallel byte and conversely takes an 8 bit byte and sends it out in serial form over an I2c bus. ...

    That I understand, however I am not able to come up with the correct sequence for the HD44780.

  • Something's not right - see this code updated for 4-bit interface... yet still no "hello world"
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    I wouldn't expect that to work...you're shifting every byte you send by 4 bits. But you should be sending 8 bits in two transactions, one without shifting. Also, I would check your slave address as typically the address are low for the base slave address, but I think in your schematic you show all the address lines being pulled high.
  • kwinnkwinn Posts: 8,697
    edited 2017-02-10 20:38
    Using the SPI chip to emulate a parallel bus is not simple. Each byte you send will contain 3 bits (P0 to P2) of control signals and 4 bits (P4 to P7) of data (P3 is not used).

    EDIT oops, missed the P3 connection to the backlight. Thanks for pointing that out Hal.

    Based on the HD44780U data sheet it looks like you need to send at least 2 bytes for every 4 bits of data, so 4 bytes for each character to be displayed.

    I am willing to help out with the commands that need to be sent, but I do not have that lcd so it may be a very slow process. See the attached drawing for the function of each output bit sent to the SPI chip along with a timing diagram of reads and writes.

    1711 x 1397 - 230K
  • John, you are correct - something is not right. Actually, almost everything is not right! With the exception of the first 3 commands during initialization every transfer to the LCD is done in 4 bit mode. That means every character or command you send has to be sent in TWO chunks, and each chunk will require TWO transfers to toggle the CS line.
    Now that you (and we) know how the 8574 is connected to the LCD that knowledge can be used to build your code. But first you have to understand the bit structure of the character to be transferred. Your code has to specifically build the character to be transferred so that the LCD can recognize and act upon that character.
    In your case the bit structure is as follows;
    bit 0: RS must be set to 0 if an instruction (or command) and set to 1 if this is a data operation.
    bit 1: R/W must be set to 1 if reading data from LCD back to the Stamp, or set to 0 when writing from the Stamp to the LCD. (Almost always will be 0)
    bit 2: CS called E everywhere else. This has to be 0 for the first toggle transfer, and then set to 1 for the 2nd toggle transfer. If this is not toggled down and up the LCD ignores everything.
    bit 3: P3 called BL for backlight everywhere else. When set to 1 it will turn on the LCD backlight. (obviously, 0 will turn it off)
    bits 4-7: data bits. 4 data bits or 1 nibble to be sent to the LCD. The first "chunk" will be the upper nibble of the data you wish to transfer. The second "chunk" will be the lower nibble of the character.

    Each "toggle" transfer is an I2COUT transfer with the character in the data field between the brackets [ ] made specifically for that transfer.
    Each "chunk" is actually two "toggle" transfers. Remember, if you don't toggle the CS bit, NOTHING will happen.

    Did Chris & I not tell you this configuration is not trivial to program?
  • Also, I would check your slave address ...

    The address is correct and verified as 0x27

  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2017-02-10 21:34
    john_s wrote: »
    The address is correct and verified as 0x27

    Okay, so you have all three jumpers off then and not shorted.

    Looks like the product page is here: http://www.sunrom.com/p/i2c-lcd-backpack-pcf8574

    Also note there are multiple threads on these forums about this: http://forums.parallax.com/search?Search=LCD+I2C+PCF8574
  • kwinn wrote: »
    ... you need to send at least 2 bytes for every 4 bits of data, so 4 bytes for each character to be displayed....

    Kwin, not sure I understand but I'm eager to see a sequence of commands that does what you've just said and test it. Thanks!

  • Hal Albach wrote: »
    ...
    bit 0: RS must be set to 0...

    Hal, in regards to all those LCD lines other then D5-D7; aren't they taken care by the PCF8574 without our need to "tell" it anything ?

  • The PCF8574 does not do anything but receive an 8 bit I2C transfer and present it on an 8 bit parallel output. What you send out over the bus is what will appear at the output. It is up to you to insure that ALL the control bits are in their proper state when you issue the I2COUT statement.
  • Hal, what I tried to ask was that when I send the clear display code $01 over I2C to the PCF8574 then the 8574 takes care on its own to properly deliver it to the 44780 and clear the LCD without me knowing the details on how it did it.
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    john_s wrote: »
    Hal, what I tried to ask was that when I send the clear display code $01 over I2C to the PCF8574 then the 8574 takes care on its own to properly deliver it to the 44780 and clear the LCD without me knowing the details on how it did it.

    No. Hal's post was right on the mark. The PCF8574 doesn't know anything about what it's connected to or what the pins need to do. You must be implicit in everything you send to it.

  • OK, so you send $01 over the I2c bus and the 8574 dutifully receives it and sets its output bits accordingly. That is, bit 0 is a 1 and all the rest are 0. Since we know how the output bits of the 8574 are connected to the LCD inputs, here is what the LCD "sees";
    bit 0: RS = 1, which means this is a data input sequence, something to display.
    bit 1: R/W = 0, which means this is a write from the stamp to the LCD.
    bit 2: CS = 0, which means this is the first half of the CS toggle.
    bit 3: P3 = 0, backlight is off
    bits 4 - 7: data bits 4 -7 = 0000, places 0000 to the data input lines.

    Now let us look at sending $01 shifted left 4 times;
    Understand that shifting left 4 times leaves bits 0 - 3 all at zero.
    bit 0: RS = 0, which means this is now a command operation
    bit 1: R/W = 0, which means this is a write to the LCD from the Stamp
    bit2: CS = 0, first half of CS toggle
    bit3: p3 =0, backlight is still off
    bits 4-7: data bits 4 - 7 are now set to 0001

    In either case, nothing happens because you have not toggled the CS line ( it has to transition from 0 to 1 in order for the LCD do anything), and so far only half of a character or command has been sent.

    here is the sequence for sending a $01 command to the LCD so that it will be acted upon with your configuration.
    1: set up the output character so that it reads '00001001' and send it over the I2C bus. Data bits are the upper nibble of $01 and CS is 0, backlight is on
    2: change the output character so that the CS bit is now 1, '00001101' and send it over the bus. The LCD "sees" the CS toggle and accepts the first half of this command, backlight is on.
    3: change the output character to read '00011001" and send it over the bus. Data bits are now the lower nibble of $01 and CS is 0, backlight is on
    4: change the output character to read '00011101" and send it over the bus. again the LCD "sees" the CS toggle and accepts the 2nd half of the command, sees that it is $01 and clears the display, backlight is on.

    The 8574 will not, on it's own, toggle the CS line. You have to do that.

    Since you are essentially bit-banging the display, you have to know the details on what has to be done.
  • Hal, thank you for your great explanation! With this and others comments I plan to plow through the code during the weekend. Cheers
  • John;
    I made a slight error in the example sequence for sending a $01 command to the LCD. Bit 0 of the output character, RS, the rightmost bit, should be 0 for a command operation, not 1, which is for data input. My apologies if this caused you any confusion.
  • kwinnkwinn Posts: 8,697
    Here is a spreadsheet with the required bits to produce the signals the HD44780 needs to load the four most significant data bits. Of course you will also need a table of the HD44780 commands to load into the four msb's.
  • Tracy AllenTracy Allen Posts: 6,656
    edited 2017-02-11 19:00
    A side note, plug for an i2c display that I like, the Newhaven NHD-C0220BiZ-FSW-FBW-3V3M. It is 20x2 character, chip-on-glass, less than 1ma supply current at 3.3v (apart from its backlight). The controller is an ST7036i dot matrix display driver that intrinsically supports i2c.

  • Hal Albach wrote: »
    John;
    I made a slight error in the example sequence for sending a $01 command to the LCD. Bit 0 of the output character, RS, the rightmost bit, should be 0 for a command operation, not 1, which is for data input. My apologies if this caused you any confusion.

    Hal, I keep reading and still cannot spot the place where you've "made a slight error". Can you help and highlight where it was? Thanks
  • It's in the example where I showed four 8 bit codes to be sent to the LCD, they all ended with bit 0, the RS bit, the rightmost bit set to 1, which is for the LCD data register. Should have been set to 0, for the LCD instruction, or command, register.
  • Got it - thanks
  • john_sjohn_s Posts: 369
    edited 2017-02-13 15:33
    One more thing - will I be able to send "Hello world" as a single string when using I2COUT or do I have to brake it and send char after character instead?
  • kwinnkwinn Posts: 8,697
    edited 2017-02-13 16:52
    john_s wrote: »
    One more thing - will I be able to send "Hello world" as a single string when using I2COUT or do I have to brake it and send char after character instead?

    You can send a single string like "Hello world" using the string formatter. See page 232 of the "BASIC Stamp Syntax and Reference Manual Version 2.2"
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    kwinn wrote: »
    john_s wrote: »
    One more thing - will I be able to send "Hello world" as a single string when using I2COUT or do I have to brake it and send char after character instead?

    You can send a single string like "Hello world" using the string formatter. See page 232 of the "BASIC Stamp Syntax and Reference Manual Version 2.2"

    Actually in this case that won't work. If you create a subroutine to handle to individual writes, you can also create a string subroutine to take a zero-delimited string and send it one character at a time calling the data subroutine.
  • Chris, not sure how to create this kind of a sub - can you provide an example?
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    Once you have the code to send a complete command to the display, such as clear the display, you can turn that into a subroutine where you pass a byte to the subroutine via variable. That command is executed and the subroutine returns, ready for the next command or data byte. You can either create one subroutine that handles both, or you could do it the way I did...Unfortunately my only examples of dealing with Parallel displays are for the Z80 in assembly. Here's the Parallel LCD print routine, which also handles certain special characters like Bell, Clear Screen, Home, etc.
    PRINT	CALL CHKBUSY
    	LD A,(HL)
    	INC HL
    	OR A
    	RET Z
    	CP $07				;BELL?
    	JP Z,BELLJMP
    	CP $0C				;FORM FEED?
    	JP Z,FORMJMP
    	CP $02				;HOME?
    	JP Z,HOMEJMP
    	CP $0A				;LINE FEED?
    	JP Z,LINEJMP
    	OUT (LCDD),A
    	JP PRINT
    BELLJMP	CALL BEEP
    	JP PRINT
    FORMJMP	CALL FFEED
    	JP PRINT
    HOMEJMP	CALL HOME
    	JP PRINT
    LINEJMP	CALL LFEED
    	JP PRINT
    HOME	CALL CHKBUSY
    	LD A,$02
    	OUT (LCDI),A
    	CALL CHKBUSY
    	RET
    FFEED	CALL CHKBUSY
    	LD A,$01
    	OUT (LCDI),A
    	CALL CHKBUSY
    	RET
    
  • Thanks!
Sign In or Register to comment.