How to display a bitmap on a 1.54 inch 128x64 OLED?
How to Display a Bitmap on a 1.54 Inch 128x64 OLED
To display a bitmap on a 1.54 inch 128x64 OLED display, you need to convert your image into a monochrome byte array that matches the display’s resolution, then send it via SPI or I2C using a microcontroller like an Arduino or ESP32. The process involves three core steps: preparing the bitmap data, initializing the display driver (typically SSD1306 or SH1106), and writing the data to the frame buffer. Let’s break this down with real hardware specifics, code examples, and performance data so you can implement it reliably.
The display itself is a 128x64 pixel monochrome OLED, meaning each pixel is either on or off. Common controllers are the SSD1306 (for 128x64) or SH1106 (which has 132x64 memory but displays 128x64). The physical interface is usually 4-wire SPI (SCLK, MOSI, DC, CS, plus RST) or I2C. For bitmap display, SPI is faster—typical SPI clock speeds are 4-8 MHz, allowing a full frame update in about 2-4 milliseconds. I2C maxes out at 400 kHz, taking 20-30 ms per frame. If you’re animating or updating frequently, SPI is the practical choice.
Step 1: Convert Your Bitmap to a Byte Array
Your source image must be 128x64 pixels, grayscale or black-and-white. Use a tool like Image2LCD (free, Windows) or LCD Image Converter (cross-platform). Set the output format to monochrome, 1 bit per pixel, with horizontal scan order (most common for SSD1306). The resulting array will be 1024 bytes (128 * 64 / 8). For example, a simple checkerboard pattern would produce alternating 0xAA and 0x55 bytes. If you’re using a 1.54 inch 128x64 oled display from a vendor like 1.54 inch 128x64 oled display, the pixel mapping is standard: column 0-127, page 0-7 (each page is 8 rows).
Step 2: Display Initialization
The SSD1306 requires a specific initialization sequence after power-up. Here’s a typical sequence with commands and delays (based on the datasheet, which you should always reference):
Send 0xAE (display off)
Send 0xD5, 0x80 (set display clock divide ratio/oscillator frequency)
Send 0xA8, 0x3F (set multiplex ratio to 64)
Send 0xD3, 0x00 (set display offset)
Send 0x40 (set start line to 0)
Send 0x8D, 0x14 (enable charge pump)
Send 0x20, 0x00 (set memory addressing mode to horizontal)
Send 0xA1 (set segment remap to column 127)
Send 0xC8 (set COM output scan direction)
Send 0xDA, 0x12 (set COM pins hardware configuration)
Send 0x81, 0xCF (set contrast—value 0xCF is typical for 3.3V)
Send 0xD9, 0xF1 (set pre-charge period)
Send 0xDB, 0x40 (set VCOMH deselect level)
Send 0xA4 (resume to RAM content display)
Send 0xA6 (set normal display, not inverted)
Send 0x2E (deactivate scroll)
Send 0xAF (display on)
Wait at least 100 ms after power-on before sending commands. The entire init takes about 50-80 bytes over SPI, which is negligible in time.
Step 3: Write Bitmap Data to the Frame Buffer
The SSD1306 has a 1024-byte internal RAM (GDDRAM). To display a full bitmap, you set the column and page start addresses, then send the 1024 bytes sequentially. In horizontal addressing mode (command 0x20, 0x00), after sending the first byte, the column address auto-increments. After 128 columns, the page increments. This is efficient for full-screen bitmaps.
Here’s a minimal Arduino function using SPI (assuming you’ve set up SPI pins correctly):
void displayBitmap(const uint8_t *bitmap) {
digitalWrite(CS, LOW);
sendCommand(0x21); // set column address range
sendCommand(0); // start column
sendCommand(127); // end column
sendCommand(0x22); // set page address range
sendCommand(0); // start page
sendCommand(7); // end page
for (uint16_t i = 0; i < 1024; i++) {
sendData(bitmap[i]);
}
digitalWrite(CS, HIGH);
}
Where sendCommand() pulls DC low, sendData() pulls DC high, and both clock out the byte via SPI.transfer(). If you’re using I2C, the same data is sent but wrapped in a control byte (0x00 for command, 0x40 for data).
Performance Data: SPI vs I2C for Bitmap Display
Let’s quantify the difference. Assume a 128x64 bitmap (1024 bytes). At 8 MHz SPI, each byte takes 8 clock cycles = 1 µs. So 1024 bytes = 1.024 ms, plus command overhead (~0.1 ms). Total: ~1.2 ms per frame. At 400 kHz I2C, each byte takes 9 clock cycles (8 data + 1 ACK) = 22.5 µs, so 1024 bytes = 23 ms, plus addressing overhead (~0.5 ms). Total: ~24 ms per frame. That’s 20x slower. For static images, both work fine. For animations or scrolling, SPI is mandatory.
Bitmap Orientation and Memory Mapping
The SSD1306’s memory is organized into 8 pages, each 128 bytes wide. Page 0 covers rows 0-7, page 1 covers rows 8-15, etc. Your bitmap array must be arranged in the same order: byte 0 = pixels for column 0, rows 0-7 (bit 0 = row 0, bit 7 = row 7). If your image converter outputs a different orientation (e.g., vertical scan), you’ll need to transpose it. Most tools have a “scan direction” option—set it to horizontal. If you get a mirrored or upside-down image, flip the column or page addressing in the init (commands 0xA0/0xA1 for column remap, 0xC0/0xC8 for COM scan).
Practical Considerations for Real-World Bitmaps
- Fonts and text: If you’re displaying text, pre-render each character as a 5x7 or 8x8 bitmap and store them in a lookup table. A 5x7 character takes 5 bytes (7 rows, 1 bit per row). For a full screen of text (16 rows of 8 characters each), you’d need 128 * 16 = 2048 bytes of font data, which is manageable on a typical microcontroller with 32 KB flash.
- Image compression: For complex images, consider run-length encoding (RLE) to reduce flash usage. A typical photo might compress to 30-50% of its original size. Decompress on the fly into a buffer before sending to the display.
- Partial updates: If only a small region changes, you can set the column and page range to just that area. For example, a 20x20 icon can be updated by sending only 20 * 3 = 60 bytes (since 20 rows span 3 pages), taking 0.06 ms over SPI. This is critical for battery-powered devices where every millisecond of CPU time matters.
Hardware Wiring and Voltage Levels
The 1.54 inch OLED typically runs at 3.3V logic, but many modules have onboard regulators that accept 5V VCC. Check your module’s datasheet. For SPI, connect:
VCC to 3.3V or 5V (check specs)
GND to ground
SCLK to SPI clock pin (e.g., Arduino pin 13)
MOSI to SPI MOSI pin (pin 11)
DC to any digital pin (e.g., pin 9)
CS to any digital pin (e.g., pin 10)
RST to any digital pin (e.g., pin 8)
If using I2C, connect SDA and SCL with 4.7kΩ pull-up resistors to 3.3V. The I2C address is usually 0x3C or 0x3D, depending on the SA0 pin level.
Common Pitfalls and Debugging
- Blank screen: Check the contrast command (0x81). A value below 0x80 might be too dim. Also ensure the charge pump is enabled (0x8D, 0x14).
- Garbage pixels: Usually a timing issue—ensure your SPI clock is not too fast for the display (most SSD1306 handle up to 10 MHz, but some clones are flaky above 4 MHz). Add a 1 µs delay between bytes if needed.
- Wrong orientation: Swap the column remap (0xA0 vs 0xA1) or COM scan (0xC0 vs 0xC8).
- Memory corruption: If you’re using interrupts or other tasks, disable them during SPI transfers to avoid partial writes.
Real-World Example: Displaying a 128x64 Logo
Let’s say you have a company logo as a 128x64 BMP file. Convert it using Image2LCD with these settings: output format: C array, monochrome, 1 bit per pixel, horizontal scan, MSB first. The resulting array will look like:
const uint8_t logo[] = {
0x00, 0x00, 0x00, 0x00, ... // 1024 bytes total
};
Then call displayBitmap(logo) after initialization. On an Arduino Uno at 8 MHz SPI, the screen updates in about 1.5 ms. You can also store multiple bitmaps in PROGMEM to save RAM. For example, a 10-frame animation would require 10,240 bytes of flash, which is fine on a 32 KB microcontroller.
Advanced Techniques: Double Buffering and DMA
If you’re doing animations, use a 1024-byte RAM buffer to compose the next frame while the display shows the current one. Then swap buffers with a single memcpy() or by updating the display from the new buffer. On ESP32, you can use SPI DMA to send the bitmap without CPU intervention—set up a DMA descriptor that points to the bitmap array, and the transfer happens in the background. This frees the CPU for other tasks and allows frame rates up to 60 fps (since each frame takes ~1 ms over SPI, you have 15 ms of idle time per frame at 60 fps).
Power Consumption Data
The OLED itself draws about 20-30 mA when all pixels are on, and 0.1 mA when off (sleep mode). The SSD1306 has a sleep command (0xAE) that reduces current to <10 µA. If you’re displaying a bitmap with mostly black pixels (off), the current drops proportionally. For a typical 50% fill bitmap, expect ~15 mA. This is important for battery-powered designs—you can toggle the display on/off based on user activity.
Testing with a Logic Analyzer
If you’re having trouble, hook up a logic analyzer (even a cheap $10 one) to the SPI lines. You should see CS go low, then 8-bit commands with DC low, followed by data bytes with DC high. The clock should be clean, with no glitches. Measure the timing: the first byte should arrive within 100 µs of CS assertion. If you see extra pulses or missing bits, check your wiring and SPI mode (mode 0, CPOL=0, CPHA=0 is standard).
Alternative: Using Graphics Libraries
Libraries like Adafruit_SSD1306 or U8g2 abstract away the low-level details. For example, with Adafruit_SSD1306, you can call display.drawBitmap(0, 0, logo, 128, 64, WHITE) and then display.display(). Under the hood, it does the same byte-by-byte transfer. But if you need maximum performance or minimal code size, writing your own driver gives you control—you can skip the library overhead (which can add 2-3 KB of flash and 100-200 bytes of RAM). For a production device, a custom driver is often preferred.
Conclusion of the Technical Details
The key takeaway is that displaying a bitmap on a 1.54 inch 128x64 OLED is a straightforward process once you understand the memory layout and SPI/I2C timing. The 1024-byte frame buffer is small enough for any microcontroller, and the display’s response time is fast enough for real-time updates. Focus on getting the initialization right, use a reliable image converter, and test with a simple pattern like a checkerboard before moving to complex graphics. The hardware is robust—most modules survive thousands of write cycles without degradation. If you hit a snag, compare your SPI waveform to the datasheet’s timing diagram; that’s usually the culprit.
Schick dein Dataset zur Diagnose.
30 Minuten. Wir schauen uns Schema, Nulls, Drift und Label-Korruption an — und du gehst mit einer konkreten Liste der drei größten Risiken raus. Kein Pitch, kein Folgedruck.