kopia lustrzana https://github.com/peterhinch/micropython-samples
V0.2 Adapted for improved framebuf module
rodzic
4d5ce589a5
commit
c56753fde0
|
@ -9,12 +9,18 @@ presented [here](https://github.com/peterhinch/micropython-font-to-py.git).
|
|||
|
||||
![Picture](ssd1306.JPG)
|
||||
|
||||
## Release notes
|
||||
|
||||
V0.2 17th Dec 2016 The ``Writer`` class now uses the framebuf pixel method. This
|
||||
trades a 2:1 drop in performance for portability between devices with different
|
||||
mappings. File ssd1306_drv.py is no longer provided as the framebuf scrolling
|
||||
bug is now fixed.
|
||||
|
||||
# Files
|
||||
|
||||
1. ssd1306_test.py A simple test program.
|
||||
2. ssd1306.py A snapshot of the current official driver.
|
||||
3. ssd1306_drv.py A temporary Python fix for a bug in the above.
|
||||
4. writer.py A generic Writer class. Keeps track of the text insertion point
|
||||
3. writer.py A generic Writer class. Keeps track of the text insertion point
|
||||
over multiple fonts, handles newline and vertical scrolling if required.
|
||||
|
||||
In addition several font files are provided as samples.
|
||||
|
@ -27,7 +33,7 @@ to a Pyboard. It is untested on other platforms, but I'd expect it to be
|
|||
portable to any device supporting the official driver. If in doubt, install and
|
||||
test this first.
|
||||
|
||||
Copy files 1-4 and ``freesans20.py`` to the target and issue
|
||||
Copy files 1-3 and ``freesans20.py`` to the target and issue
|
||||
|
||||
```python
|
||||
import ssd1306_test
|
||||
|
@ -78,32 +84,12 @@ default text overrunning the bottom of the display will cause text above to
|
|||
scroll up to accommodate it. Setting ``row_clip`` will override this behaviour
|
||||
causing text to be clipped.
|
||||
|
||||
A final classmethod is provided for possible future use.
|
||||
|
||||
``mapping`` The current official ssd1306 driver configures the hardware to use
|
||||
an unorthodox mapping of bytes onto pixels. The original plan for the
|
||||
``framebuf`` module was that it should support vertical and horizontal mappings
|
||||
only. Should the official ssd1306 driver be changed to use vertical mapping
|
||||
(which the device supports) this method may be used to accommodate it. This mode
|
||||
has been tested.
|
||||
|
||||
# Use of font_to_py.py
|
||||
|
||||
To convert font files to Python for use with this driver the default (vertical)
|
||||
mapping and bit order should be used. The only optional argument which may be
|
||||
needed is ``-f`` if fixed-width rendering is desired.
|
||||
|
||||
# Note
|
||||
|
||||
The official SSD1306 driver is based on the framebuf module which is in a state
|
||||
of active development. The code presented here extends the official driver and
|
||||
consequently may be less than stable if there is significant change to the
|
||||
underlying framebuffer class. Further, the official driver's scroll method is
|
||||
buggy and at the time of writing there is a moratorium on PR's. I have coded a
|
||||
Python workround in the file ssd1306_drv.py. Hopefully in time this file will
|
||||
become redundant and the official driver will support vertical scrolling over
|
||||
long distances.
|
||||
|
||||
# License
|
||||
|
||||
Any code placed here is released under the MIT License (MIT).
|
||||
|
|
|
@ -32,7 +32,7 @@ class SSD1306:
|
|||
self.external_vcc = external_vcc
|
||||
self.pages = self.height // 8
|
||||
self.buffer = bytearray(self.pages * self.width)
|
||||
self.framebuf = framebuf.FrameBuffer1(self.buffer, self.width, self.height)
|
||||
self.framebuf = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MVLSB)
|
||||
self.poweron()
|
||||
self.init_display()
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -26,8 +26,10 @@
|
|||
# https://learn.adafruit.com/monochrome-oled-breakouts/wiring-128x32-spi-oled-display
|
||||
# https://www.proto-pic.co.uk/monochrome-128x32-oled-graphic-display.html
|
||||
|
||||
# V0.2 Dec 17th 2016 Now supports updated framebuf module.
|
||||
|
||||
import machine
|
||||
from ssd1306_drv import SSD1306_SPI, SSD1306_I2C
|
||||
from ssd1306 import SSD1306_SPI, SSD1306_I2C
|
||||
from writer import Writer
|
||||
|
||||
# Fonts
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# writer.py Implements the Writer class.
|
||||
# V0.1 Peter Hinch Nov 2016
|
||||
# V0.2 Peter Hinch Dec 2016: supports updated framebuf module.
|
||||
|
||||
# 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
|
||||
|
|
Ładowanie…
Reference in New Issue