Source code for luma.lcd.device

# -*- coding: utf-8 -*-
# Copyright (c) 2013-17 Richard Hull and contributors
# See LICENSE.rst for details.

"""
Collection of serial interfaces to LCD devices.
"""

# Example usage:
#
#   from luma.core.interface.serial import spi
#   from luma.core.render import canvas
#   from luma.lcd.device import pcd8544
#   from PIL import ImageDraw
#
#   serial = spi(port=0, device=0)
#   device = pcd8544(serial)
#
#   with canvas(device) as draw:
#      draw.rectangle(device.bounding_box, outline="white", fill="black")
#      draw.text(30, 40, "Hello World", fill="white")
#
# As soon as the with-block scope level is complete, the graphics primitives
# will be flushed to the device.
#
# Creating a new canvas is effectively 'carte blanche': If you want to retain
# an existing canvas, then make a reference like:
#
#    c = canvas(device)
#    for X in ...:
#        with c as draw:
#            draw.rectangle(...)
#
# As before, as soon as the with block completes, the canvas buffer is flushed
# to the device

import time
from luma.core.lib import rpi_gpio
from luma.core.device import device
from luma.core.interface.serial import noop
import luma.core.error
import luma.core.framebuffer
import luma.lcd.const
from luma.lcd.segment_mapper import dot_muncher


__all__ = ["pcd8544", "st7735", "st7920", "ht1621", "uc1701x", "st7567", "ili9341"]


class GPIOBacklight:
    """
    Helper class for controlling the LCD backlight using a digital GPIO output pin.

    :param gpio: GPIO interface (must be compatible with `RPi.GPIO <https://pypi.python.org/pypi/RPi.GPIO>`_).
    :param pin: the GPIO pin to use for the backlight.
    :type pin: int
    :param active_low: Set to True if active low (default), False otherwise.
    :type active_low: bool
    :raises luma.core.error.UnsupportedPlatform: GPIO access not available.

    .. versionadded:: 2.3.0
    """
    def __init__(self, gpio, pin=18, active_low=True):
        self._gpio = gpio
        self._pin = pin
        if active_low:
            self._enabled = self._gpio.LOW
            self._disabled = self._gpio.HIGH
        else:
            self._enabled = self._gpio.HIGH
            self._disabled = self._gpio.LOW

        try:
            self._gpio.setup(self._pin, self._gpio.OUT)
        except RuntimeError as e:
            if str(e) == 'Module not imported correctly!':
                raise luma.core.error.UnsupportedPlatform('GPIO access not available')

    def __call__(self, is_enabled):
        """
        Toggle the LCD Backlight

        :param is_enabled: Turn on or off the backlight.
        :type is_enabled: bool
        """
        assert is_enabled in (True, False)
        self._gpio.output(self._pin, self._enabled if is_enabled else self._disabled)


class PWMBacklight:
    """
    Helper class for controlling the LCD backlight using a PWM channel pin.

    :param gpio: GPIO interface (must be compatible with `RPi.GPIO <https://pypi.python.org/pypi/RPi.GPIO>`_).
    :param pin: the GPIO pin to use for the backlight.
    :type pin: int
    :param frequency: The frequency to use for the PWM channel.
    :type frequency: float
    :raises luma.core.error.UnsupportedPlatform: GPIO access not available.

    .. versionadded:: 2.3.0
    """
    def __init__(self, gpio, pin=18, frequency=362):
        self._gpio = gpio

        try:
            self._pwm = self._gpio.PWM(pin, frequency)
            self._pwm.start(0.0)
        except RuntimeError as e:
            if str(e) == 'Module not imported correctly!':
                raise luma.core.error.UnsupportedPlatform('GPIO access not available')

    def __call__(self, value):
        """
        Set the LCD Backlight

        :param value: Sets the value of the backlight.  Can provide a bool
        (True/False) to turn on/off or a float to set the backlight intensity in
        percentage (0 <= value <= 100.0).
        :type value: bool or float
        """
        if value in (True, False):
            value = 100.0 if value else 0.0
        assert 0.0 <= value <= 100.0
        self._pwm.ChangeDutyCycle(value)


