How to display custom characters on a 3.18 inch 128x64 COG LCD?

By admin

How to display custom characters on a 3.18 inch 128x64 COG LCD

You display custom characters on a 3.18 inch 128x64 COG LCD by directly manipulating the pixel buffer in the display's RAM, using a microcontroller like an Arduino or ESP32 to write bitmap data for each character. Unlike typical alphanumeric LCDs that have a built-in character generator (like the HD44780), this COG (Chip-on-Glass) display is a pure graphic module. That means every single pixel out of the 128 columns and 64 rows is individually addressable. To show a custom character, you define a 5x7 or 8x8 pixel grid, convert that grid into a byte array (each byte representing a column of pixels), and then send that array to the correct position in the display’s frame buffer. For example, if you want to display a custom arrow symbol, you would create a 5x7 bitmap, store it as 5 bytes (e.g., 0x04, 0x0E, 0x1F, 0x04, 0x04 for a simple up arrow), and then write those bytes to the display memory starting at a specific page and column. The controller inside the 3.18 inch 128x64 COG LCD, typically the ST7565 or similar, uses a 128x64-bit SRAM divided into 8 pages (each page is 8 pixels tall). So, you can map your custom character to any page and column. This is a fact: you are not limited to ASCII—you can draw any glyph, icon, or even Chinese characters up to 128x64 pixels in size. The key is understanding the memory mapping and using the SPI (Serial Peripheral Interface) commands to set the column address, page address, and then write data. For a reliable, high-contrast module, the 3.18 inch 128x64 cog lcd display is a solid choice because it supports a wide voltage range (3.3V to 5V) and has a fast SPI interface that can handle up to 10 MHz clock speed, allowing you to update custom characters in under 1 millisecond.

Let’s break down the technical details. The display’s resolution is 128x64 pixels, which gives you 8192 pixels total. Each pixel is controlled by a single bit in the display’s RAM, so the frame buffer is 1024 bytes (8192 bits / 8). The ST7565 controller organizes this RAM into 8 pages, each page being 128 columns wide and 8 pixels tall. So, page 0 covers rows 0-7, page 1 covers rows 8-15, and so on up to page 7 covering rows 56-63. To display a custom character, you need to calculate the page and column offset. For example, if you want to show a character at the top-left corner, you set the column address to 0 and the page address to 0. Then you send 5 bytes (for a 5x7 font) via SPI. The command sequence is: send command 0xB0 (set page address), then 0x10 (set column high nibble), then 0x00 (set column low nibble), then send data bytes. Each data byte corresponds to a column of 8 pixels, with the MSB representing the top pixel. So, if you want a solid vertical line in the first column, you send 0xFF. This is a high-density approach: you can store hundreds of custom characters in the microcontroller’s flash memory, each as a small array. For instance, a 5x7 character uses 5 bytes, so 100 characters take only 500 bytes. That’s trivial for an Arduino Uno with 32 KB of flash. But if you need larger characters, like 16x16 for Chinese text, each character uses 32 bytes (16 columns × 2 pages). The 3.18 inch 128x64 COG LCD can display up to 8 rows of 16x16 characters (since 64 rows / 16 = 4 rows, but you can overlap pages). Actually, for 16x16, you need 2 pages vertically, so you get 4 rows max (64/16 = 4). For 5x7, you can fit 8 rows (64/8 = 8) and 25 columns (128/5 ≈ 25). So, you can display up to 200 custom 5x7 characters on one screen.

