A frame buffer is a region of memory that holds the pixel data for a complete display frame, with each location in the buffer corresponding to one or more pixels on the output device. In the common scanout case, display controller or graphics hardware reads this memory continuously to drive the screen, while the CPU or GPU writes new pixel data to compose the next image. In other configurations, the frame buffer is an off-screen rendering target that is later copied or pushed to a display controller.
In practice
On MCUs with an integrated LCD controller (such as the STM32F4/F7/H7 series with their LTDC peripheral, or the NXP i.MX RT family), the frame buffer is a contiguous block of RAM pointed to by the display controller's base address register. The controller fetches pixel data directly from memory and streams it to the panel without CPU intervention. On lower-end parts without dedicated display hardware, a frame buffer may be used as a software staging buffer and then pushed manually in a timer ISR or transmitted over SPI to an external display controller like the ILI9341 or ST7789, though "frame buffer" in that context is sometimes used loosely to mean any staging buffer holding the pending image.
Memory sizing is a frequent constraint. A 320x240 display at 16 bits per pixel (RGB565) requires 150 KB of RAM, which exceeds the total SRAM of many Cortex-M0/M0+ and 8-bit devices. Designers on constrained parts often resort to partial buffering (rendering and transmitting one horizontal band at a time), tile-based rendering, or storing the frame buffer in external PSRAM or SDRAM. The blog post "Flood Fill, or: The Joy of Resource Constraints" illustrates the kinds of algorithmic tradeoffs that arise when you cannot afford a full frame buffer.
A classic technique for eliminating display tearing is double-buffering: one buffer is actively read by the display controller while the CPU writes the next frame into a second buffer. A swap (or flip) occurs at the vertical blanking interval so the controller never reads a partially updated frame. The blog post "Scorchers, Part 3: Bare-Metal Concurrency With Double-Buffering and the Revolving Fireplace" covers the concurrency mechanics of this pattern in a bare-metal context. Triple buffering adds a third buffer to decouple rendering rate from display refresh rate, at the cost of additional memory.
Pixel format choice directly affects both buffer size and rendering complexity. Common formats in embedded systems include RGB565 (2 bytes/pixel), RGB888 (3 bytes/pixel, sometimes padded to 4 depending on bus or alignment requirements), ARGB8888 (4 bytes/pixel), and indexed/palettized formats (1 byte/pixel with a color lookup table) used on very constrained devices. The display controller must be configured to match the pixel format stored in the buffer; a mismatch produces garbled output and is a common bring-up bug.
Frequently asked
Does the frame buffer have to live in internal SRAM?
No. On MCUs with an external memory interface (such as FMC/FSMC on STM32F4 and higher, or SEMC on i.MX RT), the frame buffer commonly resides in external
SDRAM or PSRAM. This is often necessary for larger displays; a 800x480 RGB565 frame takes about 750 KB, far beyond the internal
SRAM of most MCUs. The tradeoff is added
latency and bus contention when both the CPU and display controller access external memory simultaneously.
What causes screen tearing, and how does double-buffering fix it?
Tearing occurs when the display controller reads the buffer while the CPU is still writing a new frame, causing the top portion of the display to show one frame and the bottom portion to show another. Double-buffering fixes this by keeping two buffers: the display controller reads from one (the 'front' buffer) while the CPU writes to the other (the 'back' buffer). At the vertical blanking interval, the pointers are swapped atomically so the controller always reads a complete, coherent frame.
What is a dirty rectangle or partial update, and when is it useful?
A dirty rectangle tracks the screen region that has changed since the last frame. Instead of refreshing the entire frame buffer and retransmitting the whole display, only the changed region is redrawn and sent. This is especially valuable when the frame buffer is pushed over a slow interface like
SPI, where transmitting a full 320x240 RGB565 frame at 40 MHz SPI takes around 12 ms. GUI frameworks like LVGL support dirty-region tracking, though the behavior depends on configuration and display port integration.
How does a palettized (indexed color) frame buffer differ from a direct-color one?
In a palettized format, each pixel value is an index into a color lookup table (CLUT) rather than a direct color. A common embedded variant uses 8 bits per pixel with a 256-entry CLUT, halving memory usage compared to RGB565. The display controller (or software) expands each index to the full color at render or scan-out time. The constraint is that the entire frame can only use colors drawn from the palette, which limits gradients and photographic content but works well for UI graphics and icons.
Can a microcontroller without a hardware display controller use a frame buffer?
Yes, but with caveats. Without a hardware LCD controller, the CPU must transfer pixel data to the display manually, typically over
SPI or a parallel bus to an external controller IC. A frame buffer in
RAM can still be used as a compositing target, but flushing it to the display consumes CPU time and bus bandwidth. On very constrained parts (small Cortex-M0, 8-bit AVR/PIC), a full-frame buffer may not fit in RAM at all, so partial buffers or direct scanline rendering are used instead.
Differentiators vs similar concepts
A frame buffer is sometimes confused with a canvas, a sprite sheet, or a display list. A canvas (as used in some GUI frameworks) is an abstract drawing surface that may or may not map 1:1 to a hardware frame buffer. A sprite sheet is a texture atlas held in memory for blitting, not a scanout target. A display list (used in hardware like the Bridgetek FT81x/BT81x EVE series) is a sequence of drawing commands interpreted by a GPU to produce a frame, rather than a pre-rasterized pixel array. The key property of a true frame buffer is that it stores rasterized pixel values in a layout directly consumable by a display controller or scan-out engine.