kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
Prior to merge
rodzic
2868bcaf2e
commit
35f8b23a52
180
DRIVERS.md
180
DRIVERS.md
|
@ -55,15 +55,21 @@ access via the `Writer` and `CWriter` classes is documented
|
||||||
5. [ePaper displays](./DRIVERS.md#5-epaper-displays)
|
5. [ePaper displays](./DRIVERS.md#5-epaper-displays)
|
||||||
5.1 [Adafruit monochrome eInk Displays](./DRIVERS.md#51-adafruit-monochrome-eink-displays)
|
5.1 [Adafruit monochrome eInk Displays](./DRIVERS.md#51-adafruit-monochrome-eink-displays)
|
||||||
5.1.1 [EPD constructor args](./DRIVERS.md#511-epd-constructor-args)
|
5.1.1 [EPD constructor args](./DRIVERS.md#511-epd-constructor-args)
|
||||||
5.1.2 [EPD public methods](./DRIVERS.md#512-epd-public-methods)
|
5.1.2 [Public methods](./DRIVERS.md#512-public-methods)
|
||||||
5.1.3 [EPD public bound variables](./DRIVERS.md#513-epd-public-bound-variables)
|
5.1.3 [Events](./DRIVERS.md#513-events)
|
||||||
5.1.4 [FeatherWing Wiring](./DRIVERS.md#514-featherwing-wiring)
|
5.1.4 [Public bound variables](./DRIVERS.md#514-public-bound-variables)
|
||||||
5.1.5 [Micropower use](./DRIVERS.md#515-micropower-use)
|
5.1.5 [FeatherWing Wiring](./DRIVERS.md#515-featherwing-wiring)
|
||||||
|
5.1.6 [Micropower use](./DRIVERS.md#516-micropower-use)
|
||||||
5.2 [Waveshare eInk Display HAT](./DRIVERS.md#52-waveshare-eink-display-hat) Pi HAT repurposed for MP hosts.
|
5.2 [Waveshare eInk Display HAT](./DRIVERS.md#52-waveshare-eink-display-hat) Pi HAT repurposed for MP hosts.
|
||||||
5.2.1 [EPD constructor args](./DRIVERS.md#521-epd-constructor-args)
|
5.2.1 [EPD constructor args](./DRIVERS.md#521-epd-constructor-args)
|
||||||
5.2.2 [EPD public methods](./DRIVERS.md#522-epd-public-methods)
|
5.2.2 [Public methods](./DRIVERS.md#522-public-methods)
|
||||||
5.2.3 [EPD public bound variables](./DRIVERS.md#523-epd-public-bound-variables)
|
5.2.3 [Events](./DRIVERS.md#523-events)
|
||||||
|
5.2.4 [public bound variables](./DRIVERS.md#524-public-bound-variables)
|
||||||
5.3 [Waveshare 400x300 Pi Pico display](./DRIVERS.md#53-waveshare-400x300-pi-pico-display) Excellent display can also be used with other hosts.
|
5.3 [Waveshare 400x300 Pi Pico display](./DRIVERS.md#53-waveshare-400x300-pi-pico-display) Excellent display can also be used with other hosts.
|
||||||
|
5.3.1 [Constructor args](./DRIVERS.md#531-constructor-args)
|
||||||
|
5.3.2 [Public methods](./DRIVERS.md#532-public-methods)
|
||||||
|
5.3.3 [Events](./DRIVERS.md#533-events)
|
||||||
|
5.3.4 [Public bound variables](./DRIVERS.md#534-public-bound-variables)
|
||||||
6. [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support)
|
6. [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support)
|
||||||
7. [Writing device drivers](./DRIVERS.md#7-writing-device-drivers)
|
7. [Writing device drivers](./DRIVERS.md#7-writing-device-drivers)
|
||||||
8. [Links](./DRIVERS.md#8-links)
|
8. [Links](./DRIVERS.md#8-links)
|
||||||
|
@ -1036,9 +1042,10 @@ see below.
|
||||||
* `asyn=False` Setting this `True` invokes an asynchronous mode. See
|
* `asyn=False` Setting this `True` invokes an asynchronous mode. See
|
||||||
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
||||||
|
|
||||||
### 5.1.2 EPD public methods
|
### 5.1.2 Public methods
|
||||||
|
|
||||||
|
All methods are synchronous.
|
||||||
|
|
||||||
##### Synchronous methods
|
|
||||||
* `init` No args. Issues a hardware reset and initialises the hardware. This
|
* `init` No args. Issues a hardware reset and initialises the hardware. This
|
||||||
is called by the constructor. It needs to explicitly be called to exit from a
|
is called by the constructor. It needs to explicitly be called to exit from a
|
||||||
deep sleep.
|
deep sleep.
|
||||||
|
@ -1049,12 +1056,18 @@ see below.
|
||||||
a period: `ready` status should be checked before issuing `refresh`.
|
a period: `ready` status should be checked before issuing `refresh`.
|
||||||
* `wait_until_ready` No args. Pause until the device is ready.
|
* `wait_until_ready` No args. Pause until the device is ready.
|
||||||
|
|
||||||
##### Asynchronous methods
|
### 5.1.3 Events
|
||||||
* `updated` Asynchronous. No args. Pause until the framebuffer has been copied
|
|
||||||
to the display.
|
|
||||||
* `wait` Asynchronous. No args. Pause until the display refresh is complete.
|
|
||||||
|
|
||||||
### 5.1.3 EPD public bound variables
|
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).
|
||||||
|
* `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
|
||||||
|
`ssd.refresh()`.
|
||||||
|
EPD.
|
||||||
|
|
||||||
|
### 5.1.4 Public bound variables
|
||||||
|
|
||||||
* `height` Integer. Height in pixels. Treat as read-only.
|
* `height` Integer. Height in pixels. Treat as read-only.
|
||||||
* `width` Integer. Width in pixels. Treat as read-only.
|
* `width` Integer. Width in pixels. Treat as read-only.
|
||||||
|
@ -1063,7 +1076,11 @@ see below.
|
||||||
seconds to enable viewing. This enables generic nanogui demos to be run on an
|
seconds to enable viewing. This enables generic nanogui demos to be run on an
|
||||||
EPD.
|
EPD.
|
||||||
|
|
||||||
### 5.1.4 FeatherWing wiring
|
Note that in synchronous applications with `demo_mode=False`, `refresh` returns
|
||||||
|
while the display is updating. Applications should issue `wait_until_ready`
|
||||||
|
before issuing another refresh.
|
||||||
|
|
||||||
|
### 5.1.5 FeatherWing wiring
|
||||||
|
|
||||||
The [pinout is listed here](https://learn.adafruit.com/adafruit-eink-display-breakouts/pinouts-2).
|
The [pinout is listed here](https://learn.adafruit.com/adafruit-eink-display-breakouts/pinouts-2).
|
||||||
The `busy` line is brought out to a labelled pad on the PCB. It can be linked
|
The `busy` line is brought out to a labelled pad on the PCB. It can be linked
|
||||||
|
@ -1096,7 +1113,7 @@ The FeatherWing has a reset button which shorts the RST line to Gnd. To avoid
|
||||||
risk of damage to the microcontroller pin if the button is pressed, the pin
|
risk of damage to the microcontroller pin if the button is pressed, the pin
|
||||||
should be configured as open drain.
|
should be configured as open drain.
|
||||||
|
|
||||||
### 5.1.5 Micropower use
|
### 5.1.6 Micropower use
|
||||||
|
|
||||||
Developers of micropower applications will need to familiarise themselves with
|
Developers of micropower applications will need to familiarise themselves with
|
||||||
the power saving features of their board. Information may be found in
|
the power saving features of their board. Information may be found in
|
||||||
|
@ -1210,12 +1227,14 @@ 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`
|
* `asyn=False` Setting this `True` invokes an asynchronous mode. See
|
||||||
|
[EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
||||||
|
|
||||||
### 5.2.2 EPD public methods
|
### 5.2.2 EPD public methods
|
||||||
|
|
||||||
##### Synchronous methods
|
All methods are synchronous.
|
||||||
* `init` No args. Issues a hardware reset and initialises the hardware. This
|
|
||||||
|
* `init` No args. Issues a hardware reset and initialises the hardware. This
|
||||||
is called by the constructor. It needs to explicitly be called to exit from a
|
is called by the constructor. It needs to explicitly be called to exit from a
|
||||||
deep sleep.
|
deep sleep.
|
||||||
* `sleep` No args. Puts the display into deep sleep. If called while a refresh
|
* `sleep` No args. Puts the display into deep sleep. If called while a refresh
|
||||||
|
@ -1225,12 +1244,17 @@ Pins 26-40 unused and omitted.
|
||||||
a period: `ready` status should be checked before issuing `refresh`.
|
a period: `ready` status should be checked before issuing `refresh`.
|
||||||
* `wait_until_ready` No args. Pause until the device is ready.
|
* `wait_until_ready` No args. Pause until the device is ready.
|
||||||
|
|
||||||
##### Asynchronous methods
|
### 5.2.3 Events
|
||||||
* `updated` Asynchronous. No args. Pause until the framebuffer has been copied
|
|
||||||
to the display.
|
|
||||||
* `wait` Asynchronous. No args. Pause until the display refresh is complete.
|
|
||||||
|
|
||||||
### 5.2.3 EPD public bound variables
|
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).
|
||||||
|
* `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
|
||||||
|
`ssd.refresh()`.
|
||||||
|
|
||||||
|
### 5.2.4 Public bound variables
|
||||||
|
|
||||||
* `height` Integer. Height in pixels. Treat as read-only.
|
* `height` Integer. Height in pixels. Treat as read-only.
|
||||||
* `width` Integer. Width in pixels. Treat as read-only.
|
* `width` Integer. Width in pixels. Treat as read-only.
|
||||||
|
@ -1239,6 +1263,10 @@ Pins 26-40 unused and omitted.
|
||||||
seconds to enable viewing. This enables generic nanogui demos to be run on an
|
seconds to enable viewing. This enables generic nanogui demos to be run on an
|
||||||
EPD.
|
EPD.
|
||||||
|
|
||||||
|
Note that in synchronous applications with `demo_mode=False`, `refresh` returns
|
||||||
|
while the display is updating. Applications should issue `wait_until_ready`
|
||||||
|
before issuing another refresh.
|
||||||
|
|
||||||
## 5.3 Waveshare 400x300 Pi Pico display
|
## 5.3 Waveshare 400x300 Pi Pico display
|
||||||
|
|
||||||
The driver for this display now supports partial updates.
|
The driver for this display now supports partial updates.
|
||||||
|
@ -1255,6 +1283,8 @@ gc.collect() # Precaution before instantiating framebuf.
|
||||||
ssd = SSD() # Create a display instance. For normal applications.
|
ssd = SSD() # Create a display instance. For normal applications.
|
||||||
# ssd = SSD(asyn=True) # Alternative for asynchronous applications.
|
# ssd = SSD(asyn=True) # Alternative for asynchronous applications.
|
||||||
```
|
```
|
||||||
|
### 5.3.1 Constructor args
|
||||||
|
|
||||||
For other hosts the pins need to be specified in `color_setup.py` via the
|
For other hosts the pins need to be specified in `color_setup.py` via the
|
||||||
following constructor args:
|
following constructor args:
|
||||||
|
|
||||||
|
@ -1266,7 +1296,9 @@ following constructor args:
|
||||||
* `asyn=False` Set `True` for asynchronous applications. Leave `False` for
|
* `asyn=False` Set `True` for asynchronous applications. Leave `False` for
|
||||||
microgui where the arg has no effect.
|
microgui where the arg has no effect.
|
||||||
|
|
||||||
##### Synchronous methods
|
### 5.3.2 Public methods
|
||||||
|
|
||||||
|
All methods are synchronous.
|
||||||
|
|
||||||
* `init` No args. Issues a hardware reset and initialises the hardware. This
|
* `init` No args. Issues a hardware reset and initialises the hardware. This
|
||||||
is called by the constructor. It needs to explicitly be called to exit from a
|
is called by the constructor. It needs to explicitly be called to exit from a
|
||||||
|
@ -1280,22 +1312,37 @@ following constructor args:
|
||||||
* `set_partial()` Enable partial updates.
|
* `set_partial()` Enable partial updates.
|
||||||
* `set_full()` Restore normal update operation.
|
* `set_full()` Restore normal update operation.
|
||||||
|
|
||||||
After issuing `set_partial()`, subsequent updates will be partial. Normal
|
After issuing `set_partial()`, subsequent updates will be partial. Normal
|
||||||
updates are restored by issuing `set_full()`. These methods should not be
|
updates are restored by issuing `set_full()`. These methods should not be
|
||||||
issued while an update is in progress.
|
issued while an update is in progress.
|
||||||
|
|
||||||
Partial updates are fast and visually unobtrusive but they are prone to
|
Partial updates are fast and visually unobtrusive but they are prone to
|
||||||
ghosting.
|
ghosting.
|
||||||
|
|
||||||
##### Asynchronous methods
|
### 5.3.3 Events
|
||||||
|
|
||||||
* `wait` No args. If an update is in progress, pause until the display refresh
|
These provide synchronisation in asynchronous applications where `asyn=True`.
|
||||||
is complete, otherwise return is immediate.
|
They are only needed in more advanced asynchronous applications and their use
|
||||||
* `updated` No args. Pause until the framebuffer has been copied to the
|
is discussed in [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support).
|
||||||
display. It is now safe to modify the framebuf, but display update may still
|
* `updated` Set when framebuf has been copied to device. It is now safe to
|
||||||
be in progress.
|
modify widgets without risk of display corruption.
|
||||||
|
* `complete` Set when display update is complete. It is now safe to call
|
||||||
|
`ssd.refresh()`.
|
||||||
|
|
||||||
###### [Contents](./DRIVERS.md#contents)
|
### 5.3.4 Public bound variables
|
||||||
|
|
||||||
|
* `height` Integer. Height in pixels. Treat as read-only.
|
||||||
|
* `width` Integer. Width in pixels. Treat as read-only.
|
||||||
|
* `demo_mode=False` Boolean. If set `True` after instantiating, `refresh()`
|
||||||
|
will block until display update is complete, and then for a further two
|
||||||
|
seconds to enable viewing. This enables generic nanogui demos to be run on an
|
||||||
|
EPD.
|
||||||
|
|
||||||
|
Note that in synchronous applications with `demo_mode=False`, `refresh` returns
|
||||||
|
while the display is updating. Applications should issue `wait_until_ready`
|
||||||
|
before issuing another refresh.
|
||||||
|
|
||||||
|
###### [Contents](./DRIVERS.md#contents)
|
||||||
|
|
||||||
# 6. EPD Asynchronous support
|
# 6. EPD Asynchronous support
|
||||||
|
|
||||||
|
@ -1303,9 +1350,9 @@ 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
|
Further, the comments address the case where the driver is instantiated with
|
||||||
`asyn=True`. In the default case an EPD can be used like any other display.
|
`asyn=True`.
|
||||||
|
|
||||||
When GUI code issues
|
When synchronous code issues
|
||||||
```python
|
```python
|
||||||
refresh(ssd) # Several seconds on an EPD
|
refresh(ssd) # Several seconds on an EPD
|
||||||
```
|
```
|
||||||
|
@ -1313,45 +1360,48 @@ the GUI updates the frame buffer contents and calls the device driver's `show`
|
||||||
method. This causes the contents to be copied to the display hardware and a
|
method. This causes the contents to be copied to the display hardware and a
|
||||||
redraw to be inititated. This typically takes several seconds unless partial
|
redraw to be inititated. This typically takes several seconds unless partial
|
||||||
updates are enabled. The method (and hence `refresh`) blocks until the physical
|
updates are enabled. The method (and hence `refresh`) blocks until the physical
|
||||||
refresh is complete. The device drivers block for an additional 2 seconds: this
|
refresh is complete. If `demo_mode` is set, device drivers block for an
|
||||||
enables demos written for normal displays to work (the 2 second pause allowing
|
additional 2 seconds to enable demos written for normal displays to work (the
|
||||||
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, and the process is
|
||||||
modified if, in `color_setup.py`, an `EPD` is instantiated with `asyn=True`. In
|
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
|
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
|
task `._as_show` and returns immediately. The task yields to the scheduler as
|
||||||
necessary to ensure that blocking is limited to around 30ms. With `asyn=True`
|
necessary to ensure that blocking is limited to around 30ms. If screen updates
|
||||||
synchronous applications will not work: it is necessary to take control of the
|
take place at a low rate the only precaution necessary is to ensure that
|
||||||
sequencing of refresh.
|
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:
|
||||||
|
|
||||||
In this case user code should ensure that changes to the framebuffer are
|
|
||||||
postponed until the buffer contents have been copied to the display. Further, a
|
|
||||||
subsequent refresh should be postponed until the physical refresh is complete.
|
|
||||||
To achieve this the `ssd` instance has the following methods:
|
|
||||||
* `.updated()` (async) Pauses until the buffer is copied to the device.
|
|
||||||
* `.wait()` (async) Pauses until physical refresh is complete.
|
|
||||||
* `.ready()` (synchronous) Immediate return: `True` if physical refresh is
|
|
||||||
complete.
|
|
||||||
|
|
||||||
If `.refresh()` is issued before the physical display refresh is complete a
|
|
||||||
`RuntimeError` will occur.
|
|
||||||
|
|
||||||
The following illustrates the kind of approach which may be used with a display
|
|
||||||
instantiated with `asyn=True`:
|
|
||||||
```python
|
```python
|
||||||
|
async def run():
|
||||||
while True:
|
while True:
|
||||||
# Before refresh, ensure that a previous refresh is complete
|
# get data
|
||||||
# Not strictly necessary if .updated() used after refresh.
|
# Update screen widgets
|
||||||
await ssd.wait()
|
ssd.refresh() # Launches background refresh
|
||||||
refresh(ssd) # Immediate return. Creates a task to copy content to EPD.
|
await asyncio.sleep(60)
|
||||||
# Wait until the framebuf content has been passed to EPD.
|
|
||||||
await ssd.updated()
|
|
||||||
# Trigger an event which allows other tasks to update the
|
|
||||||
# framebuffer in background
|
|
||||||
evt.set() # Waiting task must clear the Event
|
|
||||||
await asyncio.sleep(180) #
|
|
||||||
```
|
```
|
||||||
|
With `asyn=True` 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
|
||||||
|
application has two `Event` instances, one requesting refresh and the other
|
||||||
|
requesting widget updates:
|
||||||
|
```python
|
||||||
|
async def refresh_task():
|
||||||
|
while True:
|
||||||
|
await refresh_request.wait() # Another task has requested refresh
|
||||||
|
refresh_request.clear()
|
||||||
|
ssd.refresh() # Launch background refresh
|
||||||
|
await ssd.updated.wait() # Wait until framebuf copied to device
|
||||||
|
data_request.set() # Ask other tasks to update widgets
|
||||||
|
await ssd.complete.wait()
|
||||||
|
# Now safe to respond to refresh_request and issue ssd.refresh()
|
||||||
|
```
|
||||||
|
The `updated` and `complete` events are cleared when `ssd.refresh` is called
|
||||||
|
and are set as the background refresh proceeds.
|
||||||
|
|
||||||
Some displays support partial updates. This is currently restricted to the
|
Some displays support partial updates. This is currently restricted to the
|
||||||
[Pico Epaper 4.2"](https://www.waveshare.com/pico-epaper-4.2.htm). Partial
|
[Pico Epaper 4.2"](https://www.waveshare.com/pico-epaper-4.2.htm). Partial
|
||||||
updates are much faster and are visually non-intrusive at a cost of "ghosting"
|
updates are much faster and are visually non-intrusive at a cost of "ghosting"
|
||||||
|
@ -1363,7 +1413,7 @@ synchronous methods are provided:
|
||||||
These must not be issued while an update is in progress.
|
These must not be issued while an update is in progress.
|
||||||
|
|
||||||
See the demo `eclock_async.py` for an example of managing partial updates: once
|
See the demo `eclock_async.py` for an example of managing partial updates: once
|
||||||
per hour a full update is performed.
|
per hour (on the half-hour) a full update is performed.
|
||||||
|
|
||||||
###### [Contents](./DRIVERS.md#contents)
|
###### [Contents](./DRIVERS.md#contents)
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,8 @@ class EPD(framebuf.FrameBuffer):
|
||||||
self._lsc = landscape
|
self._lsc = landscape
|
||||||
self._asyn = asyn
|
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()
|
||||||
# Dimensions in pixels. Waveshare code is portrait mode.
|
# Dimensions in pixels. Waveshare code is portrait mode.
|
||||||
# Public bound variables required by nanogui.
|
# Public bound variables required by nanogui.
|
||||||
self.width = 264 if landscape else 176
|
self.width = 264 if landscape else 176
|
||||||
|
@ -130,15 +131,6 @@ class EPD(framebuf.FrameBuffer):
|
||||||
dt = ticks_diff(ticks_ms(), t)
|
dt = ticks_diff(ticks_ms(), t)
|
||||||
print('wait_until_ready {}ms {:5.1f}mins'.format(dt, dt/60_000))
|
print('wait_until_ready {}ms {:5.1f}mins'.format(dt, dt/60_000))
|
||||||
|
|
||||||
async def wait(self):
|
|
||||||
await asyncio.sleep_ms(0) # Ensure tasks run that might make it unready
|
|
||||||
while not self.ready():
|
|
||||||
await asyncio.sleep_ms(100)
|
|
||||||
|
|
||||||
# Pause until framebuf has been copied to device.
|
|
||||||
async def updated(self):
|
|
||||||
await self._updated.wait()
|
|
||||||
|
|
||||||
# For polling in asynchronous code. Just checks pin state.
|
# For polling in asynchronous code. Just checks pin state.
|
||||||
# 0 == busy. Comment in official code is wrong. Code is correct.
|
# 0 == busy. Comment in official code is wrong. Code is correct.
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
@ -196,13 +188,13 @@ class EPD(framebuf.FrameBuffer):
|
||||||
await asyncio.sleep_ms(0)
|
await asyncio.sleep_ms(0)
|
||||||
t = ticks_ms()
|
t = ticks_ms()
|
||||||
|
|
||||||
self._updated.set() # framebuf has now been copied to the device
|
self.updated.set() # framebuf has now been copied to the device
|
||||||
self._updated.clear()
|
|
||||||
cmd(b'\x12') # DISPLAY_REFRESH
|
cmd(b'\x12') # DISPLAY_REFRESH
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
while self._busy() == 0:
|
while self._busy() == 0:
|
||||||
await asyncio.sleep_ms(200) # Don't release lock until update is complete
|
await asyncio.sleep_ms(200) # Don't release lock until update is complete
|
||||||
self._as_busy = False
|
self._as_busy = False
|
||||||
|
self.complete.set()
|
||||||
|
|
||||||
# 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)):
|
||||||
|
@ -210,6 +202,8 @@ class EPD(framebuf.FrameBuffer):
|
||||||
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
|
||||||
|
self.updated.clear()
|
||||||
|
self.complete.clear()
|
||||||
asyncio.create_task(self._as_show())
|
asyncio.create_task(self._as_show())
|
||||||
return
|
return
|
||||||
t = ticks_us()
|
t = ticks_us()
|
||||||
|
|
|
@ -42,7 +42,8 @@ class EPD(framebuf.FrameBuffer):
|
||||||
# ._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
|
||||||
self._updated = asyncio.Event()
|
self.updated = asyncio.Event()
|
||||||
|
self.complete = asyncio.Event()
|
||||||
# Public bound variables required by nanogui.
|
# Public bound variables required by nanogui.
|
||||||
# Dimensions in pixels as seen by nanogui (landscape mode).
|
# Dimensions in pixels as seen by nanogui (landscape mode).
|
||||||
self.width = 296 if landscape else 128
|
self.width = 296 if landscape else 128
|
||||||
|
@ -114,16 +115,6 @@ class EPD(framebuf.FrameBuffer):
|
||||||
while not self.ready():
|
while not self.ready():
|
||||||
sleep_ms(100)
|
sleep_ms(100)
|
||||||
|
|
||||||
# Asynchronous wait on ready state. Pause (4.9s) for physical refresh.
|
|
||||||
async def wait(self):
|
|
||||||
await asyncio.sleep_ms(0) # Ensure tasks run that might make it unready
|
|
||||||
while not self.ready():
|
|
||||||
await asyncio.sleep_ms(100)
|
|
||||||
|
|
||||||
# Pause until framebuf has been copied to device.
|
|
||||||
async def updated(self):
|
|
||||||
await self._updated.wait()
|
|
||||||
|
|
||||||
# Return immediate status. Pin state: 0 == busy.
|
# Return immediate status. Pin state: 0 == busy.
|
||||||
def ready(self):
|
def ready(self):
|
||||||
return not(self._as_busy or (self._busy() == 0))
|
return not(self._as_busy or (self._busy() == 0))
|
||||||
|
@ -162,8 +153,7 @@ class EPD(framebuf.FrameBuffer):
|
||||||
t = ticks_ms()
|
t = ticks_ms()
|
||||||
|
|
||||||
cmd(b'\x11') # Data stop
|
cmd(b'\x11') # Data stop
|
||||||
self._updated.set()
|
self.updated.set()
|
||||||
self._updated.clear()
|
|
||||||
sleep_us(20) # Allow for data coming back: currently ignore this
|
sleep_us(20) # Allow for data coming back: currently ignore this
|
||||||
cmd(b'\x12') # DISPLAY_REFRESH
|
cmd(b'\x12') # DISPLAY_REFRESH
|
||||||
# busy goes low now, for ~4.9 seconds.
|
# busy goes low now, for ~4.9 seconds.
|
||||||
|
@ -171,6 +161,7 @@ class EPD(framebuf.FrameBuffer):
|
||||||
while self._busy() == 0:
|
while self._busy() == 0:
|
||||||
await asyncio.sleep_ms(200)
|
await asyncio.sleep_ms(200)
|
||||||
self._as_busy = False
|
self._as_busy = False
|
||||||
|
self.complete.set()
|
||||||
|
|
||||||
# draw the current frame memory.
|
# draw the current frame memory.
|
||||||
def show(self, buf1=bytearray(1)):
|
def show(self, buf1=bytearray(1)):
|
||||||
|
@ -178,6 +169,8 @@ class EPD(framebuf.FrameBuffer):
|
||||||
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.
|
||||||
|
self.updated.clear()
|
||||||
|
self.complete.clear()
|
||||||
asyncio.create_task(self._as_show())
|
asyncio.create_task(self._as_show())
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -120,10 +120,17 @@ class EPD(framebuf.FrameBuffer):
|
||||||
self.spi.init(baudrate = 4_000_000)
|
self.spi.init(baudrate = 4_000_000)
|
||||||
self._asyn = asyn
|
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()
|
||||||
|
|
||||||
|
# Public bound variables required by nanogui.
|
||||||
|
# Dimensions in pixels as seen by nanogui
|
||||||
self.width = _EPD_WIDTH
|
self.width = _EPD_WIDTH
|
||||||
self.height = _EPD_HEIGHT
|
self.height = _EPD_HEIGHT
|
||||||
|
# Other public bound variable.
|
||||||
|
# Special mode enables demos written for generic displays to run.
|
||||||
|
self.demo_mode = False
|
||||||
|
|
||||||
self.buf = bytearray(_EPD_HEIGHT * _BWIDTH)
|
self.buf = bytearray(_EPD_HEIGHT * _BWIDTH)
|
||||||
self.mvb = memoryview(self.buf)
|
self.mvb = memoryview(self.buf)
|
||||||
self.ibuf = bytearray(1000) # Buffer for inverted pixels
|
self.ibuf = bytearray(1000) # Buffer for inverted pixels
|
||||||
|
@ -241,16 +248,6 @@ class EPD(framebuf.FrameBuffer):
|
||||||
while not self.ready():
|
while not self.ready():
|
||||||
time.sleep_ms(100)
|
time.sleep_ms(100)
|
||||||
|
|
||||||
async def wait(self):
|
|
||||||
while not self.ready():
|
|
||||||
await asyncio.sleep_ms(100)
|
|
||||||
|
|
||||||
# Pause until framebuf has been copied to device.
|
|
||||||
async def updated(self):
|
|
||||||
self._updated.clear()
|
|
||||||
await self._updated.wait()
|
|
||||||
self._updated.clear()
|
|
||||||
|
|
||||||
# For polling in asynchronous code. Just checks pin state.
|
# For polling in asynchronous code. Just checks pin state.
|
||||||
# 0 == busy. Comment in official code is wrong. Code is correct.
|
# 0 == busy. Comment in official code is wrong. Code is correct.
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
@ -280,11 +277,12 @@ class EPD(framebuf.FrameBuffer):
|
||||||
nbytes = min(nbytes, nleft)
|
nbytes = min(nbytes, nleft)
|
||||||
if not ((npass := npass + 1) % 16):
|
if not ((npass := npass + 1) % 16):
|
||||||
await asyncio.sleep_ms(0) # Control blocking time
|
await asyncio.sleep_ms(0) # Control blocking time
|
||||||
self._updated.set()
|
self.updated.set()
|
||||||
self.send_command(b"\x12") # Nonblocking .display_on()
|
self.send_command(b"\x12") # Nonblocking .display_on()
|
||||||
while not self.busy_pin(): # Wait on display hardware
|
while not self.busy_pin(): # Wait on display hardware
|
||||||
await asyncio.sleep_ms(0)
|
await asyncio.sleep_ms(0)
|
||||||
self._busy = False
|
self._busy = False
|
||||||
|
self.complete.set()
|
||||||
|
|
||||||
async def do_refresh(self, split): # For micro-gui
|
async def do_refresh(self, split): # For micro-gui
|
||||||
assert (not self._busy), "Refresh while busy"
|
assert (not self._busy), "Refresh while busy"
|
||||||
|
@ -295,6 +293,8 @@ class EPD(framebuf.FrameBuffer):
|
||||||
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 self._asyn:
|
||||||
|
self.updated.clear()
|
||||||
|
self.complete.clear()
|
||||||
asyncio.create_task(self._as_show())
|
asyncio.create_task(self._as_show())
|
||||||
return
|
return
|
||||||
self.send_command(b"\x13")
|
self.send_command(b"\x13")
|
||||||
|
@ -308,6 +308,10 @@ class EPD(framebuf.FrameBuffer):
|
||||||
nbytes = min(nbytes, nleft)
|
nbytes = min(nbytes, nleft)
|
||||||
self._busy = False
|
self._busy = False
|
||||||
self.display_on()
|
self.display_on()
|
||||||
|
if not self.demo_mode:
|
||||||
|
# Immediate return to avoid blocking the whole application.
|
||||||
|
# User should wait for ready before calling refresh()
|
||||||
|
return
|
||||||
self.wait_until_ready()
|
self.wait_until_ready()
|
||||||
time.sleep_ms(2000) # Give time for user to see result
|
time.sleep_ms(2000) # Give time for user to see result
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,12 @@ async def test():
|
||||||
wri.set_clip(True, True, False) # Clip to screen, no wrap
|
wri.set_clip(True, True, False) # Clip to screen, no wrap
|
||||||
refresh(ssd, True)
|
refresh(ssd, True)
|
||||||
if epaper:
|
if epaper:
|
||||||
await ssd.wait()
|
await ssd.complete.wait()
|
||||||
ec = EClock(wri, 10, 10, 200, fgcolor=WHITE, bgcolor=BLACK)
|
ec = EClock(wri, 10, 10, 200, fgcolor=WHITE, bgcolor=BLACK)
|
||||||
ec.value(t := time.localtime()) # Initial drawing
|
ec.value(t := time.localtime()) # Initial drawing
|
||||||
refresh(ssd)
|
refresh(ssd)
|
||||||
if epaper:
|
if epaper:
|
||||||
await ssd.wait()
|
await ssd.complete.wait()
|
||||||
mins = t[4]
|
mins = t[4]
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
@ -51,7 +51,7 @@ async def test():
|
||||||
ec.value(t)
|
ec.value(t)
|
||||||
refresh(ssd)
|
refresh(ssd)
|
||||||
if epaper:
|
if epaper:
|
||||||
await ssd.wait()
|
await ssd.complete.wait()
|
||||||
await asyncio.sleep(10)
|
await asyncio.sleep(10)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
Ładowanie…
Reference in New Issue