Raspberry Pi, Python and graphical LCD 122×32 @ NJU6450

Before we do a restart and focus on creating GfxLCD module we need to know something more about other graphical displays. I have two more, one is monochrome 122×32 on NJU6450 chipset and second is OLED 0.96″ on SSD1306.
This time we gonna run NJU6450.
Why we need to know more? Because we will create a main gfx class and it’s drivers for different LCDs. And we should know biggest differences so we can be prepared for them. Like TFT is a color display but NJU is monochrome, so what to do with colors? How to do a conversion?
But before we answer those question let’s wire it and do a simple drawing.

GitHub

Planning

Plan require us to wire a display, write functions for sending command, data and initialize a display. Next, it would be nice to draw something to know how to do it. A pixel drawing is enough.
This time no fancy circles, arcs or anything.

Wiring

I know that my display has an NJU6450 chipset inside, so I just googled for data sheets. I found one, read it and found some funny information. This display is made from two and that is why we have two E signals, for left and right part. This is similar to 40×4 char LCD with HD44780 chipset.
There is only 8-bit mode so we need to wire all pins (I thought that it is similar to hd44780 and has 4-bit mode). And I found very important information, it works on 8 pixels (a page) and not 1 pixel. More on this latter.
Back to wires. Wiring, writing initialization and command/data functions and.. failure!
Question is, wire or software?

I spent a few hours on this and still nothing good. So googled once again and.. I found three different pinout’s description.. yeh.. typical. And then I started to think… looked at the model, it is ABG122032G01-YHY. And yes! I got it. Wiring and nothing burned…good.. /RST pin does something.. ok hopefully this is it.
So remember to check your model and it’s pinouts! I found many different schemas.

 LCD                          Raspberry Pi
1 (Vss)  ------- GND
2 (Vdd)  ------- +5V
3 (V0)   ---[-\-] 10k
               \--- GND
4 (A0)   ---------------------- G17
5 (E1)   ---------------------- G22
6 (E2)   ---------------------- G21
7 (R/W)  ------- GND
8 (D0)   ---------------------- G23
9 (D1)   ---------------------- G24
10 (D2)  ---------------------- G25
11 (D3)  ---------------------- G12
12 (D4)  ---------------------- G16
13 (D5)  ---------------------- G20
14 (D6)  ---------------------- G26
15 (D7)  ---------------------- G19
16 (RST) ------- +5V
17 (A)   ------- +5V
18 (K)   ------- GND

Pin V0 controls contrast, connect it via potentiometer 10k to GND. Read/write pin (R/W) connect to ground. We have the same problem like with char LCD, reading can damage RPi because it is a 5V signal.
Pin A0 describes if we send data or command. RST is funny, it reset display on descending slope (while grounding). In this example, we disabled hardware reset.

 

Initialization

Look into documentation and all is described, we need to forge it into code. Like a few times earlier let’s start from class with cmd, data, send and init:

import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)


class Gfx(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.pins = {
            'A0': 17,
            'E1': 22,
            'E2': 21,
            'D0': 23,
            'D1': 24,
            'D2': 25,
            'D3': 12,
            'D4': 16,
            'D5': 20,
            'D6': 26,
            'D7': 19
        }

        self.data_pins = [
            'D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7'
        ]
        for pin in self.pins:
            GPIO.setup(self.pins[pin], GPIO.OUT)
            GPIO.output(self.pins[pin], 0)

    def init(self):
        """initialize display"""
        GPIO.output(self.pins['A0'], 0)
        GPIO.output(self.pins['E1'], 1)
        GPIO.output(self.pins['E2'], 1)
        init_sequence = [0xae, 0xa4, 0xa9, 0xe2, 0xa0, 0xaf]
        for cmd in init_sequence:
            self.cmd(cmd, 0)
            self.cmd(cmd, 1)

    def cmd(self, char, enable):
        """send command"""
        GPIO.output(self.pins['A0'], 0)
        self.send(char, enable)

    def data(self, char, enable):
        """send data"""
        GPIO.output(self.pins['A0'], 1)
        self.send(char, enable)

    def send(self, data, enable):
        """Write to gpio"""
        GPIO.output(self.pins['E1'], 0)
        GPIO.output(self.pins['E2'], 0)
        for i in self.data_pins:
            value = data & 0x01
            GPIO.output(self.pins[i], value)
            data >>= 1

        GPIO.output(self.pins['E'+str(enable+1)], 1)
        time.sleep(0.00025)
        GPIO.output(self.pins['E1'], 0)
        GPIO.output(self.pins['E2'], 0)

The code looks familiar? It should 🙂 Same structure was used in TFT and HD44780 classes.
Some explanation is required for init function. I had a problem when I initialized screen one after another. So I changed the code to do simultaneous initialization.
Look at the documentation, page 10. There is a table with instruction codes. So things that we do:
– 0xAE – disable screen
– 0xA4 – enable dynamic driving
– 0xA9 – set duty ratio to 1/32
– 0xE2 – reset, sets start line to 1
– 0xA0 – clockwise reading
– 0xAF – enable screen

After this screen is ready for action.

Drawing

This one is funny. Why? Because we are not working on pixels but rather on a group of 8 pixels, in horizontal. What does it mean?
It means that if we send 0xFF we turn on 8 horizontal pixels. If we send 0x129 we turn first and the last pixel in 8 pixels line. They call this a page. So we operate on pages and columns.
Our height is 8 times lower 🙂 We need to do less writes to display something but it is quite hard to write drawing functions. We must know what is on display, read it, join with new data and send it back.
But again we can only write to display, no reading (+5V can damage RPi) so how to bypass it? In final version we will use a buffer and work on it.
Something strange again 🙂

This is a proof of concept so let’s focus on displaying anything. Filling and pixel should be enough.

Let’s try and write a function to fill the screen:

    def set_xy(self, x, y):
        """set xy pos"""
        if x < self.width/2:
            self.cmd(0xB8 | y, 0)
            self.cmd(0x00 | x, 0)
        else:
            self.cmd(0xB8 | y, 1)
            self.cmd(0x00 | (x - self.width/2), 1)

    def fill(self, c=0):
        for j in range(0, self.height/8):
            for i in range(0, self.width):
                self.set_xy(i, j)
                if i < self.width/2:
                    self.data(c, 0)
                else:
                    self.data(c, 1)

Function set_xy sets a page and a column. As our display is made from two LCDs, we calculate and send data to proper one.
And to fill screen we use double-loop.

Usage of this:

g = Gfx(122, 32)
g.init()
g.fill(0)
g.fill(255)

The pixel or rather the page can be light via:

    def draw_pixels(self, x, y, c=0xff):
        """draw a pixel /line"""
        self.set_xy(x, y/8)
        if x < self.width/2:
            self.data(c, 0)
        else:
            self.data(c, 1)

and usage:

g = Gfx(122, 32)
g.init()
g.fill(0)
g.fill(255)
g.fill(0)
g.draw_pixels(0, 0)
g.draw_pixels(70, 16, 129)
g.draw_pixels(120, 2, 153)

Summary

We have done some research on another type of screen. We know how to initialize it and display something. Good. It is very important for our gfx module.

Advertisements

One comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s