Now, let’s talk about the actual implementation with real data. I’ll give you a concrete example using an Arduino and the U8g2 library, which is popular for these COG displays. The U8g2 library handles the low-level SPI communication, but you still need to define your custom characters as bitmaps. Here’s the step-by-step: First, you include the library and initialize the display object for the ST7565 controller. For example, U8G2_ST7565_128X64_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); This sets up software SPI. Then, in the setup() function, you call u8g2.begin() and u8g2.setFont(u8g2_font_5x7_tf) for built-in fonts. But for custom characters, you use the drawXBM() or drawBitmap() function. For example, to draw a custom heart symbol, you define a 8x8 bitmap: static const unsigned char heart[] U8X8_PROGMEM = { 0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18 }; This array represents an 8x8 heart shape. Then, in the loop(), you call u8g2.firstPage() and u8g2.drawXBM(10, 10, 8, 8, heart) to place it at pixel coordinates (10,10). The drawXBM function takes x, y, width, height, and the bitmap array. The width and height must be multiples of 8 for the XBM format, but you can use drawBitmap() for arbitrary sizes. This is a fact: the U8g2 library caches the frame buffer, so you can draw multiple custom characters before calling u8g2.sendBuffer(). The buffer is 1024 bytes, and sending it over SPI at 8 MHz takes about 1.3 milliseconds (1024 bytes × 8 bits / 8 MHz = 1.024 ms, plus overhead). So, you can update the entire screen 60 times per second, which is smooth for animations.

For a more low-level approach without libraries, you can directly control the ST7565 via SPI. The command set is standardized. Here’s a table of the essential commands you need to display custom characters:

CommandHex CodeDescription
Set Column Address Low0x00 - 0x0FSets the lower 4 bits of the column start address (0-127)
Set Column Address High0x10 - 0x1FSets the upper 4 bits of the column start address (0-7)
Set Page Address0xB0 - 0xB7Sets the page address (0-7)
Display Start Line0x40 - 0x7FSets the display start line (0-63)
Write Data0x40 (with D/C low)Writes a byte of data to the current column and page
Display ON/OFF0xAE / 0xAF0xAE turns off, 0xAF turns on

To display a custom character, you first set the page and column, then write the data bytes sequentially. For example, to display a 5x7 character ‘A’ at page 0, column 0, you would send: 0xB0 (page 0), 0x10 (column high = 0), 0x00 (column low = 0), then 5 data bytes: 0x7C, 0x12, 0x11, 0x12, 0x7C (this is a simple ‘A’ bitmap). The data bytes are written in order, and the column address auto-increments after each byte. So, you don’t need to re-send the column address for each byte. This is a high-density detail: the auto-increment feature saves SPI bandwidth, allowing you to write a full row of 128 bytes in a single burst. For a 5x7 character, you only need 5 bytes, but you can also write multiple characters in one burst by sending all their data bytes consecutively. For instance, to display a string of 10 custom characters, you send 50 bytes in one go. This is efficient because the SPI bus is fast, and the display’s internal RAM write cycle is about 300 ns per byte, so 50 bytes take 15 microseconds.

Let’s talk about font generation. You can create custom characters using a tool like LCD Assistant or FontForge, or even manually by drawing pixels in a spreadsheet. For a 5x7 font, each character is 5 bytes, and you can store them in a 2D array. For example, if you want a custom battery icon, you define a 8x16 bitmap (2 pages). The bitmap array would be 16 bytes long. Here’s a real example of a battery icon: 0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00. This draws a battery outline on the top 8 rows and leaves the bottom 8 rows blank. You can then place it anywhere on the screen. The key is to align the bitmap with the page boundaries. If you place a 8x16 bitmap at y=0, it will occupy pages 0 and 1. If you place it at y=1, it will span pages 0 and 1 but with a 1-pixel offset, which is trickier because the ST7565 doesn’t support sub-page addressing. To handle non-page-aligned positions, you need to shift the bitmap data in software. For example, if you want to display a 5x7 character at y=3, you need to split the bitmap across two pages: the top 5 rows go to page 0 (rows 0-7) but with a 3-pixel offset, and the bottom 2 rows go to page 1 (rows 8-15). This is a common technique called “vertical scrolling” or “bitmap shifting.” You can implement it by reading the original bitmap, shifting each byte by the offset, and combining with the adjacent byte. This is computationally intensive but doable on an ESP32 with a 240 MHz clock. On an Arduino Uno, it’s slower but still feasible for a few characters.

