How to Use a 0.96 Inch OLED with STM32
To get a 0.96 inch OLED working with an STM32 microcontroller, you need to connect the display via I2C or SPI, configure the STM32’s peripheral libraries, and write firmware to initialize the OLED and send pixel data. The most common driver chip for these displays is the SSD1306, which handles a 128x64 monochrome matrix. For a practical setup, I recommend using the I2C interface because it only requires two wires (SDA and SCL) plus power, freeing up other pins on your STM32 for sensors or actuators. The specific model I’m referring to is the 0.96 inch 128x64 i2c oled display, which operates at 3.3V logic levels—perfect for STM32 boards that typically run at 3.3V. You’ll need to pull up the I2C lines with 4.7kΩ resistors to VCC, though some breakout boards include these onboard. The I2C address is usually 0x3C or 0x3D, depending on the SA0 pin level; check your datasheet. For the STM32 side, you can use the HAL library or direct register writes. I’ll walk through the hardware connections, software setup, and common pitfalls with concrete data.
Hardware Connections: Pinout and Voltage Levels
The OLED display has four pins: VCC, GND, SCL, and SDA. VCC accepts 3.3V to 5V, but the logic levels are 3.3V, so you can power it from the STM32’s 3.3V rail. The STM32F103C8T6 (Blue Pill) draws about 50mA in active mode, and the OLED adds around 20mA during full brightness, so a 3.3V regulator on the board handles it. Connect SCL to PB6 (I2C1_SCL) and SDA to PB7 (I2C1_SDA) on the STM32. If you’re using an STM32L4 series, I2C1 is on PB8 and PB9, but check the datasheet. The I2C bus speed should be set to 400kHz (fast mode) for smooth updates—100kHz works but updates slower. For SPI versions, you’d need CS, DC, RES, and MOSI/SCK, but I2C is simpler for beginners. Use a multimeter to verify VCC is 3.3V at the OLED before connecting, as 5V can damage the SSD1306. The OLED’s internal charge pump generates the 7-8V needed for the pixels, so no external boost converter is required.
Software Setup: Initializing the SSD1306
Start by configuring the I2C peripheral in STM32CubeIDE or your preferred tool. Set the I2C clock speed to 400kHz, enable the peripheral, and assign the pins. The SSD1306 requires a sequence of commands after power-up: a reset via software (send 0xAE to turn off display, then 0x20 for memory addressing mode, 0x00 for horizontal mode). The initialization sequence is about 25 bytes. Here’s a typical command list:
| Command | Hex Value | Purpose |
|---|---|---|
| Display Off | 0xAE | Turn off display during setup |
| Set MUX Ratio | 0xA8, 0x3F | 64 rows for 128x64 |
| Set Display Offset | 0xD3, 0x00 | No vertical shift |
| Set Display Start Line | 0x40 | Start at row 0 |
| Set Segment Re-map | 0xA1 | Column address 0 mapped to SEG0 |
| Set COM Pins | 0xDA, 0x12 | Alternative COM pin configuration |
| Set Contrast | 0x81, 0x7F | Medium brightness (0-255) |
| Set Charge Pump | 0x8D, 0x14 | Enable internal charge pump |
| Display On | 0xAF | Turn on display |
Send these commands via I2C write with the slave address (0x3C << 1 = 0x78 for write). The first byte of each transmission is the control byte: 0x00 for command, 0x40 for data. After initialization, the display is blank because the GDDRAM is all zeros. To show pixels, you write data to the GDDRAM starting at column 0, page 0. The SSD1306 divides the 64 rows into 8 pages (each page is 8 rows). So, a 128x64 display has 128 columns by 8 pages. You send 128 bytes per page, then move to the next page. The data byte’s bits correspond to the rows within that page: bit 0 is the bottom row, bit 7 is the top row. For example, to draw a vertical line at column 0, you’d write 0xFF to page 0, column 0, then 0xFF to page 1, column 0, and so on.
Firmware Implementation: Sending Pixel Data
In your STM32 code, use the HAL_I2C_Mem_Write function to send commands and data. For instance, to send a command byte: HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT, &cmd, 1, 100). For data, change the memory address to 0x40. The GDDRAM is write-only, so you can’t read back the current pixel state. This means you must maintain a framebuffer in the STM32’s RAM—a 128x64 monochrome buffer requires 1024 bytes (128 * 64 / 8). The STM32F103 has 20KB of SRAM, so that’s fine. Update the buffer with pixel manipulation functions: set a pixel by calculating the page (y / 8) and bit position (y % 8). Then, copy the entire buffer to the OLED using a loop that sends 128 bytes per page. At 400kHz I2C, transmitting 1024 bytes takes about 26ms (1024 * 9 bits / 400kHz), which is acceptable for static images but slow for animations. For faster updates, consider using DMA with I2C, but that’s more complex. Alternatively, use the SPI interface which can hit 10MHz, reducing update time to under 1ms. The I2C version is fine for text or simple graphics.
Common Issues and Debugging
One frequent problem is the OLED not turning on. Check the I2C address using an I2C scanner sketch—if the address is 0x3D, adjust your code. The SSD1306 also has a reset pin on some modules; if your board has one, connect it to a GPIO and toggle it low for 10ms during init. Another issue is garbled display: this often happens when the contrast is too high or the charge pump is disabled. Set contrast to 0x7F (127) and ensure the charge pump command (0x8D, 0x14) is sent after power-up. The display might also show no output if the segment re-map is wrong—try 0xA0 instead of 0xA1 for left-to-right mapping. The OLED’s operating temperature range is -40°C to 85°C, so it’s safe for most environments. Power consumption is 20mA typical at full brightness, but you can reduce it to 5mA by setting contrast to 0x10 and using sleep mode (0xAE). The STM32’s I2C peripheral can handle multiple slaves, so you can share the bus with sensors like the BMP280 or MPU6050, as long as addresses don’t conflict.
Performance Metrics and Data
The SSD1306’s frame rate is limited by the I2C bus: at 400kHz, a full screen update takes 26ms, giving about 38 FPS. With SPI at 10MHz, you can achieve 100 FPS. The display’s viewing angle is >160 degrees, and the contrast ratio is 2000:1 (typical for OLEDs). The pixel pitch is 0.16mm for a 0.96-inch diagonal, so text at 6x8 pixels is readable. For a 128x64 resolution, the maximum font size is 16x16 pixels for 8 characters per line. The SSD1306 supports horizontal, vertical, and page addressing modes. Horizontal mode is best for full-screen updates: after setting the column start and end addresses (0x21, 0x00, 0x7F) and page start and end (0x22, 0x00, 0x07), you can send 1024 bytes sequentially. The GDDRAM is volatile, so the display blanks on power loss. The STM32’s RTC can wake the display from sleep, but that’s a separate topic. The OLED’s lifetime is rated at 100,000 hours for typical use, but bright pixels degrade faster—keep contrast below 0x80 for longevity.
Practical Example: Displaying a Bitmap
To show a 128x64 bitmap, convert it to a byte array using a tool like LCD Assistant. The array should be 1024 bytes, organized in page order. For a test, create a checkerboard pattern: fill every other byte with 0xAA. Send the array after initialization. The STM32’s flash can store multiple bitmaps, but the 64KB flash on the F103 limits you to about 60 full-screen images. Use the following code snippet to update the display:
uint8_t buffer[1024];
for (int page = 0; page < 8; page++) {
HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT, &buffer[page * 128], 128, 100);
}
This loops through each page and sends 128 bytes. The buffer is updated by a drawing function, like drawing a circle or text. For text, use a 5x7 font table stored in flash. The STM32’s SysTick timer can generate a 1ms interrupt to update the display at 30 FPS, but avoid blocking the main loop—use a flag to trigger updates. The OLED’s I2C bus can be shared with other devices, but ensure the bus capacitance is under 400pF for 400kHz operation. Longer wires increase capacitance, so keep connections under 10cm.
Advanced: Using DMA and Interrupts
For non-blocking updates, configure the I2C with DMA. In STM32CubeIDE, enable DMA for I2C1 TX, set the data width to byte, and use a circular buffer for the framebuffer. The DMA transfer complete interrupt can signal the main loop to update the buffer. The STM32F103’s DMA controller has 7 channels, so dedicate one to I2C TX. The transfer rate with DMA is the same as polling, but the CPU is free to handle other tasks. The SSD1306 supports a “scroll” command (0x26 or 0x27) for hardware scrolling, which shifts the display horizontally without CPU intervention. This is useful for marquee text. The command sets the scroll speed (0x00 to 0x07, where 0x07 is fastest at about 6 frames per second). Disable scrolling with 0x2E before updating the GDDRAM. The OLED’s internal oscillator runs at 400kHz, so scrolling is smooth. The STM32’s low-power modes can put the display to sleep by sending 0xAE, then entering STOP mode. The OLED’s wake-up time is 100ms, so plan for that in your firmware.