GDE021A1 is a graphics display with a resolution 172×72 pixels, each pixel is 2 bits deep (4 shades of grey). The display has an internal controller SSD1606 with a framebuffer. The framebuffer size is 172*72*2/8=3096 Bytes. When the display is powered up, the system processor sends initialization sequence that first sets up controller’s internal registers (the controller SSD1606 is fairly generic) and then sends new framebuffer content. The display controller then autonomously pushes the framebuffer contents to the physical screen.
The display controller can be configured to orient the framebuffer almost any way. I configured it into a landscape mode, with the X-axis going right and the Y-axis down, as shown on the photo.
With this orientation, pixels on the X-axis are indexed 0 to 171, and pixels on the Y-axis are 0 to 71.
Display framebuffer is organized in bytes. Each byte keeps 4 pixels, each pixel requires 2 bits to express 4 shades of grey (4*2=8 bit). The drawing below shows layout of bytes within the framebuffer. It is a little bit unusual because bytes are oriented vertically. The first byte (at index zero) covers pixels (X,Y)=(0,0), (0,1), (0,2), and (0,3). The second byte (at index one) covers pixels (1,0), (1,1), (1,2), and (1,3), and so on.
Now we can draw any static image on the display simply by sending a bitmap image into the framebuffer. To draw an interactive text and graphics on the display we need a graphics library.
To this end I used u8glib graphics library. It is an open-source code, runs on ARM, AVR, and Arduino, and it has minimal resource requirements. The u8glib supports many different displays but, of course, it does not support the EPD display. Writing a new device driver was not so difficult, however.
The picture below shows the software architecture. At the bottom there is the real EPD display hardware, complete with integrated controller SSD1606. Above it the large dotted white rectangle is the system processor Cortex-M3 running the software. The u8glib graphics library comprises mainly of public API functions, and of a local framebuffer stored in system RAM.
Public API functions are used by an application to put text, lines, circles, arcs, etc. onto the display. They draw pixels into the local framebuffer. When all screen drawing operations are complete, the library calls a hook to actually update the screen. The EPD driver powers up the display, initializes registers and waveform tables, and sends the whole framebuffer as one block of data. The display controller receives the framebuffer, and then drives the actual eInk physics/magic to put the contents on screen.
The video below shows rendering of text and some graphics primitives (line, circle) on the display:
As usual, all source code can be found in PIP-Watch git repository.