peterhinch / micropython-nano-gui

A lightweight MicroPython GUI library for display drivers based on framebuf class
MIT License
507 stars 88 forks source link

Rotation for SSD1351 #49

Closed polygonfuture closed 1 year ago

polygonfuture commented 1 year ago

Hi Peter, Thank you so much for your work on these libraries.

I'm looking to rotate the SSD1351 by 180 degrees (for a physically upside down display).
I noticed 1327 has the ability to be rotated. Though its driver looks in some ways more simplified / abstracted compared to SSD1351.

Do you have a suggestion of how I might achieve a 180 rotation of a 1351 OLED RGB screen? (Its 16bit color model).

peterhinch commented 1 year ago

There are potentially two ways to do rotation. One is by sending commands to the driver chip and the other way is by adapting the code that outputs the frame buffer to the chip. The first requires studying the datasheet to find out what is supported. The second is fairly straightforward for 180°: you need to output rows in reverse order and also reverse the order in which pixels are output to columns.

polygonfuture commented 1 year ago

For modifying the framebuffer, would this be done within the show() function within the ssd1351_16bit.py for example?

peterhinch commented 1 year ago

The .show() method would perform the rotation. The method of transferring the data to the SPI bus would be adapted to output rows and columns in the correct order.

polygonfuture commented 1 year ago

I solved this issue sending a command to the SSD1351 driver chipset in the SSD1351 python driver. For me this was the ssd1351_16bit.py file in the __init__ section.

As peter mentioned, the datasheet has a command table for a byte command to send to the SSD1351 chipset. The chipset I have is manufactured by Solomon Systech.

You send a Set Re-Map command listed on page 32 of the datasheet.

Based on the table, you do this with two commands, the first tells the chipset we are going to send a Set Remap command. With this command we send a 7 bit value that controls 6 different functions on the SSD1351.

self._write(b'\xA0', 0)
self.write(b'\x76', 1)

In my case I used 0x76 which translates to 01110110 command in the table. Each binary bit represents a specific command the chipset interprets. This correctly flips columns and rows to give a 180 degree visual rotation from the user perspective.

The updated SSD1351 class now reads:

class SSD1351(framebuf.FrameBuffer):
    # Convert r, g, b in range 0-255 to a 16 bit colour value RGB565
    #  acceptable to hardware: rrrrrggggggbbbbb
    @staticmethod
    def rgb(r, g, b):
        return ((r & 0xf8) << 5) | ((g & 0x1c) << 11) | (b & 0xf8) | ((g & 0xe0) >> 5)

    def __init__(self, spi, pincs, pindc, pinrs, height=128, width=128, init_spi=False):
        if height not in (96, 128):
            raise ValueError('Unsupported height {}'.format(height))
        self.spi = spi
        self.spi_init = init_spi
        self.pincs = pincs
        self.pindc = pindc  # 1 = data 0 = cmd
        self.height = height  # Required by Writer class
        self.width = width
        mode = framebuf.RGB565
        self.palette = BoolPalette(mode)
        gc.collect()
        self.buffer = bytearray(self.height * self.width * 2)
        super().__init__(self.buffer, self.width, self.height, mode)
        self.mvb = memoryview(self.buffer)
        pinrs(0)  # Pulse the reset line
        utime.sleep_ms(1)
        pinrs(1)
        utime.sleep_ms(1)
        if self.spi_init:  # A callback was passed
            self.spi_init(spi)  # Bus may be shared
        # See above comment to explain this allocation-saving gibberish.
        self._write(b'\xfd\x12\xfd\xb1\xae\xb3\xf1\xca\x7f\xa0\x74'\
        b'\x15\x00\x7f\x75\x00\x7f\xa1\x00\xa2\x00\xb5\x00\xab\x01'\
        b'\xb1\x32\xbe\x05\xa6\xc1\xc8\x80\xc8\xc7\x0f'\
        b'\xb4\xa0\xb5\x55\xb6\x01\xaf', 0)

        self._write(b'\xA0',0)  #SET REMAP COMMAND
        self._write(b'\x76',1)  #SEND 7 BIT COMMAND IN HEX FORMAT THAT FLIPS DISPLAY.  IN BINARY:  01110110

        self.show()
        gc.collect()
peterhinch commented 1 year ago

Thanks for posting this, it is a useful resource if the requirement crops up again.

tjlynchny commented 8 months ago

I'm working with a Waveshare 1.27" rgb OLED screen (128 x 96) that uses the ssd1351 driver. I, too, needed to rotate the display 180 degrees, but when I tried polygonfuture's mod for the driver, things did not quite work correctly. I dug around in the same datasheet and came up with a fix: In addition to polygonfuture's two mod lines, I added the following which alters the Display Start Line (top of page 33 of the datasheet):

        self._write(b'\xA1',0)  # set command: 'Set Display Start Line'
        self._write(b'\x60',1)  # set start line to 96

Peter, I see you've marked this closed, but I hope you can add this fix to the thread.

And a huge thank you for nanogui and your other fantastic micropython libraries. They make working with micropython the absolute best.

-tim