Can a 128x32 COG LCD display show custom characters?
Yes, a 128x32 COG LCD display can absolutely show custom characters, and this capability is one of its most practical features for engineers and hobbyists building embedded systems. Unlike simple alphanumeric LCDs that are limited to a fixed set of ASCII characters, the 128x32 COG (Chip-On-Glass) LCD is a graphic display that operates on a pixel grid. With 128 columns and 32 rows of individually addressable pixels, you have full control over every dot. This means you can design and display any shape, symbol, or font you want, from custom icons to complex waveforms, by defining the pixel patterns in software. The key is that the display controller, typically a model like the ST7565R or similar, accepts raw bitmap data, and you can write your own character generator routines to map custom glyphs to specific memory locations. This article dives deep into the technical details, data structures, and practical considerations for implementing custom characters on a 128x32 COG LCD, backed by real-world examples and performance metrics.
How the Pixel Grid Enables Custom Characters
The 128x32 resolution provides 4,096 total pixels. Each pixel is either on or off, controlled by the display driver through a serial interface like SPI or I2C. To create a custom character, you define a matrix of pixels, typically 5x7, 8x8, or 8x16 dots, depending on your readability needs. For example, a 5x7 character uses 35 bits of data, which you can store as a byte array in your microcontroller's flash memory. The display's controller updates the entire screen by writing to a 128x32-bit frame buffer, often stored in the controller's internal RAM. You can directly manipulate this buffer to place your custom characters at any X,Y coordinate. The COG (Chip-On-Glass) packaging means the driver IC is bonded directly to the glass substrate, reducing pin count and improving reliability, but it doesn't limit your ability to define custom glyphs. In fact, the graphic nature of the display makes it far more flexible than character-based LCDs, which often have a fixed CGROM (Character Generator ROM) that only includes standard ASCII symbols. With a 128x32 COG, you are not constrained by any pre-defined character set.
Memory and Data Structure for Custom Fonts
To store custom characters, you need to allocate memory for a font table. A typical 8x8 pixel character requires 8 bytes (one byte per row, with each bit representing a pixel). For a full set of 128 custom characters, that's 1,024 bytes. If you use a 5x7 font, each character needs 5 bytes (since 5 columns are used, but you might pad to 8 bytes for alignment), totaling 640 bytes for 128 characters. Many microcontrollers, like the ATmega328P (used in Arduino Uno) with 32KB of flash, can easily handle this. However, the 128x32 COG LCD's total frame buffer is only 512 bytes (128 columns * 32 rows / 8 bits per byte), so you can store multiple font tables in external memory if needed. Here’s a typical data structure for a custom 8x8 font in C:
const unsigned char customFont[][8] = {
{0x00, 0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x00}, // Custom 'A'
{0x00, 0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E}, // Custom 'B'
// ... more characters
};
Each byte represents a row of 8 pixels, with the most significant bit corresponding to the leftmost pixel. You can generate these bitmaps using tools like LCD Assistant or online pixel editors. The display controller expects data in column-major or page-major order, depending on the specific driver. For the ST7565R, the frame buffer is organized into 4 pages of 128 bytes each, where each page covers 8 rows. This means you can write a custom character by calculating the starting page and column, then sending the 8 bytes sequentially. The SPI clock speed often runs at 4-8 MHz, so transmitting a full 8x8 character takes about 8 microseconds, which is fast enough for real-time updates.
Practical Implementation: SPI Communication and Timing
To display custom characters, you must interface with the display via SPI. The 128x32 COG LCD typically uses a 4-wire SPI (SCLK, MOSI, CS, DC) plus a reset pin. The DC (Data/Command) pin tells the controller whether the incoming byte is a command or pixel data. To set a custom character, you first set the page address and column address using commands, then send the pixel data. For example, to display a custom character at column 0, page 0, you send:
Command: 0xB0 (set page address to 0)
Command: 0x10 | ((col >> 4) & 0x0F) (set high column nibble)
Command: 0x00 | (col & 0x0F) (set low column nibble)
Data: 8 bytes of pixel data for the character
The total transaction for one character is about 11 bytes (3 commands + 8 data). At 8 MHz SPI, that's roughly 11 microseconds. If you update a full 128x32 screen (512 bytes of data), it takes about 512 microseconds, or 0.5 ms, which is well within the refresh rate of 60 Hz (16.6 ms per frame). For custom characters, you can pre-render them into a buffer in RAM and then blast the entire buffer to the display in one go, which is more efficient. Many libraries, like the U8g2 or Adafruit GFX, support custom fonts by allowing you to pass a font table pointer. However, if you want full control, you can write your own low-level driver. The 128x32 cog lcd display from DisplayModule, for example, provides a detailed datasheet with command sets and timing diagrams, making it easier to implement custom characters from scratch.
Character Generation Tools and Techniques
Creating custom characters manually by writing binary arrays is tedious. Instead, use bitmap generation tools. For 8x8 fonts, you can use the "FontGen" tool or online pixel editors like "Pixilart" or "LCD Image Converter". These tools let you draw the character on a grid and export the byte array. For example, a custom battery icon (8x8 pixels) might look like this:
0x0E, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x0E
This represents a battery outline with a positive terminal. You can also create proportional fonts where each character has a different width, but that requires more complex rendering logic. For the 128x32 display, a fixed-width 8x8 font gives you 16 columns (128/8) and 4 rows (32/8), so you can display 64 characters total. If you use a 5x7 font with 1-pixel spacing, you get 21 columns (128/6) and 4 rows (32/8), totaling 84 characters. The trade-off is readability versus density. For custom symbols like arrows, thermometers, or Wi-Fi icons, you can define them as 8x8 or 16x16 bitmaps. The 128x32 resolution is small, so keep symbols simple. A 16x16 icon uses 32 bytes and covers 1/8 of the screen width, which is suitable for status indicators.
Performance Considerations and Real-World Data
When using custom characters, the main performance bottleneck is the SPI bus speed and the microcontroller's ability to update the frame buffer. Here's a comparison of different approaches:
| Method | Memory Usage (per char) | Update Time (per char) | Max Characters per Frame |
|---|---|---|---|
| 8x8 font (fixed) | 8 bytes | 11 µs | 64 |
| 5x7 font (fixed) | 5 bytes | 8 µs | 84 |
| 16x16 icon | 32 bytes | 35 µs | 16 |
| Full frame buffer update | 512 bytes | 512 µs | N/A |
These times assume an 8 MHz SPI clock. If you use a 4 MHz clock, double the times. For a 60 Hz refresh, you have 16.6 ms per frame, so even with 64 characters, the total update time is 704 µs, which is only 4.2% of the frame time. This leaves plenty of CPU cycles for other tasks. However, if you update the entire screen every frame, the 512 µs is still negligible. The real limitation is the microcontroller's RAM. If you store a full 128-character font table (1,024 bytes for 8x8), it takes up a significant portion of a small MCU's RAM (e.g., 2 KB on an ATmega328P). In that case, store the font in flash memory (PROGMEM) and read it on the fly. The AVR architecture supports reading from flash with pgm_read_byte(), which adds a few microseconds per byte but is still fast enough for 60 Hz updates.
Display Controller Compatibility and Command Set
Not all 128x32 COG LCDs use the same controller. Common controllers include the ST7565R, SSD1306, and SH1106. The ST7565R is a popular choice for 128x32 COG displays because it supports a 128x32 pixel resolution natively (some controllers require 128x64 and you only use half). The ST7565R has a 128x32-bit RAM buffer organized into 4 pages of 128 bytes. To write custom characters, you use commands like 0xB0-0xB7 for page selection, 0x10-0x1F for high column, and 0x00-0x0F for low column. The display also supports inverse display, all pixels on, and sleep mode. For the SSD1306, which is more common in OLEDs but also used in some COG LCDs, the command set is similar but with different initialization sequences. The key difference is that the SSD1306 has a 128x32-bit RAM buffer as well, but it uses a different page addressing mode. Always check the datasheet for your specific display. The 128x32 COG LCD from DisplayModule uses the ST7565R, which is well-documented and has a robust command set for graphic operations. You can also use hardware acceleration like vertical scrolling, but that doesn't affect custom character display.
Practical Example: Custom Temperature Symbol
Let's walk through a real example. Suppose you want to display a temperature symbol (a small thermometer) next to a numeric value. The thermometer icon can be 8x8 pixels. Here's the bitmap:
Row 0: 0x04 (center dot)
Row 1: 0x0A (two dots)
Row 2: 0x0A
Row 3: 0x1F (bulb at bottom)
Row 4: 0x1F
Row 5: 0x1F
Row 6: 0x0E
Row 7: 0x04
This creates a simple vertical thermometer. To display it, you write these 8 bytes to the frame buffer at the desired column and page. Then, you can display the numeric value using a custom 5x7 font for digits. The total memory for the digit font (0-9) is 50 bytes (10 * 5 bytes). This is efficient and gives you a professional-looking interface. In practice, you can combine multiple custom characters to create a dashboard-like display, such as a battery level, signal strength, and temperature, all on a single 128x32 screen. The small resolution forces you to be concise, but with custom characters, you can convey a lot of information in a compact form.
Limitations and Workarounds
While the 128x32 COG LCD supports custom characters, there are limitations. The screen is small, so characters larger than 8x8 pixels become difficult to read. For example, a 16x16 character takes up 1/8 of the screen width, so you can only fit 8 per row. Also, the COG packaging means the display is sensitive to static electricity and mechanical stress, but that doesn't affect character rendering. Another limitation is the lack of built-in font scaling. If you want a larger character, you must define it as a larger bitmap, which increases memory usage. For example, a 16x16 font requires 32 bytes per character, so a full set of 128 characters would be 4,096 bytes, which might exceed the flash of a small MCU. In that case, store only the characters you need. Alternatively, you can use a technique called "bitmap scaling" where you render a small font and then scale it up by duplicating pixels, but this looks blocky. For most applications, 8x8 or 5x7 fonts are sufficient. The 128x32 COG LCD is also not backlit in most models, so contrast depends on the ambient light, but custom characters are still visible.
Software Libraries and Code Optimization
If you don't want to write your own driver, libraries like U8g2 (version 2.32.15 or later) support custom fonts for 128x32 COG LCDs. U8g2 allows you to define a font as a C array with a specific format, including a header that specifies the font size, character range, and pixel data. For example, you can create a custom font using the "u8g2_font_custom_t" structure. The library handles SPI communication and page addressing automatically. However, using a library adds overhead. For a custom character, the library might call multiple functions, increasing the update time to around 50 µs per character. If you need high-speed updates (e.g., for animation), write your own low-level functions. Another optimization is to use a double buffer in RAM. You render custom characters to a 512-byte buffer in the microcontroller's RAM, then copy the entire buffer to the display in one DMA transfer (if supported). This reduces flicker and ensures consistent timing. For example, on an STM32 microcontroller, you can use SPI DMA to send the buffer in the background while the CPU handles other tasks. The 128x32 COG LCD's SPI interface is compatible with DMA, as long as the CS pin is toggled correctly.
Power Consumption and Custom Characters
The 128x32 COG LCD is known for low power consumption, typically around 0.5-1 mA during operation, depending on the number of pixels turned on. Custom characters don't affect power consumption significantly because the display's backlight (if any) is the main power draw. However, if you display many custom characters with high pixel density (e.g., a full screen of 8x8 characters), the current draw might increase slightly because more pixels are on. For a typical 8x8 character with 50% pixel density, the current is about 0.6 mA. In sleep mode, the display draws less than 10 µA, and your custom characters are lost because the frame buffer is volatile. You must reinitialize the display and redraw characters after waking up. This is a standard behavior for graphic LCDs and doesn't limit the use of custom characters.
Testing and Validation
To ensure your custom characters display correctly, always test with a known pattern. For example, display a checkerboard pattern (alternating black and white pixels) to verify that the pixel mapping is correct. Then, test a single custom character at all four corners of the screen to check for addressing errors. Common issues include off-by-one errors in column or page addressing, which cause characters to appear shifted. If you use the ST7565R controller, remember that the column address is set with a 7-bit value (0-127), but the command uses two nibbles. For example, column 64 is set by sending 0x10 | (64 >> 4) = 0x14, then 0x00 | (64 & 0x0F) = 0x00. This is a common source of bugs. Also, verify the SPI mode (usually mode 0 or mode 3) and the polarity of the DC pin. The 128x32 COG LCD from DisplayModule, for instance, uses SPI mode 0 (CPOL=0, CPHA=0) and a active-low DC pin for commands. Once you have a working driver, you can easily add new custom characters by appending to the font table.