How to control 2 graphic LCDs with one Arduino

Hello

I plan to control 2 graphic LCD (http://www.sparkfun.com/products/710) using the KS0108 Graphics LCD library (Arduino Playground - GLCDks0108).

The library assumes you use one port (A,B or C) to connect one LCD.

I would like to know what I should modify in the library to use 2 LCDs?
Do I need to duplicate the library changing all functions name to address LCD 1 or LCD 2 (e.g. GLCD.ClearScreen1() for LCD 1 and GLCD.ClearScreen2() for LCD 2) ? Or is there any more elegant way to do it?
So far I am a user of the existing libraries but didn’t try myself to write one…
Thank you for your support.

Raoul.

Note: I am using a Mega 2560

This is easy to do with the character mode LCDs and the same technique may very well work with graphical LCDs. Check out this thread from the old forum: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265969050

Don

I have to complain that either ks0108 or glcd library (if they're not the same) is pretty big in SRAM so hope two instances will work. Essentially like what floresta pointed out (the post), if you can, instantiate two GLCDs and with same data lines but different CS1 and CS2.

Hi
Thank you for your quick reply, I will try it but seems it should work.
Raoul.

Hi again
I am trying to use 2 instances of the graphic library; the issue here is that the pin assignment is done in the header file ( that it is not the case for LiquidCrystal)...

"To change pin assignments you must modify the ks0108.h header file. Find the section in the file that begins:

//
/* Configuration for assigning LCD bits to Arduino Pins */
/
/

You will see the defines for the five command pins with their default pin assignments:

Name Arduino pin number
#define CSEL1 14 (Analog pin 0)
#define CSEL2 15 (Analog pin 1)
#define R_W 16 (Analog pin 2)
#define D_I 17 (Analog pin 3)
#define EN 18 (Analog pin 4)"

Do you have any idea to workaround? Maybe adding a function to the library to assign the pins instead of doing it in the header file?

Thank you.
Raoul.

So far I am a user of the existing libraries but didn’t try myself to write one…

This would be a good opportunity to do just that. Why not start by modifying the existing library to do the pin assignments the way that LiquidCrystal does?

Don

Don
You are right...
The LiquidCrystal is part of the Arduino software package, do you know where to download the source in order to understand it ?
Thanks.
Raoul

do you know where to download the source in order to understand it ?

It's already on your computer if you have installed the Arduino IDE. Where it is depends on which operating system you are using and which version of the Arduino IDE you have installed. Look for a 'Libraries' folder and LiquidCrystal should be there.

Don

liudr:
I have to complain that either ks0108 or glcd library (if they're not the same) is pretty big in SRAM so hope two instances will work. Essentially like what floresta pointed out (the post), if you can, instantiate two GLCDs and with same data lines but different CS1 and CS2.

Please elaborate on this. (I am the co-author of the glcd library).
Great pains were taken to minimize the RAM usage for the library.
That is why there is no frame buffer involved, which is what allows all
demo sketches shipped with the latest glcd v3 beta to work on a mega168 which
has only 1k of SRAM.

--- bill

Raoul6391:
Hello

I plan to control 2 graphic LCD (http://www.sparkfun.com/products/710) using the KS0108 Graphics LCD library (Arduino Playground - GLCDks0108).

The library assumes you use one port (A,B or C) to connect one LCD.

I would like to know what I should modify in the library to use 2 LCDs?
Do I need to duplicate the library changing all functions name to address LCD 1 or LCD 2 (e.g. GLCD.ClearScreen1() for LCD 1 and GLCD.ClearScreen2() for LCD 2) ? Or is there any more elegant way to do it?
So far I am a user of the existing libraries but didn’t try myself to write one…
Thank you for your support.

Raoul.

Note: I am using a Mega 2560

Raul,
The library can run dual displays. I did this during the development of the new level code
that is in the current v3 beta version of the library.

I'd insert a photo of this running the demo sketches, but this forum
does not allow posting images.
(Yes I know it allows linking but I'm not going to upload it somewhere else)

There is a trick though. You must trick the library into "thinking" it is on larger display.
So rather than having 2 separate displays you change the config file to say it is
a 128x128 display with 4 chip selects.

The great thing is that it only takes 2 additional pins to do this.
Wiring up the displays is pretty simple as all the other pins,
D0-D7, EN, RW, DI, are all shared and wired together.

Then what you end up with is a 128x128 display is split across dual displays.
Not quite the same as 2 independent displays, but still very usable.
You can set up text areas that are limited to a single which makes text very easy.
For the graphics you have to be a bit careful not to transverse between the two
displays. And avoid things like GLCD.ClearScreen()

I have to go right now, but can elaborate further if you need more assistance.

--- bill

A bit of followup on the glcd library which is the next revision of the ks0108 library.
It was renamed to "glcd" because it now supports more than the ks0108.

The glcd v3 library is the "new and improved" ks0108 library.

With respect to device instantiation and runtime defining the pins,
this is not a simple task given the current code.

The current code has a single global instantiation "GLCD" and as you've seen defines the pins in a header file
rather than use runtime initialization.

There is a simple reason for this: Performance.
The standard arduino digital i/o routines are VERY slow and also only work on single pins.
By defining the pins in a header file the library code has access to them at compile.
Through much pre-processing magic (in avrio.h) and gcc compiler optimizations,
the final code for reading/writing pins can be usually reduced to a single AVR machine instruction.
The avrio.h code also optimizes across multiple pins so that depending on the AVR pins used,
things like reading the glcd data port can even be crushed down into a single instruction
vs having to call 8 digital read/write functions.
avrio is also smart enough to use nibble operations when possible, and will revert to individual
bit i/o if necessary.
If the pins were runtime declared, the performance would drop significantly.

With respect to the GLCD global instantiation. That was done for several reasons.
The main one being to try to simplify the user experience since it is automatically instantiated,
there is no runtime pin configuration, and usually the user does not need to configure
the pins in the header files. It was an attempt to offer a "it just works" out of box experience.
In that respect I think it has succeeded.

But also, when the code uses a global and only one of them, the code can also
be smaller, faster and use less RAM memory.

Speaking of memory, I did take another look at memory/RAM consumption.
There is zero initialized data. (which ends up in RAM with avr-gcc)
There is 53 bytes of static data,
There will be 12 bytes of RAM for each text area declared.

What can really eat up ram is the use of character strings.
While it might be assumed that strings land in flash (and they do),
they also will be copied to RAM.
That can be avoided if you declare your strings to put them in flash (program memory) and
then call glcd routines like Puts_P() Printf_P() to print them.

Attached are images of running the sketches supplied with the glcd v3 library
on dual ks0108 glcds using a teensy board.

--- bill

If you don't mind using an extra chip per display, you could have a lot of them. I did an article about driving graphical LCD displays using I2C or SPI here:

With I2C you could potentially have 8 displays (since you share the same 2 pins: SDA/SCL) and you can configure the chip to have up to 8 addresses. With SPI you can have as many displays as you can spare SS pins for (3 wires for MOSI/MISO/SCK plus one extra one for SS per display).

With a Mega you might not need to bother because you have some spare pins, but it is an idea.

True, adding an i2C i/o expander chip would offer a way to support several displays with very few pins
but not with the current glcd library implementation since the glcd library doesn't currently support
multiple instances.

--- bill

Bill,

On the topic of hardcoding pin selections, there may be some middle ground. I had run into a similar problem when creating the PrimeVfd library:Arduino Playground - PrimeVfd . digitalWrite() was nowhere near fast enough but I didn't want to limit the library to one pinout option and I wanted to support multiple displays.

What I did was to support a selection of hardcoded pin choices, with many choices intended to be used together with only the select pins differing. It is easy to expand the pin choices. The selection is specified as an enum and the constructor sets up some member variables based on that selection. This has negligible overhead for the most part. At the top of any method that is going to do something expensive, I copy the variable into a local register to help the compiler keep the variable in a register. When I looked at the assembly it looked pretty efficient.

I ran into one gotcha which may not even apply to glcd. PrimeVfd needs to clock out byte data which means setting a single output pin based on a bit value in a variable. If I hardcode to a pin on bit zero for example, then that becomes very easy. Even if it is not bit zero, it is fairly efficient if the amount of bit shifting is fixed. However, if the bit number is variable then it involves either a shift of unknown length at compile time (super expensive apparently) or an if-else operation. For this case however, it is possible to implement a branchless select which adds only a few instructions per bit.

This will burn some code space and add a few variables also. Overall, I felt the modest performance and memory hit was worth making the library more flexible, and IMO, a little easier to use.

  • Jayeson

Jayeson,
Its a really tough situation for using ks0108 glcd type devices on Arduinos,
particularly with the 168/328 based boards.

It is kind of the convergence of a perfect storm that makes things so difficult.

There are so many pins involved, that it is tough to pick a set of pins
for a default configuration or even a group of configurations.

Also, unlike character type lcds, the glcd software is
having to deal with 8192 bits of pixel data in a low level
interface. This means many thousands of times more i/o operations
to get things done over typical character lcds or intelligent glcds.
(and that is for a 128x64 glcd, with larger displays it is even more data)

The Arduino i/o interface is individual pin oriented.
So at 16mhz having 4us of overhead to deal with each individual pin vs 62.5 ns becomes
very significant, especially when looking at so many i/o operations.
With direct port i/o on the large arduino boards all 8 bits of a data byte
can be written in a single AVR machine cycle.

Another very painful thing is (IMHO) the poor choice of pin assignments Atmel chose for
the mega 8/16/168/328 AVRs. Because of the pin assignments, it is impossible to get
an 8 bit byte in a single AVR register on those processors when using the serial port
on those AVRs.
Even using nibbles across multiple ports can be challenging because of the
alternate port functions and the very limited number of pins on those AVRs.

Now toss in the inability to use a local frame buffer because of SRAM constraints,
(which means you may have to read remote glcd memory before you can write to update a pixel)
you can't really off load any of the processing to an interrupt routine as the glcd update
would make the ISR take too long (potentially several milliseconds),
and wanting to support user defined fonts of any size, and you quickly enter into a world
of having make many painful choices and sacrifices in order to get the functionality
working with reasonable performance across many different AVRs and different sized glcd displays.

The current implementation made many intentional design decisions and sacrifices
along the way to try to make the library easy to use and offer reasonably high performance
across all the arduino boards.
Ease of use was at the top of the list.

One thing that is in the current v3 implementation is that the user can use any arduino pin
for any glcd function. He just has to define these in a config file vs defining them runtime in his sketch.
This has allowed users to easily reconfigure the library to support freeing up pins for alternate functions.
The low level code has many complex macros and inline functions
under the hood that will automagically determine the best/fastest way to use AVR direct port i/o to
access the pins. For 8 bit operations it can reduce that down to a single AVR instruction or
use nibbles, individual pin i/o or any combination depending on the pins selected.
This optimization can't be done if the pin information is not known at compile time by the library
or if multiple instances are used.

Multiple instances was not an initial priority especially given the number of pins involved
for these types of displays.

That said, multiple instances might be something that gets a second look in the future
but in my view, in order to really support multiple instances, the interface needs to be
moved to some form of serial interface, SPI/I2C etc... vs individual control and data lines.
Which could be done very inexpensively using an i/o expander chip.

--- bill

I would have to go with Bill here. The simplest thing (and quite fast too) is to use the expander chip. My timings showed it works quite fast (especially if you use an SPI expander). For example, draw 96 characters of text in 54 ms. This is partly because you don't have to worry about generically setting individual bits, to send a byte of data to the LCD you simply SPI transfer that byte out, in itself quite a fast operation.

The library I wrote for the SPI/I2C expander supports multiple displays (link above). I doubt it supports all the stuff glcd does, but it may be enough for whatever you are trying to do.

Bill, Thanks for the information. Thanks for the work on the library also, it is quite excellent.

Once you get into using external chips, I think the challenge then becomes, what is that external chip. For not a whole lot more money than an I/O expander it is possible to by another AVR microcontroller and oscillator. That will free up a lot of resources on the Arduino. At this point, a shield starts to make sense as wiring up the displays can be a bit of a chore.

If I want lots of pins for cheap and don't mind dealing with all the wiring, my choice is to go with a Teensy(++), which is actually the only hardware I've used the graphics LCDs on.

Hi

Thank you again Bill for the time and for the (great) library.
It's abig help for users like me that are not at all software developers...

I started to work with 2 serial GLCD and I was stopped by the lack of performance, it was very slow and difficult to control overflows...
I tried GLCD V3 and the difference of performance is not only amazing but was solving most of the issues I had previously.
It was a great new for my project.

As I need 2 displays, I will try the aproach you proposed to "simulate" a larger 128X128 display.
I was wondering if it is possible make the library to "think" 256X64 instead of 128X128, that will make my life easier to rewrite code. Anyway it is not a big deal to adapt the code for 128X128...

Raoul

Yes you can easily create a larger display that is 256x64 instead of 128x128 if that works better.
It is just a matter of how you define the geometry and the chip selects.

It shouldn't be that difficult as you won't have to modify any code and
the the master ks0108 panel config files already have the necessary support for this.

There are 2 ways this can be done.
You can either use a autoconfig panel config file and then use pin configuration files.
OR you can use a manual panel config file.
When using a manual panel config file everything is in that one file including all the pins.
A manual panel config file will not select different pins depending on the board type.
Its pins are hard-coded/locked down.

The changes for either are basically the same.

====================
For a autoconfig panel config file

You can take a look at the ks0108-192x64_panel.h configuration file
to get an idea how this would work.

You can modify the autoconfig ks0108-Panel.h header file,
but I'd create a new header file by copying one of those
and use that one for this instead.

Then,
You will want to modify the DISPLAY_WIDTH define to 256

Then change the NBR_CHIP_SELECT_PINS to 4
Also (just for clarity if you run diags) alter the glcd_PanelConfigName string.
(it isn't really needed but might help avoid any confusion later)

This assumes you are going to use 2 glcd modules each with 2 chip selects.
(which is normal unless you want to add a decode chip so you can avoid having use 2 extra AVR pins)

You will then need to modify the master glcd_Config.h file to include
your new configuration file vs the original ks0108-Panel.h file.

==================
For a manual panel config file

Take a look at ks0108_Manual_Config.h
The same changes mentioned above apply.
The only difference is that the manual config file also has
the pins defined inside it vs the autoconfig panel config file references pin configuration files.

================

That's it as far as software configuration goes.
The code will figure out everything else at compile time.

The rest will be wiring up the panels.

Be careful about the current needs. Some backlights draw quite a bit of current and
if you are powering from a USB port,
You may have to limit the the backlight current a bit more than normal so
you don't draw too much power from the USB port.

You will then be able to use the text areas to limit text output to specific regions
of an individual display.
Or let the text span them both and wrap/scroll them both.

There are some special big number fonts provided if you want to display
numbers. They are really LARGE and are good for things like a clock or other
numeric type displays.

It will be a fun project.

--- bill

Bill
Thanks again.
I will try this week-end and will keep you posted.
Raoul