From d518b4a7722602ee6692f0f73a9c0ff38656465c Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Sat, 17 Dec 2016 12:16:07 +0000 Subject: [PATCH] V0.2 Adapted for improved framebuf module --- DRIVERS.md | 6 --- driver_test.py | 2 +- ssd1306_drv.py | 122 ------------------------------------------------- writer.py | 66 +++++++++----------------- 4 files changed, 22 insertions(+), 174 deletions(-) delete mode 100644 ssd1306_drv.py diff --git a/DRIVERS.md b/DRIVERS.md index 71a2fe3..55e995d 100644 --- a/DRIVERS.md +++ b/DRIVERS.md @@ -43,12 +43,6 @@ The ``Writer`` class exposes the following class methods: the physical display. If ``col_clip`` is ``False`` characters will wrap onto the next line. If ``row_clip`` is ``False`` the display will, where necessary, scroll up to ensure the line is rendered. - 3. ``mapping`` Arg: an integer. This defines the mapping of bytes in the - buffer onto pixels. The module exposes three constants for use here: ``VERT`` - ``HORIZ`` and ``WEIRD``, the latter being specific to the official SSD1306 - driver. ``VERT`` is for true vertically mapped displays. ``HORIZ``, for - horizontally mapped devices, is currently unsupported. By default the mapping - is for SSD1306 devices using the official driver. As class methods these settings apply to all font objects. The insertion point of characters is maintained regardless of the font in use. diff --git a/driver_test.py b/driver_test.py index 29f1aba..04a6ec4 100644 --- a/driver_test.py +++ b/driver_test.py @@ -31,7 +31,7 @@ import machine import utime from writer import Writer -from ssd1306_drv import SSD1306_I2C, SSD1306_SPI # Until official module is fixed +from ssd1306 import SSD1306_I2C, SSD1306_SPI import freeserif import freesans20 import inconsolata16 diff --git a/ssd1306_drv.py b/ssd1306_drv.py deleted file mode 100644 index 4c34788..0000000 --- a/ssd1306_drv.py +++ /dev/null @@ -1,122 +0,0 @@ -# ssd1306_drv.py An implementation of a Display class for SSD1306 based displays -# V0.1 Peter Hinch Nov 2016 - -# https://learn.adafruit.com/monochrome-oled-breakouts/wiring-128x32-spi-oled-display -# https://www.proto-pic.co.uk/monochrome-128x32-oled-graphic-display.html - -# Version supports vertical and "horizontal" modes. - -# The MIT License (MIT) -# -# Copyright (c) 2016 Peter Hinch -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -# SSD1306 classes to fix the official one whose scroll method is broken -# This also demonstrates a way to do scrolling for devices with true vertical -# mapping (the official driver sets the device into its "horizontal" mode). -# This is a hybrid mode where bytes are aligned vertically but arranged -# horizontally - -import ssd1306 - -class SSD1306(object): - def __init__(self, device): - self.width = device.width # In pixels - self.height = device.height - self.bpc = (self.height + 7) >> 3 - self.device = device - self.vmap = False # Currently the official driver uses "horizontal" - - @property - def buffer(self): # property emulates underlying device - return self.device.buffer - - def show(self): - self.device.show() - - # Return map-independent offset into buffer - def _offset(self, x, y): - if self.vmap: - return y + x * self.bpc - else: - return y * self.width + x - - def scroll(self, x, y): - dy = abs(y) - if dy: - self._scrolly(dy, y > 0) - dx = abs(x) - if dx: - self._scrollx(dx, x > 0) - - def _scrolly(self, ystep, down): - buf = self.device.buffer - bpc = self.bpc - ystep_bytes = (ystep >> 3) + 1 - ystep_bits = ystep & 7 - if down: - for x in range(self.width): - for ydest in range(bpc - 1, -1, -1): - ysource = ydest - ystep_bytes - data = 0 - if ysource + 1 >= 0: - data = buf[self._offset(x, ysource + 1)] << ystep_bits - if ysource >= 0: - data |= buf[self._offset(x, ysource)] >> 8 - ystep_bits - buf[self._offset(x, ydest)] = data - else: - for x in range(self.width): - for ydest in range(bpc): - ysource = ydest + ystep_bytes - data = 0 - if ysource < bpc: - data = buf[self._offset(x, ysource)] << (8-ystep_bits) - if ysource - 1 < bpc: - data |= buf[self._offset(x, ysource - 1)] >> ystep_bits - buf[self._offset(x, ydest)] = data - - def _scrollx(self, xstep, right): # scroll x - buf = self.device.buffer - bpc = self.bpc - for y in range(bpc): - if right: # Scroll right - for xdest in range(self.width - 1, -1, -1): - data = 0 - xsource = xdest - xstep - if xsource >= 0: - data = buf[self._offset(xsource, y)] - buf[self._offset(xdest, y)] = data - else: - for xdest in range(0, self.width): - data = 0 - xsource = xdest + xstep - if xsource < self.width: - data = buf[self._offset(xsource, y)] - buf[self._offset(xdest, y)] = data - -class SSD1306_I2C(SSD1306): - def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): - device = ssd1306.SSD1306_I2C(width, height, i2c, addr, external_vcc) - super().__init__(device) - -class SSD1306_SPI(SSD1306): - def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): - device = ssd1306.SSD1306_SPI(width, height, spi, dc, res, cs, external_vcc) - super().__init__(device) diff --git a/writer.py b/writer.py index c97be20..2a03c82 100644 --- a/writer.py +++ b/writer.py @@ -1,5 +1,5 @@ # writer.py Implements the Writer class. -# V0.1 Peter Hinch Nov 2016 +# V0.2 Peter Hinch Dec 2016 # The MIT License (MIT) # @@ -24,21 +24,15 @@ # THE SOFTWARE. # A Writer supports rendering text to a Display instance in a given font. -# Currently supports vertical mapping and the SSD1306 "pseudo-horizontal" map -# only # Multiple Writer instances may be created, each rendering a font to the # same Display object. -VERT = 0 -HORIZ = 1 -WEIRD = 2 class Writer(object): - text_row = 0 # attributes common to all Writer instances + text_row = 0 # attributes common to all Writer instances text_col = 0 - row_clip = False # Clip or scroll when screen full - col_clip = False # Clip or new line when row is full - bmap = WEIRD # SSD1306 pseudo-horizontal + row_clip = False # Clip or scroll when screen full + col_clip = False # Clip or new line when row is full @classmethod def set_textpos(cls, row, col): @@ -50,13 +44,6 @@ class Writer(object): cls.row_clip = row_clip cls.col_clip = col_clip - @classmethod - def mapping(cls, bmap): - if bmap in (VERT, HORIZ): - cls.bmap = bmap - else: - raise ValueError('Unsupported mapping') - def __init__(self, device, font): super().__init__() self.device = device @@ -65,8 +52,6 @@ class Writer(object): raise OSError('Font must be vertically mapped') self.screenwidth = device.width # In pixels self.screenheight = device.height - div, mod = divmod(device.height, 8) - self.bytes_per_col = div + 1 if mod else div def _newline(self): height = self.font.height() @@ -83,41 +68,32 @@ class Writer(object): self._printchar(char) def _printchar(self, char): - bmap = Writer.bmap # Buffer mapping if char == '\n': self._newline() return - fbuff = self.device.buffer glyph, char_height, char_width = self.font.get_ch(char) - if Writer.text_row+char_height > self.screenheight and Writer.row_clip: - return + if Writer.text_row + char_height > self.screenheight: + if Writer.row_clip: + return + self._newline() if Writer.text_col + char_width > self.screenwidth: if Writer.col_clip: return else: self._newline() + div, mod = divmod(char_height, 8) - gbytes = div + 1 if mod else div # No. of bytes per column of glyph - start_row, align = divmod(Writer.text_row, 8) - for scol in range(0, char_width): # Source column - dcol = scol + Writer.text_col # Destination column - dest_row_byte = start_row - for gbyte in range(gbytes): # Each glyph byte in column - if bmap == VERT: - dest = dcol * self.bytes_per_col + dest_row_byte - elif bmap == WEIRD: - dest = dcol + dest_row_byte * self.screenwidth - source = scol * gbytes + gbyte - data = fbuff[dest] & (0xff >> (8 - align)) - fbuff[dest] = data | glyph[source] << align - if align and (dest_row_byte + 1) < self.bytes_per_col: - if bmap == VERT: - dest += 1 - elif bmap == WEIRD: - dest += self.screenwidth - data = fbuff[dest] & (0xff << align) - fbuff[dest] = data | glyph[source] >> (8 - align) - dest_row_byte += 1 - if dest_row_byte >= self.bytes_per_col: + gbytes = div + 1 if mod else div # No. of bytes per column of glyph + device = self.device + for scol in range(char_width): # Source column + dcol = scol + Writer.text_col # Destination column + drow = Writer.text_row # Destination row + for srow in range(char_height): # Source row + gbyte, gbit = divmod(srow, 8) + if drow >= self.screenheight: break + if gbit == 0: # Next glyph byte + data = glyph[scol * gbytes + gbyte] + device.pixel(dcol, drow, data & (1 << gbit)) + drow += 1 Writer.text_col += char_width