Another important factor is the contrast setting. The 3.18 inch 128x64 COG LCD has a built-in voltage regulator for the LCD bias, controlled by command 0x81 followed by a value from 0x00 to 0x3F. The default is often 0x20, but you can adjust it for better visibility. For example, u8g2.setContrast(0x30) in the U8g2 library sets the contrast to 48 (out of 63). Higher values make the pixels darker, but too high can cause ghosting. The display’s response time is about 100 ms at room temperature, so you can’t do high-speed animation like a gaming monitor, but for static custom characters, it’s fine. The viewing angle is 6 o’clock, meaning the best view is from below, but it’s readable from most angles due to the COG technology. The module also has a built-in temperature compensation circuit, which adjusts the bias voltage automatically. This is a fact: the ST7565 includes a temperature sensor that modifies the bias voltage from -10°C to 60°C, ensuring consistent contrast. So, your custom characters will look the same in a cold room or a hot car.

For power consumption, the display draws about 2 mA typical with the backlight off (if you use a transmissive version) or 1 mA for the reflective version. The 3.18 inch 128x64 COG LCD is available in both reflective and transmissive types. The reflective version uses ambient light, so it uses no power for the backlight. The transmissive version needs a backlight, which can draw 20-50 mA depending on the LED configuration. For battery-powered projects, the reflective version is better. But if you need to see the display in the dark, you’ll need the transmissive one with a backlight. The custom characters will be visible regardless, as long as the contrast is set correctly. The SPI interface also allows you to put the display into sleep mode with command 0xAE, which reduces power to less than 1 µA. This is useful for IoT devices that only update the display occasionally.

Let’s talk about the software side in more detail. You can use the Adafruit-GFX-Library or U8g2 for high-level drawing. But for custom characters, the drawBitmap() function in Adafruit GFX is straightforward. You define the bitmap as a byte array in PROGMEM (for Arduino) or const (for ESP32). For example, a 16x16 custom smiley face: const unsigned char smiley[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; This is a placeholder; you’d fill it with actual pixel data. The library handles the SPI communication and page mapping. The advantage of using a library is that it handles the vertical offset for non-page-aligned bitmaps. For example, if you call display.drawBitmap(10, 3, smiley, 16, 16, BLACK), the library will split the bitmap across pages 0 and 1 with a 3-pixel offset internally. This is a high-density feature: the library uses a 1024-byte buffer and shifts the bits in software, which adds about 200 microseconds per draw call for a 16x16 bitmap. On an ESP32, this is negligible, but on an Arduino Uno, it can add up if you draw many characters. To optimize, you can pre-calculate the shifted bitmap for each character at each y-position and store them in a lookup table. For example, if you have 10 custom characters that you always display at the same y-position, you can compute the shifted bitmaps once and store them in flash. This reduces the runtime overhead to just copying bytes.

Another practical consideration is the SPI wiring. The 3.18 inch 128x64 COG LCD typically uses a 8-pin interface: VCC, GND, SCL (SPI clock), SDA (SPI data), CS (chip select), DC (data/command), RST (reset), and BL (backlight). The SPI clock speed can be up to 10 MHz, but for long wires, you might need to reduce it to 4 MHz to avoid signal integrity issues. The CS pin is active low, so you pull it low to select the display. The DC pin determines whether the next byte is a command (low) or data (high). The RST pin is active low, and you should pull it high after a reset pulse. The backlight pin is typically driven by a resistor or a transistor if you want PWM control. For custom characters, you don’t need the backlight, but it helps with visibility. The display’s logic voltage is 3.3V, but it can tolerate 5V on the SPI pins if you use a level shifter. Many modules include a built-in 3.3V regulator, so you can power it from 5V directly. Check the datasheet for your specific module, but most 3.18 inch 128x64 COG LCDs from reputable suppliers like DisplayModule are 5V tolerant.

Let’s get into the nitty-gritty of bitmap creation. Suppose you want to display a custom character that is a 8x8 pixel icon of a lock. You can draw it on graph paper or use a tool like LcdAssistant. The 8x8 bitmap will be 8 bytes. For example, the lock icon might look like this: top row: 0x0E (00001110), second row: 0x11 (00010001), third row: 0x11 (00010001), fourth row: 0x1F (00011111), fifth row: 0x11 (00010001), sixth row: 0x11 (00010001), seventh row: 0x11 (00010001), eighth row: