V0.2 Adapted for improved framebuf module

pull/3/merge
Peter Hinch 2016-12-17 12:16:07 +00:00
rodzic 686d1c92bb
commit d518b4a772
4 zmienionych plików z 22 dodań i 174 usunięć

Wyświetl plik

@ -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.

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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