How to read data from a 3.18 inch 128x64 COG LCD?
To read data from a 3.18 inch 128x64 COG LCD, you first need to interface it with a microcontroller using a serial peripheral interface (SPI) or parallel interface, depending on your specific module. Most COG (Chip-on-Glass) displays in this size, like the 3.18 inch 128x64 cog lcd display, use a standard SPI protocol with four or five wires: CS (chip select), SCK (serial clock), MOSI (master out slave in), and DC (data/command). Some also include a RESET line. The key is sending the correct initialization commands, then writing pixel data to the internal RAM. The display controller, typically a ST7565R or equivalent, maps each pixel to a bit in a 128x64 byte array. You send data in pages of 8 bits vertically, meaning you address columns 0 to 127 and pages 0 to 7. For reading, you don’t literally read pixel states back from the LCD—most COG displays are write-only for graphics. Instead, you read data from your own buffer in the microcontroller’s RAM, which mirrors the display’s content. This buffer is essential for partial updates, animations, or touch interaction if you add a touch panel. Let me break down the hardware and software steps with hard numbers and real-world constraints.
Hardware Interface and Signal Timing
For a typical 3.18 inch 128x64 COG LCD, the SPI clock frequency can go up to 10 MHz, but many microcontrollers like an Arduino Uno or ESP32 run it at 4 MHz to avoid signal integrity issues. The display operates at 3.3V logic, but some modules include a built-in voltage regulator for 5V tolerance. The current draw is around 1.5 mA to 2.5 mA during normal operation, with backlight LEDs consuming an additional 20 mA to 40 mA depending on brightness. The pinout is usually: pin 1 to VSS (ground), pin 2 to VDD (3.3V), pin 3 to CS, pin 4 to RESET, pin 5 to DC, pin 6 to SCK, pin 7 to MOSI, pin 8 to LED+ (backlight anode), and pin 9 to LED- (cathode). Some modules combine CS and DC into a single pin, but that’s rare. The COG technology bonds the driver IC directly to the glass, reducing thickness to about 1.5 mm, making it ideal for battery-powered devices. The display’s viewing angle is 6 o’clock, meaning the best contrast is from the front, with a typical contrast ratio of 5:1. The response time is 80 ms to 120 ms at room temperature, which is fine for static text but not for fast video. The operating temperature range is -20°C to +70°C, so it works in most indoor and outdoor environments.
Initialization Sequence: Command Bytes and Data
To read data from the display, you first initialize it by sending a sequence of commands. The ST7565R controller requires a startup routine that takes about 10 ms. Here’s a typical initialization sequence with hex values:
Command 1: 0xE2 (Software Reset) – wait 1 ms.
Command 2: 0x2F (Power Control Set: internal booster, regulator, and follower on).
Command 3: 0x24 (Regulator Resistor Set: fine-tune contrast, typical value 0x24 for medium brightness).
Command 4: 0x81 (Set Electronic Volume) followed by 0x20 (contrast value, range 0x00 to 0x3F).
Command 5: 0xA1 (Segment Direction: reverse for left-to-right mapping).
Command 6: 0xC0 (Common Output Scan Direction: normal).
Command 7: 0xA6 (Display Normal, not inverted).
Command 8: 0xA4 (Display All Points: normal, not all on).
Command 9: 0xAF (Display ON).
Total command bytes: 9. Total data bytes: 1 (for contrast). The entire sequence takes about 15 ms. After this, the display is ready to accept pixel data. You must set the column address range using 0x10 (high nibble) and 0x00 (low nibble) for column 0, and page address using 0xB0 for page 0. Each page is 8 pixels tall, so to cover the full 64 rows, you cycle through pages 0 to 7. For each page, you send 128 bytes of data (one byte per column). That’s 128 bytes per page times 8 pages equals 1024 bytes total for a full frame. At 4 MHz SPI, transmitting 1024 bytes takes about 2 ms. So you can update the entire screen at 500 Hz, but the LCD’s internal refresh rate is typically 60 Hz to 75 Hz, so you don’t need to send data faster than that.
Data Buffer Management and Reading Process
You cannot read back the pixel data from the LCD itself because the ST7565R does not support a read command over SPI. The datasheet for the ST7565R shows that the only read operation is for the status register, which returns the busy flag and display on/off status. To “read” the display data, you maintain a buffer in your microcontroller’s RAM. For a 128x64 monochrome display, you need 1024 bytes. On an Arduino Uno, that’s half of the 2 KB SRAM, so you have to be careful. On an ESP32 with 520 KB SRAM, it’s trivial. The buffer is a 2D array: uint8_t buffer[8][128]; where the first index is the page (0 to 7) and the second is the column (0 to 127). Each bit in a byte represents a pixel: bit 0 is the top pixel of the page, bit 7 is the bottom. So to read a pixel at (x, y), you compute page = y / 8, bit = y % 8, and then check if buffer[page][x] & (1 << bit). This is a simple bitwise AND operation. For example, if you want to read the pixel at x=50, y=20, you do: page = 20 / 8 = 2, bit = 20 % 8 = 4, and then check (buffer[2][50] & 0x10) != 0. This gives you a 1 or 0. You can then use this data for collision detection in games, for touch calibration, or for partial screen updates where you only send changed bytes.
Partial Updates and Performance Optimization
If you’re reading data for partial updates, you don’t need to send the entire 1024-byte buffer every time. Instead, you compare the current buffer with the previous buffer byte by byte. Only send the bytes that changed. This reduces SPI traffic by 50% to 90% for typical UI updates. For example, if you’re updating a single text character (8x8 pixels), you only need to send 8 bytes per page, and if the character spans two pages, that’s 16 bytes total. The SPI transaction time drops from 2 ms to 32 µs. But you still need to read the buffer to know which bytes changed. You do this by iterating through the buffer and comparing with the old buffer. Use memcmp() or a manual loop. On a 16 MHz Arduino, comparing 1024 bytes takes about 100 µs. So the total time for a partial update is around 150 µs, which is fast enough for 60 fps animations. For a full-screen update, the bottleneck is the SPI transfer, not the buffer read. The buffer read is just a memory access, which is nanoseconds on most microcontrollers. So the practical limit is the SPI speed and the LCD’s internal refresh.
Real-World Example: Reading Data for a Thermometer Display
Let’s say you’re building a temperature display using a 3.18 inch 128x64 COG LCD. You read a temperature sensor every second, then update the numerical value on the screen. You maintain a buffer of the current screen content. When the temperature changes from 25.3°C to 25.4°C, you only need to redraw the digits. You read the buffer to find the old digit positions, then clear them by writing zeros, then write the new digits. This avoids flicker. The buffer read operation is just a memory read, so it’s instant. The SPI write for the new digits takes about 50 µs for a 6-digit number (each digit is 8x8 pixels, so 6 digits times 8 bytes per digit = 48 bytes, at 4 MHz that’s 96 µs). Total update time: less than 150 µs. The display’s internal refresh rate is 60 Hz, so you can update every 16.67 ms without any ghosting. The contrast can be adjusted by sending 0x81 followed by a value from 0x00 to 0x3F. For a 3.3V supply, a value of 0x20 gives a good balance. If you’re using a 5V supply, you might need 0x30. The exact value depends on the LCD’s bias setting, which is usually set by the resistor on the module. Some modules have a potentiometer for contrast, but most COG displays use software control.
Electrical Characteristics and Power Considerations
The 3.18 inch 128x64 COG LCD typically draws 1.5 mA to 2.5 mA from the 3.3V supply for the logic, and the backlight LED draws 20 mA to 40 mA depending on the resistor. If you’re powering it from a battery, you can turn off the backlight by setting the LED pin low, reducing total current to under 3 mA. The display’s standby current is 0.1 mA when the display is off (command 0xAE). The SPI lines are 3.3V tolerant, but if you’re using a 5V microcontroller, you need level shifters or voltage dividers. The SCK and MOSI lines can handle up to 5V if the module has a built-in regulator, but check the datasheet. The CS and DC lines are also 3.3V. The RESET pin is active low, and you should hold it low for at least 1 µs after power-up. The internal oscillator runs at about 1.5 MHz, which sets the frame rate. You can adjust the frame rate by changing the internal resistor, but it’s not recommended. The display’s pixel pitch is 0.54 mm, giving a dot size of 0.50 mm x 0.50 mm with a gap of 0.04 mm. The active area is 69.12 mm x 34.56 mm. The module’s overall dimensions are 80.0 mm x 46.0 mm x 1.5 mm, making it one of the thinnest LCDs in this size class. The COG bonding means no flex cable, just a glass edge with exposed pads, which is fragile. You need to handle it with care and use a ZIF connector or solder directly with a low-temperature iron.
Software Libraries and Code Structure
Most hobbyists use the u8g2 library, which supports the ST7565R controller with a simple constructor: U8G2_ST7565_128X64_1_HW_SPI u8g2(U8G2_R0, CS, DC, RESET); This library handles all the initialization and buffer management. You call u8g2.firstPage() and u8g2.nextPage() to iterate through pages. The library internally maintains a 1024-byte buffer. To read a pixel, you call u8g2.getBuffer() to get a pointer to the buffer, then access it as a byte array. For example, uint8_t *buf = u8g2.getBuffer(); then pixel = (buf[page * 128 + x] >> bit) & 1; This is a direct read from the buffer. The library also provides u8g2.drawPixel(x, y, color) to set a pixel, and u8g2.getDisplay() to read the current display state (though it’s just the buffer). For custom firmware, you can write your own driver. The SPI transaction is: set CS low, set DC low for command, send byte, set DC high for data, send bytes, set CS high. The timing is critical: the CS low to SCK rising edge must be at least 100 ns, and the SCK high time must be at least 50 ns. At 4 MHz, each SCK cycle is 250 ns, so it’s fine. The data setup time is 20 ns, and hold time is 10 ns. These are easily met by any microcontroller.
Error Handling and Debugging
If the display doesn’t work, check the initialization sequence. A common mistake is sending the wrong contrast value or forgetting the power control commands. Use a logic analyzer to verify the SPI signals. The CS, SCK, and MOSI should show clean square waves. The DC line should toggle between low (command) and high (data). The RESET pin should be high after the initial low pulse. If the display shows random pixels, the segment direction might be reversed. Send 0xA0 instead of 0xA1 to flip the mapping. If the display is too dim, increase the contrast value from 0x20 to 0x30. If it’s too bright, decrease it. The electronic volume command (0x81) followed by a value from 0x00 to 0x3F controls the contrast linearly. The internal booster voltage can be set with 0x2F (booster on), 0x28 (booster off), or 0x2C (booster on with different resistor). The regulator resistor set (0x24) is usually set at the factory, but you can adjust it. The follower ratio is set with 0x60 to 0x63, where 0x60 is the default. For a 3.3V supply, 0x60 works. For a 5V supply, 0x63 might be needed to reduce the voltage. The display’s internal charge pump generates a negative voltage for the LCD drive, which is around -8V to -10V. This is normal. You can measure the VOUT pin on the driver IC, but it’s not accessible on most modules.
Advanced Techniques: Double Buffering and DMA
For smooth animations, use double buffering. Maintain two buffers: one for the current frame and one for the next frame. While the SPI is sending the current buffer, you update the next buffer. On an ESP32 with DMA, you can set up a SPI transaction that sends the entire buffer without CPU intervention. The DMA controller handles the data transfer, freeing the CPU for other tasks. The buffer read is still a memory read, but you can read from the buffer that’s not being sent. This allows you to update the display at 60 fps while running a game loop at 60 fps. The total memory required is 2048 bytes, which is fine on an ESP32. On an Arduino, you’re limited to 2 KB total, so double buffering is not possible. Instead, you can use a single buffer and update only changed regions. The read operation is the same: you read from the buffer to determine what to send. The key is to minimize the number of SPI transactions. Each transaction has overhead: CS low, send command, send data, CS high. For a single byte, the overhead is about 10 µs. For 128 bytes, it’s about 130 µs. So it’s more efficient to send large chunks. The display’s column address can be set to a range, so you can send a contiguous block of bytes. For example, to update columns 10 to 20 on page 3, you set the column address to 10 (high nibble 0x10, low nibble 0x0A), then send 11 bytes. This is faster than sending 11 individual bytes with separate commands.
Interface with Touch Panels and Sensors
If you add a resistive touch panel over the LCD, you read the touch coordinates using an ADC. The touch panel is a separate circuit that doesn’t interact with the LCD’s SPI bus. You read the X and Y positions by driving the touch panel’s rows and columns. The coordinates are analog, so you use an ADC to convert them to digital values. Then you map them to the 128x64 pixel grid. The mapping is linear: X = (adc_x / 4095) * 128, Y = (adc_y / 4095) * 64. But you need to calibrate because the touch panel’s edges have dead zones. The calibration involves reading the minimum and maximum ADC values for each axis. Then you use those values to scale the coordinates. The touch panel adds about 1 mm to the thickness. The LCD’s buffer is used to draw a cursor or button highlights. You read the buffer to determine if a touch is on a button. For example, if you have a button at (10, 10) to (30, 30), you check if the touch coordinates fall within that rectangle. Then you read the pixel data around that area to see if the button is already drawn. This is a simple boundary check. The touch panel’s response time is 10 ms to 20 ms, so you need to debounce the input. The LCD’s update rate is 60 Hz, so you can poll the touch every 16 ms. The buffer read for the button area is just a few bytes, so it’s fast.
Manufacturing and Quality Control Data
The 3.18 inch 128x64 COG LCD is manufactured using a process that involves bonding the driver IC to the glass with anisotropic conductive