There are two modes for achieving grayscale on the SSD1306 display. The first mode seems to work only on a few SSD1306 displays. However, I found a second mode that works by setting zoom=1
and driving the VSYNC by chaning the mux register periodically. In this case, the display overwrites the screen two times.
commands:
0xd6,1, //zoom:1
,0xa8,63, //mux 64
0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3, // this must be enough nops to cause 150us(?) delay (by transfering them to the display)
0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,
0xa8,1 // mux:1
To see a demonstration of the new grayscale mode, please watch the following YouTube video:
https://www.youtube.com/shorts/bLATW7clPwE
Please note that the original undocumented mode that works only on a few (newer?) displays is described at the bottom of this document.
this can be easily achieved by the Mux trick (and changing contrast)
https://www.youtube.com/watch?v=LOilqUmZNBU&feature=youtu.be
This is the result of testing the registers of the SSD1306 for undocumented features. The following registers were found in both displays.
Test: Display 1
- 0: Black screen
- 255: Normal
- 254: Off
- 253: Off
- 0xf: On
- 7: Darkened, elapsed
- 0b1011: Normal
- 0b1001: Black
- 0b1010: Black
- 0b1110: Black
- 0b0011: Darkened, elapsed (identical to 7) - Bit 2 probably has no function on screen 1 but does on screen 2
Display 2
- 0b0011: Off
- 0b0111: Normal
- 0b1011: Normal, darker than 0b1111
- 0b1111: On (identical to 255)
The following values work on both displays:
- 0b1111: Screen on
- 0b0000: Off
The upper 4 bits seem to have no function. This register probably controls the power of the OLED. Switching from 15 to 0 makes the screen go black in about 2 frames.
Test: Display 1
Writing to this register causes a black screen, and it appears to be irreversible.
This register is documented for SSD1303 as DC Control.
Test: Display 1
- Bit 0 seems to have no effect.
- Influences brightness.
This register is documented for SSD1303 as Area Color and low power display mode. Probably low power mode is present.
Test: Display 1
- Affects brightness when written to.
- Probably supports low power mode.
This command draws a rectangle
0x24
,0,
2, // row
fuzz_d2, // pattern to fill
4, // row
32 // xstart
,64 // xend
bit 7 stripes bit ? black screen
set bit 1 reset 4 for proper operation, brightness effect, black
Probably discharge cycles: https://www.youtube.com/shorts/cD0YHYcuCro bit 7 (0x80) must be set, or the command will have no effect, bits 0-3 are probably the charge cycles, setting it to 15 results in the heighest frame rate
Test: Display 1
- 1p (timing? row timing?)
Display 2
- 3p
- Parameter 1: Timing effect
- Parameter 2: Nothing visible directly
- Parameter 3: Nothing visible directly
Test: Display 1
not present
Display 2
- One bit has a brightness effect
Test: Display 1
- Lock VRAM (1<<4?)
- 32 crash?
This register is documented for SSD1309 as a lock register.
Test: Display 1
- Command lock works with 0x16 for lock, 0x12 for unlock
- Works as expected
Test: Display 1
- Something with row timing?, when in 128x32x3 bit 0 flashes the screen, must be 0...other bits slight brightness effect
Display 2
- Picture black depending on bit
Additional undocumented stuff is in VCOMH. after clearing one of the top 1 bits the lower 4 bit allow finer control.
Mux can be used for VSYNC. The trick is based on the idea to let the SSD1306 display a single (or 2) lines, and then set the image height (mux) to 64, wait at least 150us and set mux back to 1 (or 0, 0 would be better, but my newer display needs mux:1, which also works on my older). To display a single frame (oneshot style) you do the following:
- Set mux to 63.
- Wait 150 us. (just insert the nop commmand a few times in your command stream)
- Set mux to 1 (one of my displays has problems with mux 0).
- repeat at your intended framerate
- make the top 2 lines black or they will glow pretty bright.
- Mux=1 results in a 2 pixels high image.
- Move this image with the "Set Display Offset Register" from the top of the screen to the bottom.
- For each line, set the sourceline.
- Delay a bit, 9kHz rate of the calls seems fine.
- Now you can draw the image two times, increment the source line every other line, and change the brightness between the two phases.
- You'll get a 4-color grayscale mode.
The scroll area functions as line compare registers and works fine without the scroll commands, just change display offset, and startline...this allows for a scrolling area in the center of the screen
The content scroll commands of the SSD1309 allow scrolling on the X and Y axes without the display continuously scrolling.
- The second dummy in content scroll, which "should" be set to 1, acts as a flag for at least "clear new pixels/rotate image", and new pixel=black/white.
- content scroll at mux=1 is very fast: https://www.youtube.com/watch?v=DmiUed8LGRo
The normal scroll commands accept a window on the X axis. Both dummy parameters, which should be set to 255, represent the start and end points. This information is documented in the SSD1309 and SSD1306B datasheets.
The SSD1306B datasheet documents a larger range in the charge pump.
To achieve grayscale mode,the palette will close to 0,195,255, follow this procedure:
- Disable Remap (0xc0,0xa0) to avoid artifacts.
- Set Zoom=1 (register 0xd6). Display a single frame: the ssd1306 will "overrun" and draw the zoomed image 2 times, so the pixels from row 32 in vram get added to row 0 and 1, from row 33 to 2 and 3
- Set the mux to 63.
- Wait for 150 microseconds.
- Set the mux to 1. (Note: One of my displays has problems with mux 0.)
- wait, goto 3 at 60hz rate.
this is a test for the undocumented register 0x9a=2 of the ssd1306, it enables a 128x32 grayscale mode, this is stretched using the zoom command to fill the screen ( 0xd6=1)
if this works for you please open a ticket, if it does not please also open a ticket!
see function TestCmd for the used commands (0x9a,2,0xd6,1), Set Mux to 64
https://www.youtube.com/shorts/1Vx-WqN9S30
the pixel format is simple 2 pixels on y become one, summed up pixel at (0,0) = 1 , pixel at (0,1) = 0 -> both pixels get 0.5 brightness if both are 1 its 1 brightness for both (and for 0 the same)
Undocumented Behavior: Multiplex Command and Frame Control
The multiplex command on the SSD1306 display controller allows you to control the number of lines displayed on the screen. Normally, setting the multiplex value to 0 should display the lowest line, which becomes frozen and unmovable. However, it has been observed that some displays may experience issues when using multiplex=0, resulting in undesirable behavior.
To address this problem, an alternative approach using multiplex=1 is recommended. When multiplex is set to 1 or 0, an interesting trick can be employed to prevent the first two lines of the display from glowing continuously. By setting the multiplex value to 1 or 0, the display will maintain a very high frame rate while keeping these one or two lines constantly displayed.
To achieve frame control using this trick, follow these steps:
Set multiplex to 1 or 0: This ensures that the display continues to show the first one or two lines at a high frame rate.
Set multiplex to 63: At this point, the display starts showing the frame, and its internal line counter begins incrementing up to 63.
After the display has shown more than one line (approximately 100 microseconds), set multiplex back to 1: By triggering this transition, you gain control over the display of every subsequent frame.
By employing this method, you can manipulate the display to show specific frames by carefully timing the transition of the multiplex command. This undocumented behavior provides a means of frame control and can be useful for certain applications where precise display timing is required.
undocumented register A9 test:display 1: 0: black screen 255: normal 254: off 253: off 0xf:on 7: darkened, elapsed 0b1011: normal 0b1001: black 0b1010: black 0b1110: black 0b0011: darkened, elapsed identical 7, bit 2 probably no function on screen 1 but on screen 2 display 2: 0b0011: off 0b0111: normal 0b1011: normal darker than 0b1111, 0b1111: on, identical 255
-> works on both: 0b1111: screen on 0b0000: off