Micro:bit V2 で 電子ペーパー表示ができた!

micro:bit v1だと、メモリ不足で歯が立たない
>>> microbit.reset()
MemoryError:
MicroPython v1.9.2-34-gd64154c73 on 2017-09-01; micro:bit v1.0.1 with nRF51822
Type “help()” for more information.
>>>
>>> micropython.mem_info()
stack: 516 out of 1800
GC: total: 10048, used: 672, free: 9376
No. of 1-blocks: 18, 2-blocks: 9, max blk sz: 3, max free sz: 562
>>>

micro:bit v2では、8KBのソースが複数個ロードでき、余裕で動作する。
>>> microbit.reset()
MicroPython v1.13 on 2020-12-21; micro:bit v2.0.0-beta.3 with nRF52833
Type “help()” for more information.
>>>
>>> micropython.mem_info()
stack: 392 out of 7680
GC: total: 64512, used: 24832, free: 39680
No. of 1-blocks: 583, 2-blocks: 199, max blk sz: 189, max free sz: 1266
>>>

# MemoryDisplayライブラリ (MemoDisp.py)

# The MIT License (MIT)
#
# Copyright (c) 2018 ladyada for Adafruit Industries
# pylint: disable=line-too-long
"""
`adafruit_sharpmemorydisplay`
====================================================
  https://github.com/adafruit/circuitpython/releases

"""
from microbit import *
from micropython import const
# import sharp_memorydisp
_SHARPMEM_BIT_WRITECMD = const(0x80)  # in lsb
_SHARPMEM_BIT_VCOM = const(0x40)  # in lsb
_SHARPMEM_BIT_CLEAR = const(0x20)  # in lsb
spi_clk = pin13
spi_scs = pin12