@rpi_gpio
class backlit_device(device):
    """
    Controls a backlight (active low), assumed to be on GPIO 18 (``PWM_CLK0``) by default.

    :param gpio: GPIO interface (must be compatible with `RPi.GPIO <https://pypi.python.org/pypi/RPi.GPIO>`_).
    :param gpio_LIGHT: The GPIO pin to use for the backlight.
    :type gpio_LIGHT: int
    :param active_low: Set to true if active low (default), false otherwise.
    :type active_low: bool
    :param pwm_frequency: Use PWM for backlight brightness control with the specified frequency when provided.
    :type pwm_frequency: float
    :raises luma.core.error.UnsupportedPlatform: GPIO access not available.

    .. versionadded:: 2.0.0
    """
    def __init__(self, const=None, serial_interface=None, gpio=None, gpio_LIGHT=18, active_low=True, pwm_frequency=None, **kwargs):
        super(backlit_device, self).__init__(const, serial_interface)

        gpio = gpio or self.__rpi_gpio__()
        if pwm_frequency:
            self.backlight = PWMBacklight(gpio, pin=gpio_LIGHT, frequency=pwm_frequency)
        else:
            self.backlight = GPIOBacklight(gpio, pin=gpio_LIGHT, active_low=active_low)

        self.persist = True
        self.backlight(True)

    def cleanup(self):
        """
        Attempt to reset the device & switching it off prior to exiting the
        python process.
        """
        super(backlit_device, self).cleanup()
        if self.persist:
            self.backlight(False)


