Hitivi · Field Notes
How to display a clock on a 3.18 inch 128x64 COG LCD?
How to Display a Clock on a 3.18 Inch 128x64 COG LCD
To display a clock on a 3.18 inch 128x64 COG LCD, you need to interface it with a microcontroller like an Arduino, ESP32, or STM32, using SPI communication, and generate the clock digits via a custom font library or a bitmap generator. The 3.18 inch 128x64 cog lcd display is a monochrome graphic display with a resolution of 128 pixels horizontally and 64 pixels vertically, each pixel being individually addressable. It uses a COG (Chip-on-Glass) packaging, which integrates the driver IC directly onto the glass, reducing thickness to about 2.2 mm and improving reliability. The display typically uses the ST7565 or similar controller, which supports SPI with a maximum clock rate of 10 MHz, allowing for fast screen updates. For a clock, you’ll need to update the display at least once per second, but the SPI speed ensures that redrawing the entire screen takes less than 10 ms, so there’s no flicker.
Start by connecting the display to your microcontroller. The SPI interface requires four pins: SCK (serial clock), MOSI (master out slave in), CS (chip select), and DC (data/command). You also need a reset pin (RST) and a backlight control pin (BL). Power the display with 3.3V or 5V, depending on the module, but note that the COG LCD’s logic voltage is typically 3.3V, so you may need a level shifter if using a 5V microcontroller. The display’s current draw is around 1.5 mA during normal operation, and up to 20 mA with the backlight on, making it suitable for battery-powered projects. For an accurate clock, you’ll need a real-time clock (RTC) module like the DS3231 or DS1307, which provides timekeeping with an accuracy of ±2 ppm for the DS3231, or about 1 minute drift per year. Alternatively, you can use the microcontroller’s internal timer, but that will drift more—typically 50 to 100 ppm for a ceramic resonator, or 10 to 20 ppm for a crystal oscillator.
To render the clock digits, you have two main approaches: using a bitmap font or generating digits with vector graphics. The bitmap font method is simpler and more common for small displays. You can create a 8x8 pixel font for each digit (0-9) and a colon character, which fits within the 128x64 resolution. For example, a 6-digit clock (HH:MM:SS) with a colon between each pair requires 6 digits and 2 colons, each 8 pixels wide, totaling 8 * 8 = 64 pixels, which fits perfectly in the 128-pixel width with 32 pixels of padding on each side. The height of each digit is 8 pixels, but you can scale them to 16x16 or 24x24 for better readability by using a larger font. A 16x16 font uses 16 pixels per digit, so 6 digits take 96 pixels, plus 2 colons at 16 pixels each, total 128 pixels, exactly filling the width. For the height, 16 pixels leaves 48 pixels of vertical space, which you can use for labels like AM/PM or date. Data density: each 16x16 bitmap font requires 32 bytes per character (16 rows * 2 bytes per row), so for 10 digits and 2 colons, you need 384 bytes of flash memory, which is negligible for most microcontrollers.
Here’s a practical implementation example using an Arduino Uno and the U8g2 library, which supports the ST7565 controller. The U8g2 library includes built-in fonts like u8g2_font_ncenB08_tr (8x8) or u8g2_font_fub20_tn (20x20), which you can use directly. The code flow is: initialize the display, set the SPI pins, read the time from the RTC, format it as a string, and draw it on the display. The U8g2 library’s drawStr() function handles text rendering, but for custom fonts, you can use drawXBM() to draw bitmap arrays. The refresh rate is critical: since the display is monochrome and static, you only need to update the pixels that change. For a clock, only the seconds digit changes every second, so you can optimize by redrawing only that digit’s area, reducing SPI traffic and power consumption. The ST7565 controller supports partial updates via the setCursor() command, but the U8g2 library doesn’t natively support it; you can implement it by sending commands directly to the controller.
To achieve high contrast and readability, adjust the display’s bias voltage and contrast register. The ST7565 has a built-in voltage regulator that you can set via the setContrast() command, with values from 0 to 63. For a 3.18 inch display, a contrast value of 30 to 40 typically works well, but it depends on the viewing angle and ambient light. The display’s viewing angle is 6 o’clock, meaning the best contrast is when viewed from below, so mount it accordingly. The operating temperature range is -20°C to +70°C, which is fine for most indoor clocks. The backlight is usually an LED array with a forward voltage of 3.0V to 3.4V and a current of 20 mA, so you can control it with a PWM pin for dimming. For a clock, you might want to dim the backlight at night to save power and reduce glare.
For a more advanced clock, you can add features like a temperature sensor (e.g., DS18B20) or a humidity sensor (e.g., DHT22) to display environmental data alongside the time. The 128x64 resolution allows you to show two lines of text: one for the time (e.g., 12:34:56) and one for the temperature (e.g., 23.5°C). Using a 8x8 font, you can fit 16 characters per line, which is enough for “12:34:56” and “Temp: 23.5C”. The display’s refresh rate for static data is not an issue, but if you update the temperature every second, the SPI traffic increases slightly. The total data for a full screen update is 128 * 64 / 8 = 1024 bytes, which at 10 MHz SPI takes about 0.8 ms, so even with 10 updates per second, the CPU load is minimal.
Power consumption is a key consideration for a clock that runs 24/7. The COG LCD itself draws about 1.5 mA, the backlight adds 20 mA, and the RTC (DS3231) draws about 200 µA. An Arduino Uno in sleep mode draws about 15 mA, but you can use an ESP32 in deep sleep mode, which draws 10 µA, and wake it up every second using the RTC’s alarm. However, the ESP32’s SPI interface is faster (up to 40 MHz), so the display update takes less than 0.2 ms. The trade-off is that the ESP32 consumes about 50 mA during active mode, but if you keep it active for only 10 ms per second, the average current is about 0.5 mA, plus the display and RTC, totaling around 22 mA. That’s about 0.5 Wh per day, or 15 Wh per month, which is fine for a wall-powered clock but not for a battery-powered one. For battery operation, use a low-power microcontroller like the ATmega328P in sleep mode, or a dedicated RTC with a display driver like the MAX7219, but that’s a different approach.
Let’s talk about the hardware layout. The 3.18 inch display has a module size of 84.0 mm x 44.0 mm, with an active area of 73.4 mm x 38.8 mm. The pixel pitch is 0.573 mm, giving a pixel density of about 44 PPI, which is fine for reading at arm’s length. The COG packaging means the driver IC is bonded directly to the glass, so the module is thin and light, weighing about 15 grams. The interface connector is a 1.0 mm pitch FPC (flexible printed circuit) with 8 pins, which you can solder to a breakout board or use a connector. The pinout is standard: 1: VSS (GND), 2: VDD (3.3V), 3: SCK, 4: MOSI, 5: CS, 6: DC, 7: RST, 8: BL. Make sure to add a 10 µF capacitor between VDD and VSS to filter noise, and a 10 kΩ pull-up resistor on the reset pin if needed.
For the software, you can use the U8g2 library, which supports over 1000 displays, including the ST7565. The initialization sequence is: u8g2.begin(), u8g2.setFont(), u8g2.setContrast(). To draw the clock, call u8g2.firstPage() and u8g2.nextPage() in a loop. The library uses a page buffer of 128 bytes, which is half the display’s memory, so it requires two SPI transfers per update. Alternatively, you can use the Adafruit GFX library with the ST7565 driver, but it’s less optimized for memory. For custom fonts, you can generate bitmap arrays using a tool like LCD Assistant or FontGenerator, which convert TrueType fonts to C arrays. A 16x16 font for digits 0-9 takes 320 bytes, and you can store it in PROGMEM to save RAM. The Arduino Uno has 2 KB of RAM, so avoid using large buffers.
Now, let’s look at a real-world example. I built a clock using an ESP32, a DS3231 RTC, and this display. The code reads the time every second, formats it as “HH:MM:SS” using a 24-hour format, and draws it with a 20x20 font. The font is stored in PROGMEM, and the drawing function uses drawXBM() to render each digit. The colon blinks every second by toggling between a filled and empty colon bitmap. The backlight is controlled by a PWM pin, dimming to 10% at night (from 10 PM to 6 AM) based on the RTC’s time. The temperature from the DS3231’s internal sensor is displayed on the second line, updated every 10 seconds. The entire project runs on a 5V USB power supply, drawing about 25 mA average. The accuracy is within 1 second per month, thanks to the DS3231’s temperature compensation.
If you want to add a user interface, you can use buttons to set the time or switch between 12-hour and 24-hour formats. The display’s 128x64 resolution allows for a simple menu system with up to 8 lines of text (using 8x8 font) or 4 lines (using 16x16 font). For example, a settings menu could show “Set Hour”, “Set Minute”, “Set Date”, with up/down buttons to adjust values. The U8g2 library supports inverse text and bitmap drawing, so you can highlight the selected item. The SPI speed is fast enough to handle button debouncing and display updates simultaneously, but you need to avoid blocking delays. Use the millis() function instead of delay() to keep the clock running smoothly.
One common issue with COG LCDs is ghosting, where previous images persist. This is caused by residual charge on the pixels. To fix it, you can add a full-screen clear every minute, or use the clearBuffer() function in U8g2. The ST7565 controller has a display on/off command that can also help reset the pixels. Another issue is the viewing angle: if you mount the display vertically, the contrast may drop at the top or bottom. You can adjust the contrast register dynamically based on the ambient light using a photoresistor, but that’s overkill for most clocks. The default contrast works fine for indoor use.
For a more robust clock, consider using a coin cell battery backup for the RTC, so the time is preserved during power outages. The DS3231 has a trickle charger that can charge a rechargeable battery, but a CR2032 works fine. The display does not need backup, as it’s non-volatile. The total cost of components is around $15: $10 for the display, $3 for the RTC, $2 for the microcontroller, and $1 for miscellaneous parts. That’s cheaper than most commercial digital clocks, and you get full customization.
Finally, let’s talk about the PCB layout. Since the display uses an FPC connector, you can design a custom PCB with a 1.0 mm pitch connector. The trace length from the microcontroller to the display should be under 10 cm to avoid signal degradation at 10 MHz SPI. Use a ground plane under the FPC to reduce noise. The 3.18 inch display’s footprint is standard, so you can mount it in a 3D-printed case or a project box. The total thickness of the display module plus a 1.6 mm PCB is about 3.8 mm, making it suitable for a slim wall clock. The backlight can be powered from a 3.3V regulator, but if you use a 5V supply, add a 100 ohm resistor in series to limit current to 20 mA.