Experimenting with the OLED Click

OLED W clickOne thing many of my experiments can often use is an alphanumeric display, even better a graphic one. OLED displays have become really cheap nowadays and their low current consumption (when compared to a TFT display or an LCD with backlighting) makes them an attractive solution. But developing an ad hoc PCB and connecting with the typical flex strip can be quite a challenge for most prototyping needs.

Now it happens that there are no less than three OLED click boards currently listed on the Mikroelektronika catalog: a color model (OLED-C), and two monochrome models, white(OLED-W) and blue (OLED-B). I figured the color version featuring a 96×96 matrix with 16-bit color resolution would be cool, but it would also eat up too much memory ( 96x 96 x 2 = 18,432 bytes) if I was ever to use its full resolution/capabilities. The smaller ( 96x 40) monochromatic displays are just slightly cheaper but most importantly require only 480 bytes to paint a complete picture (96x 40 /8 bytes) something that appeared immediately to be more in tune with the needs and capabilities of my typical small 8-bit application (sensor/driver/controller/node…). I bought myself an OLED W click and started experimenting with it right away.

All  OLED displays nowadays feature a smart controller (Chip on Board) that abstracts all the complexity of the actual rows and columns (of pixels) driving as well as keeps a complete image in a local RAM array removing the need to perform constant scanning. The OLED-W click features an SSD1306 controller which presents both an SPI and an I2C interface to the application microcontroller. Both interfaces are also presented on the mikroBUS  connector but only the SPI is enabled by default (as in most click boards, a simple 0 ohm resistor  can be moved to change selection).

Setting up the project in MPLAB X and configuring a PIC16 microcontroller (PIC16F1709) to use the SPI peripheral (MSSP) in master  mode was a matter of a couple of clicks thanks to the MPLAB Code Configurator.

SPI Master (mode 0)

The only detail worth noting in the image above (or button worth clicking…) being the selection of the right Clock Edge. For some reason the MCC seems to prefer the Idle to Active edge ( a.k.a. mode 1) vs. what is in my experience the most frequent case, and surely the case of the SSD1306, that requires an Active to Idle clock edge (mode 0)!

As for the IOs, all I had to do was enabling the standard pin assignments for the SPI port and add three additional pins for the Data/Command register selection (OLED_DC), the device reset (OLED_RST) and chip select (OLED_CS) so to fit the wiring of the board (Simplicity) in use. Porting the project to work with the Curiosity board might require some changes.

GPIO configuration

Once the GPIO (pin_manager.c) and SPI  (spi.c) drivers were generated by MCC, and so was my main.c file,  I am lazy and I like to let MPLAB work for me, the next hurdle was to get the OLED controller initialised properly.

The init procedure is for all such displays controllers the only tricky part. Fortunately, in most cases it can be simply copied from an example file provided directly by the (display) manufacturer. In our case, Mikroelektronika being so diligent in supporting its products, it meant just taking “inspiration” ( a.k.a. cut & paste) directly from their MikroC examples.

void OLED_Initialize( void)
    OLED_RST_LAT = 0;
    OLED_RST_LAT = 1;
    OLED_Command(SSD1306_DISPLAYOFF);             //0xAE  Set OLED Display Off
    OLED_Command(SSD1306_SETDISPLAYCLOCKDIV);     //0xD5  Set Display Clock Divide Ratio
    OLED_Command(SSD1306_SETMULTIPLEX);           //0xA8  Set Multiplex Ratio
    OLED_Command(SSD1306_SETDISPLAYOFFSET);       //0xD3  Set Display Offset
    OLED_Command(SSD1306_SETSTARTLINE);           //0x40  Set Display Start Line
    OLED_Command(SSD1306_CHARGEPUMP);             //0x8D  Set Charge Pump
    OLED_Command(0x14);                           //0x14  Enable Charge Pump
    OLED_Command(SSD1306_COMSCANDEC);             //0xC8  Set COM Output Scan Direction
    OLED_Command(SSD1306_SETCOMPINS);             //0xDA  Set COM Pins Hardware Config
    OLED_Command(SSD1306_SETCONTRAST);            //0x81   Set Contrast Control
    OLED_Command(SSD1306_SETPRECHARGE);           //0xD9   Set Pre-Charge Period
    OLED_Command(SSD1306_SETVCOMDETECT);          //0xDB   Set VCOMH Deselect Level
    OLED_Command(SSD1306_DISPLAYALLON_RESUME);    //0xA4   Set Entire Display On/Off
    OLED_Command(SSD1306_NORMALDISPLAY);          //0xA6   Set Normal/Inverse Display
    OLED_Command(SSD1306_DISPLAYON);              //0xAF   Set OLED Display On
} // OLED_Initialize

The initialisation sequence above is in large part dictated by the physical construction of the display module (composed of the display surface and the SSD1306 controller mounted on the same glass support). Its wiring dictates how rows and columns have to be scanned and which commons/segments are used. The result is a particular arrangement of the RAM array (image buffer) contents on the screen.

With a minimum of investigation it can be determined that the display is split in 5 rows (or pages in SSD documentation lingo) each composed of 8 pixel lines. There are then 96 columns that span the horizontal screen width although in inverse order – increasing addresses cover columns from right to left.

Check out the code posted on Github for this project and the many other cross linked for use with the Simplicity (and in future) Curiosity boards for all of you Rocket Scientists!

OLED logo

Note: While it is possible to invert the vertical scanning order, I could not find a simple way to reverse the horizontal scanning order. It looks like this display was really meant to be used upside down. It was simple enough though to take care of it in software when writing the PutImage() function (used to create the image above) or rather reordering bytes of data in the image array itself (in logo.c)  in flash.

[Correction] I did find the way to invert the horizontal scan order by adding the following line to the OLED_Initialize() function:

 OLED_Command(SSD1306_SETSEGMENTREMAP); //0xA1 Set Segment Remap Inv

Since the OLED Click screen is only 96 columns wide (while the SSD1306 considers 128), this requires us only to add a little offset when we select the column:

void OLED_SetColumn( uint8_t add)
   add += 32;
   OLED_Command(( SSD1306_SETHIGHCOLUMN | (add >> 4))); // SET_HIGH_COLUMN
   OLED_Command(( 0x0f & add)); // SET LOW_COLUMN

To be continued…

This entry was posted in PIC16 and tagged , , , . Bookmark the permalink.