adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.1k stars 1.22k forks source link

EPaperDisplay sequences are limited to less than 128 bytes due to DELAY byte implementation #5850

Closed dkulinski closed 2 years ago

dkulinski commented 2 years ago

CircuitPython version

Adafruit CircuitPython 7.1.0 on 2021-12-28; Adafruit ItsyBitsy RP2040 with rp2040
Board ID:adafruit_itsybitsy_rp2040

Code/REPL

"""
`dkulinsk_ws2in9v2`
================================================================================
CircuitPython `displayio` driver for Waveshare 2.9in V2 Display
* Author(s): Daniel Kulinski
Implementation Notes
--------------------
**Hardware:**
* `WaveShare 2.9" Black and White eInk Display <https://https://www.waveshare.com/wiki/2.9inch_e-Paper_Module>`_
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
  https://github.com/adafruit/circuitpython/releases
"""

import displayio
import digitalio
import board

__version__ = "0.0.0"
__repo__ = ""

_START_SEQUENCE = (
    b'\x12\x00' # Software Reset
    b'\x01\x03\x27\x01\x00' # Driver output control
    b'\x11\x01\x03' # data entry mode
    b'\x44\x02\x00\x0F' # Set Window X RAM
    b'\x45\x04\x00\x00\x27\x01' # Set Window Y RAM
    b'\x21\x02\x00\x80' # Display update control
    b'\x4E\x01\x00' # Set Cursor X
    b'\x4F\x02\x00\x00' # Set Cursor Y
    b'\x80\x99\x66\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x10\x66\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x80\x66\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x10\x66\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x08\x00\x00\x00\x00\x02\x0A\x0A\x00\x0A\x0A\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x08\x00\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x44\x44\x44\x44\x44\x44\x00\x00\x00' # LUT
    b'\x3f\x01\x22'
    b'\x03\x01\x17' # gate voltage
    b'\x04\x03\x41\x00\x32' # source voltage
    b'\x2c\x01\x36' # VCOM
    )

_STOP_SEQUENCE = b'\x10\x01\x01'

class WS2IN9V2(displayio.EPaperDisplay):
    r"""WS2IN9V2 driver
    :param bus: The data bus the display is on
    :param \**kwargs:
        See below
    :Keyword Arguments:
        * *width* (``int``) --
          Display width
        * *height* (``int``) --
          Display height
        * *rotation* (``int``) --
          Display rotation
        * *busy_pin* (``microcontroller.Pin``) --
          Busy to read for busy state
    """

    def __init__(self, bus, **kwargs):
        stop_sequence = bytearray(_STOP_SEQUENCE)
        try:
            bus.reset()
        except RuntimeError:
            # No reset pin defined, so no deep sleeping
            stop_sequence = b""

        start_sequence = bytearray(_START_SEQUENCE)
        width = kwargs['width']
        height = kwargs['height']
        board_busy_pin = kwargs.get("busy_pin",None)
        if "rotation" in kwargs and kwargs["rotation"] % 180 != 90:
            width, height = height, width
        start_sequence[13] = (width - 1) & 0xFF
        start_sequence[14] = ((width - 1) >> 8) & 0xFF

        super().__init__(
            bus,
            start_sequence,
            stop_sequence,
            **kwargs,
            ram_width=250,
            ram_height=296,
            busy_state=True,
            write_black_ram_command=0x24,
            write_color_ram_command=0x26,
            black_bits_inverted=False,
            set_column_window_command=0x44,
            set_row_window_command=0x45,
            set_current_column_command=0x4E,
            set_current_row_command=0x4F,
            refresh_display_command=0x20,
            colstart=1,
            always_toggle_chip_select=True,
            grayscale=True,
        )

