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.


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)]
            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
                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):
          , j))

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


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
            width = self.height
        if pos_x < width//2:
            self.driver.cmd(0xB8 | pos_y, 0)
            self.driver.cmd(0x00 | pos_x, 0)
            self.driver.cmd(0xB8 | pos_y, 1)
            self.driver.cmd(0x00 | (pos_x - width//2), 1)

Ok, maybe it is not so hard 🙂


This closes the rotation task. All chips can now rotate in 90, 180 and 270 direction. Good 🙂


Leave a Reply

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

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

Google+ photo

You are commenting using your Google+ 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 )


Connecting to %s