CS_ACTIVE = const(1)
CS_INACTIVE = const(0)
# Colors
BLACK = const(0)
WHITE = const(255)
# Parameters to Change
COLS = const(144)
ROWS = const(168)
COLBYTES = const(COLS // 8)
TEXT_NORMAL = const(2)
ROTATION = list(range(4))

_icons = [getattr(Image, x) for x in dir(Image)
          if hasattr(getattr(Image, x), 'get_pixel')]

def reverse_bit(num):
    """Turn an LSB byte to an MSB byte, and vice versa. Used for SPI as
    it is LSB for the SHARP, but 99% of SPI implementations are MSB only!"""
    result = 0
    for _ in range(8):
        result <<= 1
        result += num & 1
        num >>= 1
    return result
class SharpMemoryDisplay():
    """A driver for sharp memory displays, you can use any size but the
    full display must be buffered in memory!"""

    def __init__(self, spiP, width, height, *,
                 baudrate=2000000, portrait=True, bottom=False):
        self._scs_pin = spi_scs
        # The SCS pin is active HIGH so we can't use bus_device. exciting!
        # scs_pin.switch_to_output(value=True)
        self._baudrate = baudrate
        self._portrait = portrait
        self._bottom = bottom
        self._spi = spiP
        self.width = width
        self.height = height
        # prealloc for when we write the display
        self._buf = bytearray(1)
        self.buffer = bytearray((width // 8) * height)
        # Set the vcom bit to a defined state
        self._vcom = True

    def fill(self, color, start=0, stop=0):
        """completely fill/clear the buffer with a color"""
        if color == 1:
            fill = WHITE
        else:
            fill = BLACK
        if stop == 0:
            bufl = len(self.buffer)
        else:
            bufl = stop*COLBYTES
        if not self._portrait:
            start = 0
            bufl = len(self.buffer)
        elif self._bottom :
            bufl = (self.height - start)*COLBYTES
            if stop>0:
                start = (self.height - stop)*COLBYTES
        for i in range(start, bufl):
            self.buffer[i] = fill

    def clear(self):
        """completely fill/clear the buffer """
        self._scs_pin.write_digital(CS_ACTIVE)
        self._buf[0] = _SHARPMEM_BIT_CLEAR
        self._spi.write(self._buf)
        self._buf[0] = 0
        self._spi.write(self._buf)  # we send one last 0 byte
        self._scs_pin.write_digital(CS_INACTIVE)

    def show(self, start=1, stop=0, rotate=0):
        portrait, bottom = ROTATION[rotate]
        # CS pin is inverted so we have to do this all by hand
        self._scs_pin.write_digital(CS_ACTIVE)
        # toggle the VCOM bit
        self._buf[0] = _SHARPMEM_BIT_WRITECMD
        if self._vcom:
            self._buf[0] |= _SHARPMEM_BIT_VCOM
        self._vcom = not self._vcom
        self._spi.write(self._buf)

        if (not portrait) or stop == 0:
            start = 1
            stop = self.height
            stopl = self.height + 1
        else:
            stopl = stop + 1
        if portrait and bottom :
            stopl = (self.height - start+1) + 1
            start = (self.height - stop) + 1
        line_len = COLBYTES
        slice_from = (start-1) * line_len
        for line in range(start, stopl):
            self._buf[0] = reverse_bit(line)
            self._spi.write(self._buf)
            self._spi.write(
                 memoryview(self.buffer[slice_from : slice_from + line_len]))
            slice_from += line_len
            self._buf[0] = 0
            self._spi.write(self._buf)
        self._spi.write(self._buf)  # we send one last 0 byte
        self._scs_pin.write_digital(CS_INACTIVE)

    def draw_line(self, x0, y0, x1, y1, color=1, rotate=0):
        dx = abs(x1 - x0)
        sx = 1 if x0 < x1 else -1
        dy = -abs(y1 - y0)
        sy = 1 if y0 < y1 else -1

        err = dx + dy
        while True:
            self.set_pixel(x0, y0, color, rotate=0)
            if x0 == x1 and y0 == y1:
                break
            e2 = 2 * err
            if e2 > dy:
                err += dy
                x0 += sx
            if e2 <= dx:
                err += dx
                y0 += sy

    def set_pixel(self, x, y, color=1, rotate=0):
        portrait, bottom = ROTATION[rotate]
        if portrait :
            if bottom :
                x = COLS - x
            shift = 7 - x % 8
            x //= 8
            offset = y
            if bottom :
                offset = ROWS - offset
            offset = offset * COLBYTES + x
        else:
            if not bottom :
                y = COLS - y
            shift = 7 - y % 8
            y //= 8
            offset = x
            if bottom :
                offset = ROWS - offset
            offset = offset * COLBYTES + y
        if offset >= len(self.buffer):
            return
        byte_b = self.buffer[offset]
        if color == 1:
            # Mask *out* a bit to set as black
            byte_b &= ~(0b1 << shift)
        if color == 0:
            # a bit to set as white
            byte_b |= (0b1 << shift)
        self.buffer[offset] = byte_b

    def draw_bin(self, filename):
        with open(filename, "rb") as f:
            f.readinto(self.buffer)

spi.init(baudrate=8000000, bits=8, mode=0)
# Create blank image for drawing.

# デモプログラム

# ここにコードを書いてね :-)
from microbit import *
from micropython import const
import time
import MemoDisp

COLS = const(144)
ROWS = const(168)
COLBYTES = const(COLS // 8)
# Colors
BLACK = const(0)
WHITE = const(255)

# Parameters to Change
WIDTH = 144
HEIGHT = 168
PORTRAIT = (HEIGHT > WIDTH)
BOTTOMUP = True
BORDER = 5
OFFSET_X = 4
OFFSET_Y = 4
TEXT_NORMAL = const(2)

MemoDisp.ROTATION[0] = (PORTRAIT, BOTTOMUP)
MemoDisp.ROTATION[1] = (not PORTRAIT, BOTTOMUP)
MemoDisp.ROTATION[2] = (PORTRAIT, not BOTTOMUP)
MemoDisp.ROTATION[3] = (not PORTRAIT, not BOTTOMUP)

def char_len(char, text_size=TEXT_NORMAL):
    if char in b"\"*+-0123<=>ABCDEFHKLOPQSUXZ[]^bcdefghjklnopqrsxz{":
        return 4 * text_size
    if char in b"!'.:i|":
        return 2 * text_size
    if char in b" (),;I`}":
        return 3 * text_size
    return 5 * text_size

def text_len(text, text_size=TEXT_NORMAL):
    return sum(char_len(c, text_size=text_size) + text_size for c in text)

def draw_image(icon, x, y, color=1, type="T", size=TEXT_NORMAL, rotate=0):
    cols = 5 * size
    rows = 5 * size
    for ox in range(cols):
        if x + ox < 0:
            continue
        if x + ox >= WIDTH:
            return False
        for oy in range(rows):
            if (not icon.get_pixel(ox // size,
                                   oy // size)) and type == "T":
                disp.set_pixel(ox + x + OFFSET_X,
                               oy + y + OFFSET_Y, color ^ 1, rotate=rotate)
                continue
            try:
                disp.set_pixel(ox + x + OFFSET_X,
                               oy + y + OFFSET_Y, color, rotate=rotate)
            except IndexError:
                pass
    return True

def write_text(text, x, y, color=1, text_size=TEXT_NORMAL, rotate=0):
    image = None
    for letter in text:
        image = None
        letter_width = char_len(letter, text_size=text_size)
        if x+letter_width >= WIDTH or ord(letter) == 13:
            y += text_size*5+2
            x = OFFSET_X
        if letter != " " and (x + letter_width) >= 1:
            if ord(letter) > 127:
                image = MemoDisp._icons[ord(letter) - 128]
            else:
                try:
                    image = Image(letter)
                except:
                    pass
        if image is not None:
            if not draw_image(image, x, y, color,
                              type="T", size=text_size, rotate=rotate):
                return
        if ord(letter) != 13:
            x += letter_width + text_size

    del image

# display = adafruit_sharpmemorydisplay.SharpMemoryDisplay(spi, scs, 96, 96)
disp = MemoDisp.SharpMemoryDisplay(spi, COLS, ROWS,
                          portrait=PORTRAIT, bottom=BOTTOMUP)

# Clear display.
disp.fill(1)
disp.show()
# Draw Some Text
text = "Hello World! Good morning."
write_text(text, 0, 5, rotate=0)
disp.draw_line(0, 5+OFFSET_Y + TEXT_NORMAL*5, text_len(text),
                  5+OFFSET_Y + TEXT_NORMAL*5, color=1)
disp.show(5, 35)
write_text(text, 0, 5, rotate=1)
disp.show(5, 35, rotate=1)
write_text(text, 0, 5, rotate=2)
disp.show(5, 35, rotate=2)
write_text(text, 0, 5, rotate=3)
disp.show(5, 35, rotate=3)
sleep(300)
disp.show(5, 35)
disp.fill(1, 5, 35)
sleep(500)
disp.show(5, 35)
write_text("Time = ", 0, 5)
start = time.ticks_ms()
while not button_a.is_pressed():
    past = time.ticks_diff(time.ticks_ms(), start)
    if past % 100 == 0:
        write_text(str(past // 100), TEXT_NORMAL*5*7, 5)
        disp.show(5, 35)
    if past > 800:
        start = time.ticks_ms()
# Display image
with open('Screen1.bin', "wb") as f:
    f.write(disp.buffer)
    
disp.fill(0)
disp.show()
sleep(1000)
disp.draw_bin('Screen1.bin')
disp.show()
# disp.clear()
カテゴリー: Micro:bit, お知らせ パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です