[docs]class pcd8544(backlit_device): """ Serial interface to a monochrome PCD8544 LCD display. On creation, an initialization sequence is pumped to the display to properly configure it. Further control commands can then be called to affect the brightness and other settings. :param serial_interface: The serial interface (usually a :py:class:`luma.core.interface.serial.spi` instance) to delegate sending data and commands through. :param rotate: An integer value of 0 (default), 1, 2 or 3 only, where 0 is no rotation, 1 is rotate 90° clockwise, 2 is 180° rotation and 3 represents 270° rotation. :type rotate: int """ def __init__(self, serial_interface=None, rotate=0, **kwargs): super(pcd8544, self).__init__(luma.lcd.const.pcd8544, serial_interface, **kwargs) self.capabilities(84, 48, rotate) self._mask = [1 << (i // self._w) % 8 for i in range(self._w * self._h)] self._offsets = [(self._w * (i // (self._w * 8))) + (i % self._w) for i in range(self._w * self._h)] self.contrast(0xB0) self.clear() self.show()
[docs] def display(self, image): """ Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the PCD8544 LCD display. """ assert(image.mode == self.mode) assert(image.size == self.size) image = self.preprocess(image) self.command(0x20, 0x80, 0x40) buf = bytearray(self._w * self._h // 8) off = self._offsets mask = self._mask for idx, pix in enumerate(image.getdata()): if pix > 0: buf[off[idx]] |= mask[idx] self.data(list(buf))
[docs] def contrast(self, value): """ Sets the LCD contrast """ assert(0 <= value <= 255) self.command(0x21, 0x14, value | 0x80, 0x20)
[docs]class st7567(backlit_device): """ Serial interface to a monochrome ST7567 128x64 pixel LCD display. On creation, an initialization sequence is pumped to the display to properly configure it. Further control commands can then be called to affect the brightness and other settings. :param serial_interface: The serial interface (usually a :py:class:`luma.core.interface.serial.spi` instance) to delegate sending data and commands through. :param rotate: An integer value of 0 (default), 1, 2 or 3 only, where 0 is no rotation, 1 is rotate 90° clockwise, 2 is 180° rotation and 3 represents 270° rotation. :type rotate: int .. versionadded:: 1.1.0 """ def __init__(self, serial_interface=None, rotate=0, **kwargs): super(st7567, self).__init__(luma.lcd.const.st7567, serial_interface, **kwargs) self.capabilities(128, 64, rotate) self._pages = self._h // 8 self.command(0xA3) # Bias 1/7 self.command(0xA1) self.command(0xC0) # Normal Orientation self.command(0xA6) # Normal Display (0xA7 = inverse) self.command(0x40) self.command(0x2F) self.command(0x22) self.command(0xAF) self.contrast(57) self.clear() self.show()
[docs] def display(self, image): """ Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the ST7567 LCD display """ assert(image.mode == self.mode) assert(image.size == self.size) image = self.preprocess(image) set_page_address = 0xB0 image_data = image.getdata() pixels_per_page = self.width * 8 buf = bytearray(self.width) for y in range(0, int(self._pages * pixels_per_page), pixels_per_page): self.command(set_page_address, 0x04, 0x10) set_page_address += 1 offsets = [y + self.width * i for i in range(8)] for x in range(self.width): buf[x] = \ (image_data[x + offsets[0]] and 0x01) | \ (image_data[x + offsets[1]] and 0x02) | \ (image_data[x + offsets[2]] and 0x04) | \ (image_data[x + offsets[3]] and 0x08) | \ (image_data[x + offsets[4]] and 0x10) | \ (image_data[x + offsets[5]] and 0x20) | \ (image_data[x + offsets[6]] and 0x40) | \ (image_data[x + offsets[7]] and 0x80) self.data(list(buf))
[docs] def contrast(self, value): """ Sets the LCD contrast """ assert(0 <= value <= 255) self.command(0x81, value)
[docs]class st7735(backlit_device): """ Serial interface to a 262K color (6-6-6 RGB) ST7735 LCD display. On creation, an initialization sequence is pumped to the display to properly configure it. Further control commands can then be called to affect the brightness and other settings. :param serial_interface: the serial interface (usually a :py:class:`luma.core.interface.serial.spi` instance) to delegate sending data and commands through. :param width: The number of pixels laid out horizontally. :type width: int :param height: The number of pixels laid out vertically. :type width: int :param rotate: An integer value of 0 (default), 1, 2 or 3 only, where 0 is no rotation, 1 is rotate 90° clockwise, 2 is 180° rotation and 3 represents 270° rotation. :type rotate: int :param framebuffer: Framebuffering strategy, currently values of ``diff_to_previous`` or ``full_frame`` are only supported. :type framebuffer: str :param bgr: Set to ``True`` if device pixels are BGR order (rather than RGB). :type bgr: bool :param h_offset: Horizontal offset (in pixels) of screen to device memory (default: 0). :type h_offset: int :param v_offset: Vertical offset (in pixels) of screen to device memory (default: 0). :type v_offset: int .. versionadded:: 0.3.0 """ def __init__(self, serial_interface=None, width=160, height=128, rotate=0, framebuffer="diff_to_previous", h_offset=0, v_offset=0, bgr=False, **kwargs): super(st7735, self).__init__(luma.lcd.const.st7735, serial_interface, **kwargs) self.capabilities(width, height, rotate, mode="RGB") self.framebuffer = getattr(luma.core.framebuffer, framebuffer)(self) if h_offset != 0 or v_offset != 0: def offset(bbox): left, top, right, bottom = bbox return (left + h_offset, top + v_offset, right + h_offset, bottom + v_offset) self.apply_offsets = offset else: self.apply_offsets = lambda bbox: bbox # Supported modes supported = (width, height) in [(160, 80), (160, 128), (128, 128)] if not supported: raise luma.core.error.DeviceDisplayModeError( "Unsupported display mode: {0} x {1}".format(width, height)) # RGB or BGR order order = 0x08 if bgr else 0x00 self.command(0x01) # reset self.command(0x11) # sleep out & booster on self.command(0xB1, 0x01, 0x2C, 0x2D) # frame rate control: normal mode self.command(0xB2, 0x01, 0x2C, 0x2D) # frame rate control: idle mode self.command(0xB3, 0x01, 0x2C, 0x2D, # frame rate control: partial mode dot inversion mode 0x01, 0x2C, 0x2D) # frame rate control: line inversion mode self.command(0xB4, 0x07) # display inversion: none self.command(0xC0, 0xA2, 0x02, 0x84) # power control 1: -4.6V auto mode self.command(0xC1, 0xC5) # power control 2: VGH self.command(0xC2, 0x0A, 0x00) # power control 3: OpAmp current small, boost freq self.command(0xC3, 0x8A, 0x2A) # power control 4: BCLK/2, Opamp current small & Medium low self.command(0xC4, 0x8A, 0xEE) # power control 5: partial mode/full-color self.command(0xC5, 0x0E) # VCOM Control 1 self.command(0x36, 0x60 | order) # memory data access control self.command(0x20) # display inversion off self.command(0x3A, 0x06) # interface pixel format self.command(0x13) # partial off (normal) self.command(0xE0, # gamma adjustment (+ polarity) 0x0F, 0x1A, 0x0F, 0x18, 0x2F, 0x28, 0x20, 0x22, 0x1F, 0x1B, 0x23, 0x37, 0x00, 0x07, 0x02, 0x10) self.command(0xE1, # gamma adjustment (- polarity) 0x0F, 0x1B, 0x0F, 0x17, 0x33, 0x2C, 0x29, 0x2E, 0x30, 0x30, 0x39, 0x3F, 0x00, 0x07, 0x03, 0x10) self.clear() self.show()
[docs] def display(self, image): """ Renders a 24-bit RGB image to the ST7735 LCD display. The 8-bit RGB values are passed directly to the devices internal storage, but only the 6 most-significant bits are used by the display. :param image: The image to render. :type image: PIL.Image.Image """ assert(image.mode == self.mode) assert(image.size == self.size) image = self.preprocess(image) if self.framebuffer.redraw_required(image): left, top, right, bottom = self.apply_offsets(self.framebuffer.bounding_box) width = right - left height = bottom - top self.command(0x2A, left >> 8, left & 0xFF, (right - 1) >> 8, (right - 1) & 0xFF) # Set column addr self.command(0x2B, top >> 8, top & 0xFF, (bottom - 1) >> 8, (bottom - 1) & 0xFF) # Set row addr self.command(0x2C) # Memory write i = 0 buf = bytearray(width * height * 3) for r, g, b in self.framebuffer.getdata(): if not(r == g == b == 0): # 262K format buf[i] = r buf[i + 1] = g buf[i + 2] = b i += 3 self.data(list(buf))
[docs] def contrast(self, level): """ NOT SUPPORTED :param level: Desired contrast level in the range of 0-255. :type level: int """ assert(0 <= level <= 255)
[docs] def command(self, cmd, *args): """ Sends a command and an (optional) sequence of arguments through to the delegated serial interface. Note that the arguments are passed through as data. """ self._serial_interface.command(cmd) if len(args) > 0: self._serial_interface.data(list(args))
[docs]class ili9341(backlit_device): """ Serial interface to a 262k color (6-6-6 RGB) ILI9341 LCD display. On creation, an initialization sequence is pumped to the display to properly configure it. Further control commands can then be called to affect the brightness and other settings. :param serial_interface: the serial interface (usually a :py:class:`luma.core.interface.serial.spi` instance) to delegate sending data and commands through. :param width: The number of pixels laid out horizontally. :type width: int :param height: The number of pixels laid out vertically. :type width: int :param rotate: An integer value of 0 (default), 1, 2 or 3 only, where 0 is no rotation, 1 is rotate 90° clockwise, 2 is 180° rotation and 3 represents 270° rotation. :type rotate: int :param framebuffer: Framebuffering strategy, currently values of ``diff_to_previous`` or ``full_frame`` are only supported. :type framebuffer: str :param bgr: Set to ``True`` if device pixels are BGR order (rather than RGB). :type bgr: bool :param h_offset: Horizontal offset (in pixels) of screen to device memory (default: 0). :type h_offset: int :param v_offset: Vertical offset (in pixels) of screen to device memory (default: 0). :type v_offset: int .. versionadded:: 2.2.0 """ def __init__(self, serial_interface=None, width=320, height=240, rotate=0, framebuffer="diff_to_previous", h_offset=0, v_offset=0, bgr=False, **kwargs): super(ili9341, self).__init__(luma.lcd.const.ili9341, serial_interface, **kwargs) self.capabilities(width, height, rotate, mode="RGB") self.framebuffer = getattr(luma.core.framebuffer, framebuffer)(self) if h_offset != 0 or v_offset != 0: def offset(bbox): left, top, right, bottom = bbox return (left + h_offset, top + v_offset, right + h_offset, bottom + v_offset) self.apply_offsets = offset else: self.apply_offsets = lambda bbox: bbox # Supported modes supported = (width, height) in [(320, 240), (240, 240), (320, 180)] # full, 1x1, 16x9 if not supported: raise luma.core.error.DeviceDisplayModeError( "Unsupported display mode: {0} x {1}".format(width, height)) # RGB or BGR order order = 0x00 if bgr else 0x08 # Note: based on the Adafruit implementation at # `https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display` (MIT licensed) self.command(0xef, 0x03, 0x80, 0x02) # ? self.command(0xcf, 0x00, 0xc1, 0x30) # Power control B self.command(0xed, 0x64, 0x03, 0x12, 0x81) # Power on sequence control self.command(0xe8, 0x85, 0x00, 0x78) # Driver timing control A self.command(0xcb, 0x39, 0x2c, 0x00, 0x34, 0x02) # Power control A self.command(0xf7, 0x20) # Pump ratio control self.command(0xea, 0x00, 0x00) # Driver timing control B self.command(0xc0, 0x23) # Power Control 1, VRH[5:0] self.command(0xc1, 0x10) # Power Control 2, SAP[2:0], BT[3:0] self.command(0xc5, 0x3e, 0x28) # VCM Control 1 self.command(0xc7, 0x86) # VCM Control 2 self.command(0x36, 0x20 | order) # Memory Access Control self.command(0x3a, 0x46) # Pixel Format 6-6-6 self.command(0xb1, 0x00, 0x18) # FRMCTR1 self.command(0xb6, 0x08, 0x82, 0x27) # Display Function Control self.command(0xf2, 0x00) # 3Gamma Function Disable self.command(0x26, 0x01) # Gamma Curve Selected self.command(0xe0, # Set Gamma (+ polarity) 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, 0x00) self.command(0xe1, # Set Gamma (- polarity) 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, 0x0f) self.command(0x11) # Sleep out self.clear() self.show()
[docs] def display(self, image): """ Renders a 24-bit RGB image to the ILI9341 LCD display. The 8-bit RGB values are passed directly to the devices internal storage, but only the 6 most-significant bits are used by the display. :param image: The image to render. :type image: PIL.Image.Image """ assert(image.mode == self.mode) assert(image.size == self.size) image = self.preprocess(image) if self.framebuffer.redraw_required(image): left, top, right, bottom = self.apply_offsets(self.framebuffer.bounding_box) self.command(0x2a, left >> 8, left & 0xff, (right - 1) >> 8, (right - 1) & 0xff) # Set column addr self.command(0x2b, top >> 8, top & 0xff, (bottom - 1) >> 8, (bottom - 1) & 0xff) # Set row addr self.command(0x2c) # Memory write self.data(self.framebuffer.image.crop(self.framebuffer.bounding_box).tobytes())
[docs] def contrast(self, level): """ NOT SUPPORTED :param level: Desired contrast level in the range of 0-255. :type level: int """ assert(0 <= level <= 255)
[docs] def command(self, cmd, *args): """ Sends a command and an (optional) sequence of arguments through to the delegated serial interface. Note that the arguments are passed through as data. """ self._serial_interface.command(cmd) if len(args) > 0: self._serial_interface.data(list(args))
[docs]@rpi_gpio class ht1621(backlit_device): """ Serial interface to a seven segment HT1621 monochrome LCD display. On creation, an initialization sequence is pumped to the display to properly configure it. Further control commands can then be called to affect the brightness and other settings. :param gpio: The GPIO library to use (usually RPi.GPIO) to delegate sending data and commands through. :param width: The number of 7 segment characters laid out horizontally. :type width: int :param rotate: An integer value of 0 (default), 1, 2 or 3 only, where 0 is no rotation, 1 is rotate 90° clockwise, 2 is 180° rotation and 3 represents 270° rotation. :type rotate: int :param WR: The write (SPI clock) pin to connect to, default BCM 11. :type WR: int :param DAT: The data pin to connect to, default BCM 10. :type DAT: int :param CS: The chip select pin to connect to, default BCM 8. :type CS: int .. versionadded:: 0.4.0 """ def __init__(self, gpio=None, width=6, rotate=0, WR=11, DAT=10, CS=8, **kwargs): if 'serial_interface' in kwargs: del kwargs['serial_interface'] super(ht1621, self).__init__(luma.lcd.const.ht1621, noop(), gpio=gpio, **kwargs) self.capabilities(width, 8, rotate) self.segment_mapper = dot_muncher self._gpio = gpio or self.__rpi_gpio__() self._WR = self._configure(WR) self._DAT = self._configure(DAT) self._CS = self._configure(CS) self.command(0x30) # Internal RC oscillator @ 256KHz self.command(0x52) # 1/2 Bias and 4 commons self.command(0x02) # System enable self.clear() self.show() def _configure(self, pin): if pin is not None: self._gpio.setup(pin, self._gpio.OUT) return pin
[docs] def display(self, image): """ Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the PCD8544 LCD display. """ assert(image.mode == self.mode) assert(image.size == self.size) image = self.preprocess(image) buf = [] for x in range(self._w): byte = 0 for y in range(self._h): if image.getpixel((x, y)) > 0: byte |= 1 << y buf.append(byte) self.data(buf)
[docs] def command(self, cmd): gpio = self._gpio gpio.output(self._CS, gpio.LOW) self._write_bits(0x80, 4) # Command mode self._write_bits(cmd, 8) gpio.output(self._CS, gpio.HIGH)
[docs] def data(self, data): gpio = self._gpio gpio.output(self._CS, gpio.LOW) self._write_bits(0xA0, 3) # Write mode self._write_bits(0x00, 6) # Address for byte in data: self._write_bits(byte, 8) gpio.output(self._CS, gpio.HIGH)
def _write_bits(self, value, num_bits): gpio = self._gpio for _ in range(num_bits): gpio.output(self._WR, gpio.LOW) bit = gpio.HIGH if value & 0x80 > 0 else gpio.LOW gpio.output(self._DAT, bit) value <<= 1 gpio.output(self._WR, gpio.HIGH)
[docs] def cleanup(self): """ Attempt to reset the device & switching it off prior to exiting the python process. """ super(ht1621, self).cleanup() self.command(0x00) # System disable self._gpio.cleanup()
[docs]class uc1701x(backlit_device): """ Serial interface to a monochrome UC1701X LCD display. On creation, an initialization sequence is pumped to the display to properly configure it. Further control commands can then be called to affect the brightness and other settings. :param serial_interface: The serial interface (usually a :py:class:`luma.core.interface.serial.spi` instance) to delegate sending data and commands through. :param rotate: An integer value of 0 (default), 1, 2 or 3 only, where 0 is no rotation, 1 is rotate 90° clockwise, 2 is 180° rotation and 3 represents 270° rotation. :type rotate: int .. versionadded:: 0.5.0 """ def __init__(self, serial_interface=None, rotate=0, **kwargs): super(uc1701x, self).__init__(luma.lcd.const.uc1701x, serial_interface, **kwargs) self.capabilities(128, 64, rotate) self._pages = self._h // 8 self.command(0xE2) # System reset self.command(0x2C) # Power: Boost ON self.command(0x2E) # Power: Voltage Regulator ON self.command(0x2F) # Power: Voltage Follower ON self.command(0xF8, 0x00) # Booster ratio to 4x self.command(0x23) # Set resistor ratio = 3 self.command(0xA2) # Bias 1/9 self.command(0xC0) # Set COM direction self.command(0xA1) # Set SEG direction self.command(0xAC) # Static indicator self.command(0xA6) # Disable inverse self.command(0xA5) # Display all points self.command(0xA4) # Normal Display self.contrast(0xB0) self.clear() self.show()
[docs] def display(self, image): """ Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the UC1701X LCD display. """ assert(image.mode == self.mode) assert(image.size == self.size) image = self.preprocess(image) set_page_address = 0xB0 image_data = image.getdata() pixels_per_page = self.width * 8 buf = bytearray(self.width) for y in range(0, int(self._pages * pixels_per_page), pixels_per_page): self.command(set_page_address, 0x04, 0x10) set_page_address += 1 offsets = [y + self.width * i for i in range(8)] for x in range(self.width): buf[x] = \ (image_data[x + offsets[0]] and 0x01) | \ (image_data[x + offsets[1]] and 0x02) | \ (image_data[x + offsets[2]] and 0x04) | \ (image_data[x + offsets[3]] and 0x08) | \ (image_data[x + offsets[4]] and 0x10) | \ (image_data[x + offsets[5]] and 0x20) | \ (image_data[x + offsets[6]] and 0x40) | \ (image_data[x + offsets[7]] and 0x80) self.data(list(buf))
[docs] def contrast(self, value): """ Sets the LCD contrast """ assert(0 <= value <= 255) self.command(0x81, value >> 2)
[docs]class st7920(device): def __init__(self, serial_interface=None, width=128, height=64, rotate=0, framebuffer="diff_to_previous", **kwargs): super(st7920, self).__init__(luma.core.const.common, serial_interface) self.capabilities(width, height, rotate, mode="1") self.framebuffer = getattr(luma.core.framebuffer, framebuffer)(self) if width != 128 or height != 64: raise luma.core.error.DeviceDisplayModeError( "Unsupported display mode: {0} x {1}".format(width, height)) self.command(0x20) self.command(0x24) self.command(0x26) self.clear() self.show()
[docs] def command(self, cmd): self._serial_interface.data([0xF8, cmd & 0xF0, (cmd & 0x0F) << 4]) time.sleep(1.0 / 1000000)
[docs] def data(self, data): self._serial_interface.data([0xFA, data & 0xF0, (data & 0x0F) << 4]) time.sleep(1.0 / 1000000)
[docs] def display(self, image): assert(image.mode == self.mode) assert(image.size == self.size) image = self.preprocess(image) pix = list(image.getdata()) w = self._w // 16 for idx, data in enumerate([pix[i:i + 16] for i in range(0, len(pix), 16)]): x = idx % w y = idx // w if y < 32: self.command(0x80 | y) self.command(0x80 | x) else: self.command(0x80 | (y & 0x1F)) self.command(0x88 | x) value = 0 for bit in data: value = (value << 1) | (1 if bit > 0 else 0) print("idx = {3}, x = {0}, y = {1}, value = {4:04X}, data = {2}".format(x, y, data, idx, value)) self.data(value >> 8) self.data(value & 0xFF)
[docs] def contrast(self, level): """ NOT SUPPORTED :param level: Desired contrast level in the range of 0-255. :type level: int """ assert(0 <= level <= 255)
[docs] def show(self): pass
[docs] def hide(self): pass