Merge pull request #9 from ladyada/master
Big refactor to match arduino logic separation
This commit is contained in:
commit
c974cecf36
9 changed files with 842 additions and 322 deletions
|
|
@ -27,120 +27,285 @@ CircuitPython driver for Adafruit ePaper display breakouts
|
|||
"""
|
||||
|
||||
import time
|
||||
import digitalio
|
||||
from micropython import const
|
||||
from digitalio import Direction
|
||||
from adafruit_epd import mcp_sram
|
||||
|
||||
class Adafruit_EPD:
|
||||
class Adafruit_EPD: # pylint: disable=too-many-instance-attributes, too-many-public-methods
|
||||
"""Base class for EPD displays
|
||||
"""
|
||||
BLACK = 0
|
||||
WHITE = 1
|
||||
INVERSE = 2
|
||||
RED = 3
|
||||
DARK = 4
|
||||
LIGHT = 5
|
||||
BLACK = const(0)
|
||||
WHITE = const(1)
|
||||
INVERSE = const(2)
|
||||
RED = const(3)
|
||||
DARK = const(4)
|
||||
LIGHT = const(5)
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, width, height, rst_pin, dc_pin, busy_pin, srcs_pin, cs_pin, spi):
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
# Setup reset pin.
|
||||
def __init__(self, width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin): # pylint: disable=too-many-arguments
|
||||
self._width = width
|
||||
self._height = height
|
||||
|
||||
# Setup reset pin, if we have one
|
||||
self._rst = rst_pin
|
||||
self._rst.direction = digitalio.Direction.OUTPUT
|
||||
if rst_pin:
|
||||
self._rst.direction = Direction.OUTPUT
|
||||
|
||||
# Setup busy pin.
|
||||
# Setup busy pin, if we have one
|
||||
self._busy = busy_pin
|
||||
self._busy.direction = digitalio.Direction.INPUT
|
||||
if busy_pin:
|
||||
self._busy.direction = Direction.INPUT
|
||||
|
||||
# Setup dc pin.
|
||||
# Setup dc pin (required)
|
||||
self._dc = dc_pin
|
||||
self._dc.direction = digitalio.Direction.OUTPUT
|
||||
|
||||
# Setup cs pin.
|
||||
self._cs = cs_pin
|
||||
self._cs.direction = digitalio.Direction.OUTPUT
|
||||
|
||||
self.spi_device = spi
|
||||
|
||||
self.sram = mcp_sram.Adafruit_MCP_SRAM(srcs_pin, spi)
|
||||
# pylint: enable=too-many-arguments
|
||||
|
||||
def begin(self, reset=True):
|
||||
"""Begin display and reset if desired."""
|
||||
self._cs.value = True
|
||||
self._dc.direction = Direction.OUTPUT
|
||||
self._dc.value = False
|
||||
|
||||
if reset:
|
||||
# Setup cs pin (required)
|
||||
self._cs = cs_pin
|
||||
self._cs.direction = Direction.OUTPUT
|
||||
self._cs.value = True
|
||||
|
||||
# SPI interface (required)
|
||||
self.spi_device = spi
|
||||
self._spibuf = bytearray(1)
|
||||
self._single_byte_tx = False
|
||||
|
||||
self.sram = None
|
||||
if sramcs_pin:
|
||||
self.sram = mcp_sram.Adafruit_MCP_SRAM(sramcs_pin, spi)
|
||||
|
||||
self._buf = bytearray(3)
|
||||
self._buffer1_size = self._buffer2_size = 0
|
||||
self._buffer1 = self._buffer2 = None
|
||||
self._framebuf1 = self._framebuf2 = None
|
||||
self._colorframebuf = self._blackframebuf = None
|
||||
self._black_inverted = self._color_inverted = True
|
||||
self.hardware_reset()
|
||||
|
||||
def display(self): # pylint: disable=too-many-branches
|
||||
"""show the contents of the display buffer"""
|
||||
self.power_up()
|
||||
|
||||
self.set_ram_address(0, 0)
|
||||
|
||||
if self.sram:
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self.sram.cs_pin.value = False
|
||||
#send read command
|
||||
self._buf[0] = mcp_sram.Adafruit_MCP_SRAM.SRAM_READ
|
||||
#send start address
|
||||
self._buf[1] = 0
|
||||
self._buf[2] = 0
|
||||
self.spi_device.write(self._buf, end=3)
|
||||
self.spi_device.unlock()
|
||||
|
||||
#first data byte from SRAM will be transfered in at the
|
||||
#same time as the EPD command is transferred out
|
||||
databyte = self.write_ram(0)
|
||||
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self._dc.value = True
|
||||
|
||||
if self.sram:
|
||||
for _ in range(self._buffer1_size):
|
||||
databyte = self._spi_transfer(databyte)
|
||||
self.sram.cs_pin.value = True
|
||||
else:
|
||||
for databyte in self._buffer1:
|
||||
self._spi_transfer(databyte)
|
||||
|
||||
self._cs.value = True
|
||||
self.spi_device.unlock()
|
||||
time.sleep(.002)
|
||||
|
||||
if self.sram:
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self.sram.cs_pin.value = False
|
||||
#send read command
|
||||
self._buf[0] = mcp_sram.Adafruit_MCP_SRAM.SRAM_READ
|
||||
#send start address
|
||||
self._buf[1] = self._buffer1_size >> 8
|
||||
self._buf[2] = self._buffer1_size
|
||||
self.spi_device.write(self._buf, end=3)
|
||||
self.spi_device.unlock()
|
||||
|
||||
#first data byte from SRAM will be transfered in at the
|
||||
#same time as the EPD command is transferred out
|
||||
databyte = self.write_ram(1)
|
||||
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self._dc.value = True
|
||||
|
||||
if self.sram:
|
||||
for _ in range(self._buffer2_size):
|
||||
databyte = self._spi_transfer(databyte)
|
||||
self.sram.cs_pin.value = True
|
||||
else:
|
||||
for databyte in self._buffer2:
|
||||
self._spi_transfer(databyte)
|
||||
|
||||
self._cs.value = True
|
||||
self.spi_device.unlock()
|
||||
self.update()
|
||||
|
||||
|
||||
def hardware_reset(self):
|
||||
"""If we have a reset pin, do a hardware reset by toggling it"""
|
||||
if self._rst:
|
||||
self._rst.value = False
|
||||
time.sleep(.1)
|
||||
time.sleep(0.1)
|
||||
self._rst.value = True
|
||||
time.sleep(.1)
|
||||
time.sleep(0.1)
|
||||
|
||||
def command(self, cmd, data=None, end=True):
|
||||
"""Send command byte to display."""
|
||||
self._cs.value = True
|
||||
self._dc.value = False
|
||||
self._cs.value = False
|
||||
outbuf = bytearray(1)
|
||||
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self.spi_device.write_readinto(bytearray([cmd]), outbuf)
|
||||
ret = self._spi_transfer(cmd)
|
||||
|
||||
if data is not None:
|
||||
self.data(data)
|
||||
else:
|
||||
self.spi_device.unlock()
|
||||
|
||||
self._dc.value = True
|
||||
for b in data:
|
||||
self._spi_transfer(b)
|
||||
if end:
|
||||
self._cs.value = True
|
||||
|
||||
return outbuf[0]
|
||||
|
||||
def data(self, dat):
|
||||
"""Send data to display."""
|
||||
self._dc.value = True
|
||||
self.spi_device.write(dat)
|
||||
self._cs.value = True
|
||||
self.spi_device.unlock()
|
||||
|
||||
def draw_pixel(self, x, y, color):
|
||||
"""This should be overridden in the subclass"""
|
||||
pass
|
||||
return ret
|
||||
|
||||
def _spi_transfer(self, databyte):
|
||||
"""Transfer one byte, toggling the cs pin if required by the EPD chipset"""
|
||||
self._spibuf[0] = databyte
|
||||
if self._single_byte_tx:
|
||||
self._cs.value = False
|
||||
self.spi_device.write_readinto(self._spibuf, self._spibuf)
|
||||
if self._single_byte_tx:
|
||||
self._cs.value = True
|
||||
return self._spibuf[0]
|
||||
|
||||
def power_up(self):
|
||||
"""Power up the display in preparation for writing RAM and updating.
|
||||
must be implemented in subclass"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def power_down(self):
|
||||
"""Power down the display, must be implemented in subclass"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def update(self):
|
||||
"""Update the display from internal memory, must be implemented in subclass"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def write_ram(self, index):
|
||||
"""Send the one byte command for starting the RAM write process. Returns
|
||||
the byte read at the same time over SPI. index is the RAM buffer, can be
|
||||
0 or 1 for tri-color displays. must be implemented in subclass"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def set_ram_address(self, x, y):
|
||||
"""Set the RAM address location, must be implemented in subclass"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def set_black_buffer(self, index, inverted):
|
||||
"""Set the index for the black buffer data (0 or 1) and whether its inverted"""
|
||||
if index == 0:
|
||||
self._blackframebuf = self._framebuf1
|
||||
elif index == 1:
|
||||
self._blackframebuf = self._framebuf2
|
||||
else:
|
||||
raise RuntimeError("Buffer index must be 0 or 1")
|
||||
self._black_inverted = inverted
|
||||
|
||||
def set_color_buffer(self, index, inverted):
|
||||
"""Set the index for the color buffer data (0 or 1) and whether its inverted"""
|
||||
if index == 0:
|
||||
self._colorframebuf = self._framebuf1
|
||||
elif index == 1:
|
||||
self._colorframebuf = self._framebuf2
|
||||
else:
|
||||
raise RuntimeError("Buffer index must be 0 or 1")
|
||||
self._color_inverted = inverted
|
||||
|
||||
def _color_dup(self, func, args, color):
|
||||
black = getattr(self._blackframebuf, func)
|
||||
red = getattr(self._colorframebuf, func)
|
||||
if self._blackframebuf is self._colorframebuf: # monochrome
|
||||
black(*args, color=(color != Adafruit_EPD.WHITE) != self._black_inverted)
|
||||
else:
|
||||
black(*args, color=(color == Adafruit_EPD.BLACK) != self._black_inverted)
|
||||
red(*args, color=(color == Adafruit_EPD.RED) != self._color_inverted)
|
||||
|
||||
def pixel(self, x, y, color):
|
||||
"""draw a single pixel in the display buffer"""
|
||||
self._color_dup('pixel', (x, y), color)
|
||||
|
||||
#framebuf methods
|
||||
def fill(self, color):
|
||||
"""fill the screen with the passed color"""
|
||||
self.fill_rect(0, 0, self.width, self.height, color)
|
||||
red_fill = ((color == Adafruit_EPD.RED) != self._color_inverted) * 0xFF
|
||||
black_fill = ((color == Adafruit_EPD.BLACK) != self._black_inverted) * 0xFF
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def fill_rect(self, x, y, width, height, color):
|
||||
if self.sram:
|
||||
self.sram.erase(0x00, self._buffer1_size, black_fill)
|
||||
self.sram.erase(self._buffer1_size, self._buffer2_size, red_fill)
|
||||
else:
|
||||
self._blackframebuf.fill(black_fill)
|
||||
self._colorframebuf.fill(red_fill)
|
||||
|
||||
def rect(self, x, y, width, height, color): # pylint: disable=too-many-arguments
|
||||
"""draw a rectangle"""
|
||||
self._color_dup('rect', (x, y, width, height), color)
|
||||
|
||||
def fill_rect(self, x, y, width, height, color): # pylint: disable=too-many-arguments
|
||||
"""fill a rectangle with the passed color"""
|
||||
if width < 1 or height < 1 or (x+width) <= 0:
|
||||
return
|
||||
if (y+height) <= 0 or y >= self.height or x >= self.width:
|
||||
return
|
||||
xend = min(self.width, x+width)
|
||||
yend = min(self.height, y+height)
|
||||
x = max(x, 0)
|
||||
y = max(y, 0)
|
||||
for _x in range(xend - x):
|
||||
for _y in range(yend - y):
|
||||
self.draw_pixel(x + _x, y + _y, color)
|
||||
return
|
||||
self._color_dup('fill_rect', (x, y, width, height), color)
|
||||
|
||||
def pixel(self, x, y, color=None):
|
||||
"""draw a pixel"""
|
||||
if x < 0 or x >= self.width or y < 0 or y >= self.height:
|
||||
return None
|
||||
#TODO: figure this out when we know what framebuffer we
|
||||
# will actually use
|
||||
#if color is None:
|
||||
# return self.get_pixel(self, x, y)
|
||||
def line(self, x_0, y_0, x_1, y_1, color): # pylint: disable=too-many-arguments
|
||||
"""Draw a line from (x_0, y_0) to (x_1, y_1) in passed color"""
|
||||
self._color_dup('line', (x_0, y_0, x_1, y_1), color)
|
||||
|
||||
self.draw_pixel(x, y, color)
|
||||
return None
|
||||
def text(self, string, x, y, color, *, font_name="font5x8.bin"):
|
||||
"""Write text string at location (x, y) in given color, using font file"""
|
||||
if self._blackframebuf is self._colorframebuf: # monochrome
|
||||
self._blackframebuf.text(string, x, y, font_name=font_name,
|
||||
color=(color != Adafruit_EPD.WHITE) != self._black_inverted)
|
||||
else:
|
||||
self._blackframebuf.text(string, x, y, font_name=font_name,
|
||||
color=(color == Adafruit_EPD.BLACK) != self._black_inverted)
|
||||
self._colorframebuf.text(string, x, y, font_name=font_name,
|
||||
color=(color == Adafruit_EPD.RED) != self._color_inverted)
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
"""The width of the display, accounting for rotation"""
|
||||
if self.rotation in (0, 2):
|
||||
return self._width
|
||||
return self._height
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
"""The height of the display, accounting for rotation"""
|
||||
if self.rotation in (0, 2):
|
||||
return self._height
|
||||
return self._width
|
||||
|
||||
@property
|
||||
def rotation(self):
|
||||
"""The rotation of the display, can be one of (0, 1, 2, 3)"""
|
||||
return self._framebuf1.rotation
|
||||
|
||||
@rotation.setter
|
||||
def rotation(self, val):
|
||||
self._framebuf1.rotation = val
|
||||
self._framebuf2.rotation = val
|
||||
|
||||
def hline(self, x, y, width, color):
|
||||
"""draw a horizontal line"""
|
||||
|
|
@ -150,9 +315,35 @@ class Adafruit_EPD:
|
|||
"""draw a vertical line"""
|
||||
self.fill_rect(x, y, 1, height, color)
|
||||
|
||||
def rect(self, x, y, width, height, color):
|
||||
"""draw a rectangle"""
|
||||
self.fill_rect(x, y, width, 1, color)
|
||||
self.fill_rect(x, y+height, width, 1, color)
|
||||
self.fill_rect(x, y, 1, height, color)
|
||||
self.fill_rect(x+width, y, 1, height, color)
|
||||
|
||||
def image(self, image):
|
||||
"""Set buffer to value of Python Imaging Library image. The image should
|
||||
be in RGB mode and a size equal to the display size.
|
||||
"""
|
||||
if image.mode != 'RGB':
|
||||
raise ValueError('Image must be in mode RGB.')
|
||||
imwidth, imheight = image.size
|
||||
if imwidth != self.width or imheight != self.height:
|
||||
raise ValueError('Image must be same dimensions as display ({0}x{1}).' \
|
||||
.format(self.width, self.height))
|
||||
# Grab all the pixels from the image, faster than getpixel.
|
||||
pix = image.load()
|
||||
|
||||
for y in iter(range(image.size[1])):
|
||||
for x in iter(range(image.size[0])):
|
||||
if x == 0:
|
||||
x = 1
|
||||
pixel = pix[x, y]
|
||||
|
||||
addr = int(((self._width - x) * self._height + y)/8)
|
||||
|
||||
if pixel == (0xFF, 0, 0):
|
||||
addr = addr + self._buffer1_size
|
||||
current = self.sram.read8(addr)
|
||||
|
||||
if pixel in ((0xFF, 0, 0), (0, 0, 0)):
|
||||
current = current & ~(1 << (7 - y%8))
|
||||
else:
|
||||
current = current | (1 << (7 - y%8))
|
||||
|
||||
self.sram.write8(addr, current)
|
||||
|
|
|
|||
|
|
@ -28,205 +28,122 @@ CircuitPython driver for Adafruit il0373 display breakouts
|
|||
|
||||
import time
|
||||
from micropython import const
|
||||
import adafruit_framebuf
|
||||
from adafruit_epd.epd import Adafruit_EPD
|
||||
from adafruit_epd.mcp_sram import Adafruit_MCP_SRAM
|
||||
|
||||
IL0373_PANEL_SETTING = const(0x00)
|
||||
IL0373_POWER_SETTING = const(0x01)
|
||||
IL0373_POWER_OFF = const(0x02)
|
||||
IL0373_POWER_OFF_SEQUENCE = const(0x03)
|
||||
IL0373_POWER_ON = const(0x04)
|
||||
IL0373_POWER_ON_MEASURE = const(0x05)
|
||||
IL0373_BOOSTER_SOFT_START = const(0x06)
|
||||
IL0373_DEEP_SLEEP = const(0x07)
|
||||
IL0373_DTM1 = const(0x10)
|
||||
IL0373_DATA_STOP = const(0x11)
|
||||
IL0373_DISPLAY_REFRESH = const(0x12)
|
||||
IL0373_DTM2 = const(0x13)
|
||||
IL0373_PDTM1 = const(0x14)
|
||||
IL0373_PDTM2 = const(0x15)
|
||||
IL0373_PDRF = const(0x16)
|
||||
IL0373_LUT1 = const(0x20)
|
||||
IL0373_LUTWW = const(0x21)
|
||||
IL0373_LUTBW = const(0x22)
|
||||
IL0373_LUTWB = const(0x23)
|
||||
IL0373_LUTBB = const(0x24)
|
||||
IL0373_PLL = const(0x30)
|
||||
IL0373_CDI = const(0x50)
|
||||
IL0373_RESOLUTION = const(0x61)
|
||||
IL0373_VCM_DC_SETTING = const(0x82)
|
||||
_IL0373_PANEL_SETTING = const(0x00)
|
||||
_IL0373_POWER_SETTING = const(0x01)
|
||||
_IL0373_POWER_OFF = const(0x02)
|
||||
_IL0373_POWER_OFF_SEQUENCE = const(0x03)
|
||||
_IL0373_POWER_ON = const(0x04)
|
||||
_IL0373_POWER_ON_MEASURE = const(0x05)
|
||||
_IL0373_BOOSTER_SOFT_START = const(0x06)
|
||||
_IL0373_DEEP_SLEEP = const(0x07)
|
||||
_IL0373_DTM1 = const(0x10)
|
||||
_IL0373_DATA_STOP = const(0x11)
|
||||
_IL0373_DISPLAY_REFRESH = const(0x12)
|
||||
_IL0373_DTM2 = const(0x13)
|
||||
_IL0373_PDTM1 = const(0x14)
|
||||
_IL0373_PDTM2 = const(0x15)
|
||||
_IL0373_PDRF = const(0x16)
|
||||
_IL0373_LUT1 = const(0x20)
|
||||
_IL0373_LUTWW = const(0x21)
|
||||
_IL0373_LUTBW = const(0x22)
|
||||
_IL0373_LUTWB = const(0x23)
|
||||
_IL0373_LUTBB = const(0x24)
|
||||
_IL0373_PLL = const(0x30)
|
||||
_IL0373_CDI = const(0x50)
|
||||
_IL0373_RESOLUTION = const(0x61)
|
||||
_IL0373_VCM_DC_SETTING = const(0x82)
|
||||
|
||||
class Adafruit_IL0373(Adafruit_EPD):
|
||||
"""driver class for Adafruit IL0373 ePaper display breakouts"""
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, width, height, rst_pin, dc_pin, busy_pin, srcs_pin, cs_pin, spi):
|
||||
super(Adafruit_IL0373, self).__init__(width, height, rst_pin, dc_pin, busy_pin,
|
||||
srcs_pin, cs_pin, spi)
|
||||
def __init__(self, width, height, spi, *, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin):
|
||||
super(Adafruit_IL0373, self).__init__(width, height, spi, cs_pin, dc_pin,
|
||||
sramcs_pin, rst_pin, busy_pin)
|
||||
|
||||
self.bw_bufsize = int(width * height / 8)
|
||||
self.red_bufsize = int(width * height / 8)
|
||||
self._buffer1_size = int(width * height / 8)
|
||||
self._buffer2_size = int(width * height / 8)
|
||||
|
||||
self.begin()
|
||||
if sramcs_pin:
|
||||
self._buffer1 = self.sram.get_view(0)
|
||||
self._buffer2 = self.sram.get_view(self._buffer1_size)
|
||||
else:
|
||||
self._buffer1 = bytearray((width * height) // 8)
|
||||
self._buffer2 = bytearray((width * height) // 8)
|
||||
# since we have *two* framebuffers - one for red and one for black
|
||||
# we dont subclass but manage manually
|
||||
self._framebuf1 = adafruit_framebuf.FrameBuffer(self._buffer1, width, height,
|
||||
buf_format=adafruit_framebuf.MHMSB)
|
||||
self._framebuf2 = adafruit_framebuf.FrameBuffer(self._buffer2, width, height,
|
||||
buf_format=adafruit_framebuf.MHMSB)
|
||||
self.set_black_buffer(0, True)
|
||||
self.set_color_buffer(1, True)
|
||||
# pylint: enable=too-many-arguments
|
||||
|
||||
def begin(self, reset=True):
|
||||
"""Begin communication with the display and set basic settings"""
|
||||
super(Adafruit_IL0373, self).begin(reset)
|
||||
if reset:
|
||||
self.hardware_reset()
|
||||
self.power_down()
|
||||
|
||||
while self._busy.value is False:
|
||||
pass
|
||||
|
||||
self.command(IL0373_POWER_SETTING, bytearray([0x03, 0x00, 0x2b, 0x2b, 0x09]))
|
||||
self.command(IL0373_BOOSTER_SOFT_START, bytearray([0x17, 0x17, 0x17]))
|
||||
|
||||
def update(self):
|
||||
"""update the display"""
|
||||
self.command(IL0373_DISPLAY_REFRESH)
|
||||
|
||||
while self._busy.value is False:
|
||||
pass
|
||||
|
||||
self.command(IL0373_CDI, bytearray([0x17]))
|
||||
self.command(IL0373_VCM_DC_SETTING, bytearray([0x00]))
|
||||
self.command(IL0373_POWER_OFF)
|
||||
time.sleep(2)
|
||||
def busy_wait(self):
|
||||
"""Wait for display to be done with current task, either by polling the
|
||||
busy pin, or pausing"""
|
||||
if self._busy:
|
||||
while not self._busy.value:
|
||||
pass
|
||||
else:
|
||||
time.sleep(0.5)
|
||||
|
||||
def power_up(self):
|
||||
"""power up the display"""
|
||||
self.command(IL0373_POWER_ON)
|
||||
"""Power up the display in preparation for writing RAM and updating"""
|
||||
self.hardware_reset()
|
||||
self.busy_wait()
|
||||
|
||||
while self._busy.value is False:
|
||||
pass
|
||||
self.command(_IL0373_POWER_SETTING, bytearray([0x03, 0x00, 0x2b, 0x2b, 0x09]))
|
||||
self.command(_IL0373_BOOSTER_SOFT_START, bytearray([0x17, 0x17, 0x17]))
|
||||
self.command(_IL0373_POWER_ON)
|
||||
|
||||
time.sleep(.2)
|
||||
self.busy_wait()
|
||||
time.sleep(0.2)
|
||||
|
||||
self.command(IL0373_PANEL_SETTING, bytearray([0xCF]))
|
||||
self.command(IL0373_CDI, bytearray([0x37]))
|
||||
self.command(IL0373_PLL, bytearray([0x29]))
|
||||
_b1 = self.height & 0xFF
|
||||
_b2 = (self.height >> 8) & 0xFF
|
||||
_b3 = self.width & 0xFF
|
||||
_b4 = (self.width >> 8) & 0xFF
|
||||
self.command(IL0373_RESOLUTION, bytearray([_b1, _b2, _b3, _b4]))
|
||||
self.command(IL0373_VCM_DC_SETTING, bytearray([0x0A]))
|
||||
self.command(_IL0373_PANEL_SETTING, bytearray([0xCF]))
|
||||
self.command(_IL0373_CDI, bytearray([0x37]))
|
||||
self.command(_IL0373_PLL, bytearray([0x29]))
|
||||
_b1 = self._width & 0xFF
|
||||
_b2 = (self._height >> 8) & 0xFF
|
||||
_b3 = self._height & 0xFF
|
||||
self.command(_IL0373_RESOLUTION, bytearray([_b1, _b2, _b3]))
|
||||
self.command(_IL0373_VCM_DC_SETTING, bytearray([0x0A]))
|
||||
time.sleep(0.05)
|
||||
|
||||
def power_down(self):
|
||||
"""Power down the display - required when not actively displaying!"""
|
||||
self.command(_IL0373_CDI, bytearray([0x17]))
|
||||
self.command(_IL0373_VCM_DC_SETTING, bytearray([0x00]))
|
||||
self.command(_IL0373_POWER_OFF)
|
||||
|
||||
def display(self):
|
||||
"""show the contents of the display buffer"""
|
||||
self.power_up()
|
||||
def update(self):
|
||||
"""Update the display from internal memory"""
|
||||
self.command(_IL0373_DISPLAY_REFRESH)
|
||||
time.sleep(0.1)
|
||||
self.busy_wait()
|
||||
if not self._busy:
|
||||
time.sleep(15) # wait 15 seconds
|
||||
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self.sram.cs_pin.value = False
|
||||
#send read command
|
||||
self.spi_device.write(bytearray([Adafruit_MCP_SRAM.SRAM_READ]))
|
||||
#send start address
|
||||
self.spi_device.write(bytearray([0x00, 0x00]))
|
||||
self.spi_device.unlock()
|
||||
def write_ram(self, index):
|
||||
"""Send the one byte command for starting the RAM write process. Returns
|
||||
the byte read at the same time over SPI. index is the RAM buffer, can be
|
||||
0 or 1 for tri-color displays."""
|
||||
if index == 0:
|
||||
return self.command(_IL0373_DTM1, end=False)
|
||||
if index == 1:
|
||||
return self.command(_IL0373_DTM2, end=False)
|
||||
raise RuntimeError("RAM index must be 0 or 1")
|
||||
|
||||
#first data byte from SRAM will be transfered in at the
|
||||
#same time as the EPD command is transferred out
|
||||
cmd = self.command(IL0373_DTM1, end=False)
|
||||
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self._dc.value = True
|
||||
xfer = bytearray([cmd])
|
||||
outbuf = bytearray(1)
|
||||
for _ in range(self.bw_bufsize):
|
||||
outbuf[0] = xfer[0]
|
||||
self.spi_device.write_readinto(outbuf, xfer)
|
||||
self._cs.value = True
|
||||
self.sram.cs_pin.value = True
|
||||
|
||||
time.sleep(.002)
|
||||
|
||||
self.sram.cs_pin.value = False
|
||||
#send read command
|
||||
self.spi_device.write(bytearray([Adafruit_MCP_SRAM.SRAM_READ]))
|
||||
#send start address
|
||||
self.spi_device.write(bytearray([(self.bw_bufsize >> 8), (self.bw_bufsize & 0xFF)]))
|
||||
self.spi_device.unlock()
|
||||
|
||||
#first data byte from SRAM will be transfered in at the
|
||||
#same time as the EPD command is transferred out
|
||||
cmd = self.command(IL0373_DTM2, end=False)
|
||||
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self._dc.value = True
|
||||
xfer = bytearray([cmd])
|
||||
outbuf = bytearray(1)
|
||||
for _ in range(self.bw_bufsize):
|
||||
outbuf[0] = xfer[0]
|
||||
self.spi_device.write_readinto(outbuf, xfer)
|
||||
self._cs.value = True
|
||||
self.sram.cs_pin.value = True
|
||||
self.spi_device.unlock()
|
||||
|
||||
self.update()
|
||||
|
||||
def image(self, image):
|
||||
"""Set buffer to value of Python Imaging Library image. The image should
|
||||
be in RGB mode and a size equal to the display size.
|
||||
"""
|
||||
if image.mode != 'RGB':
|
||||
raise ValueError('Image must be in mode RGB.')
|
||||
imwidth, imheight = image.size
|
||||
if imwidth != self.width or imheight != self.height:
|
||||
raise ValueError('Image must be same dimensions as display ({0}x{1}).' \
|
||||
.format(self.width, self.height))
|
||||
# Grab all the pixels from the image, faster than getpixel.
|
||||
pix = image.load()
|
||||
|
||||
for y in iter(range(image.size[1])):
|
||||
for x in iter(range(image.size[0])):
|
||||
if x == 0:
|
||||
x = 1
|
||||
pixel = pix[x, y]
|
||||
|
||||
addr = int(((self.width - x) * self.height + y)/8)
|
||||
|
||||
if pixel == (0xFF, 0, 0):
|
||||
addr = addr + self.bw_bufsize
|
||||
current = self.sram.read8(addr)
|
||||
|
||||
if pixel in ((0xFF, 0, 0), (0, 0, 0)):
|
||||
current = current & ~(1 << (7 - y%8))
|
||||
else:
|
||||
current = current | (1 << (7 - y%8))
|
||||
|
||||
self.sram.write8(addr, current)
|
||||
|
||||
def draw_pixel(self, x, y, color):
|
||||
"""draw a single pixel in the display buffer"""
|
||||
if (x < 0) or (x >= self.width) or (y < 0) or (y >= self.height):
|
||||
return
|
||||
|
||||
if x == 0:
|
||||
x = 1
|
||||
|
||||
addr = ((self.width - x) * self.height + y) // 8
|
||||
if color == Adafruit_EPD.RED:
|
||||
addr = addr + self.bw_bufsize
|
||||
current = self.sram.read8(addr)
|
||||
|
||||
if color == Adafruit_EPD.WHITE:
|
||||
current = current | (1 << (7 - y%8))
|
||||
elif color in (Adafruit_EPD.RED, Adafruit_EPD.BLACK):
|
||||
current = current & ~(1 << (7 - y%8))
|
||||
elif color == Adafruit_EPD.INVERSE:
|
||||
current = current ^ (1 << (7 - y%8))
|
||||
|
||||
self.sram.write8(addr, current)
|
||||
return
|
||||
|
||||
def clear_buffer(self):
|
||||
"""clear the display buffer"""
|
||||
self.sram.erase(0x00, self.bw_bufsize, 0xFF)
|
||||
self.sram.erase(self.bw_bufsize, self.red_bufsize, 0xFF)
|
||||
|
||||
def clear_display(self):
|
||||
"""clear the entire display"""
|
||||
self.clear_buffer()
|
||||
self.display()
|
||||
def set_ram_address(self, x, y): # pylint: disable=unused-argument, no-self-use
|
||||
"""Set the RAM address location, not used on this chipset but required by
|
||||
the superclass"""
|
||||
return # on this chip it does nothing
|
||||
|
|
|
|||
149
adafruit_epd/il0398.py
Normal file
149
adafruit_epd/il0398.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018 Dean Miller for Adafruit Industries
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
`adafruit_epd.il0398` - Adafruit IL0398 - ePaper display driver
|
||||
====================================================================================
|
||||
CircuitPython driver for Adafruit IL0398 display breakouts
|
||||
* Author(s): Dean Miller, ladyada
|
||||
"""
|
||||
|
||||
import time
|
||||
from micropython import const
|
||||
import adafruit_framebuf
|
||||
from adafruit_epd.epd import Adafruit_EPD
|
||||
|
||||
_IL0398_PANEL_SETTING = const(0x00)
|
||||
_IL0398_POWER_SETTING = const(0x01)
|
||||
_IL0398_POWER_OFF = const(0x02)
|
||||
_IL0398_POWER_OFF_SEQUENCE = const(0x03)
|
||||
_IL0398_POWER_ON = const(0x04)
|
||||
_IL0398_POWER_ON_MEASURE = const(0x05)
|
||||
_IL0398_BOOSTER_SOFT_START = const(0x06)
|
||||
_IL0398_DEEP_SLEEP = const(0x07)
|
||||
_IL0398_DTM1 = const(0x10)
|
||||
_IL0398_DATA_STOP = const(0x11)
|
||||
_IL0398_DISPLAY_REFRESH = const(0x12)
|
||||
_IL0398_DTM2 = const(0x13)
|
||||
_IL0398_PDTM1 = const(0x14)
|
||||
_IL0398_PDTM2 = const(0x15)
|
||||
_IL0398_PDRF = const(0x16)
|
||||
_IL0398_LUT1 = const(0x20)
|
||||
_IL0398_LUTWW = const(0x21)
|
||||
_IL0398_LUTBW = const(0x22)
|
||||
_IL0398_LUTWB = const(0x23)
|
||||
_IL0398_LUTBB = const(0x24)
|
||||
_IL0398_PLL = const(0x30)
|
||||
_IL0398_CDI = const(0x50)
|
||||
_IL0398_RESOLUTION = const(0x61)
|
||||
_IL0398_GETSTATUS = const(0x71)
|
||||
_IL0398_VCM_DC_SETTING = const(0x82)
|
||||
|
||||
class Adafruit_IL0398(Adafruit_EPD):
|
||||
"""driver class for Adafruit IL0373 ePaper display breakouts"""
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, width, height, spi, *, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin):
|
||||
super(Adafruit_IL0398, self).__init__(width, height, spi, cs_pin, dc_pin,
|
||||
sramcs_pin, rst_pin, busy_pin)
|
||||
|
||||
self._buffer1_size = int(width * height / 8)
|
||||
self._buffer2_size = int(width * height / 8)
|
||||
|
||||
if sramcs_pin:
|
||||
self._buffer1 = self.sram.get_view(0)
|
||||
self._buffer2 = self.sram.get_view(self._buffer1_size)
|
||||
else:
|
||||
self._buffer1 = bytearray((width * height) // 8)
|
||||
self._buffer2 = bytearray((width * height) // 8)
|
||||
# since we have *two* framebuffers - one for red and one for black
|
||||
# we dont subclass but manage manually
|
||||
self._framebuf1 = adafruit_framebuf.FrameBuffer(self._buffer1, width, height,
|
||||
buf_format=adafruit_framebuf.MHMSB)
|
||||
self._framebuf2 = adafruit_framebuf.FrameBuffer(self._buffer2, width, height,
|
||||
buf_format=adafruit_framebuf.MHMSB)
|
||||
self.set_black_buffer(0, True)
|
||||
self.set_color_buffer(1, True)
|
||||
# pylint: enable=too-many-arguments
|
||||
|
||||
def begin(self, reset=True):
|
||||
"""Begin communication with the display and set basic settings"""
|
||||
if reset:
|
||||
self.hardware_reset()
|
||||
self.power_down()
|
||||
|
||||
def busy_wait(self):
|
||||
"""Wait for display to be done with current task, either by polling the
|
||||
busy pin, or pausing"""
|
||||
if self._busy:
|
||||
while not self._busy.value:
|
||||
#self.command(_IL0398_GETSTATUS)
|
||||
time.sleep(0.01)
|
||||
else:
|
||||
time.sleep(0.5)
|
||||
|
||||
def power_up(self):
|
||||
"""Power up the display in preparation for writing RAM and updating"""
|
||||
self.hardware_reset()
|
||||
self.busy_wait()
|
||||
|
||||
self.command(_IL0398_BOOSTER_SOFT_START, bytearray([0x17, 0x17, 0x17]))
|
||||
self.command(_IL0398_POWER_ON)
|
||||
|
||||
self.busy_wait()
|
||||
time.sleep(0.2)
|
||||
|
||||
self.command(_IL0398_PANEL_SETTING, bytearray([0x0F]))
|
||||
_b0 = (self._width >> 8) & 0xFF
|
||||
_b1 = self._width & 0xFF
|
||||
_b2 = (self._height >> 8) & 0xFF
|
||||
_b3 = self._height & 0xFF
|
||||
self.command(_IL0398_RESOLUTION, bytearray([_b0, _b1, _b2, _b3]))
|
||||
time.sleep(0.05)
|
||||
|
||||
def power_down(self):
|
||||
"""Power down the display - required when not actively displaying!"""
|
||||
self.command(_IL0398_CDI, bytearray([0xF7]))
|
||||
self.command(_IL0398_POWER_OFF)
|
||||
self.busy_wait()
|
||||
self.command(_IL0398_DEEP_SLEEP, bytearray([0xA5]))
|
||||
|
||||
def update(self):
|
||||
"""Update the display from internal memory"""
|
||||
self.command(_IL0398_DISPLAY_REFRESH)
|
||||
time.sleep(0.1)
|
||||
self.busy_wait()
|
||||
if not self._busy:
|
||||
time.sleep(15) # wait 15 seconds
|
||||
|
||||
def write_ram(self, index):
|
||||
"""Send the one byte command for starting the RAM write process. Returns
|
||||
the byte read at the same time over SPI. index is the RAM buffer, can be
|
||||
0 or 1 for tri-color displays."""
|
||||
if index == 0:
|
||||
return self.command(_IL0398_DTM1, end=False)
|
||||
if index == 1:
|
||||
return self.command(_IL0398_DTM2, end=False)
|
||||
raise RuntimeError("RAM index must be 0 or 1")
|
||||
|
||||
def set_ram_address(self, x, y): # pylint: disable=unused-argument, no-self-use
|
||||
"""Set the RAM address location, not used on this chipset but required by
|
||||
the superclass"""
|
||||
return # on this chip it does nothing
|
||||
172
adafruit_epd/il91874.py
Normal file
172
adafruit_epd/il91874.py
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018 Dean Miller for Adafruit Industries
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
`adafruit_epd.il91874` - Adafruit IL91874 - ePaper display driver
|
||||
====================================================================================
|
||||
CircuitPython driver for Adafruit IL91874 display breakouts
|
||||
* Author(s): Dean Miller, Ladyada
|
||||
"""
|
||||
|
||||
import time
|
||||
from micropython import const
|
||||
import adafruit_framebuf
|
||||
from adafruit_epd.epd import Adafruit_EPD
|
||||
|
||||
_IL91874_PANEL_SETTING = const(0x00)
|
||||
_IL91874_POWER_SETTING = const(0x01)
|
||||
_IL91874_POWER_OFF = const(0x02)
|
||||
_IL91874_POWER_OFF_SEQUENCE = const(0x03)
|
||||
_IL91874_POWER_ON = const(0x04)
|
||||
_IL91874_POWER_ON_MEASURE = const(0x05)
|
||||
_IL91874_BOOSTER_SOFT_START = const(0x06)
|
||||
_IL91874_DEEP_SLEEP = const(0x07)
|
||||
_IL91874_DTM1 = const(0x10)
|
||||
_IL91874_DATA_STOP = const(0x11)
|
||||
_IL91874_DISPLAY_REFRESH = const(0x12)
|
||||
_IL91874_DTM2 = const(0x13)
|
||||
_IL91874_PDTM1 = const(0x14)
|
||||
_IL91874_PDTM2 = const(0x15)
|
||||
_IL91874_PDRF = const(0x16)
|
||||
_IL91874_LUT1 = const(0x20)
|
||||
_IL91874_LUTWW = const(0x21)
|
||||
_IL91874_LUTBW = const(0x22)
|
||||
_IL91874_LUTWB = const(0x23)
|
||||
_IL91874_LUTBB = const(0x24)
|
||||
_IL91874_PLL = const(0x30)
|
||||
_IL91874_CDI = const(0x50)
|
||||
_IL91874_RESOLUTION = const(0x61)
|
||||
_IL91874_VCM_DC_SETTING = const(0x82)
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
_LUT_VCOMDC = b'\x00\x00\x00\x1a\x1a\x00\x00\x01\x00\n\n\x00\x00\x08\x00\x0e\x01\x0e\x01\x10\x00\n\n\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0e\x00\x00\n\x00#\x00\x00\x00\x01'
|
||||
_LUT_WW = b'\x90\x1a\x1a\x00\x00\x01@\n\n\x00\x00\x08\x84\x0e\x01\x0e\x01\x10\x80\n\n\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0e\x00\x00\n\x00#\x00\x00\x00\x01'
|
||||
_LUT_BW = b'\xa0\x1a\x1a\x00\x00\x01\x00\n\n\x00\x00\x08\x84\x0e\x01\x0e\x01\x10\x90\n\n\x00\x00\x08\xb0\x04\x10\x00\x00\x05\xb0\x03\x0e\x00\x00\n\xc0#\x00\x00\x00\x01'
|
||||
_LUT_BB = b'\x90\x1a\x1a\x00\x00\x01@\n\n\x00\x00\x08\x84\x0e\x01\x0e\x01\x10\x80\n\n\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0e\x00\x00\n\x00#\x00\x00\x00\x01'
|
||||
_LUT_WB = b'\x90\x1a\x1a\x00\x00\x01 \n\n\x00\x00\x08\x84\x0e\x01\x0e\x01\x10\x10\n\n\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0e\x00\x00\n\x00#\x00\x00\x00\x01'
|
||||
# pylint: enable=line-too-long
|
||||
|
||||
class Adafruit_IL91874(Adafruit_EPD):
|
||||
"""driver class for Adafruit IL91874 ePaper display breakouts"""
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, width, height, spi, *, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin):
|
||||
super(Adafruit_IL91874, self).__init__(width, height, spi, cs_pin, dc_pin,
|
||||
sramcs_pin, rst_pin, busy_pin)
|
||||
|
||||
self._buffer1_size = int(width * height / 8)
|
||||
self._buffer2_size = int(width * height / 8)
|
||||
|
||||
if sramcs_pin:
|
||||
self._buffer1 = self.sram.get_view(0)
|
||||
self._buffer2 = self.sram.get_view(self._buffer1_size)
|
||||
else:
|
||||
self._buffer1 = bytearray((width * height) // 8)
|
||||
self._buffer2 = bytearray((width * height) // 8)
|
||||
# since we have *two* framebuffers - one for red and one for black
|
||||
# we dont subclass but manage manually
|
||||
self._framebuf1 = adafruit_framebuf.FrameBuffer(self._buffer1, width, height,
|
||||
buf_format=adafruit_framebuf.MHMSB)
|
||||
self._framebuf2 = adafruit_framebuf.FrameBuffer(self._buffer2, width, height,
|
||||
buf_format=adafruit_framebuf.MHMSB)
|
||||
self.set_black_buffer(0, True)
|
||||
self.set_color_buffer(1, False)
|
||||
self._single_byte_tx = True
|
||||
|
||||
def begin(self, reset=True):
|
||||
"""Begin communication with the display and set basic settings"""
|
||||
if reset:
|
||||
self.hardware_reset()
|
||||
|
||||
self.power_down()
|
||||
|
||||
def busy_wait(self):
|
||||
"""Wait for display to be done with current task, either by polling the
|
||||
busy pin, or pausing"""
|
||||
if self._busy:
|
||||
while not self._busy.value:
|
||||
pass
|
||||
else:
|
||||
time.sleep(0.5)
|
||||
|
||||
def power_up(self):
|
||||
"""Power up the display in preparation for writing RAM and updating"""
|
||||
self.hardware_reset()
|
||||
time.sleep(0.2)
|
||||
self.command(_IL91874_POWER_ON)
|
||||
self.busy_wait()
|
||||
|
||||
self.command(_IL91874_PANEL_SETTING, bytearray([0xAF]))
|
||||
self.command(_IL91874_PLL, bytearray([0x3A]))
|
||||
self.command(_IL91874_POWER_SETTING, bytearray([0x03, 0x00, 0x2b, 0x2b, 0x09]))
|
||||
self.command(_IL91874_BOOSTER_SOFT_START, bytearray([0x07, 0x07, 0x17]))
|
||||
|
||||
self.command(0xF8, bytearray([0x60, 0xA5])) # mystery command in example code
|
||||
self.command(0xF8, bytearray([0x89, 0xA5])) # mystery command in example code
|
||||
self.command(0xF8, bytearray([0x90, 0x00])) # mystery command in example code
|
||||
self.command(0xF8, bytearray([0x93, 0xA2])) # mystery command in example code
|
||||
self.command(0xF8, bytearray([0x73, 0x41])) # mystery command in example code
|
||||
|
||||
self.command(_IL91874_VCM_DC_SETTING, bytearray([0x12]))
|
||||
self.command(_IL91874_CDI, bytearray([0x87]))
|
||||
|
||||
# Look Up Tables
|
||||
self.command(_IL91874_LUT1, _LUT_VCOMDC)
|
||||
self.command(_IL91874_LUTWW, _LUT_WW)
|
||||
self.command(_IL91874_LUTBW, _LUT_BW)
|
||||
self.command(_IL91874_LUTWB, _LUT_WB)
|
||||
self.command(_IL91874_LUTBB, _LUT_BB)
|
||||
|
||||
_b0 = (self._width >> 8) & 0xFF
|
||||
_b1 = self._width & 0xFF
|
||||
_b2 = (self._height >> 8) & 0xFF
|
||||
_b3 = self._height & 0xFF
|
||||
self.command(_IL91874_RESOLUTION, bytearray([_b0, _b1, _b2, _b3]))
|
||||
self.command(_IL91874_PDRF, bytearray([0x00]))
|
||||
|
||||
def power_down(self):
|
||||
"""Power down the display - required when not actively displaying!"""
|
||||
self.command(_IL91874_POWER_OFF, bytearray([0x17]))
|
||||
self.busy_wait()
|
||||
|
||||
if self._rst: # Only deep sleep if we can get out of it
|
||||
self.command(_IL91874_DEEP_SLEEP, bytearray([0xA5]))
|
||||
|
||||
def update(self):
|
||||
"""Update the display from internal memory"""
|
||||
self.command(_IL91874_DISPLAY_REFRESH)
|
||||
self.busy_wait()
|
||||
if not self._busy:
|
||||
time.sleep(16) # wait 16 seconds
|
||||
|
||||
def write_ram(self, index):
|
||||
"""Send the one byte command for starting the RAM write process. Returns
|
||||
the byte read at the same time over SPI. index is the RAM buffer, can be
|
||||
0 or 1 for tri-color displays."""
|
||||
if index == 0:
|
||||
return self.command(_IL91874_DTM1, end=False)
|
||||
if index == 1:
|
||||
return self.command(_IL91874_DTM2, end=False)
|
||||
raise RuntimeError("RAM index must be 0 or 1")
|
||||
|
||||
def set_ram_address(self, x, y): # pylint: disable=unused-argument, no-self-use
|
||||
"""Set the RAM address location, not used on this chipset but required by
|
||||
the superclass"""
|
||||
return # on this chip it does nothing
|
||||
|
|
@ -27,10 +27,24 @@ CircuitPython driver for Microchip SRAM chips
|
|||
"""
|
||||
|
||||
from micropython import const
|
||||
import digitalio
|
||||
from adafruit_bus_device import spi_device
|
||||
|
||||
SRAM_SEQUENTIAL_MODE = const(1 << 6)
|
||||
|
||||
class Adafruit_MCP_SRAM_View:
|
||||
"""A interface class that turns an SRAM chip into something like a memoryview"""
|
||||
def __init__(self, sram, offset):
|
||||
self._sram = sram
|
||||
self._offset = offset
|
||||
self._buf = [0]
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self._sram.read(self._offset+i, 1)[0]
|
||||
|
||||
def __setitem__(self, i, val):
|
||||
self._buf[0] = val
|
||||
self._sram.write(self._offset+i, self._buf)
|
||||
|
||||
class Adafruit_MCP_SRAM:
|
||||
"""supporting class for communicating with
|
||||
Microchip SRAM chips"""
|
||||
|
|
@ -41,40 +55,39 @@ class Adafruit_MCP_SRAM:
|
|||
|
||||
def __init__(self, cs_pin, spi):
|
||||
# Handle hardware SPI
|
||||
self._spi = spi_device.SPIDevice(spi, cs_pin, baudrate=8000000)
|
||||
self.spi_device = spi
|
||||
self.cs_pin = cs_pin
|
||||
self._buf = bytearray(3)
|
||||
self._buf[0] = Adafruit_MCP_SRAM.SRAM_WRSR
|
||||
self._buf[1] = 0x43
|
||||
with self._spi as spidev:
|
||||
spidev.write(self._buf, end=2) # pylint: disable=no-member
|
||||
|
||||
self.cs_pin.direction = digitalio.Direction.OUTPUT
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self.cs_pin.value = False
|
||||
self.spi_device.write(bytearray([Adafruit_MCP_SRAM.SRAM_WRSR, 0x43]))
|
||||
self.cs_pin.value = True
|
||||
self.spi_device.unlock()
|
||||
def get_view(self, offset):
|
||||
"""Create an object that can be used as a memoryview, with a given offset"""
|
||||
return Adafruit_MCP_SRAM_View(self, offset)
|
||||
|
||||
def write(self, addr, buf, reg=SRAM_WRITE):
|
||||
"""write the passed buffer to the passed address"""
|
||||
cmd = bytearray([reg, (addr >> 8) & 0xFF, addr & 0xFF] + buf)
|
||||
self._buf[0] = reg
|
||||
self._buf[1] = addr >> 8
|
||||
self._buf[2] = addr
|
||||
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self.cs_pin.value = False
|
||||
self.spi_device.write(cmd)
|
||||
self.cs_pin.value = True
|
||||
self.spi_device.unlock()
|
||||
with self._spi as spi:
|
||||
spi.write(self._buf, end=3) # pylint: disable=no-member
|
||||
spi.write(bytearray(buf)) # pylint: disable=no-member
|
||||
|
||||
def read(self, addr, length, reg=SRAM_READ):
|
||||
"""read passed number of bytes at the passed address"""
|
||||
cmd = bytearray([reg, (addr >> 8) & 0xFF, addr & 0xFF])
|
||||
self._buf[0] = reg
|
||||
self._buf[1] = addr >> 8
|
||||
self._buf[2] = addr
|
||||
|
||||
buf = bytearray(length)
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self.cs_pin.value = False
|
||||
self.spi_device.write(cmd)
|
||||
self.spi_device.readinto(buf)
|
||||
self.cs_pin.value = True
|
||||
self.spi_device.unlock()
|
||||
with self._spi as spi:
|
||||
spi.write(self._buf, end=3) # pylint: disable=no-member
|
||||
spi.readinto(buf) # pylint: disable=no-member
|
||||
return buf
|
||||
|
||||
def read8(self, addr, reg=SRAM_READ):
|
||||
|
|
@ -96,13 +109,11 @@ class Adafruit_MCP_SRAM:
|
|||
|
||||
def erase(self, addr, length, value):
|
||||
"""erase the passed number of bytes starting at the passed address"""
|
||||
cmd = bytearray([Adafruit_MCP_SRAM.SRAM_WRITE, (addr >> 8) & 0xFF, addr & 0xFF])
|
||||
|
||||
while not self.spi_device.try_lock():
|
||||
pass
|
||||
self.cs_pin.value = False
|
||||
self.spi_device.write(cmd)
|
||||
for _ in range(length):
|
||||
self.spi_device.write(bytearray([value]))
|
||||
self.cs_pin.value = True
|
||||
self.spi_device.unlock()
|
||||
self._buf[0] = Adafruit_MCP_SRAM.SRAM_WRITE
|
||||
self._buf[1] = addr >> 8
|
||||
self._buf[2] = addr
|
||||
fill = bytearray([value])
|
||||
with self._spi as spi:
|
||||
spi.write(self._buf, end=3) # pylint: disable=no-member
|
||||
for _ in range(length):
|
||||
spi.write(fill) # pylint: disable=no-member
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ rst = digitalio.DigitalInOut(board.D7)
|
|||
busy = digitalio.DigitalInOut(board.D6)
|
||||
|
||||
# give them all to our driver
|
||||
display = Adafruit_IL0373(152, 152, rst, dc, busy, srcs, ecs, spi)
|
||||
display = Adafruit_IL0373(152, 152, spi,
|
||||
cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs,
|
||||
rst_pin=rst, busy_pin=busy)
|
||||
|
||||
FILENAME = "blinka.bmp"
|
||||
|
||||
# clear the buffer
|
||||
display.clear_buffer()
|
||||
display.fill(Adafruit_EPD.WHITE)
|
||||
|
||||
def read_le(s):
|
||||
# as of this writting, int.from_bytes does not have LE support, DIY!
|
||||
|
|
@ -76,11 +78,11 @@ try:
|
|||
for col in range(bmpWidth):
|
||||
b, g, r = bytearray(f.read(3)) # BMP files store RGB in BGR
|
||||
if r < 0x80 and g < 0x80 and b < 0x80:
|
||||
display.draw_pixel(row, col, Adafruit_EPD.BLACK)
|
||||
display.pixel(row, col, Adafruit_EPD.BLACK)
|
||||
elif r >= 0x80 and g >= 0x80 and b >= 0x80:
|
||||
display.draw_pixel(row, col, Adafruit_EPD.WHITE)
|
||||
display.pixel(row, col, Adafruit_EPD.WHITE)
|
||||
elif r >= 0x80:
|
||||
display.draw_pixel(row, col, Adafruit_EPD.RED)
|
||||
display.pixel(row, col, Adafruit_EPD.RED)
|
||||
|
||||
except OSError as e:
|
||||
if e.args[0] == 28:
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@ import board
|
|||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
from adafruit_epd.epd import Adafruit_EPD
|
||||
from adafruit_epd.il0373 import Adafruit_IL0373
|
||||
|
||||
# create the spi device and pins we will need
|
||||
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
|
||||
while not spi.try_lock():
|
||||
pass
|
||||
spi.configure(baudrate=16000000)
|
||||
spi.unlock()
|
||||
|
||||
ecs = digitalio.DigitalInOut(board.D22)
|
||||
dc = digitalio.DigitalInOut(board.D13)
|
||||
|
|
@ -20,8 +17,14 @@ srcs = digitalio.DigitalInOut(board.D6)
|
|||
rst = digitalio.DigitalInOut(board.D19)
|
||||
busy = digitalio.DigitalInOut(board.D26)
|
||||
|
||||
|
||||
# give them all to our driver
|
||||
display = Adafruit_IL0373(152, 152, rst, dc, busy, srcs, ecs, spi)
|
||||
print("Creating display")
|
||||
display = Adafruit_IL0373(104, 212, spi,
|
||||
cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs,
|
||||
rst_pin=rst, busy_pin=busy)
|
||||
|
||||
|
||||
# Create blank image for drawing.
|
||||
# Make sure to create image with mode '1' for 1-bit color.
|
||||
width = display.width
|
||||
|
|
@ -33,7 +36,7 @@ RED = (0xFF, 0x00, 0x00)
|
|||
BLACK = (0x00, 0x00, 0x00)
|
||||
|
||||
# clear the buffer
|
||||
display.clear_buffer()
|
||||
display.fill(Adafruit_EPD.WHITE)
|
||||
|
||||
# Get drawing object to draw on image.
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
|
|
|||
60
examples/epd_shieldtest.py
Normal file
60
examples/epd_shieldtest.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# EInk Shield test
|
||||
import time
|
||||
import digitalio
|
||||
import busio
|
||||
import board
|
||||
from analogio import AnalogIn
|
||||
from adafruit_epd.epd import Adafruit_EPD
|
||||
from adafruit_epd.il91874 import Adafruit_IL91874
|
||||
|
||||
# create the spi device and pins we will need
|
||||
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
|
||||
ecs = digitalio.DigitalInOut(board.D10)
|
||||
dc = digitalio.DigitalInOut(board.D9)
|
||||
srcs = digitalio.DigitalInOut(board.D8) # can be None to use internal memory
|
||||
|
||||
# give them all to our driver
|
||||
print("Creating display")
|
||||
display = Adafruit_IL91874(176, 264, spi, # 2.7" Tri-color display
|
||||
cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs,
|
||||
rst_pin=None, busy_pin=None)
|
||||
|
||||
display.rotation = 1
|
||||
|
||||
def read_buttons():
|
||||
with AnalogIn(board.A3) as ain:
|
||||
reading = ain.value / 65535
|
||||
if reading > 0.75:
|
||||
return None
|
||||
if reading > 0.4:
|
||||
return 4
|
||||
if reading > 0.25:
|
||||
return 3
|
||||
if reading > 0.13:
|
||||
return 2
|
||||
return 1
|
||||
|
||||
while True:
|
||||
button = read_buttons()
|
||||
if not button:
|
||||
continue
|
||||
print("Button #%d pressed" % button)
|
||||
if button == 1:
|
||||
print("Clear buffer")
|
||||
display.fill(Adafruit_EPD.WHITE)
|
||||
display.display()
|
||||
if button == 2:
|
||||
print("Draw Rectangles")
|
||||
display.fill_rect(5, 5, 10, 10, Adafruit_EPD.RED)
|
||||
display.rect(0, 0, 20, 30, Adafruit_EPD.BLACK)
|
||||
display.display()
|
||||
if button == 3:
|
||||
print("Draw lines")
|
||||
display.line(0, 0, display.width-1, display.height-1, Adafruit_EPD.BLACK)
|
||||
display.line(0, display.height-1, display.width-1, 0, Adafruit_EPD.RED)
|
||||
display.display()
|
||||
if button == 4:
|
||||
print("Draw text")
|
||||
display.text('hello world', 25, 10, Adafruit_EPD.BLACK)
|
||||
display.display()
|
||||
time.sleep(0.01)
|
||||
|
|
@ -3,31 +3,46 @@ import busio
|
|||
import board
|
||||
from adafruit_epd.epd import Adafruit_EPD
|
||||
from adafruit_epd.il0373 import Adafruit_IL0373
|
||||
from adafruit_epd.il91874 import Adafruit_IL91874 # pylint: disable=unused-import
|
||||
from adafruit_epd.il0398 import Adafruit_IL0398 # pylint: disable=unused-import
|
||||
|
||||
# create the spi device and pins we will need
|
||||
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
|
||||
ecs = digitalio.DigitalInOut(board.D10)
|
||||
dc = digitalio.DigitalInOut(board.D9)
|
||||
srcs = digitalio.DigitalInOut(board.D8)
|
||||
rst = digitalio.DigitalInOut(board.D7)
|
||||
busy = digitalio.DigitalInOut(board.D6)
|
||||
srcs = digitalio.DigitalInOut(board.D7) # can be None to use internal memory
|
||||
rst = digitalio.DigitalInOut(board.D11) # can be None to not use this pin
|
||||
busy = digitalio.DigitalInOut(board.D12) # can be None to not use this pin
|
||||
|
||||
# give them all to our driver
|
||||
display = Adafruit_IL0373(152, 152, rst, dc, busy, srcs, ecs, spi)
|
||||
print("Creating display")
|
||||
#display = Adafruit_IL91874(176, 264, spi, # 2.7" Tri-color display
|
||||
#display = Adafruit_IL0373(152, 152, spi, # 1.54" Tri-color display
|
||||
#display = Adafruit_IL0373(128, 296, spi, # 2.9" Tri-color display
|
||||
#display = Adafruit_IL0398(400, 300, spi, # 4.2" Tri-color display
|
||||
display = Adafruit_IL0373(104, 212, spi, # 2.13" Tri-color display
|
||||
cs_pin=ecs, dc_pin=dc, sramcs_pin=srcs,
|
||||
rst_pin=rst, busy_pin=busy)
|
||||
|
||||
# IF YOU HAVE A FLEXIBLE DISPLAY (2.13" or 2.9") uncomment these lines!
|
||||
#display.set_black_buffer(1, False)
|
||||
#display.set_color_buffer(1, False)
|
||||
|
||||
display.rotation = 1
|
||||
|
||||
# clear the buffer
|
||||
display.clear_buffer()
|
||||
print("Clear buffer")
|
||||
display.fill(Adafruit_EPD.WHITE)
|
||||
display.pixel(10, 100, Adafruit_EPD.BLACK)
|
||||
|
||||
r_width = 5
|
||||
r_pos = display.height
|
||||
print("Draw Rectangles")
|
||||
display.fill_rect(5, 5, 10, 10, Adafruit_EPD.RED)
|
||||
display.rect(0, 0, 20, 30, Adafruit_EPD.BLACK)
|
||||
|
||||
color = Adafruit_EPD.BLACK
|
||||
while r_pos > display.height/2:
|
||||
if r_pos < display.height - 50:
|
||||
color = Adafruit_EPD.RED
|
||||
display.rect(display.width - r_pos, display.height - r_pos,
|
||||
display.width - 2*(display.width - r_pos),
|
||||
display.height - 2*(display.height - r_pos), color)
|
||||
r_pos = r_pos - r_width
|
||||
print("Draw lines")
|
||||
display.line(0, 0, display.width-1, display.height-1, Adafruit_EPD.BLACK)
|
||||
display.line(0, display.height-1, display.width-1, 0, Adafruit_EPD.RED)
|
||||
|
||||
print("Draw text")
|
||||
display.text('hello world', 25, 10, Adafruit_EPD.BLACK)
|
||||
display.display()
|
||||
|
|
|
|||
Loading…
Reference in a new issue