ePaper drivers test for uasyncio running.

pull/56/head
peterhinch 2023-05-12 18:03:01 +01:00
rodzic 35f8b23a52
commit 7be1073f48
7 zmienionych plików z 63 dodań i 44 usunięć

Wyświetl plik

@ -1039,8 +1039,8 @@ see below.
* `rst` An initialised output pin. Initial value should be 1.
* `busy` An initialised input pin.
* `landscape=True` By default the long axis is horizontal.
* `asyn=False` Setting this `True` invokes an asynchronous mode. See
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
The `asyn` arg has been removed: the driver now detects asynchronous use.
### 5.1.2 Public methods
@ -1058,9 +1058,9 @@ All methods are synchronous.
### 5.1.3 Events
These provide synchronisation in asynchronous applications where `asyn=True`.
They are only needed in more advanced asynchronous applications and their use
is discussed in [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
These provide synchronisation in asynchronous applications. They are only
needed in more advanced asynchronous applications and their use is discussed in
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
* `updated` Set when framebuf has been copied to device. It is now safe to
modify widgets without risk of display corruption.
* `complete` Set when display update is complete. It is now safe to call
@ -1227,8 +1227,8 @@ Pins 26-40 unused and omitted.
* `rst` An initialised output pin. Initial value should be 1.
* `busy` An initialised input pin.
* `landscape=False` By default the long axis is vertical.
* `asyn=False` Setting this `True` invokes an asynchronous mode. See
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
The `asyn` arg has been removed: the driver now detects asynchronous use.
### 5.2.2 EPD public methods
@ -1246,9 +1246,9 @@ All methods are synchronous.
### 5.2.3 Events
These provide synchronisation in asynchronous applications where `asyn=True`.
They are only needed in more advanced asynchronous applications and their use
is discussed in [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
These provide synchronisation in asynchronous applications. They are only
needed in more advanced asynchronous applications and their use is discussed in
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
* `updated` Set when framebuf has been copied to device. It is now safe to
modify widgets without risk of display corruption.
* `complete` Set when display update is complete. It is now safe to call
@ -1280,8 +1280,7 @@ import gc
from drivers.epaper.pico_epaper_42 import EPD as SSD
gc.collect() # Precaution before instantiating framebuf.
ssd = SSD() # Create a display instance. For normal applications.
# ssd = SSD(asyn=True) # Alternative for asynchronous applications.
ssd = SSD() # Create a display instance based on a Pico in socket.
```
### 5.3.1 Constructor args
@ -1293,8 +1292,8 @@ following constructor args:
* `dc=None` A `Pin` instance defined as `Pin.OUT`.
* `rst=None` A `Pin` instance defined as `Pin.OUT`.
* `busy=None` A `Pin` instance defined as `Pin.IN, Pin.PULL_UP`.
* `asyn=False` Set `True` for asynchronous applications. Leave `False` for
microgui where the arg has no effect.
The `asyn` arg has been removed: the driver now detects asynchronous use.
### 5.3.2 Public methods
@ -1321,9 +1320,9 @@ ghosting.
### 5.3.3 Events
These provide synchronisation in asynchronous applications where `asyn=True`.
They are only needed in more advanced asynchronous applications and their use
is discussed in [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
These provide synchronisation in asynchronous applications. They are only
needed in more advanced asynchronous applications and their use is discussed in
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
* `updated` Set when framebuf has been copied to device. It is now safe to
modify widgets without risk of display corruption.
* `complete` Set when display update is complete. It is now safe to call
@ -1349,8 +1348,6 @@ before issuing another refresh.
The following applies to nano-gui. Under micro-gui the update mechanism is
a background task. Use with micro-gui is covered
[here](https://github.com/peterhinch/micropython-micro-gui/blob/main/README.md#10-epaper-displays).
Further, the comments address the case where the driver is instantiated with
`asyn=True`.
When synchronous code issues
```python
@ -1364,15 +1361,14 @@ refresh is complete. If `demo_mode` is set, device drivers block for an
additional 2 seconds to enable demos written for normal displays to work (the
2 second pause allows the result of each refresh to be seen).
This long blocking period is not ideal in asynchronous code, and the process is
modified if, in `color_setup.py`, an `EPD` is instantiated with `asyn=True`. In
this case `refresh` calls the `show` method as before, but `show` creates a
task `._as_show` and returns immediately. The task yields to the scheduler as
necessary to ensure that blocking is limited to around 30ms. If screen updates
take place at a low rate the only precaution necessary is to ensure that
sufficient time elapses between calls to `ssd.refresh()` for the update to
complete. For example the following code fragment illustrates an application
which performs a full EPD refresh once per minute:
This long blocking period is not ideal in asynchronous code. If `refresh` is
called from a task, `refresh` calls the `show` method as before, but `show`
creates a task `._as_show` and returns immediately. The task yields to the
scheduler as necessary to ensure that blocking is limited to around 30ms. If
screen updates take place at a low rate the only precaution necessary is to
ensure that sufficient time elapses between calls to `ssd.refresh()` for the
update to complete. For example the following code fragment illustrates an
application which performs a full EPD refresh once per minute:
```python
async def run():
@ -1382,7 +1378,7 @@ async def run():
ssd.refresh() # Launches background refresh
await asyncio.sleep(60)
```
With `asyn=True` other running tasks experience latency measured in tens of ms.
Other running tasks experience latency measured in tens of ms.
Finer control is available using the two public bound `Event` instances. This
fragment assumes an application with a single task performing refreshes. The

Wyświetl plik

@ -3,7 +3,7 @@
# EPD is subclassed from framebuf.FrameBuffer for use with Writer class and nanogui.
# Optimisations to reduce allocations and RAM use.
# Copyright (c) Peter Hinch 2020
# Copyright (c) Peter Hinch 2020-2023
# Released under the MIT license see LICENSE
# Based on the following sources:
@ -16,6 +16,13 @@ import framebuf
import uasyncio as asyncio
from time import sleep_ms, ticks_ms, ticks_us, ticks_diff
def asyncio_running():
try:
_ = asyncio.current_task()
except:
return False
return True
class EPD(framebuf.FrameBuffer):
# A monochrome approach should be used for coding this. The rgb method ensures
# nothing breaks if users specify colors.
@ -23,6 +30,7 @@ class EPD(framebuf.FrameBuffer):
def rgb(r, g, b):
return int((r > 127) or (g > 127) or (b > 127))
# Discard asyn: autodetect
def __init__(self, spi, cs, dc, rst, busy, landscape=False, asyn=False):
self._spi = spi
self._cs = cs # Pins
@ -30,7 +38,6 @@ class EPD(framebuf.FrameBuffer):
self._rst = rst
self._busy = busy
self._lsc = landscape
self._asyn = asyn
self._as_busy = False # Set immediately on start of task. Cleared when busy pin is logically false (physically 1).
self.updated = asyncio.Event()
self.complete = asyncio.Event()
@ -198,7 +205,7 @@ class EPD(framebuf.FrameBuffer):
# draw the current frame memory. Blocking time ~180ms
def show(self, buf1=bytearray(1)):
if self._asyn:
if asyncio_running():
if self._as_busy:
raise RuntimeError('Cannot refresh: display is busy.')
self._as_busy = True

Wyświetl plik

@ -4,7 +4,7 @@
# EPD is subclassed from framebuf.FrameBuffer for use with Writer class and nanogui.
# Copyright (c) Peter Hinch 2020
# Copyright (c) Peter Hinch 2020-2023
# Released under the MIT license see LICENSE
# Based on the following sources:
@ -22,7 +22,14 @@ import uasyncio as asyncio
from micropython import const
from time import sleep_ms, sleep_us, ticks_ms, ticks_us, ticks_diff
_MAX_BLOCK = const(20) # Maximum blocking time (ms) for asynchronous show.
_def asyncio_running():
try:
_ = asyncio.current_task()
except:
return False
return True
MAX_BLOCK = const(20) # Maximum blocking time (ms) for asynchronous show.
class EPD(framebuf.FrameBuffer):
# A monochrome approach should be used for coding this. The rgb method ensures
@ -31,6 +38,7 @@ class EPD(framebuf.FrameBuffer):
def rgb(r, g, b):
return int((r > 127) or (g > 127) or (b > 127))
# Discard asyn: autodetect
def __init__(self, spi, cs, dc, rst, busy, landscape=True, asyn=False):
self._spi = spi
self._cs = cs # Pins
@ -38,7 +46,6 @@ class EPD(framebuf.FrameBuffer):
self._rst = rst # Active low.
self._busy = busy # Active low on IL0373
self._lsc = landscape
self._asyn = asyn
# ._as_busy is set immediately on start of task. Cleared
# when busy pin is logically false (physically 1).
self._as_busy = False
@ -165,7 +172,7 @@ class EPD(framebuf.FrameBuffer):
# draw the current frame memory.
def show(self, buf1=bytearray(1)):
if self._asyn:
if asyncio_running():
if self._as_busy:
raise RuntimeError('Cannot refresh: display is busy.')
self._as_busy = True # Immediate busy flag. Pin goes low much later.

Wyświetl plik

@ -45,6 +45,13 @@ import time
import uasyncio as asyncio
from drivers.boolpalette import BoolPalette
def asyncio_running():
try:
_ = asyncio.current_task()
except:
return False
return True
# Display resolution
_EPD_WIDTH = const(400)
_BWIDTH = _EPD_WIDTH // 8
@ -111,6 +118,7 @@ class EPD(framebuf.FrameBuffer):
def rgb(r, g, b):
return int((r > 127) or (g > 127) or (b > 127))
# Discard asyn: autodetect
def __init__(self, spi=None, cs=None, dc=None, rst=None, busy=None, asyn=False):
self.reset_pin = Pin(RST_PIN, Pin.OUT) if rst is None else rst
self.busy_pin = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP) if busy is None else busy
@ -118,7 +126,6 @@ class EPD(framebuf.FrameBuffer):
self.dc_pin = Pin(DC_PIN, Pin.OUT) if dc is None else dc
self.spi = SPI(1, sck = Pin(10), mosi = Pin(11), miso = Pin(28)) if spi is None else spi
self.spi.init(baudrate = 4_000_000)
self._asyn = asyn
self._busy = False # Set immediately on .show(). Cleared when busy pin is logically false (physically 1).
self.updated = asyncio.Event()
self.complete = asyncio.Event()
@ -284,7 +291,9 @@ class EPD(framebuf.FrameBuffer):
self._busy = False
self.complete.set()
async def do_refresh(self, split): # For micro-gui
# Specific method for micro-gui. Unsuitable EPD's lack this method. Micro-gui
# does not test for asyncio as this is guaranteed to be up.
async def do_refresh(self, split):
assert (not self._busy), "Refresh while busy"
await self._as_show() # split=5
@ -292,7 +301,7 @@ class EPD(framebuf.FrameBuffer):
if self._busy:
raise RuntimeError('Cannot refresh: display is busy.')
self._busy = True # Immediate busy flag. Pin goes low much later.
if self._asyn:
if asyncio_running():
self.updated.clear()
self.complete.clear()
asyncio.create_task(self._as_show())

Wyświetl plik

@ -38,4 +38,4 @@ pbusy = machine.Pin('Y4', machine.Pin.IN)
# Datasheet P35 indicates up to 10MHz.
spi = machine.SPI(2, baudrate=5_000_000)
gc.collect() # Precaution before instantiating framebuf
ssd = SSD(spi, pcs, pdc, prst, pbusy, asyn=True) # Create a display instance
ssd = SSD(spi, pcs, pdc, prst, pbusy) # Create a display instance

Wyświetl plik

@ -13,6 +13,6 @@ import gc
from drivers.epaper.pico_epaper_42 import EPD as SSD
gc.collect() # Precaution before instantiating framebuf
# Set asyn True to run asynchronous code such as epd_async.py
# Set False for normal synchronous code e.g. other demos.
ssd = SSD(asyn=True) # Create a display instance
ssd = SSD() # Create a display instance
# Set this to run demos written for arbitrary displays:
# ssd.demo_mode = True

Wyświetl plik

@ -32,5 +32,5 @@ prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
pbusy = machine.Pin('Y4', machine.Pin.IN)
spi = machine.SPI(2, baudrate=4_000_000) # From https://github.com/mcauser/micropython-waveshare-epaper/blob/master/examples/2in9-hello-world/test.py
gc.collect() # Precaution before instantiating framebuf
ssd = SSD(spi, pcs, pdc, prst, pbusy, landscape=False, asyn=True) # Create a display instance
ssd = SSD(spi, pcs, pdc, prst, pbusy, landscape=False) # Create a display instance
#ssd.demo_mode = True