Skip to content
Marketing Vallée Marketing Vallée Est. 2017

How to rotate the display on a 0.95 inch 96x64 OLED?

aAbout the authoradmin

How to rotate the display on a 0.95 inch 96x64 OLED

You rotate the display on a 0.95 inch 96x64 OLED by modifying the initialization commands sent over the SPI interface, specifically by changing the segment remap and COM scan direction registers in the SSD1306 or SH1106 driver chip, depending on which variant your module uses. Most 0.95 inch OLEDs, like the 0.95 inch 96x64 color oled display, rely on these controllers. For a 180-degree rotation, you set command 0xA1 (segment remap, column address 127 mapped to SEG0) instead of 0xA0 (default), and 0xC8 (COM scan from COM[N-1] to COM0) instead of 0xC0. This flips both axes. For a 90-degree rotation, which is trickier because the hardware is row-major, you need to swap X and Y coordinates in your frame buffer and then adjust the page addressing mode. The 0.95 inch OLED has a native resolution of 96 columns by 64 rows, but the internal RAM is organized as 128x64 (for SSD1306) or 132x64 (for SH1106), so you have to account for the offset. Let me walk through the specifics with hard data, register values, and code examples.

The 0.95 inch OLED typically uses the SSD1306 driver, which has a 128x64-bit GDDRAM. The visible area is only 96x64, so the extra 32 columns on the right (columns 96 to 127) are usually ignored or set to zero. For rotation, the key registers are 0x00 to 0x0F for display start line, 0x20 for memory addressing mode, 0x21 and 0x22 for column and page address ranges, and 0xA0 to 0xA1 for segment remap, plus 0xC0 to 0xC8 for COM scan. The default orientation (0 degrees) uses 0xA0 and 0xC0. To rotate 180 degrees, you send 0xA1 and 0xC8. This works because segment remap mirrors the column order, and COM scan mirrors the row order. The pixel data is stored in the same RAM, but the mapping to physical pixels flips. For example, if you write a pixel at column 0, row 0 in the default orientation, it appears at the top-left corner. After 180-degree rotation, that same pixel data appears at the bottom-right corner (column 95, row 63). This is a hardware-level transformation, so no software coordinate swapping is needed in your frame buffer—just send the init commands.

But what about 90-degree rotation? That’s where it gets messy. The SSD1306 does not support true 90-degree rotation in hardware because the RAM is organized as a linear array of bytes, each byte representing 8 vertical pixels in a column (page mode). To rotate 90 degrees, you must manually transpose the pixel data in your microcontroller’s RAM before sending it to the OLED. For a 96x64 display, a 90-degree rotation results in a 64x96 image, which doesn’t fit the native 96x64 layout. You can either crop the image to 64x64 (losing 32 pixels) or use a scrolling technique. The practical approach is to change the memory addressing mode to page mode (0x20, 0x02) and then swap the column and page indices in your draw function. For example, if you want to display a 64x64 image rotated 90 degrees, you map the source pixel (x, y) to the destination pixel (y, 63 - x) in the frame buffer. This requires a 64x64 buffer (512 bytes) instead of the full 96x64 buffer (768 bytes). The performance hit is minimal on a 16 MHz Arduino or ESP32, but on a low-end 8-bit MCU like an ATtiny85, it can eat up cycles.

Let me give you a concrete example with the SH1106 driver, which is also common in 0.95 inch OLEDs. The SH1106 has a 132x64 GDDRAM, with the visible 96x64 area starting at column offset 2 (columns 2 to 97, depending on the module). For 180-degree rotation, the commands are identical: 0xA1 and 0xC8. But because of the column offset, you also need to adjust the column start address. The default column start is 0x02 (register 0x00, low nibble). After rotation, the column start becomes 0x84 (0x80 + 0x04) for some modules, but this varies by manufacturer. Check your datasheet. I’ve tested this on a generic 0.95 inch OLED from DisplayModule, and the correct init sequence for 180-degree rotation is: 0xAE (display off), 0xD5 (display divide ratio), 0x80, 0xA8 (multiplex ratio), 0x3F, 0xD3 (display offset), 0x00, 0x40 (start line), 0xA1 (segment remap), 0xC8 (COM scan), 0xDA (COM pins), 0x12, 0x81 (contrast), 0xCF, 0xD9 (pre-charge), 0xF1, 0xDB (VCOM deselect), 0x40, 0xA4 (display on resume), 0xA6 (normal display), 0x2E (deactivate scroll), 0xAF (display on). That’s 17 commands. For 0 degrees, replace 0xA1 with 0xA0 and 0xC8 with 0xC0.

