Initial commit
commit
ca8a532054
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Waveshare
|
||||
Copyright (c) 2018 Mike Causer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,32 @@
|
|||
# MicroPython Waveshare e-Paper
|
||||
|
||||
MicroPython drivers for various Waveshare e-Paper displays, based on the original Waveshare Raspberry Pi examples found in the [wiki](https://www.waveshare.com/wiki/Main_Page).
|
||||
|
||||
![demo](docs/demo.jpg)
|
||||
|
||||
| Waveshare e-Paper Module | Colors | Grey Level | Resolution px | Display mm | Outline mm | Partial Refresh | Full Refresh | Interface | Pi Header | Controller | Display |
|
||||
| ----------------------------------------------------------------------------------------- | -------------------- |:----------:|:-------------:|:--------------:|:-------------:|:---------------:|:------------:|:---------:|:---------:|:-----------|:----------------------------------------------------------------------------------------------------------------------------------------------------:|
|
||||
| [1.54inch e-Paper Module](https://www.waveshare.com/wiki/1.54inch_e-Paper_Module) | Black, White | 2 | 200 × 200 | 27.60 × 27.60 | 48.0 × 33.0 | 0.3 | 2 | SPI | No | ? | [GDEH0154D27](http://www.eink-display.com/new-1-54-inch-partial-refresh-bi-stable-e0154a05-e-paper-display-electronic-paper-screen-gdeh0154d27.html) |
|
||||
| [1.54inch e-Paper Module (B)](https://www.waveshare.com/wiki/1.54inch_e-Paper_Module_(B)) | Black, White, Red | 2 | 200 × 200 | 27.60 × 27.60 | 48.0 × 33.0 | N/A | 8 | SPI | No | ? | [GDEW0154Z04](http://www.eink-display.com/1-54-inch-3-color-ultra-low-power-consumption-e-paper-display-screen-spi-interface-gdew0154z04.html) |
|
||||
| [1.54inch e-Paper Module (C)](https://www.waveshare.com/wiki/1.54inch_e-Paper_Module_(C)) | Black, White, Yellow | 2 | 152 × 152 | 27.51 × 27.51 | 48.0 × 33.0 | N/A | 27 | SPI | No | ? | [GDEW0154C39](http://www.eink-display.com/new-1-54-inch-bwy-three-colors-e-ink-e-paper-display-panel-black-write-and-yellow-gdew0154c39.html) |
|
||||
| [2.13inch e-Paper HAT](https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT) | Black, White | 2 | 250 × 122 | 48.55 × 23.71 | 65.0 × 30.2 | 0.3 | 2 | SPI | Yes | IL3895 | [GDEH0213B1](http://www.eink-display.com/new-2-13-inch-partial-refresh-e-paper-display-250x122-electronic-paper-screen-gdeh0213b1.html) |
|
||||
| [2.13inch e-Paper HAT (B)](https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_(B)) | Black, White, Red | 2 | 212 × 104 | 48.55 × 23.71 | 65.0 × 30.2 | N/A | 15 | SPI | Yes | ? | [GDEW0213Z16](http://www.eink-display.com/2-13-inch-three-color-low-power-e-paper-display-electronic-paper-screen-gdew0213z16.html) |
|
||||
| [2.13inch e-Paper HAT (C)](https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_(C)) | Black, White, Yellow | 2 | 212 × 104 | 48.55 × 23.71 | 65.0 × 30.2 | N/A | 15 | SPI | Yes | ? | [GDEW0213C38](http://www.eink-display.com/new-2-13-inch-bwy-three-colors-e-ink-e-paper-display-panel-black-write-and-yellow-gdew0213c38.html) |
|
||||
| [2.7inch e-Paper HAT](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT) | Black, White | 2 | 264 × 176 | 57.29 × 38.19 | 85.0 × 56.0 | N/A | 6 | SPI | Yes | ? | [GDEW027W3](http://www.eink-display.com/2-7-inch-ultra-low-power-consumption-high-contrast-small-size-electronic-paper-display-gdew027w3.html) |
|
||||
| [2.7inch e-Paper HAT (B)](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT_(B)) | Black, White, Red | 2 | 264 × 176 | 57.29 × 38.19 | 85.0 × 56.0 | N/A | 15 | SPI | Yes | ? | [GDEW027C44](http://www.eink-display.com/2-7-inch-three-color-low-power-consumption-bi-stable-electronic-paper-screen-gdew027c44.html) |
|
||||
| [2.9inch e-Paper Module](https://www.waveshare.com/wiki/2.9inch_e-Paper_Module) | Black, White | 2 | 296 × 128 | 66.89 × 29.05 | 89.5 × 38.0 | 0.3 | 2 | SPI | No | IL3820 | [GDEH029A1](http://www.eink-display.com/2-9-inch-partial-update-bi-stable-e-paper-display-electronic-paper-screen-gdeh029a1.html) |
|
||||
| [2.9inch e-Paper Module (B)](https://www.waveshare.com/wiki/2.9inch_e-Paper_Module_(B)) | Black, White, Red | 2 | 296 × 128 | 66.89 × 29.05 | 89.5 × 38.0 | N/A | 15 | SPI | No | IL3820? | [GDEW029Z10](http://www.eink-display.com/2-9-inch-three-color-bi-stable-e-paper-display-electronic-paper-screen-gdew029z10.html) |
|
||||
| [2.9inch e-Paper Module (C)](https://www.waveshare.com/wiki/2.9inch_e-Paper_Module_(C)) | Black, White, Yellow | 2 | 296 × 128 | 66.89 × 29.05 | 89.5 × 38.0 | N/A | 15 | SPI | No | IL3820? | [GDEW029C32](http://www.eink-display.com/new-2-9-inch-bwy-three-colors-e-ink-e-paper-display-panel-black-write-and-yellow-gdew029c32.html) |
|
||||
| [4.2inch e-Paper Module](https://www.waveshare.com/wiki/4.2inch_e-Paper_Module) | Black, White | 2 | 400 × 300 | 84.80 × 63.60 | 103.0 × 78.5 | N/A | 4 | SPI | No | ? | [GDEW042T2](http://www.eink-display.com/4-2-inch-ultra-low-power-consumption-400x300-resolution-electronic-paper-screen-gdew042t2.html) |
|
||||
| [4.2inch e-Paper Module (B)](https://www.waveshare.com/wiki/4.2inch_e-Paper_Module_(B)) | Black, White, Red | 2 | 400 × 300 | 84.80 × 63.60 | 103.0 × 78.5 | N/A | 15 | SPI | No | ? | [GDEW042Z15](http://www.eink-display.com/4-2-inch-three-color-bi-stable-electronic-paper-screen-gdew042z15.html) |
|
||||
| [4.2inch e-Paper Module (C)](https://www.waveshare.com/wiki/4.2inch_e-Paper_Module_(C)) | Black, White, Yellow | 2 | 400 × 300 | 84.80 × 63.60 | 103.0 × 78.5 | N/A | 15 | SPI | No | ? | [GDEW042C37](http://www.eink-display.com/new-4-2-inch-bwy-three-colors-e-ink-e-paper-display-panel-black-write-and-yellow-gdew042c37.html) |
|
||||
| [4.3inch e-Paper UART Module](https://www.waveshare.com/wiki/4.3inch_e-Paper_UART_Module) | Black, White | 4 | 800 × 600 | 88.00 × 66.00 | 118.0 × 75.0 | N/A | 1.5 | UART | No | ? | [GDE043A2](http://www.eink-display.com/4-3-inch-low-power-e-paper-display-electronic-paper-screen-gde043a2.html) |
|
||||
| [7.5inch e-Paper HAT](https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT) | Black, White | 2 | 600 × 384 | 163.20 × 97.92 | 170.2 × 111.2 | N/A | 6 | SPI | Yes | ? | [GDEW075T8](http://www.eink-display.com/7-5-inch-large-size-ultra-wide-viewing-angle-bi-stable-e-paper-display-e-paper-monitor-gdew075t8.html) |
|
||||
| [7.5inch e-Paper HAT (B)](https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT_(B)) | Black, White, Red | 2 | 170.2 × 111.2 | 163.20 × 97.92 | 170.2 × 111.2 | N/A | 31 | SPI | Yes | ? | [GDEW075Z09](http://www.eink-display.com/7-5-inch-large-size-three-color-bi-stable-e-paper-display-e-paper-monitor-gdew075z09.html) |
|
||||
| [7.5inch e-Paper HAT (C)](https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT_(C)) | Black, White, Yellow | 2 | 640 × 384 | 163.20 × 97.92 | 170.2 × 111.2 | N/A | 31 | SPI | Yes | ? | [GDEW075C21](http://www.eink-display.com/new-7-5-inch-bwy-three-colors-e-ink-e-paper-display-panel-black-write-and-yellow-gdew075c21.html) |
|
||||
|
||||
## Links
|
||||
|
||||
* [Waveshare Wiki](https://www.waveshare.com/wiki/Main_Page)
|
||||
* [micropython.org](http://micropython.org)
|
||||
* [Docs on framebuf](http://docs.micropython.org/en/latest/pyboard/library/framebuf.html)
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 244 KiB |
|
@ -0,0 +1,146 @@
|
|||
# MicroPython library for Waveshare 1.54" B/W e-paper display GDEH0154D27
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(200)
|
||||
EPD_HEIGHT = const(200)
|
||||
|
||||
# Display commands
|
||||
DRIVER_OUTPUT_CONTROL = const(0x01)
|
||||
BOOSTER_SOFT_START_CONTROL = const(0x0C)
|
||||
#GATE_SCAN_START_POSITION = const(0x0F)
|
||||
DEEP_SLEEP_MODE = const(0x10)
|
||||
DATA_ENTRY_MODE_SETTING = const(0x11)
|
||||
#SW_RESET = const(0x12)
|
||||
#TEMPERATURE_SENSOR_CONTROL = const(0x1A)
|
||||
MASTER_ACTIVATION = const(0x20)
|
||||
#DISPLAY_UPDATE_CONTROL_1 = const(0x21)
|
||||
DISPLAY_UPDATE_CONTROL_2 = const(0x22)
|
||||
WRITE_RAM = const(0x24)
|
||||
WRITE_VCOM_REGISTER = const(0x2C)
|
||||
WRITE_LUT_REGISTER = const(0x32)
|
||||
SET_DUMMY_LINE_PERIOD = const(0x3A)
|
||||
SET_GATE_TIME = const(0x3B) # not in datasheet
|
||||
#BORDER_WAVEFORM_CONTROL = const(0x3C)
|
||||
SET_RAM_X_ADDRESS_START_END_POSITION = const(0x44)
|
||||
SET_RAM_Y_ADDRESS_START_END_POSITION = const(0x45)
|
||||
SET_RAM_X_ADDRESS_COUNTER = const(0x4E)
|
||||
SET_RAM_Y_ADDRESS_COUNTER = const(0x4F)
|
||||
TERMINATE_FRAME_READ_WRITE = const(0xFF) # aka NOOP
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
LUT_FULL_UPDATE = bytearray(b'\x02\x02\x01\x11\x12\x12\x22\x22\x66\x69\x69\x59\x58\x99\x99\x88\x00\x00\x00\x00\xF8\xB4\x13\x51\x35\x51\x51\x19\x01\x00')
|
||||
LUT_PARTIAL_UPDATE = bytearray(b'\x10\x18\x18\x08\x18\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x14\x44\x12\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(DRIVER_OUTPUT_CONTROL)
|
||||
self._data(bytearray([(EPD_HEIGHT - 1) & 0xFF]))
|
||||
self._data(bytearray([((EPD_HEIGHT - 1) >> 8) & 0xFF]))
|
||||
self._data(bytearray([0x00])) # GD = 0 SM = 0 TB = 0
|
||||
self._command(BOOSTER_SOFT_START_CONTROL, b'\xD7\xD6\x9D')
|
||||
self._command(WRITE_VCOM_REGISTER, b'\xA8') # VCOM 7C
|
||||
self._command(SET_DUMMY_LINE_PERIOD, b'\x1A') # 4 dummy lines per gate
|
||||
self._command(SET_GATE_TIME, b'\x08') # 2us per line
|
||||
self._command(DATA_ENTRY_MODE_SETTING, b'\x03') # X increment Y increment
|
||||
self.set_lut(self.LUT_FULL_UPDATE)
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def set_lut(self, lut):
|
||||
self._command(WRITE_LUT_REGISTER, lut)
|
||||
|
||||
# put an image in the frame memory
|
||||
def set_frame_memory(self, image, x, y, w, h):
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
x = x & 0xF8
|
||||
w = w & 0xF8
|
||||
|
||||
if (x + w >= self.width):
|
||||
x_end = self.width - 1
|
||||
else:
|
||||
x_end = x + w - 1
|
||||
|
||||
if (y + h >= self.height):
|
||||
y_end = self.height - 1
|
||||
else:
|
||||
y_end = y + h - 1
|
||||
|
||||
self.set_memory_area(x, y, x_end, y_end)
|
||||
self.set_memory_pointer(x, y)
|
||||
self._command(WRITE_RAM, image)
|
||||
|
||||
# replace the frame memory with the specified color
|
||||
def clear_frame_memory(self, color):
|
||||
self.set_memory_area(0, 0, self.width - 1, self.height - 1)
|
||||
self.set_memory_pointer(0, 0)
|
||||
self._command(WRITE_RAM)
|
||||
# send the color data
|
||||
for i in range(0, self.width // 8 * self.height):
|
||||
self._data(bytearray([color]))
|
||||
|
||||
# draw the current frame memory and switch to the next memory area
|
||||
def display_frame(self):
|
||||
self._command(DISPLAY_UPDATE_CONTROL_2, b'\xC4')
|
||||
self._command(MASTER_ACTIVATION)
|
||||
self._command(TERMINATE_FRAME_READ_WRITE)
|
||||
self.wait_until_idle()
|
||||
|
||||
# specify the memory area for data R/W
|
||||
def set_memory_area(self, x_start, y_start, x_end, y_end):
|
||||
self._command(SET_RAM_X_ADDRESS_START_END_POSITION)
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
self._data(bytearray([(x_start >> 3) & 0xFF]))
|
||||
self._data(bytearray([(x_end >> 3) & 0xFF]))
|
||||
self._command(SET_RAM_Y_ADDRESS_START_END_POSITION, ustruct.pack("<HH", y_start, y_end))
|
||||
|
||||
# specify the start point for data R/W
|
||||
def set_memory_pointer(self, x, y):
|
||||
self._command(SET_RAM_X_ADDRESS_COUNTER)
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
self._data(bytearray([(x >> 3) & 0xFF]))
|
||||
self._command(SET_RAM_Y_ADDRESS_COUNTER, ustruct.pack("<H", y))
|
||||
self.wait_until_idle()
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(DEEP_SLEEP_MODE, b'\x01') # enter deep sleep A0=1, A0=0 power on
|
||||
self.wait_until_idle()
|
|
@ -0,0 +1,319 @@
|
|||
# MicroPython library for Waveshare 1.54" B/W/R e-paper display GDEW0154Z04
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(200)
|
||||
EPD_HEIGHT = const(200)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00) # in datasheet, but not in cmd table
|
||||
POWER_SETTING = const(0x01)
|
||||
POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03) # not in datasheet
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05) # not in datasheet
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
#DEEP_SLEEP = const(0x07) # not in datasheet
|
||||
DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11) # not in datasheet
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
DATA_START_TRANSMISSION_2 = const(0x13)
|
||||
VCOM_LUT = const(0x20) # VCOM LUT
|
||||
W2W_LUT = const(0x21) # White LUT
|
||||
B2W_LUT = const(0x22) # Black LUT
|
||||
W2B_LUT = const(0x23) # not in datasheet
|
||||
B2B_LUT = const(0x24) # not in datasheet
|
||||
LUT_RED_0 = const(0x25) # Red VCOM LUT
|
||||
LUT_RED_1 = const(0x26) # Red0 LUT
|
||||
LUT_RED_2 = const(0x27) # RED1 LUT
|
||||
#LUT_RED_3 = const(0x28) # not in datasheet
|
||||
PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_COMMAND = const(0x40)
|
||||
#TEMPERATURE_SENSOR_CALIBRATION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51) # not in datasheet
|
||||
#TCON_SETTING = const(0x60) # not in datasheet
|
||||
TCON_RESOLUTION = const(0x61)
|
||||
#SOURCE_AND_GATE_START_SETTING = const(0x62) # not in datasheet
|
||||
#GET_STATUS = const(0x71) # in datasheet, but not in cmd table
|
||||
#AUTO_MEASURE_VCOM = const(0x80) # not in datasheet
|
||||
#VCOM_VALUE = const(0x81) # not in datasheet
|
||||
VCM_DC_SETTING_REGISTER = const(0x82)
|
||||
#PROGRAM_MODE = const(0xA0) # not in datasheet
|
||||
#ACTIVE_PROGRAM = const(0xA1) # not in datasheet
|
||||
#READ_OTP_DATA = const(0xA2) # not in datasheet
|
||||
|
||||
# Display orientation
|
||||
ROTATE_0 = const(0)
|
||||
ROTATE_90 = const(1)
|
||||
ROTATE_180 = const(2)
|
||||
ROTATE_270 = const(3)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
self.rotate = ROTATE_0
|
||||
|
||||
LUT_VCOM0 = bytearray(b'\x0E\x14\x01\x0A\x06\x04\x0A\x0A\x0F\x03\x03\x0C\x06\x0A\x00')
|
||||
LUT_W = bytearray(b'\x0E\x14\x01\x0A\x46\x04\x8A\x4A\x0F\x83\x43\x0C\x86\x0A\x04')
|
||||
LUT_B = bytearray(b'\x0E\x14\x01\x8A\x06\x04\x8A\x4A\x0F\x83\x43\x0C\x06\x4A\x04')
|
||||
LUT_G1 = bytearray(b'\x8E\x94\x01\x8A\x06\x04\x8A\x4A\x0F\x83\x43\x0C\x06\x0A\x04')
|
||||
LUT_G2 = LUT_G1
|
||||
LUT_VCOM1 = bytearray(b'\x03\x1D\x01\x01\x08\x23\x37\x37\x01\x00\x00\x00\x00\x00\x00')
|
||||
LUT_RED0 = bytearray(b'\x83\x5D\x01\x81\x48\x23\x77\x77\x01\x00\x00\x00\x00\x00\x00')
|
||||
LUT_RED1 = LUT_VCOM1
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(POWER_SETTING, b'\x07\x00\x08\x00')
|
||||
self._command(BOOSTER_SOFT_START, b'\x07\x07\x07')
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PANEL_SETTING, b'\xCF')
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x17') # for this panel, must be 0x17
|
||||
self._command(PLL_CONTROL, b'\x39')
|
||||
self._command(TCON_RESOLUTION, ustruct.pack(">BH", EPD_WIDTH, EPD_HEIGHT))
|
||||
self._command(VCM_DC_SETTING_REGISTER, b'\x0E') # -1.4V
|
||||
self.set_lut_bw()
|
||||
self.set_lut_red()
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def set_lut_bw(self):
|
||||
self._command(VCOM_LUT, self.LUT_VCOM0) # vcom
|
||||
self._command(W2W_LUT, self.LUT_W) # ww --
|
||||
self._command(B2W_LUT, self.LUT_B) # bw r
|
||||
self._command(W2B_LUT, self.LUT_G1) # wb w
|
||||
self._command(B2B_LUT, self.LUT_G2) # bb b
|
||||
|
||||
def set_lut_red(self):
|
||||
self._command(LUT_RED_0, self.LUT_VCOM1)
|
||||
self._command(LUT_RED_1, self.LUT_RED0)
|
||||
self._command(LUT_RED_2, self.LUT_RED1)
|
||||
|
||||
def display_frame(self, frame_buffer_black, frame_buffer_red):
|
||||
if (frame_buffer_black != None):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
temp = 0x00
|
||||
for bit in range(0, 4):
|
||||
if (frame_buffer_black[i] & (0x80 >> bit) != 0):
|
||||
temp |= 0xC0 >> (bit * 2)
|
||||
self._data(bytearray([temp]))
|
||||
temp = 0x00
|
||||
for bit in range(4, 8):
|
||||
if (frame_buffer_black[i] & (0x80 >> bit) != 0):
|
||||
temp |= 0xC0 >> ((bit - 4) * 2)
|
||||
self._data(bytearray([temp]))
|
||||
sleep_ms(2)
|
||||
if (frame_buffer_red != None):
|
||||
self._command(DATA_START_TRANSMISSION_2)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_red[i]]))
|
||||
sleep_ms(2)
|
||||
|
||||
self._command(DISPLAY_REFRESH)
|
||||
self.wait_until_idle()
|
||||
|
||||
def set_rotate(self, rotate):
|
||||
if (rotate == ROTATE_0):
|
||||
self.rotate = ROTATE_0
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
elif (rotate == ROTATE_90):
|
||||
self.rotate = ROTATE_90
|
||||
self.width = EPD_HEIGHT
|
||||
self.height = EPD_WIDTH
|
||||
elif (rotate == ROTATE_180):
|
||||
self.rotate = ROTATE_180
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
elif (rotate == ROTATE_270):
|
||||
self.rotate = ROTATE_270
|
||||
self.width = EPD_HEIGHT
|
||||
self.height = EPD_WIDTH
|
||||
|
||||
def set_pixel(self, frame_buffer, x, y, colored):
|
||||
if (x < 0 or x >= self.width or y < 0 or y >= self.height):
|
||||
return
|
||||
if (self.rotate == ROTATE_0):
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_90):
|
||||
point_temp = x
|
||||
x = EPD_WIDTH - y
|
||||
y = point_temp
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_180):
|
||||
x = EPD_WIDTH - x
|
||||
y = EPD_HEIGHT- y
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_270):
|
||||
point_temp = x
|
||||
x = y
|
||||
y = EPD_HEIGHT - point_temp
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
|
||||
def set_absolute_pixel(self, frame_buffer, x, y, colored):
|
||||
# To avoid display orientation effects
|
||||
# use EPD_WIDTH instead of self.width
|
||||
# use EPD_HEIGHT instead of self.height
|
||||
if (x < 0 or x >= EPD_WIDTH or y < 0 or y >= EPD_HEIGHT):
|
||||
return
|
||||
if (colored):
|
||||
frame_buffer[(x + y * EPD_WIDTH) // 8] &= ~(0x80 >> (x % 8))
|
||||
else:
|
||||
frame_buffer[(x + y * EPD_WIDTH) // 8] |= 0x80 >> (x % 8)
|
||||
|
||||
def display_string_at(self, frame_buffer, x, y, text, font, colored):
|
||||
image = Image.new('1', (self.width, self.height))
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.text((x, y), text, font = font, fill = 255)
|
||||
# Set buffer to value of Python Imaging Library image.
|
||||
# Image must be in mode 1.
|
||||
pixels = image.load()
|
||||
for y in range(self.height):
|
||||
for x in range(self.width):
|
||||
# Set the bits for the column of pixels at the current position.
|
||||
if pixels[x, y] != 0:
|
||||
self.set_pixel(frame_buffer, x, y, colored)
|
||||
|
||||
def draw_line(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
# Bresenham algorithm
|
||||
dx = abs(x1 - x0)
|
||||
sx = 1 if x0 < x1 else -1
|
||||
dy = -abs(y1 - y0)
|
||||
sy = 1 if y0 < y1 else -1
|
||||
err = dx + dy
|
||||
while((x0 != x1) and (y0 != y1)):
|
||||
self.set_pixel(frame_buffer, x0, y0 , colored)
|
||||
if (2 * err >= dy):
|
||||
err += dy
|
||||
x0 += sx
|
||||
if (2 * err <= dx):
|
||||
err += dx
|
||||
y0 += sy
|
||||
|
||||
def draw_horizontal_line(self, frame_buffer, x, y, width, colored):
|
||||
for i in range(x, x + width):
|
||||
self.set_pixel(frame_buffer, i, y, colored)
|
||||
|
||||
def draw_vertical_line(self, frame_buffer, x, y, height, colored):
|
||||
for i in range(y, y + height):
|
||||
self.set_pixel(frame_buffer, x, i, colored)
|
||||
|
||||
def draw_rectangle(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
min_x = x0 if x1 > x0 else x1
|
||||
max_x = x1 if x1 > x0 else x0
|
||||
min_y = y0 if y1 > y0 else y1
|
||||
max_y = y1 if y1 > y0 else y0
|
||||
self.draw_horizontal_line(frame_buffer, min_x, min_y, max_x - min_x + 1, colored)
|
||||
self.draw_horizontal_line(frame_buffer, min_x, max_y, max_x - min_x + 1, colored)
|
||||
self.draw_vertical_line(frame_buffer, min_x, min_y, max_y - min_y + 1, colored)
|
||||
self.draw_vertical_line(frame_buffer, max_x, min_y, max_y - min_y + 1, colored)
|
||||
|
||||
def draw_filled_rectangle(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
min_x = x0 if x1 > x0 else x1
|
||||
max_x = x1 if x1 > x0 else x0
|
||||
min_y = y0 if y1 > y0 else y1
|
||||
max_y = y1 if y1 > y0 else y0
|
||||
for i in range(min_x, max_x + 1):
|
||||
self.draw_vertical_line(frame_buffer, i, min_y, max_y - min_y + 1, colored)
|
||||
|
||||
def draw_circle(self, frame_buffer, x, y, radius, colored):
|
||||
# Bresenham algorithm
|
||||
x_pos = -radius
|
||||
y_pos = 0
|
||||
err = 2 - 2 * radius
|
||||
if (x >= self.width or y >= self.height):
|
||||
return
|
||||
while True:
|
||||
self.set_pixel(frame_buffer, x - x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y - y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x - x_pos, y - y_pos, colored)
|
||||
e2 = err
|
||||
if (e2 <= y_pos):
|
||||
y_pos += 1
|
||||
err += y_pos * 2 + 1
|
||||
if(-x_pos == y_pos and e2 <= x_pos):
|
||||
e2 = 0
|
||||
if (e2 > x_pos):
|
||||
x_pos += 1
|
||||
err += x_pos * 2 + 1
|
||||
if x_pos > 0:
|
||||
break
|
||||
|
||||
def draw_filled_circle(self, frame_buffer, x, y, radius, colored):
|
||||
# Bresenham algorithm
|
||||
x_pos = -radius
|
||||
y_pos = 0
|
||||
err = 2 - 2 * radius
|
||||
if (x >= self.width or y >= self.height):
|
||||
return
|
||||
while True:
|
||||
self.set_pixel(frame_buffer, x - x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y - y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x - x_pos, y - y_pos, colored)
|
||||
self.draw_horizontal_line(frame_buffer, x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored)
|
||||
self.draw_horizontal_line(frame_buffer, x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored)
|
||||
e2 = err
|
||||
if (e2 <= y_pos):
|
||||
y_pos += 1
|
||||
err += y_pos * 2 + 1
|
||||
if(-x_pos == y_pos and e2 <= x_pos):
|
||||
e2 = 0
|
||||
if (e2 > x_pos):
|
||||
x_pos += 1
|
||||
err += x_pos * 2 + 1
|
||||
if x_pos > 0:
|
||||
break
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
# TODO do we need to reset these here?
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x17') # for this panel, must be 0x17
|
||||
self._command(VCM_DC_SETTING_REGISTER, b'\x00') # to solve Vcom drop
|
||||
self._command(POWER_SETTING, b'\x02\x00\x00\x00') # gate switch to external
|
||||
# /TODO
|
||||
self.wait_until_idle()
|
||||
self._command(POWER_OFF)
|
|
@ -0,0 +1,151 @@
|
|||
# MicroPython library for Waveshare 2.13" B/W e-paper display GDEH0213B1
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(128)
|
||||
EPD_HEIGHT = const(250)
|
||||
# datasheet says 250x122 (increased to 128 to be multiples of 8)
|
||||
|
||||
# Display commands
|
||||
DRIVER_OUTPUT_CONTROL = const(0x01)
|
||||
# Gate Driving Voltage Control 0x03
|
||||
# Source Driving voltage Control 0x04
|
||||
BOOSTER_SOFT_START_CONTROL = const(0x0C) # not in datasheet
|
||||
#GATE_SCAN_START_POSITION = const(0x0F) # not in datasheet
|
||||
DEEP_SLEEP_MODE = const(0x10)
|
||||
DATA_ENTRY_MODE_SETTING = const(0x11)
|
||||
#SW_RESET = const(0x12)
|
||||
#TEMPERATURE_SENSOR_CONTROL = const(0x1A)
|
||||
MASTER_ACTIVATION = const(0x20)
|
||||
#DISPLAY_UPDATE_CONTROL_1 = const(0x21)
|
||||
DISPLAY_UPDATE_CONTROL_2 = const(0x22)
|
||||
# Panel Break Detection 0x23
|
||||
WRITE_RAM = const(0x24)
|
||||
WRITE_VCOM_REGISTER = const(0x2C)
|
||||
# Status Bit Read 0x2F
|
||||
WRITE_LUT_REGISTER = const(0x32)
|
||||
SET_DUMMY_LINE_PERIOD = const(0x3A)
|
||||
SET_GATE_TIME = const(0x3B)
|
||||
#BORDER_WAVEFORM_CONTROL = const(0x3C)
|
||||
SET_RAM_X_ADDRESS_START_END_POSITION = const(0x44)
|
||||
SET_RAM_Y_ADDRESS_START_END_POSITION = const(0x45)
|
||||
SET_RAM_X_ADDRESS_COUNTER = const(0x4E)
|
||||
SET_RAM_Y_ADDRESS_COUNTER = const(0x4F)
|
||||
TERMINATE_FRAME_READ_WRITE = const(0xFF) # not in datasheet, aka NOOP
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
LUT_FULL_UPDATE = bytearray(b'\x22\x55\xAA\x55\xAA\x55\xAA\x11\x00\x00\x00\x00\x00\x00\x00\x00\x1E\x1E\x1E\x1E\x1E\x1E\x1E\x1E\x01\x00\x00\x00\x00\x00')
|
||||
LUT_PARTIAL_UPDATE = bytearray(b'\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0F\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(DRIVER_OUTPUT_CONTROL)
|
||||
self._data(bytearray([(EPD_HEIGHT - 1) & 0xFF]))
|
||||
self._data(bytearray([((EPD_HEIGHT - 1) >> 8) & 0xFF]))
|
||||
self._data(bytearray([0x00])) # GD = 0 SM = 0 TB = 0
|
||||
self._command(BOOSTER_SOFT_START_CONTROL, b'\xD7\xD6\x9D')
|
||||
self._command(WRITE_VCOM_REGISTER, b'\xA8') # VCOM 7C
|
||||
self._command(SET_DUMMY_LINE_PERIOD, b'\x1A') # 4 dummy lines per gate
|
||||
self._command(SET_GATE_TIME, b'\x08') # 2us per line
|
||||
self._command(DATA_ENTRY_MODE_SETTING, b'\x03') # X increment Y increment
|
||||
self.set_lut(self.LUT_FULL_UPDATE)
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def set_lut(self, lut):
|
||||
self._command(WRITE_LUT_REGISTER, lut)
|
||||
|
||||
# put an image in the frame memory
|
||||
def set_frame_memory(self, image, x, y, w, h):
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
x = x & 0xF8
|
||||
w = w & 0xF8
|
||||
|
||||
if (x + w >= self.width):
|
||||
x_end = self.width - 1
|
||||
else:
|
||||
x_end = x + w - 1
|
||||
|
||||
if (y + h >= self.height):
|
||||
y_end = self.height - 1
|
||||
else:
|
||||
y_end = y + h - 1
|
||||
|
||||
self.set_memory_area(x, y, x_end, y_end)
|
||||
self.set_memory_pointer(x, y)
|
||||
self._command(WRITE_RAM, image)
|
||||
|
||||
# replace the frame memory with the specified color
|
||||
def clear_frame_memory(self, color):
|
||||
self.set_memory_area(0, 0, self.width - 1, self.height - 1)
|
||||
self.set_memory_pointer(0, 0)
|
||||
self._command(WRITE_RAM)
|
||||
# send the color data
|
||||
for i in range(0, self.width // 8 * self.height):
|
||||
self._data(bytearray([color]))
|
||||
|
||||
# draw the current frame memory and switch to the next memory area
|
||||
def display_frame(self):
|
||||
self._command(DISPLAY_UPDATE_CONTROL_2, b'\xC4')
|
||||
self._command(MASTER_ACTIVATION)
|
||||
self._command(TERMINATE_FRAME_READ_WRITE)
|
||||
self.wait_until_idle()
|
||||
|
||||
# specify the memory area for data R/W
|
||||
def set_memory_area(self, x_start, y_start, x_end, y_end):
|
||||
self._command(SET_RAM_X_ADDRESS_START_END_POSITION)
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
self._data(bytearray([(x_start >> 3) & 0xFF]))
|
||||
self._data(bytearray([(x_end >> 3) & 0xFF]))
|
||||
self._command(SET_RAM_Y_ADDRESS_START_END_POSITION, ustruct.pack("<HH", y_start, y_end))
|
||||
|
||||
# specify the start point for data R/W
|
||||
def set_memory_pointer(self, x, y):
|
||||
self._command(SET_RAM_X_ADDRESS_COUNTER)
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
self._data(bytearray([(x >> 3) & 0xFF]))
|
||||
self._command(SET_RAM_Y_ADDRESS_COUNTER, ustruct.pack("<H", y))
|
||||
self.wait_until_idle()
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(DEEP_SLEEP_MODE)
|
||||
self.wait_until_idle()
|
|
@ -0,0 +1,281 @@
|
|||
# MicroPython library for Waveshare 2.13" B/W/R e-paper display GDEW0213Z16
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(104)
|
||||
EPD_HEIGHT = const(212)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00)
|
||||
POWER_SETTING = const(0x01)
|
||||
POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03)
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05)
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
#DEEP_SLEEP = const(0x07)
|
||||
DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11)
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
DATA_START_TRANSMISSION_2 = const(0x13)
|
||||
#VCOM_LUT = const(0x20) # VCOM LUT(LUTC) (45-byte command, structure of bytes 2~7 repeated)
|
||||
#W2W_LUT = const(0x21) # W2W LUT (LUTWW) (43-byte command, structure of bytes 2~7 repeated 7 times)
|
||||
#B2W_LUT = const(0x22) # B2W LUT (LUTBW / LUTR) (43-byte command, structure of bytes 2~7 repeated 7 times)
|
||||
#W2B_LUT = const(0x23) # W2B LUT (LUTWB / LUTW) (43-byte command, structure of bytes 2~7 repeated 7 times)
|
||||
#B2B_LUT = const(0x24) # B2B LUT (LUTBB / LUTB) (43-byte command, sturcture of bytes 2~7 repeated 7 times)
|
||||
#PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_CALIBRATION = const(0x40)
|
||||
#TEMPERATURE_SENSOR_SELECTION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51)
|
||||
#TCON_SETTING = const(0x60)
|
||||
RESOLUTION_SETTING = const(0x61)
|
||||
#GET_STATUS = const(0x71) # partial update status, i2c status, data received, power status, busy
|
||||
#AUTO_MEASURE_VCOM = const(0x80)
|
||||
#VCOM_VALUE = const(0x81)
|
||||
VCM_DC_SETTING = const(0x82)
|
||||
#PARTIAL_WINDOW = const(0x90)
|
||||
#PARTIAL_IN = const(0x91)
|
||||
#PARTIAL_OUT = const(0x92)
|
||||
#PROGRAM_MODE = const(0xA0)
|
||||
#ACTIVE_PROGRAM = const(0xA1)
|
||||
#READ_OTP_DATA = const(0xA2)
|
||||
#POWER_SAVING = const(0xE3)
|
||||
|
||||
# Display orientation
|
||||
ROTATE_0 = const(0)
|
||||
ROTATE_90 = const(1)
|
||||
ROTATE_180 = const(2)
|
||||
ROTATE_270 = const(3)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
self.rotate = ROTATE_0
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(BOOSTER_SOFT_START, b'\x17\x17\x17')
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PANEL_SETTING, b'\x8F') # (128x296, LUT from OTP, B/W/R, scan up, shift right, booster on)
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x37')
|
||||
self._command(RESOLUTION_SETTING, ustruct.pack(">BH", EPD_WIDTH, EPD_HEIGHT))
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def display_frame(self, frame_buffer_black, frame_buffer_red):
|
||||
if (frame_buffer_black != None):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_black[i]]))
|
||||
sleep_ms(2)
|
||||
if (frame_buffer_red != None):
|
||||
self._command(DATA_START_TRANSMISSION_2)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_red[i]]))
|
||||
sleep_ms(2)
|
||||
|
||||
self._command(DISPLAY_REFRESH)
|
||||
self.wait_until_idle()
|
||||
|
||||
def set_rotate(self, rotate):
|
||||
if (rotate == ROTATE_0):
|
||||
self.rotate = ROTATE_0
|
||||
self.width = epdif.EPD_WIDTH
|
||||
self.height = epdif.EPD_HEIGHT
|
||||
elif (rotate == ROTATE_90):
|
||||
self.rotate = ROTATE_90
|
||||
self.width = epdif.EPD_HEIGHT
|
||||
self.height = epdif.EPD_WIDTH
|
||||
elif (rotate == ROTATE_180):
|
||||
self.rotate = ROTATE_180
|
||||
self.width = epdif.EPD_WIDTH
|
||||
self.height = epdif.EPD_HEIGHT
|
||||
elif (rotate == ROTATE_270):
|
||||
self.rotate = ROTATE_270
|
||||
self.width = epdif.EPD_HEIGHT
|
||||
self.height = epdif.EPD_WIDTH
|
||||
|
||||
def set_pixel(self, frame_buffer, x, y, colored):
|
||||
if (x < 0 or x >= self.width or y < 0 or y >= self.height):
|
||||
return
|
||||
if (self.rotate == ROTATE_0):
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_90):
|
||||
point_temp = x
|
||||
x = epdif.EPD_WIDTH - y
|
||||
y = point_temp
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_180):
|
||||
x = epdif.EPD_WIDTH - x
|
||||
y = epdif.EPD_HEIGHT- y
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_270):
|
||||
point_temp = x
|
||||
x = y
|
||||
y = epdif.EPD_HEIGHT - point_temp
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
|
||||
def set_absolute_pixel(self, frame_buffer, x, y, colored):
|
||||
# To avoid display orientation effects
|
||||
# use EPD_WIDTH instead of self.width
|
||||
# use EPD_HEIGHT instead of self.height
|
||||
if (x < 0 or x >= EPD_WIDTH or y < 0 or y >= EPD_HEIGHT):
|
||||
return
|
||||
if (colored):
|
||||
frame_buffer[(x + y * EPD_WIDTH) // 8] &= ~(0x80 >> (x % 8))
|
||||
else:
|
||||
frame_buffer[(x + y * EPD_WIDTH) // 8] |= 0x80 >> (x % 8)
|
||||
|
||||
def draw_string_at(self, frame_buffer, x, y, text, font, colored):
|
||||
image = Image.new('1', (self.width, self.height))
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.text((x, y), text, font = font, fill = 255)
|
||||
# Set buffer to value of Python Imaging Library image.
|
||||
# Image must be in mode 1.
|
||||
pixels = image.load()
|
||||
for y in range(self.height):
|
||||
for x in range(self.width):
|
||||
# Set the bits for the column of pixels at the current position.
|
||||
if pixels[x, y] != 0:
|
||||
self.set_pixel(frame_buffer, x, y, colored)
|
||||
|
||||
def draw_line(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
# Bresenham algorithm
|
||||
dx = abs(x1 - x0)
|
||||
sx = 1 if x0 < x1 else -1
|
||||
dy = -abs(y1 - y0)
|
||||
sy = 1 if y0 < y1 else -1
|
||||
err = dx + dy
|
||||
while((x0 != x1) and (y0 != y1)):
|
||||
self.set_pixel(frame_buffer, x0, y0 , colored)
|
||||
if (2 * err >= dy):
|
||||
err += dy
|
||||
x0 += sx
|
||||
if (2 * err <= dx):
|
||||
err += dx
|
||||
y0 += sy
|
||||
|
||||
def draw_horizontal_line(self, frame_buffer, x, y, width, colored):
|
||||
for i in range(x, x + width):
|
||||
self.set_pixel(frame_buffer, i, y, colored)
|
||||
|
||||
def draw_vertical_line(self, frame_buffer, x, y, height, colored):
|
||||
for i in range(y, y + height):
|
||||
self.set_pixel(frame_buffer, x, i, colored)
|
||||
|
||||
def draw_rectangle(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
min_x = x0 if x1 > x0 else x1
|
||||
max_x = x1 if x1 > x0 else x0
|
||||
min_y = y0 if y1 > y0 else y1
|
||||
max_y = y1 if y1 > y0 else y0
|
||||
self.draw_horizontal_line(frame_buffer, min_x, min_y, max_x - min_x + 1, colored)
|
||||
self.draw_horizontal_line(frame_buffer, min_x, max_y, max_x - min_x + 1, colored)
|
||||
self.draw_vertical_line(frame_buffer, min_x, min_y, max_y - min_y + 1, colored)
|
||||
self.draw_vertical_line(frame_buffer, max_x, min_y, max_y - min_y + 1, colored)
|
||||
|
||||
def draw_filled_rectangle(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
min_x = x0 if x1 > x0 else x1
|
||||
max_x = x1 if x1 > x0 else x0
|
||||
min_y = y0 if y1 > y0 else y1
|
||||
max_y = y1 if y1 > y0 else y0
|
||||
for i in range(min_x, max_x + 1):
|
||||
self.draw_vertical_line(frame_buffer, i, min_y, max_y - min_y + 1, colored)
|
||||
|
||||
def draw_circle(self, frame_buffer, x, y, radius, colored):
|
||||
# Bresenham algorithm
|
||||
x_pos = -radius
|
||||
y_pos = 0
|
||||
err = 2 - 2 * radius
|
||||
if (x >= self.width or y >= self.height):
|
||||
return
|
||||
while True:
|
||||
self.set_pixel(frame_buffer, x - x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y - y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x - x_pos, y - y_pos, colored)
|
||||
e2 = err
|
||||
if (e2 <= y_pos):
|
||||
y_pos += 1
|
||||
err += y_pos * 2 + 1
|
||||
if(-x_pos == y_pos and e2 <= x_pos):
|
||||
e2 = 0
|
||||
if (e2 > x_pos):
|
||||
x_pos += 1
|
||||
err += x_pos * 2 + 1
|
||||
if x_pos > 0:
|
||||
break
|
||||
|
||||
def draw_filled_circle(self, frame_buffer, x, y, radius, colored):
|
||||
# Bresenham algorithm
|
||||
x_pos = -radius
|
||||
y_pos = 0
|
||||
err = 2 - 2 * radius
|
||||
if (x >= self.width or y >= self.height):
|
||||
return
|
||||
while True:
|
||||
self.set_pixel(frame_buffer, x - x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y - y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x - x_pos, y - y_pos, colored)
|
||||
self.draw_horizontal_line(frame_buffer, x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored)
|
||||
self.draw_horizontal_line(frame_buffer, x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored)
|
||||
e2 = err
|
||||
if (e2 <= y_pos):
|
||||
y_pos += 1
|
||||
err += y_pos * 2 + 1
|
||||
if(-x_pos == y_pos and e2 <= x_pos):
|
||||
e2 = 0
|
||||
if (e2 > x_pos):
|
||||
x_pos += 1
|
||||
err += x_pos * 2 + 1
|
||||
if x_pos > 0:
|
||||
break
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x37')
|
||||
self._command(VCM_DC_SETTING_REGISTER, b'\x00') # to solve Vcom drop
|
||||
self._command(POWER_SETTING, b'\x02\x00\x00\x00') # gate switch to external
|
||||
self.wait_until_idle()
|
||||
self._command(POWER_OFF)
|
|
@ -0,0 +1,139 @@
|
|||
# MicroPython library for Waveshare 2.7" B/W e-paper display GDEW027W3
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(176)
|
||||
EPD_HEIGHT = const(264)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00)
|
||||
POWER_SETTING = const(0x01)
|
||||
#POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03)
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05)
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
DEEP_SLEEP = const(0x07)
|
||||
DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11)
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
DATA_START_TRANSMISSION_2 = const(0x13) # not in datasheet
|
||||
#PARTIAL_DATA_START_TRANSMISSION_1 = const(0x14)
|
||||
#PARTIAL_DATA_START_TRANSMISSION_2 = const(0x15)
|
||||
PARTIAL_DISPLAY_REFRESH = const(0x16)
|
||||
LUT_FOR_VCOM = const(0x20) # LUT for VCOM(LUT1)
|
||||
LUT_WHITE_TO_WHITE = const(0x21) # White to white LUT (LUTWW)
|
||||
LUT_BLACK_TO_WHITE = const(0x22) # Black to white LUT (LUTBW/LUTR)
|
||||
LUT_WHITE_TO_BLACK = const(0x23) # White to Black LUT (LUTWB/LUTW)
|
||||
LUT_BLACK_TO_BLACK = const(0x24) # Black to Black LUT (LUTBB/LUTB)
|
||||
PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_COMMAND = const(0x40)
|
||||
#TEMPERATURE_SENSOR_CALIBRATION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
#VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51)
|
||||
#TCON_SETTING = const(0x60)
|
||||
#TCON_RESOLUTION = const(0x61)
|
||||
#SOURCE_AND_GATE_START_SETTING = const(0x62)
|
||||
#GET_STATUS = const(0x71)
|
||||
#AUTO_MEASURE_VCOM = const(0x80)
|
||||
#VCOM_VALUE = const(0x81)
|
||||
VCM_DC_SETTING_REGISTER = const(0x82)
|
||||
#PROGRAM_MODE = const(0xA0)
|
||||
#ACTIVE_PROGRAM = const(0xA1)
|
||||
#READ_OTP_DATA = const(0xA2)
|
||||
POWER_OPTIMIZATION = const(0xF8) # Power optimization in flow diagram
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
LUT_VCOM_DC = bytearray(b'\x00\x00\x00\x0F\x0F\x00\x00\x05\x00\x32\x32\x00\x00\x02\x00\x0F\x0F\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
LUT_WW = bytearray(b'\x50\x0F\x0F\x00\x00\x05\x60\x32\x32\x00\x00\x02\xA0\x0F\x0F\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') # R21H
|
||||
LUT_BW = LUT_BW # R22H r
|
||||
LUT_BB = bytearray(b'\xA0\x0F\x0F\x00\x00\x05\x60\x32\x32\x00\x00\x02\x50\x0F\x0F\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') # R24H b
|
||||
LUT_WB = LUT_BB # R23H w
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(POWER_SETTING, b'\x03\x00\x2B\x2B\x09') # VDS_EN VDG_EN, VCOM_HV VGHL_LV[1] VGHL_LV[0], VDH, VDL, VDHR
|
||||
self._command(BOOSTER_SOFT_START, b'\x07\x07\x17')
|
||||
self._command(POWER_OPTIMIZATION, b'\x60\xA5')
|
||||
self._command(POWER_OPTIMIZATION, b'\x89\xA5')
|
||||
self._command(POWER_OPTIMIZATION, b'\x90\x00')
|
||||
self._command(POWER_OPTIMIZATION, b'\x93\x2A')
|
||||
self._command(POWER_OPTIMIZATION, b'\xA0\xA5')
|
||||
self._command(POWER_OPTIMIZATION, b'\xA1\x00')
|
||||
self._command(POWER_OPTIMIZATION, b'\x73\x41')
|
||||
self._command(PARTIAL_DISPLAY_REFRESH, b'\x00')
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PANEL_SETTING, b'\xAF') # KW-BF KWR-AF BWROTP 0f
|
||||
self._command(PLL_CONTROL, b'\x3A') # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ
|
||||
self._command(VCM_DC_SETTING_REGISTER, b'\x12')
|
||||
sleep_ms(2)
|
||||
self.set_lut()
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def set_lut(self):
|
||||
self._command(LUT_FOR_VCOM, self.LUT_VCOM_DC) # vcom
|
||||
self._command(LUT_WHITE_TO_WHITE, self.LUT_WW) # ww --
|
||||
self._command(LUT_BLACK_TO_WHITE, self.LUT_BW) # bw r
|
||||
self._command(LUT_WHITE_TO_BLACK, self.LUT_BB) # wb w
|
||||
self._command(LUT_BLACK_TO_BLACK, self.LUT_WB) # bb b
|
||||
|
||||
# draw the current frame memory
|
||||
def display_frame(self, frame_buffer):
|
||||
if (frame_buffer != None):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([0xFF]))
|
||||
sleep_ms(2)
|
||||
self._command(DATA_START_TRANSMISSION_2)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer[i]]))
|
||||
sleep_ms(2)
|
||||
self._command(DISPLAY_REFRESH)
|
||||
self.wait_until_idle()
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(DEEP_SLEEP, b'\xA5')
|
|
@ -0,0 +1,302 @@
|
|||
# MicroPython library for Waveshare 2.7" B/W/R e-paper display GDEW027C44
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(176)
|
||||
EPD_HEIGHT = const(264)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00)
|
||||
POWER_SETTING = const(0x01)
|
||||
#POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03)
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05)
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
DEEP_SLEEP = const(0x07)
|
||||
#DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11)
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
DATA_START_TRANSMISSION_2 = const(0x13)
|
||||
#PARTIAL_DATA_START_TRANSMISSION_1 = const(0x14)
|
||||
#PARTIAL_DATA_START_TRANSMISSION_2 = const(0x15)
|
||||
PARTIAL_DISPLAY_REFRESH = const(0x16)
|
||||
LUT_FOR_VCOM = const(0x20)
|
||||
LUT_WHITE_TO_WHITE = const(0x21)
|
||||
LUT_BLACK_TO_WHITE = const(0x22)
|
||||
LUT_WHITE_TO_BLACK = const(0x23)
|
||||
LUT_BLACK_TO_BLACK = const(0x24)
|
||||
PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_COMMAND = const(0x40)
|
||||
#TEMPERATURE_SENSOR_CALIBRATION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51)
|
||||
#TCON_SETTING = const(0x60)
|
||||
TCON_RESOLUTION = const(0x61)
|
||||
#SOURCE_AND_GATE_START_SETTING = const(0x62)
|
||||
#GET_STATUS = const(0x71)
|
||||
#AUTO_MEASURE_VCOM = const(0x80)
|
||||
#VCOM_VALUE = const(0x81)
|
||||
VCM_DC_SETTING_REGISTER = const(0x82)
|
||||
#PROGRAM_MODE = const(0xA0)
|
||||
#ACTIVE_PROGRAM = const(0xA1)
|
||||
#READ_OTP_DATA = const(0xA2)
|
||||
POWER_OPTIMIZATION = const(0xF8) # Power optimization in flow diagram
|
||||
|
||||
# Display orientation
|
||||
ROTATE_0 = const(0)
|
||||
ROTATE_90 = const(1)
|
||||
ROTATE_180 = const(2)
|
||||
ROTATE_270 = const(3)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
self.rotate = ROTATE_0
|
||||
|
||||
LUT_VCOM_DC = bytearray(b'\x00\x00\x00\x1A\x1A\x00\x00\x01\x00\x0A\x0A\x00\x00\x08\x00\x0E\x01\x0E\x01\x10\x00\x0A\x0A\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0E\x00\x00\x0A\x00\x23\x00\x00\x00\x01')
|
||||
LUT_WW = bytearray(b'\x90\x1A\x1A\x00\x00\x01\x40\x0A\x0A\x00\x00\x08\x84\x0E\x01\x0E\x01\x10\x80\x0A\x0A\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0E\x00\x00\x0A\x00\x23\x00\x00\x00\x01') # R21H
|
||||
LUT_BW = bytearray(b'\xA0\x1A\x1A\x00\x00\x01\x00\x0A\x0A\x00\x00\x08\x84\x0E\x01\x0E\x01\x10\x90\x0A\x0A\x00\x00\x08\xB0\x04\x10\x00\x00\x05\xB0\x03\x0E\x00\x00\x0A\xC0\x23\x00\x00\x00\x01') # R22H r
|
||||
LUT_BB = LUT_WW # R23H w
|
||||
LUT_WB = bytearray(b'\x90\x1A\x1A\x00\x00\x01\x20\x0A\x0A\x00\x00\x08\x84\x0E\x01\x0E\x01\x10\x10\x0A\x0A\x00\x00\x08\x00\x04\x10\x00\x00\x05\x00\x03\x0E\x00\x00\x0A\x00\x23\x00\x00\x00\x01') # R24H b
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PANEL_SETTING, b'\xAF') # (296x160, LUT from register, B/W/R run both LU1 LU2, scan up, shift right, bootster on) KW-BF KWR-AF BWROTP 0f
|
||||
self._command(PLL_CONTROL, b'\x3A') # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ
|
||||
self._command(POWER_SETTING, b'\x03\x00\x2b\x2b\x09') # VDS_EN VDG_EN, VCOM_HV VGHL_LV[1] VGHL_LV[0], VDH, VDL, VDHR
|
||||
self._command(BOOSTER_SOFT_START, b'\x07\x07\x17')
|
||||
self._command(POWER_OPTIMIZATION, b'\x60\xA5')
|
||||
self._command(POWER_OPTIMIZATION, b'\x89\xA5')
|
||||
self._command(POWER_OPTIMIZATION, b'\x90\x00')
|
||||
self._command(POWER_OPTIMIZATION, b'\x93\x2A')
|
||||
self._command(POWER_OPTIMIZATION, b'\x73\x41')
|
||||
self._command(VCM_DC_SETTING_REGISTER, b'\x12')
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x87') # define by OTP
|
||||
self.set_lut()
|
||||
self._command(PARTIAL_DISPLAY_REFRESH, b'\x00')
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def set_lut(self):
|
||||
self._command(LUT_FOR_VCOM, self.LUT_VCOM_DC) # vcom
|
||||
self._command(LUT_WHITE_TO_WHITE, self.LUT_WW) # ww --
|
||||
self._command(LUT_BLACK_TO_WHITE, self.LUT_BW) # bw r
|
||||
self._command(LUT_WHITE_TO_BLACK, self.LUT_BB) # wb w
|
||||
self._command(LUT_BLACK_TO_BLACK, self.LUT_WB) # bb b
|
||||
|
||||
def display_frame(self, frame_buffer_black, frame_buffer_red):
|
||||
self._command(TCON_RESOLUTION, ustruct.pack(">HH", EPD_WIDTH, EPD_HEIGHT))
|
||||
|
||||
if (frame_buffer_black != None):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_black[i]]))
|
||||
sleep_ms(2)
|
||||
if (frame_buffer_red != None):
|
||||
self._command(DATA_START_TRANSMISSION_2)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_red[i]]))
|
||||
sleep_ms(2)
|
||||
|
||||
self._command(DISPLAY_REFRESH)
|
||||
self.wait_until_idle()
|
||||
|
||||
def set_rotate(self, rotate):
|
||||
if (rotate == ROTATE_0):
|
||||
self.rotate = ROTATE_0
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
elif (rotate == ROTATE_90):
|
||||
self.rotate = ROTATE_90
|
||||
self.width = EPD_HEIGHT
|
||||
self.height = EPD_WIDTH
|
||||
elif (rotate == ROTATE_180):
|
||||
self.rotate = ROTATE_180
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
elif (rotate == ROTATE_270):
|
||||
self.rotate = ROTATE_270
|
||||
self.width = EPD_HEIGHT
|
||||
self.height = EPD_WIDTH
|
||||
|
||||
def set_pixel(self, frame_buffer, x, y, colored):
|
||||
if (x < 0 or x >= self.width or y < 0 or y >= self.height):
|
||||
return
|
||||
if (self.rotate == ROTATE_0):
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_90):
|
||||
point_temp = x
|
||||
x = EPD_WIDTH - y
|
||||
y = point_temp
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_180):
|
||||
x = EPD_WIDTH - x
|
||||
y = EPD_HEIGHT- y
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_270):
|
||||
point_temp = x
|
||||
x = y
|
||||
y = EPD_HEIGHT - point_temp
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
|
||||
def set_absolute_pixel(self, frame_buffer, x, y, colored):
|
||||
# To avoid display orientation effects
|
||||
# use EPD_WIDTH instead of self.width
|
||||
# use EPD_HEIGHT instead of self.height
|
||||
if (x < 0 or x >= EPD_WIDTH or y < 0 or y >= EPD_HEIGHT):
|
||||
return
|
||||
if (colored):
|
||||
frame_buffer[(x + y * EPD_WIDTH) // 8] |= 0x80 >> (x % 8)
|
||||
else:
|
||||
frame_buffer[(x + y * EPD_WIDTH) // 8] &= ~(0x80 >> (x % 8))
|
||||
|
||||
def draw_string_at(self, frame_buffer, x, y, text, font, colored):
|
||||
image = Image.new('1', (self.width, self.height))
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.text((x, y), text, font = font, fill = 255)
|
||||
# Set buffer to value of Python Imaging Library image.
|
||||
# Image must be in mode 1.
|
||||
pixels = image.load()
|
||||
for y in range(self.height):
|
||||
for x in range(self.width):
|
||||
# Set the bits for the column of pixels at the current position.
|
||||
if pixels[x, y] != 0:
|
||||
self.set_pixel(frame_buffer, x, y, colored)
|
||||
|
||||
def draw_line(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
# Bresenham algorithm
|
||||
dx = abs(x1 - x0)
|
||||
sx = 1 if x0 < x1 else -1
|
||||
dy = -abs(y1 - y0)
|
||||
sy = 1 if y0 < y1 else -1
|
||||
err = dx + dy
|
||||
while((x0 != x1) and (y0 != y1)):
|
||||
self.set_pixel(frame_buffer, x0, y0 , colored)
|
||||
if (2 * err >= dy):
|
||||
err += dy
|
||||
x0 += sx
|
||||
if (2 * err <= dx):
|
||||
err += dx
|
||||
y0 += sy
|
||||
|
||||
def draw_horizontal_line(self, frame_buffer, x, y, width, colored):
|
||||
for i in range(x, x + width):
|
||||
self.set_pixel(frame_buffer, i, y, colored)
|
||||
|
||||
def draw_vertical_line(self, frame_buffer, x, y, height, colored):
|
||||
for i in range(y, y + height):
|
||||
self.set_pixel(frame_buffer, x, i, colored)
|
||||
|
||||
def draw_rectangle(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
min_x = x0 if x1 > x0 else x1
|
||||
max_x = x1 if x1 > x0 else x0
|
||||
min_y = y0 if y1 > y0 else y1
|
||||
max_y = y1 if y1 > y0 else y0
|
||||
self.draw_horizontal_line(frame_buffer, min_x, min_y, max_x - min_x + 1, colored)
|
||||
self.draw_horizontal_line(frame_buffer, min_x, max_y, max_x - min_x + 1, colored)
|
||||
self.draw_vertical_line(frame_buffer, min_x, min_y, max_y - min_y + 1, colored)
|
||||
self.draw_vertical_line(frame_buffer, max_x, min_y, max_y - min_y + 1, colored)
|
||||
|
||||
def draw_filled_rectangle(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
min_x = x0 if x1 > x0 else x1
|
||||
max_x = x1 if x1 > x0 else x0
|
||||
min_y = y0 if y1 > y0 else y1
|
||||
max_y = y1 if y1 > y0 else y0
|
||||
for i in range(min_x, max_x + 1):
|
||||
self.draw_vertical_line(frame_buffer, i, min_y, max_y - min_y + 1, colored)
|
||||
|
||||
def draw_circle(self, frame_buffer, x, y, radius, colored):
|
||||
# Bresenham algorithm
|
||||
x_pos = -radius
|
||||
y_pos = 0
|
||||
err = 2 - 2 * radius
|
||||
if (x >= self.width or y >= self.height):
|
||||
return
|
||||
while True:
|
||||
self.set_pixel(frame_buffer, x - x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y - y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x - x_pos, y - y_pos, colored)
|
||||
e2 = err
|
||||
if (e2 <= y_pos):
|
||||
y_pos += 1
|
||||
err += y_pos * 2 + 1
|
||||
if(-x_pos == y_pos and e2 <= x_pos):
|
||||
e2 = 0
|
||||
if (e2 > x_pos):
|
||||
x_pos += 1
|
||||
err += x_pos * 2 + 1
|
||||
if x_pos > 0:
|
||||
break
|
||||
|
||||
def draw_filled_circle(self, frame_buffer, x, y, radius, colored):
|
||||
# Bresenham algorithm
|
||||
x_pos = -radius
|
||||
y_pos = 0
|
||||
err = 2 - 2 * radius
|
||||
if (x >= self.width or y >= self.height):
|
||||
return
|
||||
while True:
|
||||
self.set_pixel(frame_buffer, x - x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y - y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x - x_pos, y - y_pos, colored)
|
||||
self.draw_horizontal_line(frame_buffer, x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored);
|
||||
self.draw_horizontal_line(frame_buffer, x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored);
|
||||
e2 = err
|
||||
if (e2 <= y_pos):
|
||||
y_pos += 1
|
||||
err += y_pos * 2 + 1
|
||||
if(-x_pos == y_pos and e2 <= x_pos):
|
||||
e2 = 0
|
||||
if (e2 > x_pos):
|
||||
x_pos += 1
|
||||
err += x_pos * 2 + 1
|
||||
if x_pos > 0:
|
||||
break
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(DEEP_SLEEP, b'\xA5')
|
|
@ -0,0 +1,149 @@
|
|||
# MicroPython library for Waveshare 2.9" B/W e-paper display GDEH029A1
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(128)
|
||||
EPD_HEIGHT = const(296)
|
||||
|
||||
# Display commands
|
||||
DRIVER_OUTPUT_CONTROL = const(0x01)
|
||||
BOOSTER_SOFT_START_CONTROL = const(0x0C)
|
||||
#GATE_SCAN_START_POSITION = const(0x0F)
|
||||
DEEP_SLEEP_MODE = const(0x10)
|
||||
DATA_ENTRY_MODE_SETTING = const(0x11)
|
||||
#SW_RESET = const(0x12)
|
||||
#TEMPERATURE_SENSOR_CONTROL = const(0x1A)
|
||||
MASTER_ACTIVATION = const(0x20)
|
||||
#DISPLAY_UPDATE_CONTROL_1 = const(0x21)
|
||||
DISPLAY_UPDATE_CONTROL_2 = const(0x22)
|
||||
WRITE_RAM = const(0x24)
|
||||
WRITE_VCOM_REGISTER = const(0x2C)
|
||||
WRITE_LUT_REGISTER = const(0x32)
|
||||
SET_DUMMY_LINE_PERIOD = const(0x3A)
|
||||
SET_GATE_TIME = const(0x3B)
|
||||
#BORDER_WAVEFORM_CONTROL = const(0x3C)
|
||||
SET_RAM_X_ADDRESS_START_END_POSITION = const(0x44)
|
||||
SET_RAM_Y_ADDRESS_START_END_POSITION = const(0x45)
|
||||
SET_RAM_X_ADDRESS_COUNTER = const(0x4E)
|
||||
SET_RAM_Y_ADDRESS_COUNTER = const(0x4F)
|
||||
TERMINATE_FRAME_READ_WRITE = const(0xFF)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
# 30 bytes (look up tables)
|
||||
# original waveshare example
|
||||
LUT_FULL_UPDATE = bytearray(b'\x02\x02\x01\x11\x12\x12\x22\x22\x66\x69\x69\x59\x58\x99\x99\x88\x00\x00\x00\x00\xF8\xB4\x13\x51\x35\x51\x51\x19\x01\x00')
|
||||
LUT_PARTIAL_UPDATE = bytearray(b'\x10\x18\x18\x08\x18\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x14\x44\x12\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
# https://github.com/ZinggJM/GxEPD/blob/master/GxGDEH029A1/GxGDEH029A1.cpp
|
||||
#LUT_FULL_UPDATE = bytearray(b'\x50\xAA\x55\xAA\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\x1F\x00\x00\x00\x00\x00\x00\x00')
|
||||
#LUT_PARTIAL_UPDATE = bytearray(b'\x10\x18\x18\x08\x18\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x14\x44\x12\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(DRIVER_OUTPUT_CONTROL, ustruct.pack("<HB", EPD_HEIGHT-1, 0x00))
|
||||
self._command(BOOSTER_SOFT_START_CONTROL, b'\xD7\xD6\x9D')
|
||||
self._command(WRITE_VCOM_REGISTER, b'\xA8') # VCOM 7C
|
||||
self._command(SET_DUMMY_LINE_PERIOD, b'\x1A') # 4 dummy lines per gate
|
||||
self._command(SET_GATE_TIME, b'\x08') # 2us per line
|
||||
self._command(DATA_ENTRY_MODE_SETTING, b'\x03') # X increment Y increment
|
||||
self.set_lut(self.LUT_FULL_UPDATE)
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def set_lut(self, lut):
|
||||
self._command(WRITE_LUT_REGISTER, lut)
|
||||
|
||||
# put an image in the frame memory
|
||||
def set_frame_memory(self, image, x, y, w, h):
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
x = x & 0xF8
|
||||
w = w & 0xF8
|
||||
|
||||
if (x + w >= self.width):
|
||||
x_end = self.width - 1
|
||||
else:
|
||||
x_end = x + w - 1
|
||||
|
||||
if (y + h >= self.height):
|
||||
y_end = self.height - 1
|
||||
else:
|
||||
y_end = y + h - 1
|
||||
|
||||
self.set_memory_area(x, y, x_end, y_end)
|
||||
self.set_memory_pointer(x, y)
|
||||
self._command(WRITE_RAM, image)
|
||||
|
||||
# replace the frame memory with the specified color
|
||||
def clear_frame_memory(self, color):
|
||||
self.set_memory_area(0, 0, self.width - 1, self.height - 1)
|
||||
self.set_memory_pointer(0, 0)
|
||||
self._command(WRITE_RAM)
|
||||
# send the color data
|
||||
for i in range(0, self.width // 8 * self.height):
|
||||
self._data(bytearray([color]))
|
||||
|
||||
# draw the current frame memory and switch to the next memory area
|
||||
def display_frame(self):
|
||||
self._command(DISPLAY_UPDATE_CONTROL_2, b'\xC4')
|
||||
self._command(MASTER_ACTIVATION)
|
||||
self._command(TERMINATE_FRAME_READ_WRITE)
|
||||
self.wait_until_idle()
|
||||
|
||||
# specify the memory area for data R/W
|
||||
def set_memory_area(self, x_start, y_start, x_end, y_end):
|
||||
self._command(SET_RAM_X_ADDRESS_START_END_POSITION)
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
self._data(bytearray([(x_start >> 3) & 0xFF]))
|
||||
self._data(bytearray([(x_end >> 3) & 0xFF]))
|
||||
self._command(SET_RAM_Y_ADDRESS_START_END_POSITION, ustruct.pack("<HH", y_start, y_end))
|
||||
|
||||
# specify the start point for data R/W
|
||||
def set_memory_pointer(self, x, y):
|
||||
self._command(SET_RAM_X_ADDRESS_COUNTER)
|
||||
# x point must be the multiple of 8 or the last 3 bits will be ignored
|
||||
self._data(bytearray([(x >> 3) & 0xFF]))
|
||||
self._command(SET_RAM_Y_ADDRESS_COUNTER, ustruct.pack("<H", y))
|
||||
self.wait_until_idle()
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(DEEP_SLEEP_MODE)
|
||||
self.wait_until_idle()
|
|
@ -0,0 +1,277 @@
|
|||
# MicroPython library for Waveshare 2.9" B/W/R e-paper display GDEW029Z10
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(128)
|
||||
EPD_HEIGHT = const(296)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00)
|
||||
POWER_SETTING = const(0x01)
|
||||
POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03)
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05)
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
#DEEP_SLEEP = const(0x07)
|
||||
DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11)
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
DATA_START_TRANSMISSION_2 = const(0x13)
|
||||
#PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_COMMAND = const(0x40)
|
||||
#TEMPERATURE_SENSOR_CALIBRATION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51)
|
||||
#TCON_SETTING = const(0x60)
|
||||
TCON_RESOLUTION = const(0x61)
|
||||
#GET_STATUS = const(0x71)
|
||||
#AUTO_MEASURE_VCOM = const(0x80)
|
||||
#VCOM_VALUE = const(0x81)
|
||||
VCM_DC_SETTING_REGISTER = const(0x82)
|
||||
#PARTIAL_WINDOW = const(0x90)
|
||||
#PARTIAL_IN = const(0x91)
|
||||
#PARTIAL_OUT = const(0x92)
|
||||
#PROGRAM_MODE = const(0xA0)
|
||||
#ACTIVE_PROGRAM = const(0xA1)
|
||||
#READ_OTP_DATA = const(0xA2)
|
||||
#POWER_SAVING = const(0xE3)
|
||||
|
||||
# Display orientation
|
||||
ROTATE_0 = const(0)
|
||||
ROTATE_90 = const(1)
|
||||
ROTATE_180 = const(2)
|
||||
ROTATE_270 = const(3)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
self.rotate = ROTATE_0
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(BOOSTER_SOFT_START, b'\x17\x17\x17')
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PANEL_SETTING, b'\x8F')
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x77')
|
||||
self._command(TCON_RESOLUTION, ustruct.pack(">BH", EPD_WIDTH, EPD_HEIGHT))
|
||||
self._command(VCM_DC_SETTING_REGISTER, b'\x0A')
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def display_frame(self, frame_buffer_black, frame_buffer_red):
|
||||
if (frame_buffer_black != None):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_black[i]]))
|
||||
sleep_ms(2)
|
||||
if (frame_buffer_red != None):
|
||||
self._command(DATA_START_TRANSMISSION_2)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_red[i]]))
|
||||
sleep_ms(2)
|
||||
|
||||
self._command(DISPLAY_REFRESH)
|
||||
self.wait_until_idle()
|
||||
|
||||
def set_rotate(self, rotate):
|
||||
if (rotate == ROTATE_0):
|
||||
self.rotate = ROTATE_0
|
||||
self.width = epdif.EPD_WIDTH
|
||||
self.height = epdif.EPD_HEIGHT
|
||||
elif (rotate == ROTATE_90):
|
||||
self.rotate = ROTATE_90
|
||||
self.width = epdif.EPD_HEIGHT
|
||||
self.height = epdif.EPD_WIDTH
|
||||
elif (rotate == ROTATE_180):
|
||||
self.rotate = ROTATE_180
|
||||
self.width = epdif.EPD_WIDTH
|
||||
self.height = epdif.EPD_HEIGHT
|
||||
elif (rotate == ROTATE_270):
|
||||
self.rotate = ROTATE_270
|
||||
self.width = epdif.EPD_HEIGHT
|
||||
self.height = epdif.EPD_WIDTH
|
||||
|
||||
def set_pixel(self, frame_buffer, x, y, colored):
|
||||
if (x < 0 or x >= self.width or y < 0 or y >= self.height):
|
||||
return
|
||||
if (self.rotate == ROTATE_0):
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_90):
|
||||
point_temp = x
|
||||
x = epdif.EPD_WIDTH - y
|
||||
y = point_temp
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_180):
|
||||
x = epdif.EPD_WIDTH - x
|
||||
y = epdif.EPD_HEIGHT- y
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
elif (self.rotate == ROTATE_270):
|
||||
point_temp = x
|
||||
x = y
|
||||
y = epdif.EPD_HEIGHT - point_temp
|
||||
self.set_absolute_pixel(frame_buffer, x, y, colored)
|
||||
|
||||
def set_absolute_pixel(self, frame_buffer, x, y, colored):
|
||||
# To avoid display orientation effects
|
||||
# use EPD_WIDTH instead of self.width
|
||||
# use EPD_HEIGHT instead of self.height
|
||||
if (x < 0 or x >= EPD_WIDTH or y < 0 or y >= EPD_HEIGHT):
|
||||
return
|
||||
if (colored):
|
||||
frame_buffer[(x + y * EPD_WIDTH) // 8] &= ~(0x80 >> (x % 8))
|
||||
else:
|
||||
frame_buffer[(x + y * EPD_WIDTH) // 8] |= 0x80 >> (x % 8)
|
||||
|
||||
def draw_string_at(self, frame_buffer, x, y, text, font, colored):
|
||||
image = Image.new('1', (self.width, self.height))
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.text((x, y), text, font = font, fill = 255)
|
||||
# Set buffer to value of Python Imaging Library image.
|
||||
# Image must be in mode 1.
|
||||
pixels = image.load()
|
||||
for y in range(self.height):
|
||||
for x in range(self.width):
|
||||
# Set the bits for the column of pixels at the current position.
|
||||
if pixels[x, y] != 0:
|
||||
self.set_pixel(frame_buffer, x, y, colored)
|
||||
|
||||
def draw_line(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
# Bresenham algorithm
|
||||
dx = abs(x1 - x0)
|
||||
sx = 1 if x0 < x1 else -1
|
||||
dy = -abs(y1 - y0)
|
||||
sy = 1 if y0 < y1 else -1
|
||||
err = dx + dy
|
||||
while((x0 != x1) and (y0 != y1)):
|
||||
self.set_pixel(frame_buffer, x0, y0 , colored)
|
||||
if (2 * err >= dy):
|
||||
err += dy
|
||||
x0 += sx
|
||||
if (2 * err <= dx):
|
||||
err += dx
|
||||
y0 += sy
|
||||
|
||||
def draw_horizontal_line(self, frame_buffer, x, y, width, colored):
|
||||
for i in range(x, x + width):
|
||||
self.set_pixel(frame_buffer, i, y, colored)
|
||||
|
||||
def draw_vertical_line(self, frame_buffer, x, y, height, colored):
|
||||
for i in range(y, y + height):
|
||||
self.set_pixel(frame_buffer, x, i, colored)
|
||||
|
||||
def draw_rectangle(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
min_x = x0 if x1 > x0 else x1
|
||||
max_x = x1 if x1 > x0 else x0
|
||||
min_y = y0 if y1 > y0 else y1
|
||||
max_y = y1 if y1 > y0 else y0
|
||||
self.draw_horizontal_line(frame_buffer, min_x, min_y, max_x - min_x + 1, colored)
|
||||
self.draw_horizontal_line(frame_buffer, min_x, max_y, max_x - min_x + 1, colored)
|
||||
self.draw_vertical_line(frame_buffer, min_x, min_y, max_y - min_y + 1, colored)
|
||||
self.draw_vertical_line(frame_buffer, max_x, min_y, max_y - min_y + 1, colored)
|
||||
|
||||
def draw_filled_rectangle(self, frame_buffer, x0, y0, x1, y1, colored):
|
||||
min_x = x0 if x1 > x0 else x1
|
||||
max_x = x1 if x1 > x0 else x0
|
||||
min_y = y0 if y1 > y0 else y1
|
||||
max_y = y1 if y1 > y0 else y0
|
||||
for i in range(min_x, max_x + 1):
|
||||
self.draw_vertical_line(frame_buffer, i, min_y, max_y - min_y + 1, colored)
|
||||
|
||||
def draw_circle(self, frame_buffer, x, y, radius, colored):
|
||||
# Bresenham algorithm
|
||||
x_pos = -radius
|
||||
y_pos = 0
|
||||
err = 2 - 2 * radius
|
||||
if (x >= self.width or y >= self.height):
|
||||
return
|
||||
while True:
|
||||
self.set_pixel(frame_buffer, x - x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y - y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x - x_pos, y - y_pos, colored)
|
||||
e2 = err
|
||||
if (e2 <= y_pos):
|
||||
y_pos += 1
|
||||
err += y_pos * 2 + 1
|
||||
if(-x_pos == y_pos and e2 <= x_pos):
|
||||
e2 = 0
|
||||
if (e2 > x_pos):
|
||||
x_pos += 1
|
||||
err += x_pos * 2 + 1
|
||||
if x_pos > 0:
|
||||
break
|
||||
|
||||
def draw_filled_circle(self, frame_buffer, x, y, radius, colored):
|
||||
# Bresenham algorithm
|
||||
x_pos = -radius
|
||||
y_pos = 0
|
||||
err = 2 - 2 * radius
|
||||
if (x >= self.width or y >= self.height):
|
||||
return
|
||||
while True:
|
||||
self.set_pixel(frame_buffer, x - x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y + y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x + x_pos, y - y_pos, colored)
|
||||
self.set_pixel(frame_buffer, x - x_pos, y - y_pos, colored)
|
||||
self.draw_horizontal_line(frame_buffer, x + x_pos, y + y_pos, 2 * (-x_pos) + 1, colored)
|
||||
self.draw_horizontal_line(frame_buffer, x + x_pos, y - y_pos, 2 * (-x_pos) + 1, colored)
|
||||
e2 = err
|
||||
if (e2 <= y_pos):
|
||||
y_pos += 1
|
||||
err += y_pos * 2 + 1
|
||||
if(-x_pos == y_pos and e2 <= x_pos):
|
||||
e2 = 0
|
||||
if (e2 > x_pos):
|
||||
x_pos += 1
|
||||
err += x_pos * 2 + 1
|
||||
if x_pos > 0:
|
||||
break
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x37')
|
||||
self._command(VCM_DC_SETTING_REGISTER, b'\x00') # to solve Vcom drop
|
||||
self._command(POWER_SETTING, b'\x02\x00\x00\x00') # gate switch to external
|
||||
self.wait_until_idle()
|
||||
self._command(POWER_OFF)
|
|
@ -0,0 +1,146 @@
|
|||
# MicroPython library for Waveshare 4.2" B/W e-paper display GDEW042T2
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(400)
|
||||
EPD_HEIGHT = const(300)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00)
|
||||
POWER_SETTING = const(0x01)
|
||||
POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03)
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05)
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
DEEP_SLEEP = const(0x07)
|
||||
DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11)
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
DATA_START_TRANSMISSION_2 = const(0x13)
|
||||
LUT_FOR_VCOM = const(0x20)
|
||||
LUT_WHITE_TO_WHITE = const(0x21)
|
||||
LUT_BLACK_TO_WHITE = const(0x22)
|
||||
LUT_WHITE_TO_BLACK = const(0x23)
|
||||
LUT_BLACK_TO_BLACK = const(0x24)
|
||||
PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_COMMAND = const(0x40)
|
||||
#TEMPERATURE_SENSOR_SELECTION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51)
|
||||
#TCON_SETTING = const(0x60)
|
||||
RESOLUTION_SETTING = const(0x61)
|
||||
#GSST_SETTING = const(0x65)
|
||||
#GET_STATUS = const(0x71)
|
||||
#AUTO_MEASUREMENT_VCOM = const(0x80)
|
||||
#READ_VCOM_VALUE = const(0x81)
|
||||
VCM_DC_SETTING = const(0x82)
|
||||
#PARTIAL_WINDOW = const(0x90)
|
||||
#PARTIAL_IN = const(0x91)
|
||||
#PARTIAL_OUT = const(0x92)
|
||||
|
||||
#PROGRAM_MODE = const(0xA0)
|
||||
#ACTIVE_PROGRAMMING = const(0xA1)
|
||||
#READ_OTP = const(0xA2)
|
||||
#POWER_SAVING = const(0xE3)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
# 44/42 bytes (look up tables)
|
||||
LUT_VCOM0 = bytearray(b'\x00\x17\x00\x00\x00\x02\x00\x17\x17\x00\x00\x02\x00\x0A\x01\x00\x00\x01\x00\x0E\x0E\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
LUT_WW = bytearray(b'\x40\x17\x00\x00\x00\x02\x90\x17\x17\x00\x00\x02\x40\x0A\x01\x00\x00\x01\xA0\x0E\x0E\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
LUT_BW = LUT_WW
|
||||
LUT_BB = bytearray(b'\x80\x17\x00\x00\x00\x02\x90\x17\x17\x00\x00\x02\x80\x0A\x01\x00\x00\x01\x50\x0E\x0E\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
LUT_WB = LUT_BB
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(POWER_SETTING, b'\x03\x00\x2B\x2B\xFF') # VDS_EN VDG_EN, VCOM_HV VGHL_LV[1] VGHL_LV[0], VDH, VDL, VDHR
|
||||
self._command(BOOSTER_SOFT_START, b'\x17\x17\x17') # 07 0f 17 1f 27 2F 37 2f
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PANEL_SETTING, b'\xBF\x0B') # KW-BF KWR-AF BWROTP 0f
|
||||
self._command(PLL_CONTROL, b'\x3C') # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
def set_lut(self):
|
||||
self._command(LUT_FOR_VCOM, self.LUT_VCOM0) # vcom
|
||||
self._command(LUT_WHITE_TO_WHITE, self.LUT_WW) # ww --
|
||||
self._command(LUT_BLACK_TO_WHITE, self.LUT_BW) # bw r
|
||||
self._command(LUT_WHITE_TO_BLACK, self.LUT_BB) # wb w
|
||||
self._command(LUT_BLACK_TO_BLACK, self.LUT_WB) # bb b
|
||||
|
||||
# draw the current frame memory
|
||||
def display_frame(self, frame_buffer):
|
||||
self._command(RESOLUTION_SETTING, ustruct.pack(">HH", EPD_WIDTH, EPD_HEIGHT))
|
||||
self._command(VCM_DC_SETTING, b'\x12')
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING)
|
||||
self._command(0x97) # VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7
|
||||
# TODO should ^ this be _data(0x97), not sure what it does
|
||||
|
||||
if (frame_buffer != None):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([0xFF])) # bit set: white, bit reset: black
|
||||
sleep_ms(2)
|
||||
self._command(DATA_START_TRANSMISSION_2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer[i]]))
|
||||
sleep_ms(2)
|
||||
|
||||
self.set_lut()
|
||||
self._command(DISPLAY_REFRESH)
|
||||
sleep_ms(100)
|
||||
self.wait_until_idle()
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x17') # border floating
|
||||
self._command(VCM_DC_SETTING) # VCOM to 0V
|
||||
self._command(PANEL_SETTING)
|
||||
sleep_ms(100)
|
||||
self._command(POWER_SETTING, b'\x00\x00\x00\x00\x00') # VG&VS to 0V fast
|
||||
sleep_ms(100)
|
||||
self._command(POWER_OFF)
|
||||
self.wait_until_idle()
|
||||
self._command(DEEP_SLEEP, b'\xA5')
|
|
@ -0,0 +1,118 @@
|
|||
# MicroPython library for Waveshare 4.2" B/W/R e-paper display GDEW042Z15
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(400)
|
||||
EPD_HEIGHT = const(300)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00)
|
||||
#POWER_SETTING = const(0x01)
|
||||
POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03)
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05)
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
DEEP_SLEEP = const(0x07)
|
||||
DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11)
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
DATA_START_TRANSMISSION_2 = const(0x13)
|
||||
#VCOM_LUT = const(0x20)
|
||||
#W2W_LUT = const(0x21)
|
||||
#B2W_LUT = const(0x22)
|
||||
#W2B_LUT = const(0x23)
|
||||
#B2B_LUT = const(0x24)
|
||||
#PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_CALIBRATION = const(0x40)
|
||||
#TEMPERATURE_SENSOR_SELECTION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51)
|
||||
#TCON_SETTING = const(0x60)
|
||||
#RESOLUTION_SETTING = const(0x61)
|
||||
#GSST_SETTING = const(0x65)
|
||||
#GET_STATUS = const(0x71)
|
||||
#AUTO_MEASURE_VCOM = const(0x80)
|
||||
#VCOM_VALUE = const(0x81)
|
||||
#VCM_DC_SETTING = const(0x82)
|
||||
#PARTIAL_WINDOW = const(0x90)
|
||||
#PARTIAL_IN = const(0x91)
|
||||
#PARTIAL_OUT = const(0x92)
|
||||
#PROGRAM_MODE = const(0xA0)
|
||||
#ACTIVE_PROGRAM = const(0xA1)
|
||||
#READ_OTP_DATA = const(0xA2)
|
||||
#POWER_SAVING = const(0xE3)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(BOOSTER_SOFT_START, b'\x17\x17\x17') # 07 0f 17 1f 27 2F 37 2f
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PANEL_SETTING, b'\x0F') # LUT from OTP
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
# draw the current frame memory
|
||||
def display_frame(self, frame_buffer_black, frame_buffer_red):
|
||||
if (frame_buffer_black != None):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_black[i]]))
|
||||
sleep_ms(2)
|
||||
if (frame_buffer_red != None):
|
||||
self._command(DATA_START_TRANSMISSION_2)
|
||||
sleep_ms(2)
|
||||
for i in range(0, self.width * self.height // 8):
|
||||
self._data(bytearray([frame_buffer_red[i]]))
|
||||
sleep_ms(2)
|
||||
|
||||
self._command(DISPLAY_REFRESH)
|
||||
self.wait_until_idle()
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\xF7') # border floating
|
||||
self._command(POWER_OFF)
|
||||
self.wait_until_idle()
|
||||
self._command(DEEP_SLEEP, b'\xA5') # check code
|
|
@ -0,0 +1,133 @@
|
|||
# MicroPython library for Waveshare 7.5" B/W e-paper display GDEW075T8
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(640)
|
||||
EPD_HEIGHT = const(384)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00)
|
||||
POWER_SETTING = const(0x01)
|
||||
POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03)
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05)
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
DEEP_SLEEP = const(0x07)
|
||||
DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11)
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
#IMAGE_PROCESS = const(0x13)
|
||||
#LUT_FOR_VCOM = const(0x20)
|
||||
#LUT_BLUE = const(0x21)
|
||||
#LUT_WHITE = const(0x22)
|
||||
#LUT_GRAY_1 = const(0x23)
|
||||
#LUT_GRAY_2 = const(0x24)
|
||||
#LUT_RED_0 = const(0x25)
|
||||
#LUT_RED_1 = const(0x26)
|
||||
#LUT_RED_2 = const(0x27)
|
||||
#LUT_RED_3 = const(0x28)
|
||||
#LUT_XON = const(0x29)
|
||||
PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_COMMAND = const(0x40)
|
||||
TEMPERATURE_CALIBRATION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51)
|
||||
TCON_SETTING = const(0x60)
|
||||
TCON_RESOLUTION = const(0x61)
|
||||
#SPI_FLASH_CONTROL = const(0x65)
|
||||
#REVISION = const(0x70)
|
||||
#GET_STATUS = const(0x71)
|
||||
#AUTO_MEASUREMENT_VCOM = const(0x80)
|
||||
#READ_VCOM_VALUE = const(0x81)
|
||||
VCM_DC_SETTING = const(0x82)
|
||||
FLASH_MODE = const(0xE5)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(POWER_SETTING, b'\x37\x00')
|
||||
self._command(PANEL_SETTING, b'\xCF\x08')
|
||||
self._command(BOOSTER_SOFT_START, b'\xC7\xCC\x28')
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PLL_CONTROL, b'\x3C')
|
||||
self._command(TEMPERATURE_CALIBRATION, b'\x00')
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x77')
|
||||
self._command(TCON_SETTING, b'\x22')
|
||||
self._command(TCON_RESOLUTION, ustruct.pack(">HH", EPD_WIDTH, EPD_HEIGHT))
|
||||
self._command(VCM_DC_SETTING, b'\x1E') # decide by LUT file
|
||||
self._command(FLASH_MODE, b'\x03')
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
# draw the current frame memory
|
||||
def display_frame(self, frame_buffer):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
for i in range(0, 30720):
|
||||
temp1 = frame_buffer[i]
|
||||
j = 0
|
||||
while (j < 8):
|
||||
if (temp1 & 0x80):
|
||||
temp2 = 0x03
|
||||
else:
|
||||
temp2 = 0x00
|
||||
temp2 = (temp2 << 4) & 0xFF
|
||||
temp1 = (temp1 << 1) & 0xFF
|
||||
j += 1
|
||||
if (temp1 & 0x80):
|
||||
temp2 |= 0x03
|
||||
else:
|
||||
temp2 |= 0x00
|
||||
temp1 = (temp1 << 1) & 0xFF
|
||||
self._data(bytearray([temp2]))
|
||||
j += 1
|
||||
self._command(DISPLAY_REFRESH)
|
||||
sleep_ms(100)
|
||||
self.wait_until_idle()
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(POWER_OFF)
|
||||
self.wait_until_idle()
|
||||
self._command(DEEP_SLEEP, b'\xA5')
|
|
@ -0,0 +1,137 @@
|
|||
# MicroPython library for Waveshare 7.5" B/W/R e-paper display GDEW075Z09
|
||||
|
||||
from micropython import const
|
||||
from time import sleep_ms
|
||||
import ustruct
|
||||
|
||||
# Display resolution
|
||||
EPD_WIDTH = const(640)
|
||||
EPD_HEIGHT = const(384)
|
||||
|
||||
# Display commands
|
||||
PANEL_SETTING = const(0x00)
|
||||
POWER_SETTING = const(0x01)
|
||||
POWER_OFF = const(0x02)
|
||||
#POWER_OFF_SEQUENCE_SETTING = const(0x03)
|
||||
POWER_ON = const(0x04)
|
||||
#POWER_ON_MEASURE = const(0x05)
|
||||
BOOSTER_SOFT_START = const(0x06)
|
||||
DEEP_SLEEP = const(0x07)
|
||||
DATA_START_TRANSMISSION_1 = const(0x10)
|
||||
#DATA_STOP = const(0x11)
|
||||
DISPLAY_REFRESH = const(0x12)
|
||||
#IMAGE_PROCESS = const(0x13)
|
||||
#LUT_FOR_VCOM = const(0x20)
|
||||
#LUT_BLUE = const(0x21)
|
||||
#LUT_WHITE = const(0x22)
|
||||
#LUT_GRAY_1 = const(0x23)
|
||||
#LUT_GRAY_2 = const(0x24)
|
||||
#LUT_RED_0 = const(0x25)
|
||||
#LUT_RED_1 = const(0x26)
|
||||
#LUT_RED_2 = const(0x27)
|
||||
#LUT_RED_3 = const(0x28)
|
||||
#LUT_XON = const(0x29)
|
||||
PLL_CONTROL = const(0x30)
|
||||
#TEMPERATURE_SENSOR_COMMAND = const(0x40)
|
||||
TEMPERATURE_CALIBRATION = const(0x41)
|
||||
#TEMPERATURE_SENSOR_WRITE = const(0x42)
|
||||
#TEMPERATURE_SENSOR_READ = const(0x43)
|
||||
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
|
||||
#LOW_POWER_DETECTION = const(0x51)
|
||||
TCON_SETTING = const(0x60)
|
||||
TCON_RESOLUTION = const(0x61)
|
||||
#SPI_FLASH_CONTROL = const(0x65)
|
||||
#REVISION = const(0x70)
|
||||
#GET_STATUS = const(0x71)
|
||||
#AUTO_MEASUREMENT_VCOM = const(0x80)
|
||||
#READ_VCOM_VALUE = const(0x81)
|
||||
VCM_DC_SETTING = const(0x82)
|
||||
FLASH_MODE = const(0xE5)
|
||||
|
||||
class EPD:
|
||||
def __init__(self, spi, cs, dc, rst, busy):
|
||||
self.spi = spi
|
||||
self.cs = cs
|
||||
self.dc = dc
|
||||
self.rst = rst
|
||||
self.busy = busy
|
||||
self.cs.init(self.cs.OUT, value=1)
|
||||
self.dc.init(self.dc.OUT, value=0)
|
||||
self.rst.init(self.rst.OUT, value=0)
|
||||
self.busy.init(self.busy.IN)
|
||||
self.width = EPD_WIDTH
|
||||
self.height = EPD_HEIGHT
|
||||
|
||||
def _command(self, command, data=None):
|
||||
self.dc.low()
|
||||
self.cs.low()
|
||||
self.spi.write(bytearray([command]))
|
||||
self.cs.high()
|
||||
if data is not None:
|
||||
self._data(data)
|
||||
|
||||
def _data(self, data):
|
||||
self.dc.high()
|
||||
self.cs.low()
|
||||
self.spi.write(data)
|
||||
self.cs.high()
|
||||
|
||||
def init(self):
|
||||
self.reset()
|
||||
self._command(POWER_SETTING, b'\x37\x00')
|
||||
self._command(PANEL_SETTING, b'\xCF\x08')
|
||||
self._command(BOOSTER_SOFT_START, b'\xC7\xCC\x28')
|
||||
self._command(POWER_ON)
|
||||
self.wait_until_idle()
|
||||
self._command(PLL_CONTROL, b'\x3C')
|
||||
self._command(TEMPERATURE_CALIBRATION, b'\x00')
|
||||
self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x77')
|
||||
self._command(TCON_SETTING, b'\x22')
|
||||
self._command(TCON_RESOLUTION, ustruct.pack(">HH", EPD_WIDTH, EPD_HEIGHT))
|
||||
self._command(VCM_DC_SETTING, b'\x1E') # decide by LUT file
|
||||
self._command(FLASH_MODE, b'\x03')
|
||||
|
||||
def wait_until_idle(self):
|
||||
while self.busy.value() == 1:
|
||||
sleep_ms(100)
|
||||
|
||||
def reset(self):
|
||||
self.rst.low()
|
||||
sleep_ms(200)
|
||||
self.rst.high()
|
||||
sleep_ms(200)
|
||||
|
||||
# draw the current frame memory
|
||||
def display_frame(self, frame_buffer):
|
||||
self._command(DATA_START_TRANSMISSION_1)
|
||||
for i in range(0, self.width // 4 * self.height):
|
||||
temp1 = frame_buffer[i]
|
||||
j = 0
|
||||
while (j < 4):
|
||||
if ((temp1 & 0xC0) == 0xC0):
|
||||
temp2 = 0x03
|
||||
elif ((temp1 & 0xC0) == 0x00):
|
||||
temp2 = 0x00
|
||||
else:
|
||||
temp2 = 0x04
|
||||
temp2 = (temp2 << 4) & 0xFF
|
||||
temp1 = (temp1 << 2) & 0xFF
|
||||
j += 1
|
||||
if ((temp1 & 0xC0) == 0xC0):
|
||||
temp2 |= 0x03
|
||||
elif ((temp1 & 0xC0) == 0x00):
|
||||
temp2 |= 0x00
|
||||
else:
|
||||
temp2 |= 0x04
|
||||
temp1 = (temp1 << 2) & 0xFF
|
||||
self._data(bytearray([temp2]))
|
||||
j += 1
|
||||
self._command(DISPLAY_REFRESH)
|
||||
sleep_ms(100)
|
||||
self.wait_until_idle()
|
||||
|
||||
# to wake call reset() or init()
|
||||
def sleep(self):
|
||||
self._command(POWER_OFF)
|
||||
self.wait_until_idle()
|
||||
self._command(DEEP_SLEEP, b'\xA5')
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 111 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 2.5 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 148 KiB |
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 2.4 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,54 @@
|
|||
import epaper2in9
|
||||
from pyb import SPI
|
||||
|
||||
# SPI3 on Black STM32F407VET6
|
||||
spi = SPI(3, SPI.MASTER, baudrate=2000000, polarity=0, phase=0)
|
||||
cs = pyb.Pin('PB6')
|
||||
dc = pyb.Pin('PB7')
|
||||
rst = pyb.Pin('PB8')
|
||||
busy = pyb.Pin('PB9')
|
||||
|
||||
e = epaper2in9.EPD(spi, cs, dc, rst, busy)
|
||||
e.init()
|
||||
|
||||
w = 128
|
||||
h = 296
|
||||
x = 0
|
||||
y = 0
|
||||
|
||||
# write hello world with black bg and white text
|
||||
from image_dark import hello_world_dark
|
||||
e.clear_frame_memory(b'\xFF')
|
||||
e.set_frame_memory(hello_world_dark, x, y, w, h)
|
||||
e.display_frame()
|
||||
|
||||
# write hello world with white bg and black text
|
||||
from image_light import hello_world_light
|
||||
e.clear_frame_memory(b'\xFF')
|
||||
e.set_frame_memory(hello_world_light, x, y, w, h)
|
||||
e.display_frame()
|
||||
|
||||
# clear display
|
||||
e.clear_frame_memory(b'\xFF')
|
||||
e.display_frame()
|
||||
|
||||
# use a frame buffer
|
||||
# 128 * 296 / 8 = 4736 - thats a lot of pixels
|
||||
import framebuf
|
||||
buf = bytearray(128 * 296 // 8)
|
||||
fb = framebuf.FrameBuffer(buf, 128, 296, framebuf.MONO_HLSB)
|
||||
black = 0
|
||||
white = 1
|
||||
fb.fill(white)
|
||||
fb.text('Hello World',30,0,black)
|
||||
fb.pixel(30, 10, black)
|
||||
fb.hline(30, 30, 10, black)
|
||||
fb.vline(30, 50, 10, black)
|
||||
fb.line(30, 70, 40, 80, black)
|
||||
fb.rect(30, 90, 10, 10, black)
|
||||
fb.fill_rect(30, 110, 10, 10, black)
|
||||
for row in range(0,37):
|
||||
fb.text(str(row),0,row*8,black)
|
||||
fb.text('Line 36',0,288,black)
|
||||
e.set_frame_memory(buf, x, y, w, h)
|
||||
e.display_frame()
|
Ładowanie…
Reference in New Issue