Dave's STM32 Page
Notes on using a 128 x 64 LCD


After choosing a 128x64 LCD display, there were several challenges. I couldn't find STM32 code for the KS0108 so decided to roll my own. As a starting point I used a combination of AVR code I found on-line, a 5x7 font, also found, and my old Bresneham's line and circle functions.. It is important to totally understand the GPIO controls for the processor first, so I wrote a handful of tests to set bits, set the port data direction, etc. I do like the STM32 I/O registers and the fact that they can still use the "PORTC->REG = 0x42" coding style This approach gives you full access to all the ports and all the registers for each port. It is very powerful and never got in the way at all. It allows you to easily define which ports and bits you are using, making changes simple.

I'm using the Newhaven NHD-12864WG-BTFH-V#N  LCD. I like the .43mm dot pitch: not too big, not too small. And that it is 5V but operates with 3.3V logic (see below). I also like the $25 qty. 1 price.

Most challenges were met by RTFM (Reading The Manual). You really need to read and understand the KS0108 data sheet and the registers of the STM32.

Chip Select Polarity
These 128 x 64 displays mostly use the KS0108 / KS0107 controllers. But the KS0108 can only control 64x64 dots, so 2 controllers are used. The panels typically bring out one chip select (CS) for each controller. The KS0108 has both active high and active low chip selects, so the CS polarity is a function of the panel, not the chip: the LCD panel manufacturer makes the decision. Unfortunately there is no standard here. Some manufacturers don't specify the polarity on their sparse data sheets. Newhaven specified it but got it wrong for the panel I chose. The data sheet says CS 1 and 2 are active high to select the chip. They are active low. That cost me a few debug hours.

Newhaven didn't specify the RST polarity either. The KS0108 data sheet says it is low for reset. Unless the LCD manufacturer has logic to invert RST it will be reset low, operate high. I found that just tying RST high was fine. But I haven't exhaustively tested this under varying power supply conditions, partial brownout, etc. the real test for a reset circuit.  

5V CMOS vs TTL levels
This issues is critical when you use 3.3V logic to drive a 5V LCD. In order to do this the LCD Vih must tolerate 3.3V levels. On read cycles, the processor data pins must tolerate 5V logic inputs. The KS0108B chip has TTL input levels and 5V CMOS output levels on the data bus pins. Again, some LCDs that use this chip have ambiguous specs. They say ViH must be 0.7 * VDD or 3.5V at 5V VDD (higher at 5.25V). 3.3V logic can't meet this. But the KS0108B has Vih = 2.0V min,  so no problem driving the LCD with 3.3V logic.

Data Reads
During reads, the KS0108 drives the processor input pins with 5V CMOS logic. Some 3.3V processors cannot tolerate 5V. but the STM32 can, so long as you don't have the pull-up or pull-down resistors enabled. With a non-5V tolerant part, you either need to level translate or limit the voltage with eight resistor and schottky diodes, eight FETs... Or find a 3.3V compatible LCD.

From the KS0108 data sheet:
" 3. Output register
Output register stores the data temporarily from display data RAM when CS1B, CS2B, CS3 is in active mode and R/W and RS=H, stored data in display data RAM is latched in output register. When CS1B to CS3 is in active mode and R/W=H, RS=L, status data (busy check) can read out.To read the contents of display data RAM, twice access of read instruction is needed. In first access, data in display data RAM is latched into output register. In second access, MPU can read data which is latched. That is, to read the data in display data RAM, it needs dummy read. But status read is not needed dummy read."

putpix()
The ability to set a single pixel on the display is fundamental to doing graphics. With the KS0108, this operation is quite cumbersome, meaning complicated and time consuming. Good thing I have lots of fast processor cycles. putpix(x,y,val) sets one pixel. Sounds simple, but the pseudo-code to write one pixel on the KS0108 is something like this:

Set X address (x & 0x3F)
    Set bus direction OUT
    Set RW, RS, both CS
    Set data
    Pulse E
        E= 1

        Delay 500nS
        E = 0
    Delay(5)
Set Y page (y >> 3)
    Set data
    Pulse E
    Delay(3us)
Pixel mask (1 >> y%7)
Read dummy data
    Set bus direction IN
    Set RW, RS
    Select L or R chip based on X>63
    Pulse E
    Delay(5)
Read real data
    E = 1
    Delay(1)
    Input data
    E = 0
    Delay(3us)
Re-set X address (x & 0x3F) since the last read incremented it
    Set bus direction OUT
    Set RW, RS, both CS
    Set data
    Pulse E
    Delay(3us)
Merge the mask and the read data (mask | data)
Write data
    Set bus direction OUT
    Set RW, RS
    Select L or R chip based on X>63
    Pulse E
    Delay(3us)
    
Whew! The Delay(3us) are there to avoid needing a wait for the busy flag. The Delay in the Pulse E is to time the E pulses to .5uS.

Another approach is to manage graphics in a block of CPU memory and then transfer the block of CPU memory to the LCD after the drawing is complete. This is more efficient but adds a bit of complexity. It is much faster to set a single pixel in CPU RAM. The data transfer uses writes only and can take advantage of the auto-increment of the LCD X address register. A full screen takes only 128 X 64 / 8 = 1KB of RAM. My processor has 8KB which is reasonable. One reason I don't like this approach is that it is not as convenient for larger LCD's and I'd like my code to be scalable to larger displays where possible. This processor only has 8K of RAM, not enough for one buffer of a 320x240 monochrome display (9.4 KB), never mind a color TFT or higher res LCD. As far as the software differences between managing a display in RAM vs in the controller, it wouldn't be that hard to insert an lcd_update() to move the data from RAM to the LCD every so often.

Other KS0108 Gotchas
The Samsung data sheet calls the horizontal axis Y and the vertical axis X. This is backwards for me and for most of the world.

Data reads require a 'dummy' read first. That adds another slow cycle to every read.

Data reads and writes cause the X (their Y) register to increment. This is handy in some cases but it interferes with read-modify-write cycles (Like PutPix). Also if you are incrementing between pixel 63 and 64, you need to set the Y register anyway. So a software test is needed for this. But it is faster to do this test in software than to set the X register every single time.

Pros and Cons
The pros and cons of 128x64 LCDs:
2023 update
I no longer bother with the old 128 x 64 LCDs and their parallel bus. The modern 128 x 64 OLEDs and QGVA TFTs are nicer, use simple control over SPI or I2C, and the libraries such as U8G2 are very nice. Speaking of which, I am considering redesigning the Whole House system to use an Arduino processor instead of the STm32 Discovery.


Back to STM32 Page
Multi-Zone Preamp Page
Main Page
BoatBus and AVR Projects

Last Updated: 3/29/2023