kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
ePaper drivers test for uasyncio running.
rodzic
35f8b23a52
commit
7be1073f48
54
DRIVERS.md
54
DRIVERS.md
|
@ -1039,8 +1039,8 @@ see below.
|
||||||
* `rst` An initialised output pin. Initial value should be 1.
|
* `rst` An initialised output pin. Initial value should be 1.
|
||||||
* `busy` An initialised input pin.
|
* `busy` An initialised input pin.
|
||||||
* `landscape=True` By default the long axis is horizontal.
|
* `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
|
### 5.1.2 Public methods
|
||||||
|
|
||||||
|
@ -1058,9 +1058,9 @@ All methods are synchronous.
|
||||||
|
|
||||||
### 5.1.3 Events
|
### 5.1.3 Events
|
||||||
|
|
||||||
These provide synchronisation in asynchronous applications where `asyn=True`.
|
These provide synchronisation in asynchronous applications. They are only
|
||||||
They are only needed in more advanced asynchronous applications and their use
|
needed in more advanced asynchronous applications and their use is discussed in
|
||||||
is discussed in [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
||||||
* `updated` Set when framebuf has been copied to device. It is now safe to
|
* `updated` Set when framebuf has been copied to device. It is now safe to
|
||||||
modify widgets without risk of display corruption.
|
modify widgets without risk of display corruption.
|
||||||
* `complete` Set when display update is complete. It is now safe to call
|
* `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.
|
* `rst` An initialised output pin. Initial value should be 1.
|
||||||
* `busy` An initialised input pin.
|
* `busy` An initialised input pin.
|
||||||
* `landscape=False` By default the long axis is vertical.
|
* `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
|
### 5.2.2 EPD public methods
|
||||||
|
|
||||||
|
@ -1246,9 +1246,9 @@ All methods are synchronous.
|
||||||
|
|
||||||
### 5.2.3 Events
|
### 5.2.3 Events
|
||||||
|
|
||||||
These provide synchronisation in asynchronous applications where `asyn=True`.
|
These provide synchronisation in asynchronous applications. They are only
|
||||||
They are only needed in more advanced asynchronous applications and their use
|
needed in more advanced asynchronous applications and their use is discussed in
|
||||||
is discussed in [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
||||||
* `updated` Set when framebuf has been copied to device. It is now safe to
|
* `updated` Set when framebuf has been copied to device. It is now safe to
|
||||||
modify widgets without risk of display corruption.
|
modify widgets without risk of display corruption.
|
||||||
* `complete` Set when display update is complete. It is now safe to call
|
* `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
|
from drivers.epaper.pico_epaper_42 import EPD as SSD
|
||||||
|
|
||||||
gc.collect() # Precaution before instantiating framebuf.
|
gc.collect() # Precaution before instantiating framebuf.
|
||||||
ssd = SSD() # Create a display instance. For normal applications.
|
ssd = SSD() # Create a display instance based on a Pico in socket.
|
||||||
# ssd = SSD(asyn=True) # Alternative for asynchronous applications.
|
|
||||||
```
|
```
|
||||||
### 5.3.1 Constructor args
|
### 5.3.1 Constructor args
|
||||||
|
|
||||||
|
@ -1293,8 +1292,8 @@ following constructor args:
|
||||||
* `dc=None` A `Pin` instance defined as `Pin.OUT`.
|
* `dc=None` A `Pin` instance defined as `Pin.OUT`.
|
||||||
* `rst=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`.
|
* `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
|
### 5.3.2 Public methods
|
||||||
|
|
||||||
|
@ -1321,9 +1320,9 @@ ghosting.
|
||||||
|
|
||||||
### 5.3.3 Events
|
### 5.3.3 Events
|
||||||
|
|
||||||
These provide synchronisation in asynchronous applications where `asyn=True`.
|
These provide synchronisation in asynchronous applications. They are only
|
||||||
They are only needed in more advanced asynchronous applications and their use
|
needed in more advanced asynchronous applications and their use is discussed in
|
||||||
is discussed in [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
||||||
* `updated` Set when framebuf has been copied to device. It is now safe to
|
* `updated` Set when framebuf has been copied to device. It is now safe to
|
||||||
modify widgets without risk of display corruption.
|
modify widgets without risk of display corruption.
|
||||||
* `complete` Set when display update is complete. It is now safe to call
|
* `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
|
The following applies to nano-gui. Under micro-gui the update mechanism is
|
||||||
a background task. Use with micro-gui is covered
|
a background task. Use with micro-gui is covered
|
||||||
[here](https://github.com/peterhinch/micropython-micro-gui/blob/main/README.md#10-epaper-displays).
|
[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
|
When synchronous code issues
|
||||||
```python
|
```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
|
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).
|
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
|
This long blocking period is not ideal in asynchronous code. If `refresh` is
|
||||||
modified if, in `color_setup.py`, an `EPD` is instantiated with `asyn=True`. In
|
called from a task, `refresh` calls the `show` method as before, but `show`
|
||||||
this case `refresh` calls the `show` method as before, but `show` creates a
|
creates a task `._as_show` and returns immediately. The task yields to the
|
||||||
task `._as_show` and returns immediately. The task yields to the scheduler as
|
scheduler as necessary to ensure that blocking is limited to around 30ms. If
|
||||||
necessary to ensure that blocking is limited to around 30ms. If screen updates
|
screen updates take place at a low rate the only precaution necessary is to
|
||||||
take place at a low rate the only precaution necessary is to ensure that
|
ensure that sufficient time elapses between calls to `ssd.refresh()` for the
|
||||||
sufficient time elapses between calls to `ssd.refresh()` for the update to
|
update to complete. For example the following code fragment illustrates an
|
||||||
complete. For example the following code fragment illustrates an application
|
application which performs a full EPD refresh once per minute:
|
||||||
which performs a full EPD refresh once per minute:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def run():
|
async def run():
|
||||||
|
@ -1382,7 +1378,7 @@ async def run():
|
||||||
ssd.refresh() # Launches background refresh
|
ssd.refresh() # Launches background refresh
|
||||||
await asyncio.sleep(60)
|
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
|
Finer control is available using the two public bound `Event` instances. This
|
||||||
fragment assumes an application with a single task performing refreshes. The
|
fragment assumes an application with a single task performing refreshes. The
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# EPD is subclassed from framebuf.FrameBuffer for use with Writer class and nanogui.
|
# EPD is subclassed from framebuf.FrameBuffer for use with Writer class and nanogui.
|
||||||
# Optimisations to reduce allocations and RAM use.
|
# 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
|
# Released under the MIT license see LICENSE
|
||||||
|
|
||||||
# Based on the following sources:
|
# Based on the following sources:
|
||||||
|
@ -16,6 +16,13 @@ import framebuf
|
||||||
import uasyncio as asyncio
|
import uasyncio as asyncio
|
||||||
from time import sleep_ms, ticks_ms, ticks_us, ticks_diff
|
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):
|
class EPD(framebuf.FrameBuffer):
|
||||||
# A monochrome approach should be used for coding this. The rgb method ensures
|
# A monochrome approach should be used for coding this. The rgb method ensures
|
||||||
# nothing breaks if users specify colors.
|
# nothing breaks if users specify colors.
|
||||||
|
@ -23,6 +30,7 @@ class EPD(framebuf.FrameBuffer):
|
||||||
def rgb(r, g, b):
|
def rgb(r, g, b):
|
||||||
return int((r > 127) or (g > 127) or (b > 127))
|
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):
|
def __init__(self, spi, cs, dc, rst, busy, landscape=False, asyn=False):
|
||||||
self._spi = spi
|
self._spi = spi
|
||||||
self._cs = cs # Pins
|
self._cs = cs # Pins
|
||||||
|
@ -30,7 +38,6 @@ class EPD(framebuf.FrameBuffer):
|
||||||
self._rst = rst
|
self._rst = rst
|
||||||
self._busy = busy
|
self._busy = busy
|
||||||
self._lsc = landscape
|
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._as_busy = False # Set immediately on start of task. Cleared when busy pin is logically false (physically 1).
|
||||||
self.updated = asyncio.Event()
|
self.updated = asyncio.Event()
|
||||||
self.complete = asyncio.Event()
|
self.complete = asyncio.Event()
|
||||||
|
@ -198,7 +205,7 @@ class EPD(framebuf.FrameBuffer):
|
||||||
|
|
||||||
# draw the current frame memory. Blocking time ~180ms
|
# draw the current frame memory. Blocking time ~180ms
|
||||||
def show(self, buf1=bytearray(1)):
|
def show(self, buf1=bytearray(1)):
|
||||||
if self._asyn:
|
if asyncio_running():
|
||||||
if self._as_busy:
|
if self._as_busy:
|
||||||
raise RuntimeError('Cannot refresh: display is busy.')
|
raise RuntimeError('Cannot refresh: display is busy.')
|
||||||
self._as_busy = True
|
self._as_busy = True
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
# EPD is subclassed from framebuf.FrameBuffer for use with Writer class and nanogui.
|
# 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
|
# Released under the MIT license see LICENSE
|
||||||
|
|
||||||
# Based on the following sources:
|
# Based on the following sources:
|
||||||
|
@ -22,7 +22,14 @@ import uasyncio as asyncio
|
||||||
from micropython import const
|
from micropython import const
|
||||||
from time import sleep_ms, sleep_us, ticks_ms, ticks_us, ticks_diff
|
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):
|
class EPD(framebuf.FrameBuffer):
|
||||||
# A monochrome approach should be used for coding this. The rgb method ensures
|
# 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):
|
def rgb(r, g, b):
|
||||||
return int((r > 127) or (g > 127) or (b > 127))
|
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):
|
def __init__(self, spi, cs, dc, rst, busy, landscape=True, asyn=False):
|
||||||
self._spi = spi
|
self._spi = spi
|
||||||
self._cs = cs # Pins
|
self._cs = cs # Pins
|
||||||
|
@ -38,7 +46,6 @@ class EPD(framebuf.FrameBuffer):
|
||||||
self._rst = rst # Active low.
|
self._rst = rst # Active low.
|
||||||
self._busy = busy # Active low on IL0373
|
self._busy = busy # Active low on IL0373
|
||||||
self._lsc = landscape
|
self._lsc = landscape
|
||||||
self._asyn = asyn
|
|
||||||
# ._as_busy is set immediately on start of task. Cleared
|
# ._as_busy is set immediately on start of task. Cleared
|
||||||
# when busy pin is logically false (physically 1).
|
# when busy pin is logically false (physically 1).
|
||||||
self._as_busy = False
|
self._as_busy = False
|
||||||
|
@ -165,7 +172,7 @@ class EPD(framebuf.FrameBuffer):
|
||||||
|
|
||||||
# draw the current frame memory.
|
# draw the current frame memory.
|
||||||
def show(self, buf1=bytearray(1)):
|
def show(self, buf1=bytearray(1)):
|
||||||
if self._asyn:
|
if asyncio_running():
|
||||||
if self._as_busy:
|
if self._as_busy:
|
||||||
raise RuntimeError('Cannot refresh: display is busy.')
|
raise RuntimeError('Cannot refresh: display is busy.')
|
||||||
self._as_busy = True # Immediate busy flag. Pin goes low much later.
|
self._as_busy = True # Immediate busy flag. Pin goes low much later.
|
||||||
|
|
|
@ -45,6 +45,13 @@ import time
|
||||||
import uasyncio as asyncio
|
import uasyncio as asyncio
|
||||||
from drivers.boolpalette import BoolPalette
|
from drivers.boolpalette import BoolPalette
|
||||||
|
|
||||||
|
def asyncio_running():
|
||||||
|
try:
|
||||||
|
_ = asyncio.current_task()
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
# Display resolution
|
# Display resolution
|
||||||
_EPD_WIDTH = const(400)
|
_EPD_WIDTH = const(400)
|
||||||
_BWIDTH = _EPD_WIDTH // 8
|
_BWIDTH = _EPD_WIDTH // 8
|
||||||
|
@ -111,6 +118,7 @@ class EPD(framebuf.FrameBuffer):
|
||||||
def rgb(r, g, b):
|
def rgb(r, g, b):
|
||||||
return int((r > 127) or (g > 127) or (b > 127))
|
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):
|
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.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
|
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.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 = 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.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._busy = False # Set immediately on .show(). Cleared when busy pin is logically false (physically 1).
|
||||||
self.updated = asyncio.Event()
|
self.updated = asyncio.Event()
|
||||||
self.complete = asyncio.Event()
|
self.complete = asyncio.Event()
|
||||||
|
@ -284,7 +291,9 @@ class EPD(framebuf.FrameBuffer):
|
||||||
self._busy = False
|
self._busy = False
|
||||||
self.complete.set()
|
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"
|
assert (not self._busy), "Refresh while busy"
|
||||||
await self._as_show() # split=5
|
await self._as_show() # split=5
|
||||||
|
|
||||||
|
@ -292,7 +301,7 @@ class EPD(framebuf.FrameBuffer):
|
||||||
if self._busy:
|
if self._busy:
|
||||||
raise RuntimeError('Cannot refresh: display is busy.')
|
raise RuntimeError('Cannot refresh: display is busy.')
|
||||||
self._busy = True # Immediate busy flag. Pin goes low much later.
|
self._busy = True # Immediate busy flag. Pin goes low much later.
|
||||||
if self._asyn:
|
if asyncio_running():
|
||||||
self.updated.clear()
|
self.updated.clear()
|
||||||
self.complete.clear()
|
self.complete.clear()
|
||||||
asyncio.create_task(self._as_show())
|
asyncio.create_task(self._as_show())
|
||||||
|
|
|
@ -38,4 +38,4 @@ pbusy = machine.Pin('Y4', machine.Pin.IN)
|
||||||
# Datasheet P35 indicates up to 10MHz.
|
# Datasheet P35 indicates up to 10MHz.
|
||||||
spi = machine.SPI(2, baudrate=5_000_000)
|
spi = machine.SPI(2, baudrate=5_000_000)
|
||||||
gc.collect() # Precaution before instantiating framebuf
|
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
|
||||||
|
|
|
@ -13,6 +13,6 @@ import gc
|
||||||
from drivers.epaper.pico_epaper_42 import EPD as SSD
|
from drivers.epaper.pico_epaper_42 import EPD as SSD
|
||||||
|
|
||||||
gc.collect() # Precaution before instantiating framebuf
|
gc.collect() # Precaution before instantiating framebuf
|
||||||
# Set asyn True to run asynchronous code such as epd_async.py
|
ssd = SSD() # Create a display instance
|
||||||
# Set False for normal synchronous code e.g. other demos.
|
# Set this to run demos written for arbitrary displays:
|
||||||
ssd = SSD(asyn=True) # Create a display instance
|
# ssd.demo_mode = True
|
||||||
|
|
|
@ -32,5 +32,5 @@ prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
|
||||||
pbusy = machine.Pin('Y4', machine.Pin.IN)
|
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
|
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
|
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
|
#ssd.demo_mode = True
|
||||||
|
|
Ładowanie…
Reference in New Issue