"""
Simple text script for Adafruit 2.13" 212x104 tri-color display
Supported products:
  * Adafruit 2.13" Tri-Color Display Breakout
  * Adafruit 2.13" Tri-Color Display FeatherWing
    https://www.adafruit.com/product/4086 (breakout) or
    https://www.adafruit.com/product/4128 (FeatherWing)

  This program requires the adafruit_il0373 library and the
  adafruit_display_text library in the CIRCUITPY /lib folder
  for CircuitPython 5.0 and above which has displayio support.
"""

import time
import board
import digitalio
import displayio
import dkulinski_ws2in9v2
import terminalio
from adafruit_display_text import label

BLACK = 0x00
WHITE = 0xFF

# Change text colors, choose from the following values:
# BLACK, WHITE

FOREGROUND_COLOR = WHITE
BACKGROUND_COLOR = BLACK

# Used to ensure the display is free in CircuitPython
displayio.release_displays()

# Define the pins needed for display use
# This pinout is for a Feather M4 and may be different for other boards
spi = board.SPI()  # Uses SCK and MOSI
epd_cs = board.D9
epd_dc = board.D10
epd_reset = board.D11
epd_busy = board.D12

# Create the displayio connection to the display pins
display_bus = displayio.FourWire(spi, command=epd_dc, chip_select=epd_cs,
                                 reset=epd_reset, baudrate=1000000)
time.sleep(1)  # Wait a bit

# Create the display object
DISPLAY_WIDTH = 128
DISPLAY_HEIGHT = 296

display = dkulinski_ws2in9v2.WS2IN9V2(
    display_bus,
    width=DISPLAY_WIDTH,
    height=DISPLAY_HEIGHT,
    rotation=0,
    busy_pin=board.D12,
    )

# Create a display group for our screen objects
g = displayio.Group()

# Set a background
background_bitmap = displayio.Bitmap(DISPLAY_WIDTH, DISPLAY_HEIGHT, 1)
# Map colors in a palette
palette = displayio.Palette(1)
palette[0] = BACKGROUND_COLOR

# Create a Tilegrid with the background and put in the displayio group
t = displayio.TileGrid(background_bitmap, pixel_shader=palette)
g.append(t)

# Draw simple text using the built-in font into a displayio group
text_group = displayio.Group(scale=2, x=20, y=40)
text = "Hello World!"
text_area = label.Label(terminalio.FONT, text=text, color=FOREGROUND_COLOR)
text_group.append(text_area)  # Add this text to the text group
g.append(text_group)

# Place the display group on the screen
display.show(g)

# Refresh the display to have everything show on the display
# NOTE: Do not refresh eInk displays more often than 180 seconds!
display.refresh()
time.sleep(120)
display.show(g)
display.refresh()

while True:
    pass

Behavior

No error message but display fails to initialize correctly because of the long LUT. This is for a Waveshare 2.9 inch v2 grayscale EPaperDisplay which is using the SSD1680 driver.

Description

When checking the SPI stream it is obvious that the bitwise AND of the delay byte is interfering with the LUT length.

Additional information

After talking with tannewt on Discord, he suggested adding a boolean for a two byte array length and defaulting this to false. I had worked on this but my C skills are lacking and my expression always evaluates to false regardless of what I set in my code (branch is here: https://github.com/dkulinski/circuitpython/commit/6d4e90cbcc0f11a7f68c718cffd9d5cfa89130ec ). I wanted to open an official bug report on this issue.

tannewt commented 2 years ago

The change looks correct to me. I'm happy to help you debug via Discord. Just ping me when you have time.

dkulinski commented 2 years ago

I finally found my off by one error and fixed it in my code and I'm now sending what appears to be good SPI data. EPaperDisplay looks like the RAM pointers are wrong so and if I send the LUT my data looks backwards and skips a column. If I don't send the LUT the RAM window looks off.

tannewt commented 2 years ago

I'm glad you made progress! Usually the increment direction is part of the init sequence. You should be able to update it to get the right direction. This is the fun of getting a new display going. I've attached the display-ruler.bmp I used to verify the settings.

display-ruler.zip