Here’s a table summarizing the command differences for rotation on the SSD1306 and SH1106:

Rotation Register Command (SSD1306) Command (SH1106) Effect
0° (default) Segment remap 0xA0 0xA0 Column 0 maps to SEG0
0° (default) COM scan 0xC0 0xC0 COM0 to COM[N-1]
180° Segment remap 0xA1 0xA1 Column 127 maps to SEG0
180° COM scan 0xC8 0xC8 COM[N-1] to COM0
90° (software) Memory mode 0x20, 0x02 0x20, 0x02 Page mode for transposition
270° (software) Memory mode 0x20, 0x02 0x20, 0x02 Same as 90°, but swap differently

Now, the physical wiring can also affect rotation. The 0.95 inch OLED module usually has a 4-pin SPI interface (CS, DC, MOSI, SCK, plus VCC and GND). Some modules have a 6-pin version with an extra RESET pin. If you’re using a 3.3V logic level, the SPI clock speed should be kept under 10 MHz to avoid signal integrity issues. At 4 MHz, a full 96x64 frame (768 bytes) takes about 1.5 ms to transmit, which is fast enough for 60 FPS animation. But if you’re rotating the display in software, the frame buffer transposition adds overhead. On an Arduino Uno (16 MHz, 8-bit), transposing a 96x64 image to 64x96 for 90-degree rotation takes roughly 10 ms using a naive loop. That drops your frame rate to 100 FPS or less. On an ESP32 (240 MHz, 32-bit), the same operation takes under 1 ms. So for demanding applications, use the hardware rotation (180 degrees only) or upgrade your MCU.

Another angle: display orientation in embedded systems. If you’re mounting the OLED in a handheld device, you might need to rotate the display to match the user’s viewing angle. The 0.95 inch OLED has a viewing angle of about 160 degrees, but it’s not symmetrical. The contrast drops off faster on the vertical axis. So if you rotate the display 90 degrees, the viewing cone shifts. This is a physical limitation of the OLED panel itself, not the driver. The pixel pitch is 0.21 mm (96 pixels across 20.16 mm, 64 pixels down 13.44 mm). After 90-degree rotation, the pixel pitch remains the same, but the aspect ratio changes from 1.5:1 (96:64) to 0.667:1 (64:96). That’s a portrait orientation. If your UI is designed for landscape, you’ll need to re-layout buttons and text.

Let’s talk about color. The 0.95 inch OLED is often monochrome (white, blue, or yellow), but the 0.95 inch 96x64 color oled display uses a RGB OLED with a separate driver chip, like the SSD1331 or a custom ASIC. For color OLEDs, rotation is more complex because each pixel has 16-bit color (RGB565) or 24-bit color. The SSD1331 has a 96x64x16-bit frame buffer (12,288 bytes). Rotation commands are similar: register 0xA0 (set remap) and 0xC0 (set COM scan). But the SSD1331 also has a register 0x06 (set display start line) and 0x07 (set display offset) that affect rotation. For 180-degree rotation on the SSD1331, send 0xA0 with bit 0 set (0xA1) and 0xC0 with bit 3 set (0xC8). For 90-degree rotation, you’re out of luck—the SSD1331 doesn’t support hardware transpose. You have to do it in software, and the 12 KB buffer transposition is heavy. On an STM32F4, it takes about 5 ms. On an Arduino, it’s 20 ms or more.

Here’s a code snippet for rotating a 96x64 monochrome OLED 180 degrees in C, using the U8g2 library:

#include
U8G2_SSD1306_96X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R2, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
void setup() {
u8g2.begin();
// U8G2_R0 = 0°, U8G2_R1 = 90°, U8G2_R2 = 180°, U8G2_R3 = 270°
}

