GfxLCD – rotate SSD1306 and NJU6450

We can rotate both ILI displays but NJU and SSD are awaiting their turn. Let’s give them some attention and add rotation to them.
This will close another task on endless list of improvements ๐Ÿ™‚
From what I see we should change drawing functions (recalculate coordinates) and flushing buffer should stay the same because it works directly on hardware.

SSD1306

We are lucky, we can use hardware commands to rotate by 180 degrees but 90 and 270 must be done by software. But we have a double luck ๐Ÿ™‚ All functions call draw_pixel so we need to tweak only it.
And some changes to init are also required, we need to set a proper buffer dimension. And this dimension is original size so we must revert width and height if we are rotated and reversed ๐Ÿ˜€ .

In Page class, tweaked init looks like this:

    def init(self):
        """init page"""
        if self.rotation == 0 or self.rotation == 180:
            self.buffer = [[0] * (self.height // 8) for x in range(self.width)]
        else:
            self.buffer = [[0] * (self.width // 8) for x in range(self.height)]

Our buffer must always be in the same position, position 0.

But to make it more universal (NJU has nothing to help us with rotating) we will put new functionย draw_pixel to SSD1306 class and call the parent.

    def draw_pixel(self, pos_x, pos_y):
        """draw a pixel at x,y"""
        if self.rotation == 90 or self.rotation == 270:
            pos_x, pos_y = pos_y, pos_x
        Page.draw_pixel(self, pos_x, pos_y)

Its only task is to reverse coordinates when necessary.
And crash:) We forgot to update flush function, it should use screen’s dimension, not drawable area:

    def flush(self, force=None):
        """flush buffer to device
        :force - boolean|None"""
        if force is None:
            force = self.options['auto_flush']
        if force:
            if self.rotation == 0 or self.rotation == 180:
                height, width = self.height, self.width
            else:
                width, height = self.height, self.width
            for j in range(0, height//8):
                self.set_area(0, j, width-1, j+1)
                for i in range(0, width):
                    self.driver.data(self.get_page_value(i, j))

And now it works! I added two more SSD demos to prove it :D.

NJU6450

Copy draw_pixel from SSD and create new flush and fail ๐Ÿ™‚
This is harder that I thought. We must manually recalculate position in draw_pixel and fill_rectย for each angle. And to make it funnier we operate on buffer without any reversing so we must reverse to original size in set_xy :).

   def draw_pixel(self, pos_x, pos_y):
        """draw a pixel at x,y"""
        if self.rotation == 90:
            pos_x, pos_y = self.height - pos_y - 1, pos_x
        if self.rotation == 180:
            pos_x, pos_y = self.width - pos_x - 1, self.height - pos_y - 1
        if self.rotation == 270:
            pos_x, pos_y = pos_y, self.width - pos_x - 1
        Page.draw_pixel(self, pos_x, pos_y)

    def fill_rect(self, pos_x1, pos_y1, pos_x2, pos_y2):
        """draw a filled rectangle"""
        if self.rotation == 90:
            pos_x1, pos_y1 = self.height - pos_y1 - 1, pos_x1
            pos_x2, pos_y2 = self.height - pos_y2 - 1, pos_x2
        if self.rotation == 180:
            pos_x1, pos_y1 = self.width - pos_x1 - 1, self.height - pos_y1 - 1
            pos_x2, pos_y2 = self.width - pos_x2 - 1, self.height - pos_y2 - 1
        if self.rotation == 270:
            pos_x1, pos_y1 = pos_y1 , self.width - pos_x1 - 1
            pos_x2, pos_y2 = pos_y2 , self.width - pos_x2 - 1
        Page.fill_rect(self, pos_x1, pos_y1, pos_x2, pos_y2)

    def set_xy(self, pos_x, pos_y):
        """set xy pos"""
        if self.rotation == 0 or self.rotation == 180:
            width = self.width
        else:
            width = self.height
        if pos_x < width//2:
            self.driver.cmd(0xB8 | pos_y, 0)
            self.driver.cmd(0x00 | pos_x, 0)
        else:
            self.driver.cmd(0xB8 | pos_y, 1)
            self.driver.cmd(0x00 | (pos_x - width//2), 1)

Ok, maybe it is not so hard ๐Ÿ™‚

Summary

This closes the rotation task. All chips can now rotate in 90, 180 and 270 direction. Good ๐Ÿ™‚

Advertisements

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