diff --git a/DRIVERS.md b/DRIVERS.md index 492c838..1f938be 100644 --- a/DRIVERS.md +++ b/DRIVERS.md @@ -39,6 +39,7 @@ a bare minimum of functionality required to support the above.      7.1.2 [EPD public methods](./DRIVERS.md#712-epd-public-methods)      7.1.3 [EPD public bound variables](./DRIVERS.md#713-epd-public-bound-variables)      7.1.4 [FeatherWing Wiring](./DRIVERS.md#714-featherwing-wiring) +      7.1.5 [Micropower use](./DRIVERS.md#715-micropower-use) 7.2 [Waveshare eInk Display HAT](./DRIVERS.md#72-waveshare-eink-display-hat)      7.2.1 [EPD constructor args](./DRIVERS.md#721-epd-constructor-args)      7.2.2 [EPD public methods](./DRIVERS.md#722-epd-public-methods) @@ -46,6 +47,9 @@ a bare minimum of functionality required to support the above. 8. [EPD Asynchronous support](./DRIVERS.md#8-epd-asynchronous-support) 9. [Writing device drivers](./DRIVERS.md#9-writing-device-drivers) +The [Micropower use](./DRIVERS.md#715-micropower-use) section is applicable to +EPD's in general but makes specific reference to the 2.9" micropower demo. + ###### [Main README](./README.md#1-introduction) # 1. Introduction @@ -551,9 +555,10 @@ An alternative is the In my testing there are differences between these alternatives. The FeatherWing shows a black border around the display. The reason for this is [unclear](https://github.com/adafruit/Adafruit_CircuitPython_IL0373/issues/11#issuecomment-763704622). -Secondly, while the FeatherWing behaves as expected the image on the flexible -display gradually degrades if the display is powered down. The white background -becomes speckled over a period of a few minutes. +In development I encountered instances where the image on the flexible display +gradually degraded after the system was powered down. The white background +becomes speckled over a period of a few minutes. I'm unsure of the reason for +this. The `epd29_lowpower` demo did not exhibit this. The interface breakout for the flexible display has an `ENA` pin which enables the display to be powered down. This facilitates micropower applications: the @@ -628,16 +633,6 @@ see below. seconds to enable viewing. This enables generic nanogui demos to be run on an EPD. -##### Micropower use - -To power down the breakout the `ENA` pin must be pulled to 0v. Some -microcontrollers can ensure that a GPIO pin is able to sink current when the -chip goes into deep sleep. In other cases the pin becomes high impedance. The -following ensures that a high impedance pin will cause `ENA` to be pulled low. -The N channel MOSFET must have a low threshold voltage. - -![Image](images/epd_enable.png) - ### 7.1.4 FeatherWing wiring The [pinout is listed here](https://learn.adafruit.com/adafruit-eink-display-breakouts/pinouts-2). @@ -671,6 +666,50 @@ 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 should be configured as open drain. +### 7.1.5 Micropower use + +Developers of micropower applications will need to familiarise themselves with +the power saving features of their board. Information may be found in +[micropython-micropower](https://github.com/peterhinch/micropython-micropower). +Some information is generic, but the code is Pyboard specific. Pyboard users +should copy `upower.py` to the filesystem root. Further power savings may be +achieved by precompiling or freezing code as this avoids the energy used by the +compiler (on each wakeup). Users of other platforms will need to know how to +enter and exit from deep sleep. + +I developed this using the breakout board linked to Wbus DIP28 adaptor and a +Pyboard D, powered from a LiPo cell. A Pyboard 1.1 could be used identically. +The test script `epd29_lowpower.py` requires `upower.py` as described above. +This simplifies access to the Pyboard RTC's alarms which can wake the board +from deep sleep. Wakeup from certain pins is also possible. + +To power down the breakout the `ENA` pin must be pulled to 0v. Some +microcontrollers can ensure that a GPIO pin is able to sink current when the +chip goes into deep sleep. In other cases the pin becomes high impedance. The +following ensures that a high impedance pin will cause `ENA` to be pulled low. +The N channel MOSFET must have a low threshold voltage. + +![Image](images/epd_enable.png) + +An alternative, slightly less efficient approach, is to pull down `ENA` with +a 2.2KΩ resistor and link it to a GPIO pin. The breakout has a 100KΩ resistor +to Vin. The 2.2KΩ resistor causes the breakout and display to assume the power +off state if the GPIO pin is high impedance. + +The test script `epd29_lowpower.py` assumes pin `Y5` linked to the breakout +enable. I used the 2.2KΩ resistor pull down. The code comments clarify the mode +of operation. The demo wakes every 30s. Real applications would do it much less +frequently with attendant power savings. + +Users of other EPD's may want to develop other means of powering down the EPD. +A p-channel MOSFET could be considered as described +[here](https://github.com/peterhinch/micropython-micropower/blob/master/HARDWARE.md#hardware-issues). + +In use I measured 500μA in the periods when the display is refreshing (a total +of 10s for each wakeup) and 58μA between wakeups. The Pyboard accounts for +about 6μA. 33μA will be used by the 100KΩ pullup on the breakout's power enable +line. I haven't attempted to figure out where the other 19μA is going. + ###### [Contents](./DRIVERS.md#contents) ## 7.2 Waveshare eInk Display HAT diff --git a/README.md b/README.md index 68cf415..b1e3205 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,9 @@ Demos for ePaper displays: * `waveshare_test.py` For the Waveshare eInk Display HAT 2.7" 176*274 display. * `epd29_sync.py` Demo for Adafruit 2.9" eInk display: emulates a seismograph. * `epd29_async.py` Asynchronous demo for Adafruit 2.9" eInk display. + * `epd29_lowpower.py` Micropower demo for Adafruit 2.9" eInk display. This doc + [Micropower use](./DRIVERS.md#715-micropower-use) should be read before + attempting to run this. Demos for Sharp displays: * `sharptest.py` Basic functionality check. diff --git a/gui/demos/epd29_lowpower.py b/gui/demos/epd29_lowpower.py new file mode 100644 index 0000000..b276831 --- /dev/null +++ b/gui/demos/epd29_lowpower.py @@ -0,0 +1,113 @@ +# epd29_sync.py Demo of synchronous code on 2.9" EPD display + +# Released under the MIT License (MIT). See LICENSE. +# Copyright (c) 2020 Peter Hinch + +# color_setup must set landcsape True, asyn False and must not set demo_mode + +from math import pi, sin +import upower +import machine +import pyb +pon = machine.Pin('Y5', machine.Pin.OUT_PP, value=1) # Power on before instantiating display +upower.lpdelay(1000) # Give the valves (tubes) time to warm up :) +from color_setup import ssd # Instantiate +from gui.core.writer import Writer +from gui.core.nanogui import refresh +from gui.core.fplot import CartesianGraph, Curve +from gui.widgets.meter import Meter +from gui.widgets.label import Label +from gui.widgets.dial import Dial, Pointer + +# Fonts +import gui.fonts.arial10 as arial10 +import gui.fonts.freesans20 as large + +wri = Writer(ssd, arial10, verbose=False) +wri.set_clip(False, False, False) + +wri_large = Writer(ssd, large, verbose=False) +wri_large.set_clip(False, False, False) + +def graph(): + row, col, ht, wd = 5, 140, 75, 150 + def populate(): + x = -0.998 + while x < 1.01: + z = 6 * pi * x + y = sin(z) / z + yield x, y + x += 0.05 + + g = CartesianGraph(wri, row, col, height = ht, width = wd, bdcolor=False) + curve2 = Curve(g, None, populate()) + Label(wri, row + ht + 5, col - 10, '-2.0 t: secs') + Label(wri, row + ht + 5, col - 8 + int(wd//2), '0.0') + Label(wri, row + ht + 5, col - 10 + wd, '2.0') + +def compass(): + dial = Dial(wri, 5, 5, height = 75, ticks = 12, bdcolor=None, + label='Direction', style = Dial.COMPASS) + ptr = Pointer(dial) + ptr.value(1 + 1j) + +def meter(): + m = Meter(wri, 5, 100, height = 75, divisions = 4, + label='Peak', style=Meter.BAR, legends=('0', '50', '100')) + m.value(0.72) + +def labels(): + row = 100 + col = 0 + Label(wri_large, row, col, 'Seismograph') + col = 140 + Label(wri, row, col + 0, 'Event time') + Label(wri, row, col + 60, '01:35', bdcolor=None) + Label(wri, row, col + 95, 'UTC') + row = 115 + Label(wri, row, col + 0, 'Event date') + Label(wri, row, col + 60, '6th Jan 2021', bdcolor=None) + + +# Populate the display - GUI and Writer code goes here +def populate(): + graph() + compass() + meter() + labels() + +# Initialise GUI clearing display. Populate frame buffer. Update diplay and +# leave in power down state ready for phsyical loss of power +def show(): + # Low power version of .wait_until_ready() + def wait_ready(): + while not ssd.ready(): + upower.lpdelay(1000) + + refresh(ssd, True) # Init and clear. busy will go True for ~5s + populate() + wait_ready() # wait for display ready (seconds) + refresh(ssd) + wait_ready() + ssd.sleep() # Put into "off" state + +# Handle initial power up and subsequent wakeup. +rtc = pyb.RTC() +# If we have a backup battery clear down any setting from a previously running program +rtc.wakeup(None) +reason = machine.reset_cause() # Why have we woken? +red = pyb.LED(1) +if reason in (machine.PWRON_RESET, machine.HARD_RESET, machine.SOFT_RESET): + # Code to run when the application is first started + aa = upower.Alarm('a') + aa.timeset(second = 39) + ab = upower.Alarm('b') + ab.timeset(second = 9) +elif reason == machine.DEEPSLEEP_RESET: + # Display on. Pin is pulled down by 2K2 so hi-z turns display off. + red.on() + show() + pon(0) # Physically power down display + red.off() + +pyb.standby()