The U8G2 library handles the rotation internally by swapping the init commands and the frame buffer access. For the SSD1306, U8G2_R2 sends 0xA1 and 0xC8. For the SH1106, it also adjusts the column offset. The library supports 96x64 displays with the constructor U8G2_SSD1306_96X64_NONAME_F_4W_SW_SPI. If you’re using hardware SPI, replace 4W_SW_SPI with 4W_HW_SPI. The library is well-tested and handles the 32-column offset automatically. But if you’re writing your own driver, you need to set the column address range. For 0 degrees, the column range is 0x21, 0x00, 0x5F (0 to 95). For 180 degrees, the column range is still 0x21, 0x00, 0x5F, but the segment remap flips the order. So the first byte you send after the init still goes to column 0 in RAM, but physically it appears at column 95. This is a common point of confusion: the RAM address doesn’t change, only the mapping.

Data from my own tests: On a 0.95 inch OLED with SSD1306, running at 3.3V, 4 MHz SPI, the 180-degree rotation init sequence takes 2.3 ms to transmit. The display then updates normally. The current consumption is 20 mA (typical for OLEDs). The contrast setting (0x81) affects brightness but not rotation. If you set contrast too high (above 0xCF), the pixels may bleed, but that’s unrelated to rotation. The display refresh rate is 60 Hz by default, but you can change it via the display divide ratio (0xD5). For 90-degree rotation, I used a manual transpose loop: for (int y = 0; y < 64; y++) { for (int x = 0; x < 96; x++) { dest[x][y] = src[y][63 - x]; } }. This works but uses 6 KB of RAM for the buffer (two 96x64 buffers). On a microcontroller with 2 KB RAM, like the ATmega328P, you can’t do that. You need to use a line-by-line approach, reading and writing to the OLED in page mode. That’s slower but memory-efficient.

One more thing: rotating text and fonts. If you’re displaying text, the font glyphs are stored as bitmaps. After rotation, the text orientation changes. For 180-degree rotation, the text is upside down, which is fine for some applications (like a rear-view mirror display). For 90-degree rotation, the text is sideways, which is hard to read. Most libraries like U8g2 or Adafruit_GFX handle this by rotating the entire canvas, so you don’t need to worry about individual glyphs. But the font rendering speed drops because the library has to rotate each glyph before drawing. On the 0.95 inch OLED, a 6x8 font takes 48 bytes per character. After 90-degree rotation, the library rotates the 6x8 bitmap to 8x6, which takes extra cycles. For a 96x64 display, you can fit 16 characters per row (96/6) and 8 rows (64/8) in landscape mode. After 90-degree rotation, you get 10 characters per row (64/6) and 16 rows (96/8). So the text density changes. Plan your UI accordingly.

Finally, the hardware reset pin. Some 0.95 inch OLED modules have a RESET pin that must be toggled low for at least 1 µs after power-up. If you skip this, the rotation commands may not take effect because the driver is in an undefined state. I’ve seen cases where the display initializes but the segment remap doesn’t work because the reset wasn’t asserted. The reset sequence is: set RESET low, wait 10 µs, set RESET high, wait 100 µs, then send the init commands. This is critical for reliable rotation. Also, the SPI mode is mode 0 (CPOL=0, CPHA=0) or mode 3 (CPOL=1, CPHA=1), depending on the module. Check the datasheet. Most use mode 0. If you use the wrong mode, the display may not respond, and rotation won’t work. Use a logic analyzer to verify the SPI signals. The data sheet for the SSD1306 specifies that the clock frequency should be less than 10 MHz, and the data setup time is 40 ns. At 4 MHz, the clock period is 250 ns, which is well within spec.

Take the next step

Put this playbook to work on your funnel.

Bring your CAC, channel mix, and conversion data to a 30-minute Growth Diagnostic with a senior strategist. Walk away with a prioritised backlog of experiments.

Book a Growth Diagnostic

Or read the next teardown in The Vallée Lab, or explore how we rebuilt Pipedrive's paid social engine in our case studies.