Skip to content

How to draw shapes on a 3.2 inch 256x64 OLED screen?

Words by admin
1MOV Editorial

How to draw shapes on a 3.2 inch 256x64 OLED screen

To draw shapes on a 3.2 inch 256x64 oled display module, you need to use a microcontroller like an Arduino, ESP32, or STM32, and communicate via SPI or I2C. The display is monochrome, meaning each pixel is either on or off, so you’ll work with a framebuffer in memory—typically 256x64 bits, which is 2048 bytes (256*64/8). You can use libraries like Adafruit SSD1306 or U8g2, but for a 3.2 inch 256x64 oled display module, you must adjust the dimensions to 256x64 and set the correct controller (often SH1106 or SSD1322, depending on the specific model). The key is to treat the display as a 2D array of pixels, where you set bits in the buffer and then flush the buffer to the display via SPI commands. For example, to draw a rectangle, you iterate over x and y coordinates, set bits where x is between two values and y is between two values, then call the display update function. For circles, you use the midpoint circle algorithm, which calculates pixel positions without floating-point math. Lines are drawn with Bresenham’s algorithm, which uses integer arithmetic. The SPI speed matters: typical OLEDs support up to 10 MHz, so you can update the full screen in about 2 milliseconds (2048 bytes * 8 bits/byte / 10 MHz = 1.6 ms, plus overhead). But if you’re using I2C, it’s slower—around 400 kHz, so a full refresh takes roughly 40 ms. For a 3.2 inch 256x64 oled display module, the pixel pitch is about 0.36 mm (since 3.2 inches is the diagonal, and the width is roughly 256 * 0.36 mm = 92 mm, height 64 * 0.36 mm = 23 mm). This means shapes need to be large enough to be visible—a 1-pixel line is fine, but a 2-pixel line is more readable. You can also draw filled shapes by setting all pixels within a boundary. For instance, a filled rectangle sets all bits in a region, which is efficient because you can write bytes in a loop. The display controller’s memory is organized in pages (8 pixels per page vertically), so for a 256x64 display, there are 8 pages (64/8). When drawing, you must map pixel coordinates to the correct page and column. Many libraries handle this, but if you’re writing raw SPI commands, you send a command byte followed by data. For example, to set the column address range, you send 0x21, then start column (0-127 for 128 columns, but for 256 columns, you need a different command sequence—check the datasheet). The SH1106 controller, common in larger OLEDs, uses a 132x64 memory but only displays 128x64, so for a 256x64 module, you might have two controllers or a different chip like SSD1322, which supports 256x64 natively. Always verify the controller from the datasheet of your specific 3.2 inch 256x64 oled display module. For drawing shapes, you can also use hardware acceleration if the controller supports it—some OLEDs have built-in drawing commands for rectangles and circles, but monochrome modules rarely do, so you’re stuck with software. The framebuffer approach is flexible: you can draw shapes in any order, and the display only updates when you call the flush function. This prevents flicker. For performance, use double buffering: draw to a back buffer, then swap buffers with the display. This is common in games. The memory footprint is 2 KB for one buffer, so 4 KB for double buffering, which is fine for most microcontrollers with at least 8 KB of RAM. For an Arduino Uno (2 KB RAM), double buffering is tight, but you can use a single buffer and update only changed regions. For example, if you draw a small circle, you only need to send the bounding box of that circle to the display. This is called partial update. The SPI command for setting a window is 0x21 and 0x22 for column and page addresses, but for 256x64, you might need to set the column to 0-255 and page to 0-7. Some controllers require a command to set the display start line, which is often 0. The contrast is set via 0x81, with a value from 0 to 255. For a 3.2 inch display, typical contrast is 0x80 to 0xFF for maximum brightness. The power consumption is around 20-30 mA for the OLED, so drawing shapes doesn’t affect power much, but the microcontroller’s SPI activity adds 1-2 mA. For drawing text shapes, you need a font bitmap. A 5x7 font uses 5 bytes per character, so for 256 pixels wide, you can fit 51 characters per line (256/5), and 9 lines (64/7). But for shapes, you’re better off using vector graphics. For example, to draw a triangle, you compute three line segments. The Bresenham line algorithm works by stepping through x and y, deciding which pixel to set based on error. For a line from (x0, y0) to (x1, y1), the algorithm is: dx = abs(x1-x0), dy = -abs(y1-y0), sx = sign(x1-x0), sy = sign(y1-y0), err = dx+dy. Then loop until x0==x1 and y0==y1, set pixel, and update err. For a circle, the midpoint algorithm: x = 0, y = radius, d = 1 - radius. Loop while x <= y, set eight symmetric pixels, and update d. These algorithms are standard and work on any monochrome display. The key data for a 3.2 inch 256x64 oled display module is the resolution: 256 horizontal pixels, 64 vertical. The aspect ratio is 4:1 (256:64), so shapes will appear stretched horizontally if you don’t account for pixel aspect ratio. Since pixels are square (0.36 mm each), the physical shape is correct. But if you draw a circle with radius 10 pixels, it will look circular because the pixel pitch is equal in both axes. For a 3.2 inch diagonal, the width is about 2.8 inches (71 mm) and height 0.7 inches (18 mm), based on the pixel count: 256 * 0.36 mm = 92 mm (3.62 inches), which is slightly off because the pixel pitch might be different. Actually, the display module’s active area is typically 89.38 mm x 22.38 mm for a 3.2 inch 256x64, so pixel pitch is 0.349 mm horizontally and 0.350 mm vertically. So shapes are nearly square. For drawing, you can use the Adafruit GFX library, which has functions like drawRect, fillRect, drawCircle, fillCircle, drawLine, drawTriangle, fillTriangle, drawRoundRect, etc. But you must initialize the display with the correct width and height. For example, in Arduino code: display.begin(SSD1306_SWITCHCAPVCC, 0x3C) for I2C, or for SPI, you need to define pins. The library uses a buffer of 1024 bytes for 128x64, but for 256x64, you need 2048 bytes. Some libraries have a limitation: Adafruit SSD1306 only supports up to 128x64, so you need a custom library or the U8g2 library, which supports many controllers. For U8g2, you initialize with U8G2_SH1106_256X64_1_4W_HW_SPI(u8g2, CS, DC, RST). Then you can draw shapes in a loop. For example, to draw a rectangle: u8g2.drawFrame(10, 10, 50, 30); (x, y, width, height). To fill: u8g2.drawBox(10, 10, 50, 30). For circles: u8g2.drawCircle(20, 20, 10). The U8g2 library uses a page buffer by default, which is 256 bytes for one page (8 pixels high), so you need to call u8g2.firstPage() and u8g2.nextPage() in a loop. This is efficient for memory but slower for full-screen updates. For a full buffer, use U8G2_SH1106_256X64_F_4W_HW_SPI, which uses 2048 bytes of RAM. The F variant is for full buffer. The SPI speed can be set to 8 MHz or higher. The display controller’s datasheet specifies the maximum SPI clock. For SH1106, it’s typically 10 MHz. For SSD1322, it’s 20 MHz. So for a 3.2 inch 256x64 oled display module, check the controller. If it’s SSD1322, you can use the U8g2 library with the constructor U8G2_SSD1322_256X64_1_4W_HW_SPI. The SSD1322 supports 4-bit grayscale, but if your module is monochrome, it might be a variant. Most 3.2 inch 256x64 monochrome OLEDs use SH1106 or SSD1322. For drawing shapes, you can also implement your own function that writes directly to the framebuffer. For example, in C, you define a buffer: uint8_t buffer[2048]; Then to set a pixel at (x, y): buffer[x + (y/8)*256] |= (1 << (y%8)); This is for a vertical byte arrangement. Some controllers use horizontal arrangement, so check the datasheet. The SH1106 uses vertical page addressing, where each byte is 8 vertical pixels. So for a 256x64 display, there are 256 columns and 8 pages. So the buffer index is page * 256 + column. For drawing a line, you can use the Bresenham algorithm to set pixels one by one. For a filled rectangle, you can set bytes in a loop. For example, to fill a rectangle from (x0, y0) to (x1, y1), you iterate over y from y0 to y1, and for each y, you find the byte index and set bits. This is faster than setting each pixel individually. For a circle, you can compute the bounding box and only set pixels within that box. The performance of drawing shapes depends on the microcontroller. An ESP32 at 240 MHz can draw a full screen of random pixels in about 1 ms, but an Arduino Uno at 16 MHz takes 10 ms. For a 3.2 inch 256x64 oled display module, the refresh rate is typically 60 Hz, so you have 16 ms per frame. If you draw complex shapes, you might exceed this, causing flicker. To avoid this, use double buffering and only update the display after all shapes are drawn. You can also use DMA on the ESP32 to send SPI data without CPU involvement. For example, using the ESP32 SPI library, you can send the entire framebuffer in one DMA transfer. This takes about 0.2 ms for 2048 bytes at 80 MHz SPI. So you can draw shapes at 60 fps easily. For a Raspberry Pi Pico, the SPI can run at 50 MHz, so similar performance. The key is to optimize the drawing functions. For example, drawing a rectangle by setting bytes is much faster than drawing pixels. For a 10x10 pixel rectangle, you set 10 bytes (if aligned to page boundaries), but if not aligned, you need to handle partial bytes. For a 3.2 inch display, the pixel size is large enough that you can see individual pixels, so anti-aliasing is not needed. For shapes like arcs, you can use the same algorithm as circles but only draw a portion. For example, to draw an arc from 0 to 90 degrees, you only set pixels in that quadrant. The U8g2 library has drawArc, but it’s for full circles. You can implement your own by modifying the midpoint algorithm. For drawing polygons, you can use the scanline algorithm, which fills a polygon by scanning each row and setting pixels between edges. This is complex but doable. For a 256x64 display, the number of rows is only 64, so you can iterate over each row. For each row, you find the intersections of the polygon edges with that row, sort them, and fill between pairs. This is efficient for small polygons. The memory for the edge list is small. For example, for a triangle, you have three edges. The scanline algorithm is used in many graphics libraries. For a 3.2 inch 256x64 oled display module, you can also draw shapes using the OLED’s built-in hardware if it supports it. Some OLED controllers have a command for drawing a rectangle or a circle, but these are rare in monochrome modules. The SH1106 does not have such commands. The SSD1322 has a few drawing commands, but they are for grayscale. So software is the way. For drawing shapes with rotation, you need to use matrix transformations. For example, to draw a rotated rectangle, you compute the four corners using rotation matrix, then draw lines between them. This is computationally intensive but doable on an ESP32. For a 3.2 inch display, the rotation angle can be any value. The U8g2 library does not support rotation, so you need to implement it yourself. For example, to draw a rotated rectangle, you define the center, then for each corner, compute new coordinates: x_new = cos(theta)*x - sin(theta)*y, y_new = sin(theta)*x + cos(theta)*y. Then draw lines. This requires floating-point math, which is slow on an Arduino. But on an ESP32, it’s fine. You can also use fixed-point math. For example, use a lookup table for sine and cosine. The accuracy needed is low because the resolution is only 256x64. So you can use a 256-entry table. For drawing shapes with thickness, you can draw multiple lines. For example, a thick line is a rectangle. For a thick circle, you draw two circles with different radii. The U8g2 library has drawCircle with thickness? No, but you can draw a filled circle and then draw a smaller filled circle to create a ring. For a 3.2 inch 256x64 oled display module, the contrast is high, so shapes are clearly visible. The viewing angle is 160 degrees, so shapes are readable from the side. The power consumption is low, so you can run on batteries. For drawing shapes in a loop, you need to clear the buffer each frame. This is done by setting all bytes to 0. Then draw shapes, then update. For a 3.2 inch display, the clear time is about 0.2 ms for the buffer, but the SPI update takes longer. So you can achieve 60 fps if you only update a small region. For example, if you draw a moving circle, you only update the bounding box of the circle. This is called partial update. The U8g2 library supports partial update with the setClipWindow function. For example, u8g2.setClipWindow(10, 10, 50, 30); then draw shapes, then u8g2.nextPage(). This only sends the pixels in that window. This is efficient for animations. For a 3.2 inch 256x64 oled display module, the window can be any size. The SPI command for setting a window is 0x21 for column start and end, and 0x22 for page start and end. For 256x64, the column range is 0-255, and page range is 0-7. So you can set a window of, say, columns 10-50 and pages 0-2. This reduces the data sent. For example, a 40x24 pixel window is 40*24/8 = 120 bytes, which takes 0.12 ms at 10 MHz. So you can update many windows per frame. For drawing shapes like arrows, you can combine lines and triangles. For example, an arrow is a line with a triangle at the end. The U8g2 library has no arrow function, but you can draw a line and then a filled triangle. For a 3.2 inch display, the arrow tip should be at least 5 pixels wide. For drawing shapes like stars, you can compute the vertices of a star polygon and draw lines. For example, a 5-point star has 10 vertices. You compute them using trigonometry: for each point, radius alternates between inner and outer. Then draw lines between them. This is a common pattern. For a 3.2 inch 256x64 oled display module, the star can be up to 64 pixels tall. The resolution is enough to show fine details. For drawing shapes with dithering, you can simulate grayscale by using a pattern of pixels. For example, a 2x2 pattern can create 4 shades. But this reduces resolution. For a 256x64 display, you can use a 2x2 dithering pattern to get 4 shades, but the effective resolution becomes 128x32. This is useful for gradients. For example, to draw a filled circle with a gradient, you can use dithering. The algorithm: for each pixel, compute the distance from the center, then map to a shade, then use a dithering matrix to decide if the pixel is on or off. This is complex but doable. For a 3.2 inch display, the pixel size is large, so dithering is visible. But it can create a smooth appearance. The memory for the framebuffer is still 2 KB. For drawing shapes like pie charts, you can draw arcs and fill them. For example, to draw a pie slice, you draw two lines from the center to the arc, and fill the area. The scanline algorithm works for this. For a 3.2 inch 256x64 oled display module, the center can be at (128, 32). The radius can be up to 32 pixels. So the pie chart is small but readable. For drawing shapes like graphs, you can draw axes and lines. For example, a line graph draws points and connects them. The U8g2 library has drawLine, so you can plot data. For a 256x64 display, you can show 256 data points horizontally,

About the author

admin

One of 22 programmers on the 1MOV editorial team. Our curators have come from Sundance, Venice, TIFF, Berlinale and IDFA — and they choose every title you see by hand.

Next on the programme

One film, one move — your Friday pick is curated, not recommended.

See This Week's Collection