Greyscale 2bit 4 Colour success with SSD1306


A couple of things I thought when I looked into 2 bits per pixel greyscale on the Arduboy a while ago:

Because perceived pixel intensity is the result of pixel PWM at frame rate, missing a frame is baaaad so trying to keep up rendering from the application’s main loop like most (all?) games do is fragile and probably too restrictive. With 1 bit color if you miss a frame no one notices, but when alternating ON-OFF to get shades of grey missing frames changes the shade of grey the user sees. One way to solve this is decouple + prioritise i.e. to give display rendering priority over game logic (simplest way is to use an interrupt handler).

A full screen 2 bits frame buffer is likely too large for most games (~ 500 bytes for stack and game state left). Games could reduce the portion of the screen using shades of grey to reduce frame buffer size but… if rendering is decoupled from the game loop as suggested in the previous paragraph double buffering is needed.

My guess is that greyscale applications would end up having special render loops that fill (portions of) the screen from program memory (sprites for instance) and other tricks to reduce frame buffer size, or do away with it completely and use tile + sprite based rendering loops and double buffered tile/sprite maps like in most games for personal computers from the 80s. The bottom line is that writing greyscale games with so little RAM is not as easy as filling a frame buffer and pushing it to the screen every once in a while.

(Simon) #22

Hey @dxb You have been quiet for a while. Welcome back!


Thanks @filmote! Yes, once in a while I still stick my head out :sweat_smile:. I haven’t had much spare time since April. I’m looking forward to having more time to dedicate to the Arduboy, hopefully in early 2019 :crossed_fingers:

(Kevin) #24

So we run out of memory holding a 2 bit buffer and also practically run out of time to even transmit the full buffer at full frame speed?

Sounds like grayscale is fesable for this screen if FR is broken out, but really needs a faster processor with more memory. And sounds like something with DMA would really help too and eliminate processor hits when stepping through memory.

So something with grayscale might work on an ESP32 pretty well? :slight_smile:


I think the CPU can keep up (but of course DMA would help). RAM is the limiting factor if you want to keep game development simple. 4kbytes is needed for a double buffered full screen 2 bits frame buffer.

How do you spell overkill? :laughing:

(Jim) #26

You’re right. Display update would become the priority, but as you say … it would have to be interrupt driven.
Really when you think about it, a full greyscale display would be an even better option, but of course memory requirements increase with this.

(Jim) #27

I thought to myself after posting that you’d have to be leaving out SPIF flag check, but you’d still have to know when the time was right to load the next byte into SPDR. A few NOPs would do it i suppose. Must try this myself.
Cool … never thought of doing this before.

(Jim) #28

Discussion around using CL and CLS plus FR was the fact that frame rate could be increased by supplying your own clock and maximum starting frame rate is important when multiplexing display images. Reduces flicker. My hacking revealed a major difference between display boards with regard to top attainable frame rates.
One display was 168FPS and another only 128FPS. The reason - RC Oscillator variation due to manufacturing process.

(Scott) #29

Note that you can control the display clock frequency somewhat using the
Set Display Clock Divide Ratio/Oscillator Frequency (D5h)

See section 10.1.16 of the SSD1306 datasheet.


Yes, and even on the same board the internal oscillator’s frequency changes quite a bit depending on temperature and supply voltage.

I am not sure but my guess is that, as long as games don’t use FR as timebase, user experience shouldn’t be affected very much.

(Kevin) #31

Maybe that is why the manufacturer cannot be bothered to bring out the FR pin, they are thinking “just use a display with a grayscale (or 16 bit) controller”

(Jim) #32

I already had this maxed out.


My guess is they figured the target market for this kind of panel+controller combinations wasn’t video/entertainment (and they are mostly right). I haven’t seen a panel+controller (monochrome or color) with 3 or 4 wire interface featuring a broken out VSync.

I understand the goal here was to enable greyscale graphics but VSync and HSync signals also enable raster effects like ones available on the Uzebox which IMO are the essence of 8-bit gaming as much as chip-tunes are. For some time now I have been looking for a programable and portable retro game console which has a VSync signal but found none. :weary:

(Jim) #34

I just had a go at this and ended up with the same18 MCU clocks per byte ie.1.152ms . Can’t do it any faster. SPI won’t let you write any sooner.
I gotta thank you for the idea. I’ve learnt something.

(Scott) #35

The Arduboy clocks the SPI at 8Mhz but the display can handle a bit faster (I think it’s 10Mhz). It would be interesting to know whether the maximum speed is time per byte or SPI clock cycles per byte.


I’d say time per byte as there are only 8 SPI clock cycles per byte.

There is a possibility to clock a byte out in 16 MCU cycles though by using the UART in MSPIM (Master SPI Mode). But this not useful for Arduboy as it requires different pin configuration.

I found out about this recently here


I didn’t know about MSPIM thank you for bringing it up!

(Jim) #38

I’ve converted the top half off a 2 bit .bmp pic off the internet to display on the SSD1306 OLED.
Just in case you can’t make it out, it’s a parrot. The converted original pic as well.!
It’s no simple job to do. I had to create 3 images from the original .bmp file.


(Jim) #39

10Mhz ie. 100ns is the SPI SCLK maximum. So it’s 100ns per serial bit.

Page 56 of data sheet:
tcycle Clock Cycle Time 100 ns

(Kevin) #40

Curious how come you had to make 3 bitmaps? Does your library draw each gray layer independently?

Or just no converter for a 2 bit data format?