adafruit / circuitpython

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

3-Color EPD IL0373 code will hang if no display is present #4789

Open anecdata opened 3 years ago

anecdata commented 3 years ago

Firmware

Adafruit CircuitPython 6.2.0 on 2021-04-05; FeatherS2 with ESP32S2

Code/REPL

import time
import board
import displayio

time.sleep(5)  # give time to connect serial console after reset

spi = board.SPI()

display_cs = board.IO1  # board.D9
display_dc = board.IO3  # board.D10
display_reset = board.IO33  # board.D5
epd_busy = board.IO38  # board.D6

displayio.release_displays()

# 2C EPD does not hang on `display.refresh()` (ditto ST7789 TFTs)
try:
    display_bus = displayio.FourWire(spi, command=display_dc, chip_select=display_cs, baudrate=1000000)
    time.sleep(1)
    from adafruit_ssd1675 import SSD1675
    display = SSD1675(display_bus, width=250, height=122, rotation=90, busy_pin=epd_busy)
except RuntimeError as e:
    print("SSD1675:", e)

# 3C EPD hangs on `display.refresh()`
"""
try:
    display_bus = displayio.FourWire(spi, command=display_dc, chip_select=display_cs, baudrate=1000000)
    time.sleep(1)
    from adafruit_il0373 import IL0373
    display = IL0373(display_bus, width=212, height=104, rotation=90, busy_pin=epd_busy, highlight_color=0xff0000)
except RuntimeError as e:
    print("IL0373:", e)
"""

print("displayio setup... ")
splash = displayio.Group()

# Background
background_bitmap = displayio.Bitmap(display.width, display.height, 1)
background_palette = displayio.Palette(1)
background_palette[0] = 0x888888
background = displayio.TileGrid(background_bitmap, pixel_shader=background_palette)
splash.append(background)

print("display.show(splash)... ")
display.show(splash)
print("display.refresh()... ")
display.refresh()  # <-- 3C EPD code hangs here
print()

while True:
    print(" ", time.monotonic(), "\r", end="")
    time.sleep(1)

Behavior

If the display is not connected, or nor connected correctly, the code will hang (unreponsive to control-C, requires hard reset).

This may a case of: me: "doctor, it hurts when I do that" doctor: "don't do that" But the use case is that displays are typically write-only and can't report their existence, identity, or status. There are a variety of hardware and software scenarios where code may expect a certain display and not be able to write to it.

To demonstrate the issue, no display is required, just run it as-is to run the working case, then comment out the 2C EPD block and uncomment the 3C EPD block to run the hanging case.

The SSD1675 2-color EPD and ST7789 TFT displays do not exhibit this behavior.

ladyada commented 3 years ago

correct, there's a busy pin, its waiting for the busy pin to change which it wont. its not a bug - dont do this :)

anecdata commented 3 years ago

so... close this issue?

...the 2-color EPD code does not hang. I wonder if there's some timeout missing in the 3-color case.

ladyada commented 3 years ago

well, we should probably time out on busy failure - wanna find where it does the busy wait and have it bail after some time with an exception?

anecdata commented 3 years ago

Looking at the core, the busy_state of the busy pin defaults to True, and the difference between 2C & 3C behavior is explained by that and the library inits:

Also (learned) EPDs can use either the busy pin or the refresh time parameter to determine when it's OK to refresh, i.e., the busy pin isn't required. And indeed, if the 3-color IL0373 is set up in CircuitPython code without the busy_pin=epd_busy parameter, it does not hang. That makes the circumstances for this issue even more narrow.

tannewt commented 3 years ago

I think there are two things to improve:

DJDevon3 commented 1 year ago

time to revisit this one on the FeatherS2 with 8.0.5 stable?