Character LCDs are great for building simple embedded interfaces, they are simple to use and cheap. I use character LCDs in most of my own projects and experiments. However, character LCDs are somewhat limited. It’s hard if not impossible to display an image or a logo on the screen and you’re often stuck with the same font built into the display. Unlike character displays, graphical displays provide more flexibility and will let you display images and custom fonts. In this post I’ll talk about working with a graphical OLED display. The display I’ll talk about uses the SSD1322 driver chip from Solomon Systech.

A Simplistic View of the SSD1322

The SSD1322 driver chip contains a Graphic Display Data Ram (GDDRAM) from which it displays data directly. The SSD1322 driver chip is capable of 4-bit grayscale and hence each pixel requires 4-bits of data to represent it. The particular display we’ll use has a display size of 256 by 64 pixels although the SSD1322 chip can do 480 by 128 pixels. This understanding of the SSD1322 driver chip will suffice for this post.

Working With Images On The SSD1322

We’ll write a python script to convert bitmap (.bmp) files into source code. A bitmap is a file format that represents an image as an array of pixels, this is the image format we’ll feed to our script. However, we’ll need to edit our image before we pass it to our python script. We’ll resize the image to fit our application and then we’ll convert the image to 8-bit grayscale since we don’t have a 4-bit option in GIMP. GIMP is a free image editing tool suitable for this type of editing. Our python script will then scale the image to 4-bit grayscale and convert it to C source code.

Preparing the image

Firstly we’ll open GIMP and load a monochrome image file which we wish to convert to code. Secondly we’ll click the image menu and select “Crop to Content”. Next we’ll click on the image menu again but this time we’ll select “Scale Image”, this opens a dialog box for setting the width and height of our image in pixels. We must ensure the resulting image fits into our display size of 256 by 64 pixels. After selecting the correct image size we will click on “Scale” to resize the image.

Scaling the image in GIMP.

Scaling the image in GIMP.

Now let’s click on the image menu, hover the mouse on “Mode” and select “Grayscale” since our image is monochrome.

Setting the image mode to grayscale in GIMP.

Setting the image mode to grayscale in GIMP.

Let’s click on the image menu, hover the mouse on “Precision” and select “8 bit integer” – our python script assumes that our image has an 8 bit grayscale, so this step is very important. Let’s click on the image menu again, hover the mouse on “Precision” and select “Linear light”.

Setting the image precision in GIMP.

Setting the image precision in GIMP.

Finally we’ll export the data as a bitmap file. Let’s go to “File” and click on “Export As” or use the shortcut “Shift+Ctrl+E”, this will open a dialog box. Let’s click on the “Select File Type (By Extension)” at the bottom of the dialog box and select “Windows BMP image”. We then click export after setting our prefered directory and we’re done!

Exporting the image as a .bmp file in GIMP.

Exporting the image as a .bmp file in GIMP.

Image data structure and representation in C

We need to know how the SSD1322 driver code is going to represent images if we’re to generate image code for it. The SSD1322 driver code represents an image as a structure containing a pointer to a const array, the width and height of the array.

The image data is stored in a const array, this enables the compiler to place the array in the flash memory of the microcontroller.

The code snippet below shows an example for initializing a bitmap structure for the file “image.bmp”. Here we create a pointer to the “image_bitmap” array in the above code snippet. The width and height of the bitmap are initialized via definitions.

The bitmap array and structure are all found in the bitmap source file. Therefore we’ll have to expose the structure in a header file to enable its usage by the rest of the application.

Generating the image code

Now that we have a fair idea of how the SSD1322 driver code represents bitmaps let’s generate code for it. To display a bitmap image we just need to read the bitmap (.bmp) file and convert it to source code for our microcontroller. We’ll write a python script to generate the image code. The read_bitmap() function is used to read a bitmap (.bmp) file and returns an array of pixels, the width and height of the bitmap. Inside the read_bitmap() function is a call to the “matplotlib.image” method imread() which performs the actual file reading.

Every column address of the SSD1322 represents four pixels, therefore we need to ensure that the width of the bitmap array is divisible by four. If a bitmaps width isn’t divisible by four, we add dummy data (essentially 0’s) to the end of each column. The SSD1322 chip represents two pixels in one byte but the read_bitmap() function represents one pixel in one byte thereby utilizing half of the byte since each pixel is 4 bits wide. Hence we merge two pixels into one byte of data in the format_bitmap() function.

Code is generated by writing the image data and associated comments to source (.c) and header (.h) files. To make writing the data easy we’ll create a function bitmap_to_array() which will organize all our bitmap data in a dictionary called “bitmap_table”.

Because the bitmap_to_array() function takes a list of bitmap filenames, we’re able to generate one pair of source (.c) and header (.h) files for multiple bitmaps. Lastly we’ll create the bitmap_to_c() function which does the final code generation. This function makes use of the “bitmap_table” to write bitmap data to source (.c) and header (.c) files.

Working With Text On The SSD1322

Text and image data representation in our SSD1322 driver code is very similar. Text is typically stored in an embedded system as a complete font. All the application has to do is look for the image representation of a character and output it to screen. Most fonts come as truetype font (.ttf) or opentype font (.otf) formats. We’ll add code to our python script to render font characters. But first let’s look at how the text is structured.

Text data structures and representation in C

We require an additional array to store data about each glyph or character in the font. We’ll refer to this array as the font table. Each glyph in our font is like its own mini bitmap. So we’ll need a structure for each font table entry.

The font table is stored as a constant array of font_table_entry_t structures. During code generation the font array is sorted alphabetically to enable easy access of glyph metadata.

The font is a constant array just like our bitmap, to access a glyph the SSD1322 driver code will fetch the glyph location from the font table and use that to index the font array. The width, height and other parameters of the glyph will be used to draw the glyph on the screen. A font structure is used to represent the font array as expected. Note that we have a pointer to the font table in the font structure as well as a pointer to the font array.

We’ll initialize our font structure as shown in the code snippet below.

Lastly we’ll have to expose our font structure in the header file so the rest of the application can have access.

Generating the text code

In order to display text, we’ll have to convert a font of our choice into source code just like we did for bitmaps/images. After some searching on the internet I found a library called freetype that could render fonts. Further searching led me to Dan Bader’s blog post on rendering fonts for embedded systems. We’ll use Dan’s code to render font glyphs in python and perform some further processing to make the font bitmaps compatible with our display. Now let’s organize the font data in a dictionary called “font_table” to make the work easier. Note that the actual rendering of the glyphs occur in the font_to_array() function as well as the creation of our “font_table”.

Finally, we’ll write each rendered character into a source (.c) file. Writing the font array into a file occurs in the font_to_c() function. We’ll add associated comments as we generate the code.

Putting It All Together

So now all that’s left for us to do is to enjoy the fruit of our work. Therefore we’ll make calls to font_to_c() and bitmap_to_c() to create source code for our OLED project. These calls will lead to the creation of source (.c) and header (.h) files in our working directory. The files generated by the example below are “Lato_Regular.c” and “Lato_Regular.h” for the font file “Lato-Regular.ttf” as well as “resources.c” and “resources.h” for the bitmap files.

What’s Next?

We’ll dive into the inner workings of the SSD1322 driver code in the next post. Additionally we’ll include the generated resources from this post into a microcontroller project and test it. You can access the code on Github.

References

This work is based on code written by Dan Bader, check out his blog post here: http://dbader.org/blog/monochrome-font-rendering-with-freetype-and-python.