Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong column offset 1.54" SH1106 I2C #376

Open
blenherr opened this issue Jul 24, 2023 · 5 comments
Open

Wrong column offset 1.54" SH1106 I2C #376

blenherr opened this issue Jul 24, 2023 · 5 comments

Comments

@blenherr
Copy link

Type of Raspberry Pi

CM4 + Original cm4io Board

Linux Kernel version

Linux NasPi 6.1.36-v8+ #1659 SMP PREEMPT Fri Jun 30 13:19:42 BST 2023 aarch64 GNU/Linux

Test:
python3 examples/demo.py -d sh1106 --i2c-port=1 --width=128 --height=64
Used display 4pin I2C white: https://de.aliexpress.com/item/1005002767134354.html

First 2 column where not used:
K640_20230724_175757

I had to change self.command(set_page_address, 0x02, 0x10) to self.command(set_page_address, 0x00, 0x10) in class sh1106 / def display.
K640_20230724_175818

Is this a bug or just depending on used display?
Is this the correct way to fix the issue?

@thijstriemstra
Copy link
Collaborator

No idea but glad you got it working correctly. I haven't seen reports on SH1106 I2C having a wrong offset (lately).

@thijstriemstra
Copy link
Collaborator

I wonder if 72440da is related. Could you try the older 3.11.0 luma.oled and see if that changes things @blenherr ?

@blenherr
Copy link
Author

Successfully installed luma.core-2.4.0 luma.oled-3.11.0 pillow-10.0.0 pyftdi-0.54.0

Does not help, still 2 pixel offset.
K640_20230730_125255

@blenherr
Copy link
Author

I wonder if 72440da is related. Could you try the older 3.11.0 luma.oled and see if that changes things @blenherr ?

Is 72440da related to ssd1306 only?
I use sh1106:

class sh1106(device):
    """
    Serial interface to a monochrome SH1106 OLED display.

    On creation, an initialization sequence is pumped to the display
    to properly configure it. Further control commands can then be called to
    affect the brightness and other settings.
    """

    def __init__(self, serial_interface=None, width=128, height=64, rotate=0, **kwargs):
        super(sh1106, self).__init__(luma.oled.const.sh1106, serial_interface)
        self.capabilities(width, height, rotate)
        self._pages = self._h // 8

        settings = {
            (128, 128): dict(multiplex=0xFF, displayoffset=0x02),
            (128, 64): dict(multiplex=0x3F, displayoffset=0x00),
            (128, 32): dict(multiplex=0x20, displayoffset=0x0F)
        }.get((width, height))

        if settings is None:
            raise luma.core.error.DeviceDisplayModeError(
                f"Unsupported display mode: {width} x {height}")

        self.command(
            self._const.DISPLAYOFF,
            self._const.MEMORYMODE,
            self._const.SETHIGHCOLUMN,      0xB0, 0xC8,
            self._const.SETLOWCOLUMN,       0x10, 0x40,
            self._const.SETSEGMENTREMAP,
            self._const.NORMALDISPLAY,
            self._const.SETMULTIPLEX,       settings['multiplex'],
            self._const.DISPLAYALLON_RESUME,
            self._const.SETDISPLAYOFFSET,   settings['displayoffset'],
            self._const.SETDISPLAYCLOCKDIV, 0xF0,
            self._const.SETPRECHARGE,       0x22,
            self._const.SETCOMPINS,         0x12,
            self._const.SETVCOMDETECT,      0x20,
            self._const.CHARGEPUMP,         0x14)

        self.contrast(0x7F)
        self.clear()
        self.show()

    def display(self, image):
        """
        Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the SH1106
        OLED display.

        :param image: Image to display.
        :type image: :py:mod:`PIL.Image`
        """
        assert image.mode == self.mode
        assert image.size == self.size

        image = self.preprocess(image)

        set_page_address = 0xB0
        image_data = image.getdata()
        pixels_per_page = self.width * 8
        buf = bytearray(self.width)

        for y in range(0, int(self._pages * pixels_per_page), pixels_per_page):
            self.command(set_page_address, 0x00, 0x10) # <----- HERE -----
            set_page_address += 1
            offsets = [y + self.width * i for i in range(8)]

            for x in range(self.width):
                buf[x] = \
                    (image_data[x + offsets[0]] and 0x01) | \
                    (image_data[x + offsets[1]] and 0x02) | \
                    (image_data[x + offsets[2]] and 0x04) | \
                    (image_data[x + offsets[3]] and 0x08) | \
                    (image_data[x + offsets[4]] and 0x10) | \
                    (image_data[x + offsets[5]] and 0x20) | \
                    (image_data[x + offsets[6]] and 0x40) | \
                    (image_data[x + offsets[7]] and 0x80)

            self.data(list(buf))

@blenherr
Copy link
Author

My test script:

from time import sleep
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import sh1106

# Display
PORT = 1
ADDRESS = 0x3C
WIDTH = 128
HEIGHT = 64
# Initialize device
serial = i2c(port=PORT, address=ADDRESS)
device = sh1106(serial, width=WIDTH, height=HEIGHT)

while True:
    with canvas(device) as draw:
        draw.rectangle(device.bounding_box, outline="white", fill="black")
        draw.text((30, 26), "Hello World", fill="white")
    sleep(0.01)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants