Fork me on GitHub

Framebuffer and Drawing Pixels on OLED Chip SSD1306


In the previous post we had introduced the C library for performing graphics on the OLED screen SSD1306 using a Raspberry Pi.

In this post we explain the framebuffer object and how to draw individual pixels on the screen.

The code in this post is as of git tag 0.51 or commit 294ffccc1004b35028406fc4281cef894f473d2a from Github.

UNDERSTANDING FRAMEBUFFER

The SSD1306 OLED screen has a display RAM that is of the same size as the screen pixels, with a one-to-one correspondence between pixel and a bit in the display RAM (GDRAM in the datasheet). To mimic the behavior of the RAM, we have the framebuffer object that has a buffer of the same size as the display RAM, and allows you, the developer, to draw to the framebuffer memory image, before writing that memory image directly to the display RAM of the OLED screen. This allows the programs to perform multiple drawing tasks in memory, which is faster, and then writing once via I2C to the display RAM, which is slower.

Having a framebuffer graphics object, independent of the actual SSD1306 command object, enables you to perform faster drawing by using multiple framebuffer objects. For instance, lets say you have drawn an image onto a single framebuffer object and then queued a write to the display RAM. During the time it takes to update the display RAM using I2C, you could be updating a second framebuffer object simultaneously or using multi-threading. This methodology of using more than one framebuffer has been used by game developers for several decades. This can enable fast re-drawing of the screen.

If you want to use the Raspberry Pi (or Beagleboard, etc.) with the SSD1306 OLED screen, and want to build a fun game with our library you can utilize the multiple framebuffer approach to make the game appear to display fast.

DEBUGGING FRAMEBUFFER

Sometimes, or all the time if you are me, you may want to look at what the framebuffer memory looks like, so that whatever you are drawing and expecting to see on the OLED screen, you can actually see on your computer terminal too.

To achieve that we have a function ssd1306_framebuffer_bitdump() which takes only one argument, the framebuffer pointer, and dumps the framebuffer memory to the screen in the same way it would display on the OLED screen.

#include <ssd1306_graphics.h>
/* ... some initialization code ... */
/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);
/* ... do something to the framebuffer ... */
// dump the framebuffer memory
ssd1306_framebuffer_bitdump(fbp);

Having the framebuffer object work without the need of the I2C device can be very useful for running memory leak tests using Valgrind, which we have been doing to make sure that our code is memory leak free, which it is. Valgrind works great on x86 devices but does not work reliably, as of this writing, on the Raspberry Pi. Eventually, we plan to use a USB-to-I2C device, like the TUMPA Lite which we possess, so we can test the full library directly from an x86 computer too.

DRAWING PIXELS

We have four pixel-level functions in our graphics library, three for drawing a pixel and one for knowing the pixel value. For the SSD1306 OLED screen, there are only two possible values for the pixel color: black (clear) or blue (colored). We handle that with a boolean flag.

ssd1306_framebuffer_put_pixel()

This function takes the (x,y) coordinates of where you want to draw the pixel on the framebuffer, the framebuffer pointer and a boolean flag for coloring or clearing the pixel. The (x,y) coordinates need to be within the width and height values of the OLED screen, which in our example below is 128x32.

The function returns 0 on success and -1 on error.

The below code snippet is present in the examples/i2c_128x32_graphics.c file.

#include <ssd1306_graphics.h>
/* ... some initialization code ... */

/* create the framebuffer for a OLED screen 128x32 */
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// for (x,y) (0, 0)
ssd1306_framebuffer_put_pixel(fbp, 0, 0, true);
// for (x,y) (127, 0)
ssd1306_framebuffer_put_pixel(fbp, fbp->width - 1, 0, true);
// for (x,y) (0, 31)
ssd1306_framebuffer_put_pixel(fbp, 0, fbp->height - 1, true);
// for (x,y) (127, 31)
ssd1306_framebuffer_put_pixel(fbp, fbp->width - 1, fbp->height - 1, true);
// for dumping the framebuffer to screen to view the changes
ssd1306_framebuffer_bitdump(fbp);

/* remove the framebuffer pointer */
ssd1306_framebuffer_destroy(fbp);

ssd1306_framebuffer_get_pixel()

This function returns the value of the color of the pixel, either 0 if clear (black) or 1 if colored (blue), or -1 if the arguments are invalid. This can be used by the developer in the event they want to analyze the display memory of the framebuffer. An example of where this is used is in the ssd1306_framebuffer_bitdump() function.

The usage is similar to the ssd1306_framebuffer_put_pixel() function, where the inputs are the framebuffer pointer, and the (x,y) coordinates. If the coordinates are not within the height and width of the OLED screen, or the framebuffer pointer is invalid the return value is -1.

// get the value of the color at (0,10)
int8_t color = ssd1306_framebuffer_get_pixel(fbp, 0, 10);

ssd1306_framebuffer_invert_pixel()

