Connecting a graphical LCD via a I2C using a 16-bit port expander

Are you using software SPI or the hardware SPI transceiver for this?

At the Arduino end? The hardware SPI. Why would you not use it? It clocks out bytes at about 3 microseconds each. You can connect multiple SPI devices as long as each one has its own SS line. The library I wrote lets you choose the SS line you have dedicated to the LCD (it might not be D10) so you can also connect Ethernet shields, etc.

hey can use this library if i use 2 pcf8574p as port expander.

Not unchanged. You are addressing two port expanders rather than one. The datasheet I found seems a bit light on information about the protocol, but it is probably different. I'm sure it can be done, but you will need to modify things here and there.

i need to modify library or just hardware part

I'm not sure what you mean by that. You are replacing one chip by two chips? Surely that involves a hardware change.

So you need to modify both.

thank you sir

is there any other option for mcp23017 ic. it is not easily available in india1

Mail? I get most of my stuff from overseas.

hello sir,
i have done the same thing using pcf8574. but data display on lcd very slowly. what are the speed parameter of i2c ,mcp23017 you have used. does wire length of i2c effects speed.

ooh, good job with the library! this may come in useful to me later on :slight_smile: much appreciated!

cnavnath:
i have done the same thing using pcf8574. but data display on lcd very slowly. what are the speed parameter of i2c ,mcp23017 you have used. does wire length of i2c effects speed.

At normal speed I believe I2C runs at around 10K bytes/second, and with a speedup (higher speed set) you can get to 3.5 times that.

I don't see why wire length would affect it.

If the LCD is slow it could be you are sending too much (like updating more than you need). Also the LCDs have a settling time (you have to poll a pin to see if they are ready) so it could be that speed, and not I2C speed, that is the limiting factor.

ks0108s are really dumb so the commands or data accesses are usually very fast to execute.
Usually they are ready before you can generate the next command or data access.
I've occasionally seen them take and extra 3-4us to respond but that is not the norm.

I would think that ic2 should not have an issue overrunning the lcd as the byte transfer time at 400k bits/sec is 20us.
Even if you bump the i2c clock to 1Mhz, a byte transfer time is still 8us
and you have to transfer at least 2 bytes to control the 16 bits on the output port
plus you still have I2C START + ADDRESS + STOP overhead.

--- bill

Hi Nick,

Do you know if your great work ever made it into a Backpack PCB design?

No I think not. I contacted one place that made Arduino boards and never heard from them again.

It should be simple to do, I might see if someone else will do it.

Hi, thanks for the update. I would be happy to design a PCB and send you a few if I had a schematic to work from?

Also wondering if your project can support different GLCD resolutions? Thanks

Nick,
I'm a bit confused as to how the code is really working on the read side.
The data lines should be read 320ns after E is raised but before E is lowered
because the data is only guaranteed to be valid on the data bus 10ns after E is lowered.

If I look at readData() it
raises and lowers E twice which will do the "dummy" read and then
the real "read",
but it isn't valid to read the data lines on the data bus after you lower E since
the glcd is supposed to tri-state the data bus when E is low.
So the validity of the data lines is rapidly collapsing between when the
loop ends and when the data lines are sampled lower in the routine
Since just a single AVR instruction is going to take more than 10ns I'm not sure
how the code can read the data from the glcd.

Were you getting valid data on your glcd reads? or just always getting 0 or FF?
i.e. does line() work properly by overstriking the existing pixels?
Am I missing something?

--- bill

It's working but perhaps by good luck rather than good management. Do you mean to change it to be like this?

// read the byte corresponding to the selected x,y position
byte I2C_graphical_LCD_display::I2C_graphical_LCD_display::readData ()
{
  
#ifdef WRITETHROUGH_CACHE
  return _cache [_cacheOffset];
#endif
  
  // data port (on the MCP23017) is now input
  expanderWrite (IODIRB, 0xFF);
  
  // lol, see the KS0108 spec sheet - you need to read twice to get the data
  startSend ();
    doSend (GPIOA);                  // control port
    doSend (LCD_RESET | LCD_READ | LCD_DATA | LCD_ENABLE | _chipSelect);  // set enable high 
  endSend ();

  startSend ();
    doSend (GPIOA);                  // control port
    doSend (LCD_RESET | LCD_READ | LCD_DATA | _chipSelect);  // pull enable low to toggle data 
  endSend ();

  startSend ();
  doSend (GPIOA);                  // control port
  doSend (LCD_RESET | LCD_READ | LCD_DATA | LCD_ENABLE | _chipSelect);  // set enable high 
  endSend ();

  byte data;

  if (_ssPin)
    {
    digitalWrite (_ssPin, LOW); 
    SPI.transfer ((_port << 1) | 1);  // read operation has low-bit set
    SPI.transfer (GPIOB);             // which register to read from
    data = SPI.transfer (0);          // get byte back
    digitalWrite (_ssPin, HIGH); 
    }
  else
    {
    // initiate blocking read into internal buffer
    Wire.requestFrom (_port, (byte) 1);
    
    // don't bother checking if available, Wire.receive does that anyway
    //  also it returns 0x00 if nothing there, so we don't need to bother doing that
    data = i2c_read ();
    }  

  // drop enable AFTER we have read it
  startSend ();
  doSend (GPIOA);                  // control port
  doSend (LCD_RESET | LCD_READ | LCD_DATA | _chipSelect);  // pull enable low to toggle data 
  endSend ();

  // data port (on the MCP23017) is now output again
  expanderWrite (IODIRB, 0);
  
  return data;
  
}  // end of I2C_graphical_LCD_display::readData

That also appears to work. :slight_smile:

bperrybap:
Were you getting valid data on your glcd reads? or just always getting 0 or FF?
i.e. does line() work properly by overstriking the existing pixels?

Yes I was.
Yes it does.

point5:
Hi, thanks for the update. I would be happy to design a PCB and send you a few if I had a schematic to work from?

The schematic is here:

I already did an Eagle file for it (attached). It may or may not be 100% correct, I haven't tested it.

point5:
Also wondering if your project can support different GLCD resolutions? Thanks

Not sure, it's been a while since I worked on it.

Graphicl LCD Backpack.brd (18.3 KB)

Graphicl LCD Backpack.sch (69.9 KB)