Driver for SH1122 oled display

Discuss development of drivers for external hardware and components, such as LCD screens, sensors, motor drivers, etc.
Target audience: Users and developers of drivers.
Post Reply
fdufnews
Posts: 76
Joined: Mon Jul 25, 2016 11:31 am

Driver for SH1122 oled display

Post by fdufnews » Tue Feb 09, 2021 4:50 pm

I have made a driver for an oled display based on the SH1122 driver. This display is more expensive than the usual 128x32 one but it offers a 256 x 64 graphic plane with 16 levels of grey.

The driver is based on the ssd1306 one so it has a similar interface.

The driver sh1122.py

Code: Select all

# MicroPython SH1122 OLED driver, SPI interface

from micropython import const
import framebuf


# register definitions

SET_COL_ADR_LSB = const(0X0)
SET_COL_ADR_MSB = const(0X10)
SET_DISP_START_LINE = const(0X40)
SET_CONTRAST = const(0X81)
SET_SEG_REMAP = const(0XA0)
SET_ENTIRE_ON = const(0XA4)
SET_NORM_INV = const(0XA6)
SET_MUX_RATIO = const(0XA8)
SET_CTRL_DCDC = const(0XAD)
SET_DISP = const(0XAE)
SET_ROW_ADR = const(0XB0)
SET_COM_OUT_DIR = const(0XC0)
SET_DISP_OFFSET = const(0XD3)
SET_DISP_CLK_DIV = const(0XD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_VSEG_LEVEL = const(0XDC)
SET_DISCHARGE_LEVEL = const(0X30)
           


# Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
class SH1122(framebuf.FrameBuffer):
    @staticmethod
    def rgb(r, g, b):
        return int(max(r,g,b))  # perhaps not the better but .....

    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.pages = self.height // 2
        self.buffer = bytearray(self.pages * self.width)
        super().__init__(self.buffer, self.width, self.height, framebuf.GS4_HMSB)
        self.init_display()

    def init_display(self):
        for cmd in (
            SET_DISP | 0x00,  # off
            # address setting
            SET_COL_ADR_LSB,
            SET_COL_ADR_MSB,  # horizontal
            # resolution and layout
            SET_DISP_START_LINE | 0x00,
            SET_SEG_REMAP,
            SET_MUX_RATIO,
            self.height - 1,
            SET_COM_OUT_DIR,  # scan from COM0 to COM[N]
            SET_DISP_OFFSET,
            0x00,
            # display
            SET_CONTRAST,
            0x80,  # median
            SET_ENTIRE_ON,  # output follows RAM contents
            SET_NORM_INV,  # not inverted
            SET_DISP | 0x01,
        ):  # on
            self.write_cmd(cmd)
        self.fill(0)
        self.show()

    def poweroff(self):
        self.write_cmd(SET_DISP | 0x00)

    def poweron(self):
        self.write_cmd(SET_DISP | 0x01)

    def contrast(self, contrast):
        self.write_cmd(SET_CONTRAST)
        self.write_cmd(contrast)

    def invert(self, invert):
        self.write_cmd(SET_NORM_INV | (invert & 1))

    def show(self):
        self.write_cmd(SET_COL_ADR_LSB)
        self.write_cmd(SET_COL_ADR_MSB)
        self.write_cmd(SET_ROW_ADR)
        self.write_data(self.buffer)


class SH1122_SPI(SH1122):
    def __init__(self, width, height, spi, dc, res, cs):
        self.rate = 10 * 1024 * 1024
        dc.init(dc.OUT, value=0)
        res.init(res.OUT, value=0)
        cs.init(cs.OUT, value=1)
        self.spi = spi
        self.dc = dc
        self.res = res
        self.cs = cs
        import time

        self.res(1)
        time.sleep_ms(1)
        self.res(0)
        time.sleep_ms(10)
        self.res(1)
        super().__init__(width, height)

    def write_cmd(self, cmd):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs(1)
        self.dc(0)
        self.cs(0)
        self.spi.write(bytearray([cmd]))
        self.cs(1)

    def write_data(self, buf):
        self.spi.init(baudrate=self.rate, polarity=0, phase=0)
        self.cs(1)
        self.dc(1)
        self.cs(0)
        self.spi.write(buf)
        self.cs(1)
A small test script

Code: Select all

# Display Image & text on SPI driven sh1122 OLED display 
from machine import Pin, SPI
from sh1122 import SH1122_SPI
import framebuf

WIDTH  = 256                                            # oled display width
HEIGHT = 64                                             # oled display height

spi = SPI(0)                                            # Init I2C using I2C0 defaults, SCL=Pin(GP6), MOSI=Pin(GP7), freq=1MHz
dc = Pin(4, Pin.OUT)
dc.value(0)
res = Pin(3, Pin.OUT)
res.value(1)
cs = Pin(5, Pin.OUT)
cs.value(1)


oled = SH1122_SPI(WIDTH, HEIGHT, spi, dc, res, cs)                  # Init oled display

# Raspberry Pi logo as 32x40 bytearray 16 levels of gray
buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x45\x56\x41\x00\x00\x00\x00\x14\x55\x54\x11\x00\x00\x00\x16\x8A\xAA\xAA\xAA\x73\x00\x00\x36\x9A\xAA\xAA\xA9\x61\x00\x00\x18\xAA\xAA\xAA\xAA\xAA\x30\x02\xAA\xAA\xAA\xAA\xAA\x91\x00\x00\x18\xAA\x97\x8A\xAA\xAA\x80\x07\xAA\xAA\xA8\x79\xAA\x91\x00\x00\x07\xAA\xAA\x85\x7A\xAA\xA1\x0A\xAA\xA8\x58\xAA\xAA\x70\x00\x00\x03\xAA\xAA\xAA\x63\x9A\xA1\x0A\xA9\x45\xAA\xAA\xAA\x30\x00\x00\x00\x6A\xAA\xAA\xA8\x27\x40\x03\x72\x8A\xAA\xAA\xA7\x00\x00\x00\x00\x28\xAA\xAA\xAA\x91\x00\x00\x08\xAA\xAA\xAA\x83\x00\x00\x00\x00\x03\x8A\xAA\xAA\x70\x00\x00\x06\xAA\xAA\xA8\x30\x00\x00\x00\x00\x00\x15\x8A\x95\x00\x00\x00\x00\x59\xA9\x52\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x34\x43\x10\x00\x11\x00\x00\x00\x00\x00\x00\x00\x02\x55\x51\x26\x66\x66\x62\x05\x54\x20\x00\x00\x00\x00\x00\x00\x46\x66\x30\x66\x66\x66\x66\x12\x66\x65\x00\x00\x00\x00\x00\x03\x66\x63\x00\x46\x66\x66\x66\x10\x26\x66\x40\x00\x00\x00\x00\x06\x66\x20\x00\x03\x56\x66\x41\x00\x02\x66\x61\x00\x00\x00\x00\x16\x51\x01\x23\x20\x00\x00\x13\x54\x20\x15\x62\x00\x00\x00\x00\x13\x00\x36\x66\x65\x10\x01\x66\x66\x65\x00\x32\x00\x00\x00\x00\x00\x02\x66\x66\x66\x50\x04\x66\x66\x66\x40\x00\x00\x00\x00\x03\x40\x06\x66\x66\x66\x61\x06\x66\x66\x66\x62\x05\x40\x00\x00\x36\x60\x26\x66\x66\x66\x61\x06\x66\x66\x66\x64\x06\x64\x00\x00\x66\x61\x36\x66\x66\x66\x60\x05\x66\x66\x66\x64\x16\x66\x10\x02\x66\x60\x36\x66\x66\x66\x40\x02\x66\x66\x66\x64\x06\x66\x20\x02\x66\x50\x16\x66\x66\x66\x10\x00\x46\x66\x66\x61\x05\x66\x20\x01\x66\x40\x03\x66\x66\x51\x01\x10\x04\x66\x66\x30\x04\x66\x10\x00\x56\x20\x00\x13\x32\x03\x66\x66\x30\x12\x21\x00\x02\x65\x00\x00\x14\x00\x00\x00\x00\x46\x66\x66\x65\x00\x00\x02\x31\x31\x00\x00\x00\x36\x52\x00\x01\x66\x66\x66\x66\x20\x01\x56\x64\x00\x00\x00\x00\x66\x66\x30\x03\x66\x66\x66\x66\x40\x15\x66\x66\x00\x00\x00\x00\x66\x66\x63\x03\x66\x66\x66\x66\x40\x56\x66\x66\x00\x00\x00\x00\x56\x66\x66\x11\x66\x66\x66\x66\x22\x66\x66\x65\x00\x00\x00\x00\x36\x66\x66\x30\x46\x66\x66\x65\x04\x66\x66\x64\x00\x00\x00\x00\x05\x66\x66\x40\x03\x66\x66\x30\x05\x66\x66\x51\x00\x00\x00\x00\x01\x56\x66\x40\x00\x01\x10\x00\x05\x66\x65\x10\x00\x00\x00\x00\x00\x02\x33\x10\x01\x23\x32\x10\x02\x44\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x46\x66\x66\x65\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x66\x66\x66\x66\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x26\x66\x66\x62\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x34\x43\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
# Load the raspberry pi logo into the framebuffer (the image is 32x40)
fb = framebuf.FrameBuffer(buffer, 32, 40, framebuf.GS4_HMSB)

# Clear the oled display in case it has junk on it.
oled.fill(0)

# Blit the image from the framebuffer to the oled display
oled.blit(fb, 112, 0)

# Add some text
oled.text("Raspberry Pi", 5, 5, 15)
oled.text("Pico", 5, 15, 15)
if (HEIGHT > 32):
    oled.text("This display", 5, 25, 12)
    oled.text("has more than", 5, 35, 8)
    oled.text(" 32 lines", 5, 45, 4)

if (WIDTH > 128):
    oled.text("This display", 149, 25, 12)
    oled.text("has more than", 149, 35, 8)
    oled.text(" 128 pixels", 149, 45, 4)
    
# Add a color scale
for i in range(0,16):
    oled.fill_rect(i*8, 55, 8, 8,15-i)
    oled.fill_rect(247-(i*8), 55, 8, 8,15-i)
    
# Finally update the oled display so the image & text is displayed
oled.show()

It is not clear on the picture but the Raspeberry Pi logo is in level of grey
Attachments
sh1122.jpg
sh1122.jpg (79.33 KiB) Viewed 5557 times

fdufnews
Posts: 76
Joined: Mon Jul 25, 2016 11:31 am

Re: Driver for SH1122 oled display

Post by fdufnews » Sun May 16, 2021 8:14 am

I have created a repo on github to store some example code for the Pico
The driver for the SH1122 is in the drivers directory.
In the examples/sequence directory there are 2 short pieces of code that display animations using the SH1122. There is a short video in the horsejump subdirectory.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Driver for SH1122 oled display

Post by pythoncoder » Mon May 17, 2021 7:10 am

Nice. ;)

I'm puzzled as to why, in the photo, your test script doesn't display the text "This display has more than 128 pixels".
Peter Hinch
Index to my micropython libraries.

fdufnews
Posts: 76
Joined: Mon Jul 25, 2016 11:31 am

Re: Driver for SH1122 oled display

Post by fdufnews » Mon May 17, 2021 11:38 am

The test script has evolved since I took the photo.
Here's an uptodate one
Attachments
IMG_20210515_171731.jpg
IMG_20210515_171731.jpg (74.55 KiB) Viewed 4956 times

Mark's Bench
Posts: 9
Joined: Wed May 12, 2021 9:47 pm

Re: Driver for SH1122 oled display

Post by Mark's Bench » Wed May 19, 2021 12:19 am

That looks great - thank you for posting it! I have an SH1122 in a box somewhere - could never get it to work right so I put it in a "safe place". Once I find it I'll give it a shot with your driver.

fdufnews
Posts: 76
Joined: Mon Jul 25, 2016 11:31 am

Re: Driver for SH1122 oled display

Post by fdufnews » Wed May 19, 2021 7:14 am

It's a pretty good display. I just warn you that displaying static images for a long time leaves marks.

Post Reply