When you want to invert the color of a specific pixel location, they could do two function calls: one to ssd1306_framebuffer_get_pixel() to get the current pixel color and one to ssd1306_framebuffer_put_pixel() to change it. However, to speed up this in a single call by using the xor operation, we have the ssd1306_framebuffer_invert_pixel() function available to us.

If the color of the existing pixel at (x,y) is colored (blue) a call to this function will clear it, and if the existing pixel is clear (black) a call to this function will color it.

Below is a modified version of the above example, demonstrating coloring the pixel using the ssd1306_framebuffer_invert_pixel() function.

#include <ssd1306_graphics.h>
/* ... some initialization code ... */

/* create the framebuffer for a OLED screen 128x32 */
// the framebuffer is clear by default
ssd1306_framebuffer_t *fbp = ssd1306_framebuffer_create(128, 32, NULL);

// for (x,y) (0, 0)
ssd1306_framebuffer_invert_pixel(fbp, 0, 0);
// for (x,y) (127, 0)
ssd1306_framebuffer_invert_pixel(fbp, fbp->width - 1, 0);
// for (x,y) (0, 31)
ssd1306_framebuffer_invert_pixel(fbp, 0, fbp->height - 1);
// for (x,y) (127, 31)
ssd1306_framebuffer_invert_pixel(fbp, fbp->width - 1, fbp->height - 1);
// for dumping the framebuffer to screen to view the changes
ssd1306_framebuffer_bitdump(fbp);

/* remove the framebuffer pointer */
ssd1306_framebuffer_destroy(fbp);

ssd1306_framebuffer_put_pixel_rotation()

There are cases that you may have when you are building display screens wher the drawings need to be rotated in position along the screen by mirror imaging the drawing using a rotation method. This can be useful for drawing mirror images of an existing diagram on one side of the screen, or for drawing rotating images.

To accomplish this task you could draw a pixel in a framebuffer and then re-draw the same image by keeping the same (x,y) coordinates, and instead rotating the drawn pixels by 90°, 180°, and 270° and back to 0°. This can be achieved by using the ssd1306_framebuffer_put_pixel_rotation() function.

It has the same signature as the ssd1306_framebuffer_put_pixel() function, with an extra argument of a rotation flag with values 1, 2 or 3 which represent the number of 90° rotations that need to be made. In fact, the ssd1306_framebuffer_put_pixel() is the same as this function, with the rotation flag set as 0.

// let's draw a diagonal line on one corner
for (size_t i = 0; i < 16; ++i) {
    ssd1306_framebuffer_put_pixel(fbp, i, i, true);
}
ssd1306_framebuffer_bitdump(fbp);

// let's mirror the same line across all 4 corners using rotation
for (size_t i = 0; i < 16; ++i) {
    ssd1306_framebuffer_put_pixel_rotation(fbp, i, i, true, 1 /* rotate by 90 degrees */);
}
for (size_t i = 0; i < 16; ++i) {
    ssd1306_framebuffer_put_pixel_rotation(fbp, i, i, true, 2 /* rotate by 180 degrees */);
}
for (size_t i = 0; i < 16; ++i) {
    ssd1306_framebuffer_put_pixel_rotation(fbp, i, i, true, 3 /* rotate by 270 degrees */);
}
ssd1306_framebuffer_bitdump(fbp);

Below is what the framebuffer dump looks like with the first line on the top-left corner, and the three mirror images of the line on the other corners.

0000 |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| 
0001 .|...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|. 
0002 ..|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....|.. 
0003 ...|.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|... 
0004 ....|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|.... 
0005 .....|.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|..... 
0006 ......|. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... 
0007 .......| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |....... 
0008 ........ |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| ........ 
0009 ........ .|...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|. ........ 
000A ........ ..|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....|.. ........ 
000B ........ ...|.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|... ........ 
000C ........ ....|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|.... ........ 
000D ........ .....|.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|..... ........ 
000E ........ ......|. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... ........ 
000F ........ .......| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |....... ........ 
0010 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0011 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0012 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0013 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0014 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0015 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0016 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0017 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0018 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0019 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
001F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0020 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0021 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0022 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0023 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0024 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0025 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0026 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0027 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0028 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0029 ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002A ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002B ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002C ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002D ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002E ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
002F ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ 
0030 ........ .......| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |....... ........ 
0031 ........ ......|. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... ........ 
0032 ........ .....|.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|..... ........ 
0033 ........ ....|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|.... ........ 
0034 ........ ...|.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|... ........ 
0035 ........ ..|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....|.. ........ 
0036 ........ .|...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|. ........ 
0037 ........ |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......| ........ 
0038 .......| ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ |....... 
0039 ......|. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .|...... 
003A .....|.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ..|..... 
003B ....|... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ...|.... 
003C ...|.... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ....|... 
003D ..|..... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .....|.. 
003E .|...... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ......|. 
003F |....... ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ .......|