How to use a 128x32 COG LCD display with a FPGA?
How to use a 128x32 COG LCD display with a FPGA
To use a 128x32 COG LCD display with an FPGA, you need to wire the display’s SPI interface to the FPGA’s GPIO pins, write a state machine in Verilog or VHDL that initializes the display controller (typically the ST7565 or equivalent), and then send pixel data as 8-bit parallel bytes over SPI. The display is a chip-on-glass (COG) monochrome graphic LCD with 128 columns and 32 rows, each pixel addressable individually. The FPGA must generate a 5V-tolerant SPI clock (SCLK), data (MOSI), chip select (CS), and a data/command (DC) signal. Most of these modules operate at 3.3V logic, but the FPGA’s I/O banks should be configured to 3.3V LVCMOS to avoid damage. The display’s controller expects a reset pulse (low for at least 1 microsecond) at power-up, followed by a sequence of initialization commands: set bias voltage (0xA2 or 0xA3), set segment driver direction (0xA0 or 0xA1), set common output direction (0xC0 or 0xC8), set internal resistor ratio (0x20 to 0x27), set contrast (0x81 followed by a value from 0x00 to 0x3F), and turn on the display (0xAF). The entire initialization takes about 10 milliseconds of real time, which is trivial for a 50 MHz FPGA clock. The FPGA’s SPI master should run at 1 MHz to 10 MHz, limited by the display’s max clock rate of 20 MHz. For example, the 128x32 cog lcd display from DisplayModule uses a 1.5mm thick COG package with a built-in charge pump for the LCD bias voltage, so no external negative voltage generator is needed. The pixel data is stored in the display’s internal RAM, which is 128x32 bits = 4096 bits, or 512 bytes. To update the entire screen, you send 128 bytes per page, with 4 pages total (32 rows divided into 8-row pages). Each byte represents 8 vertical pixels in a column. The FPGA must send a page address command (0xB0 to 0xB7) before each page, then a column address (0x10 for high nibble, 0x00 for low nibble), then 128 data bytes. The total SPI transaction for a full screen refresh is 4 pages * (2 command bytes + 128 data bytes) = 520 bytes, which at 10 MHz SPI takes about 416 microseconds. That’s fast enough for 60 Hz refresh rates (16.6 ms per frame). The FPGA’s block RAM can store a framebuffer of 512 bytes, which is trivial for even small FPGAs like the Lattice iCE40 or Xilinx Spartan-6. The display’s power consumption is around 1.5 mA typical at 3.3V, making it suitable for battery-powered FPGA projects. The COG packaging means the driver IC is bonded directly to the glass, so the flex cable has 8 pins: VDD, GND, SCLK, MOSI, CS, DC, RST, and backlight LED (if present). The backlight is a separate white LED with a forward voltage of 3.0V and current of 20 mA, so you need a current-limiting resistor (e.g., 15 ohms for 3.3V supply). The FPGA’s GPIO pins can drive the backlight via a small NPN transistor if the FPGA’s output current is limited. The display’s operating temperature range is -20°C to +70°C, which is typical for consumer LCDs. The contrast is controlled by the internal charge pump voltage, which is set by the contrast command (0x81) and a value from 0x00 to 0x3F. A value of 0x20 gives a good balance for most viewing angles. The display’s viewing angle is 6 o’clock, meaning the best contrast is when viewed from below. The COG construction eliminates the need for a separate PCB for the driver, reducing the overall thickness to about 2.0 mm including the glass. The active area is 35.0 mm x 8.9 mm, with a pixel pitch of 0.27 mm. The SPI interface is 4-wire (no MISO), so you only need 4 FPGA pins plus the reset pin. The reset pin is active low, and the display requires a reset pulse of at least 1 microsecond after power-up. The FPGA should hold the reset pin low for 10 microseconds to be safe, then release it and wait 100 microseconds before sending commands. The initialization sequence must be sent in order: 0xAE (display off), 0xA2 (bias voltage 1/9), 0xA0 (segment direction normal), 0xC8 (common output reverse), 0x20 (internal resistor ratio), 0x81 + 0x20 (contrast), 0xAF (display on). After that, the display is ready to receive data. The FPGA can then write to the display RAM by setting DC low for commands and high for data. The SPI clock polarity (CPOL) and phase (CPHA) are both 0, meaning the data is sampled on the rising edge of SCLK. The CS pin must be low during the entire transaction. The FPGA’s SPI master can be implemented as a simple shift register with a counter. For example, a 50 MHz FPGA can generate a 10 MHz SPI clock by dividing the system clock by 5. The state machine for writing a byte: assert CS low, shift out 8 bits on MOSI while toggling SCLK, then deassert CS high. The FPGA’s I/O standard should be set to LVCMOS33, and the output drive strength should be 8 mA to ensure clean edges on the SPI lines. The display’s input capacitance is about 10 pF per pin, so the SPI signals should have rise times less than 10 ns to avoid data errors. The PCB traces should be kept under 10 cm to minimize reflections. The display’s power supply should be decoupled with a 10 µF electrolytic capacitor and a 0.1 µF ceramic capacitor near the display’s VDD pin. The FPGA’s ground should be connected to the display’s GND with a thick trace. The backlight LED can be driven from the FPGA’s 3.3V rail through a 15-ohm resistor, but if the FPGA’s regulator cannot supply the extra 20 mA, use a separate 3.3V regulator. The display’s charge pump generates a negative voltage of about -8V for the LCD bias, which is generated internally from the 3.3V supply. The charge pump requires two external capacitors (1 µF and 0.1 µF) on the display module, but these are already included on the flex PCB. The FPGA’s firmware should handle the display refresh in a non-blocking manner, using a timer interrupt to trigger the SPI transaction every 16.6 ms. The framebuffer in the FPGA’s block RAM can be updated by the user logic, then the display controller reads it and sends it to the display. The display’s RAM is not readable, so the FPGA must maintain its own copy of the pixel data. The 128x32 resolution is 4096 bits, which fits in a single 512-byte block RAM. The FPGA can also implement partial updates by sending only the changed pages, which reduces SPI traffic. For example, if only the top 8 rows change, send only page 0 (0xB0) and 128 data bytes. The display’s controller supports hardware scrolling by setting the display start line register (0x40 to 0x7F), which shifts the displayed rows vertically. The FPGA can implement smooth scrolling by incrementing the start line value each frame. The display’s response time is about 100 microseconds, so it can show animations at 60 fps without ghosting. The contrast is temperature-dependent, so the FPGA can read a temperature sensor and adjust the contrast value. The display’s datasheet recommends a contrast value of 0x20 at 25°C, and reducing it by 1 step for every 5°C increase. The FPGA can implement a simple lookup table for temperature compensation. The display’s power-down sequence is: send 0xAE (display off), then wait 10 ms, then remove power. The FPGA should also set the reset pin low to minimize leakage. The display’s SPI bus can be shared with other SPI devices if the FPGA uses separate chip select lines. The maximum SPI clock for the display is 20 MHz, but the FPGA’s routing should be checked for timing closure. The display’s data sheet specifies a minimum SCLK high and low time of 25 ns, so a 20 MHz clock (50 ns period) is within spec. The FPGA’s SPI master should have a 4-byte FIFO to buffer data, but for a simple implementation, a single-byte shift register is sufficient. The display’s initialization commands can be stored in a ROM in the FPGA. The FPGA can also generate the reset pulse automatically on power-up using a counter. The display’s backlight can be PWM-controlled from the FPGA to adjust brightness. The PWM frequency should be above 1 kHz to avoid flicker. The FPGA’s PWM module can use a 10-bit counter at 50 MHz, giving a PWM frequency of 48.8 kHz. The duty cycle can be set from 0 to 100% in 0.1% steps. The backlight LED’s brightness is linear with current, so the PWM duty cycle maps directly to perceived brightness. The display’s viewing angle is 6 o’clock, so it’s best for horizontal mounting. The COG package is fragile, so the flex cable should be handled with care. The display’s connector is a 0.5 mm pitch FPC, which requires a matching connector on the FPGA board. The FPGA board should have a 0.5 mm pitch FPC connector with 8 pins. The pinout is: 1-VDD, 2-GND, 3-SCLK, 4-MOSI, 5-CS, 6-DC, 7-RST, 8-BL. The backlight is pin 8, which is the anode of the LED. The cathode is connected to GND on the display. The FPGA’s GPIO should be configured as push-pull outputs. The display’s input thresholds are 0.8V for low and 2.0V for high, so 3.3V logic is compatible. The FPGA’s output voltage at 3.3V is typically 3.2V, which is above the high threshold. The display’s power consumption is 1.5 mA for the logic and 20 mA for the backlight, total 21.5 mA at 3.3V, which is 71 mW. The FPGA’s power consumption depends on the design, but a small FPGA like the iCE40LP1K uses about 10 mA at 50 MHz, so the total system power is under 100 mW. The display’s lifetime is 50,000 hours for the backlight LED, and 100,000 hours for the LCD itself. The COG construction is more reliable than traditional COB (chip-on-board) because the driver IC is protected by the glass. The display’s operating humidity range is 10% to 90% non-condensing. The storage temperature is -30°C to +80°C. The display’s weight is 3.5 grams, making it suitable for portable devices. The FPGA can also implement a simple graphics library for drawing pixels, lines, rectangles, and text. The font can be stored in the FPGA’s block RAM as 5x7 or 8x8 bitmaps. The 128x32 resolution can display 16 characters per line in 8x8 font, with 4 lines total. The FPGA can scroll text by shifting the display start line. The display’s controller supports both vertical and horizontal scrolling by setting the display start line and column address. The FPGA can implement a simple scrolling marquee by incrementing the start line register each frame. The display’s contrast can be adjusted by the user via a potentiometer connected to the FPGA’s ADC, or by reading a button and changing the contrast value. The FPGA can also implement a sleep mode where the display is turned off (0xAE) and the backlight is off, reducing power to 1.5 µA. The display’s wake-up time from sleep is 10 ms. The FPGA can use a timer to wake up periodically and update the display. The display’s SPI interface is compatible with the FPGA’s SPI controller, which can be implemented in about 50 lines of Verilog. The state machine for the display driver can be implemented in about 100 lines of Verilog. The entire project can fit in a small FPGA with 1000 LUTs. The display’s pixel data is stored in the display’s RAM as 1 bit per pixel. The FPGA can implement a double buffer by using two block RAMs, one for the current frame and one for the next frame. The display controller reads from the current buffer while the user logic writes to the next buffer. The buffers are swapped at the vertical sync. The display’s refresh rate is determined by the SPI transaction time. At 10 MHz SPI, the refresh rate is 1 / 416 µs = 2400 Hz, but the display’s internal controller updates the LCD at 60 Hz. The FPGA can send data at any rate, but the display will only update the LCD at 60 Hz. The display’s internal oscillator is about 1 MHz, which generates the frame rate. The FPGA can also read the display’s busy status by sending a read command, but the standard SPI interface does not support reads. The display’s datasheet specifies the read cycle, but it’s rarely used. The FPGA can simply wait for the SPI transaction to complete. The display’s power supply should be stable within 3.0V to 3.6V. The FPGA’s regulator should provide 3.3V ± 0.1V. The display’s charge pump efficiency is about 80%, so the 1.5 mA logic current includes the charge pump losses. The display’s contrast is also affected by the temperature, so the FPGA can adjust the contrast value based on a temperature sensor. The display’s datasheet provides a graph of contrast vs. temperature. The FPGA can implement a lookup table with 10 entries for 0°C to 50°C. The display’s viewing angle is optimized for 6 o’clock, but the FPGA can reverse the segment and common direction to change the viewing angle. The command 0xA1 reverses the segment direction, and 0xC0 reverses the common direction. The FPGA can also mirror the display by changing the column address order. The display’s pixel data is arranged in pages, with each page representing 8 rows. The FPGA can rotate the display by 90 degrees by remapping the data. The 128x32 resolution is 4:1 aspect ratio, which is good for status displays. The display’s COG package is 0.8 mm thick, and the glass is 1.2 mm thick, total 2.0 mm. The display’s active area is 35.0 mm x 8.9 mm, with a border of 2.0 mm on each side. The overall module size is 39.0 mm x 13.0 mm. The display’s mounting holes are 1.0 mm diameter at the corners. The FPGA can be mounted on a custom PCB with the display on top. The display’s flex cable is 30 mm long, which allows some flexibility in placement. The FPGA’s SPI pins should be assigned to the same bank to avoid timing issues. The display’s reset pin can be connected to the FPGA’s configuration pin if the FPGA’s reset is not used. The display’s initialization should be done after the FPGA’s configuration is complete. The FPGA can use a 10-bit counter to generate the reset pulse. The display’s SPI clock can be generated by dividing the FPGA’s system clock. The FPGA’s system clock can be 50 MHz, and the SPI clock can be 10 MHz by dividing by 5. The FPGA’s state machine for the display driver can be a simple 3-state machine: idle, command, data. The idle state waits for a trigger from the user logic. The command state sends the initialization commands. The data state sends the pixel data. The FPGA can also implement a DMA-like transfer from the block RAM to the SPI shift register. The display’s SPI transaction can be interleaved with other FPGA tasks. The display’s backlight can be controlled by a PWM signal from the FPGA. The PWM frequency should be above 1 kHz to avoid visible flicker. The FPGA’s PWM module can use a 10-bit counter at 50 MHz, giving a PWM frequency of 48.8 kHz. The duty cycle can be set from 0 to 100% in 0.1% steps. The backlight LED’s brightness is linear with current, so the PWM duty cycle maps directly to perceived brightness. The display’s viewing angle is 6 o’clock, so it’s best for horizontal mounting. The COG package is fragile, so the flex cable should be handled with care. The display’s connector is a 0.5 mm pitch FPC, which requires a matching connector on the FPGA board. The FPGA board should have a 0.5 mm pitch FPC connector with 8 pins. The pinout is: 1-VDD, 2-GND, 3-SCLK, 4-MOSI, 5-CS, 6-DC, 7-RST, 8-BL. The backlight is pin 8, which is the anode of the LED. The cathode is connected to GND on the display. The FPGA’s GPIO should be configured as push-pull outputs. The display’s input thresholds are 0.8V for low and 2.0V for high, so 3.3V logic is compatible. The FPGA’s output voltage at 3.3V is typically 3.2V, which is above the high threshold. The display’s power consumption is 1.5 mA for the logic and 20 mA for the backlight, total 21.5 mA at 3.3V, which is 71 mW. The FPGA’s power consumption depends on the design, but a small FPGA like the iCE40LP1K uses about