It is useful! As expected, RAM and CPU consumption are major issues. Saving RAM by computing the data to send to the LCD line by line proved to only be possible for text and extremely simple graphics, even with overclocking. The timing requirements are far looser than those involved in generating video signals, but the refresh rate needed to avoid flicker is actually quite high, the LCDs are intended to be driven at 60 Hz. This leaves 260 microseconds to compute each line. Another annoyance is the LCD drive voltage, which is a negative supply rated to be as much as -20 volts…I’m now using the charge pump from a MAX232 level converter for the LCD supply.
I switched to frame buffers in RAM and upgraded to an ATmega32 I had lying around. It’s still not light on the CPU: even with text and frame buffer graphics, it consumes roughly 30% of the CPU time of a ATmega32 running at 16 MHz, varying depending on refresh rate and the specifics of what is being drawn, and on display size…graphics is somewhat faster than text, and displays much larger than mine probably can’t be used without blanking the display while other operations are being performed. Worse, gray pixels are indicated by setting pixels in one of two buffers, full black by setting pixels in both buffers, and displaying each buffer for two frames before switching to the other, to avoid odd effects due to the LCD responding faster in one direction. This requires two frame buffers, doubling SRAM consumption, and also requires a higher refresh rate to avoid flicker, which means even fewer cycles free for other things. I ended up using nested interrupts to allow timely LCD updates while without bringing everything else to a halt, an approach which seems to work well.
There’s not enough RAM on the ATmega32 for a full-screen frame buffer, but mixing text and graphics is fairly trivial. You could of course store larger static bitmaps in flash, this isn’t something that interested me. It is quite straightforward to split the display into quarters based on the column controllers, though, and I’ve mostly been using it as a 7×24 character text display (with 8×8 pixel characters and 1 pixel line spacing), and a 64×64 graphics area with gray support. This takes a 168 byte character buffer and two 512 byte frame buffers, 1192 bytes in total. The ATmega32 has 2048 bytes of SRAM, leaving 856 bytes of SRAM free for other uses. I could just barely do a 7×20 + 64×96 mode (1676 B), or I could drop the grays and devote up to 3/4 of the display to graphics. Since there are 2048*8 pixels on the display, you can also simply draw all RAM onto the display, with some interesting results:
The gray pixels are a bonus, the display was never intended to support them. On my display, they flicker badly at less than 80 Hz, but this likely depends a great deal on display and driving voltage. Flicker in gray shades can be reduced by setting alternating pixels in each buffer. Eliminating gray pixel support halves frame buffer RAM usage, or doubles screen area that can be used for graphics, and considerably reduces the redraw rate required to avoid flicker, reducing CPU consumption. It should also be possible to do gray values in the text area, at the cost of higher flash usage…a full 256-character set of 8×8 pixel font bitmaps takes up 2 KB, gray values would double that. Large SRAM sizes typically come along with large flash sizes though, and I am planning to investigate the possibilities of crude antialiasing for improving legibility of text.
As mentioned, the display seems to respond differently in when driven in different directions. This could be exploited to gain two different gray levels by only displaying each buffer once, but I did not find the resulting gray levels to be of much use in my display…I got “medium gray” and “almost invisible”. The possibility merits further exploration, though…different LCDs might behave differently, and other drive voltages or tweaks to the refresh timing might help.
Source code: https://github.com/cjameshuff/glcd