diff --git a/DRIVERS.md b/DRIVERS.md index 7162b5a..1cf44a0 100644 --- a/DRIVERS.md +++ b/DRIVERS.md @@ -315,11 +315,11 @@ cross-platform but assume `micropython.viper` capability. They use 8-bit or 4-bit color to minimise the RAM used by the frame buffer. Drivers for [Adafruit 1.8" display](https://www.adafruit.com/product/358). - * `st7735r.py` 8-bit color. + * `st7735r.py` 8-bit color. * `st7735r_4bit.py` 4-bit color for further RAM reduction. For [Adafruit 1.44" display](https://www.adafruit.com/product/2088). - * `st7735r144.py` 8-bit color. + * `st7735r144.py` 8-bit color. * `st7735r144_4bit` 4 bit color. Users of other ST7735R based displays should beware: there are many variants @@ -360,8 +360,8 @@ soft SPI may be used but hard may be faster. * `init_spi=False` This optional arg enables flexible options in configuring the SPI bus. See below. -#### The init_spi constructor arg - +#### The init_spi constructor arg + The `False` default assumes exclusive access to the bus. It is initialised by `color_setup.py` and those settings are left in place. If a callback function is passed, it will be called prior to each SPI bus write. This is for shared @@ -419,7 +419,7 @@ On an ESP32 without SPIRAM, `nano-gui` runs but frozen bytecode. The RP2 Pico runs both GUI's. See [Color handling](./DRIVERS.md#11-color-handling) for details of the -implications of 4-bit color. +implications of 4-bit color. The driver uses the `micropython.viper` decorator. If your platform does not support this, the Viper code will need to be rewritten with a substantial hit @@ -524,6 +524,7 @@ Display types (values for `display`): `TDISPLAY` For the TTGO T-Display and Waveshare Pico LCD. `PI_PICO_LCD_2` Waveshare Pico LCD 2 determined by Mike Wilson. `DFR0995` DFR0995 Contributed by @EdgarKluge +`WAVESHARE_13` Waveshare 1.3" 240x240 LCD contributed by Aaron Mittelmeier ### init_spi @@ -948,7 +949,7 @@ contiguous RAM is available. ### 4.4.1 Micropower applications These comments largely assume a Pyboard host. The application should import -`upower` from +`upower` from [micropython-micropower](https://github.com/peterhinch/micropython-micropower). This turns the USB interface off if not in use to conserve power. It also provides an `lpdelay` function to implement a delay using `pyb.stop()` to @@ -990,9 +991,9 @@ the CPU enabling user code to continue to run. The standard refresh method blocks (monopolises the CPU) until refresh is complete, adding an additional 2s delay. This enables the demo scripts to run unchanged, with the 2s delay allowing the results to be seen before the next -refresh begins. This is fine for simple applications. The drivers also support +refresh begins. This is fine for simple applications. The drivers also support concurrency with `uasyncio`. Such applications can perform other tasks while a -refresh is in progress. See +refresh is in progress. See [EPD Asynchronous support](./DRIVERS.md#6-epd-asynchronous-support). Finally the [Waveshare 400x300 Pi Pico display](./DRIVERS.md#53-waveshare-400x300-pi-pico-display) @@ -1010,7 +1011,7 @@ An alternative is the [wiring details](./DRIVERS.md#514-featherwing-wiring) listed below. In my testing there are differences between these alternatives. The FeatherWing -shows a black border around the display. The reason for this is +shows a black border around the display. The reason for this is [unclear](https://github.com/adafruit/Adafruit_CircuitPython_IL0373/issues/11#issuecomment-763704622). In development I encountered instances where the image on the flexible display gradually degraded after the system was powered down. The white background @@ -1398,8 +1399,8 @@ The driver supports the WeAct Studio SSD1680 2.9 inch 296*128 pixel [display](https://github.com/WeActStudio/WeActStudio.EpaperModule) that uses the [SSD1680 driver](https://github.com/WeActStudio/WeActStudio.EpaperModule/blob/master/Doc/SSD1680.pdf). -This display lacks many features when compared to the ones from Waveshare, -two important examples are fast refresh and partial refresh. The big pro however is the price, +This display lacks many features when compared to the ones from Waveshare, +two important examples are fast refresh and partial refresh. The big pro however is the price, it costs half the money of the Waveshare 2.9in alternative. The driver is cross platform and supports landscape or portrait mode. To keep @@ -1556,7 +1557,7 @@ hardware. ## 7.4 Mapped drivers In the simplest case the `FrameBuffer` mode is chosen to match a mode used by -the hardware. The `rgb` static method converts colors to that format and +the hardware. The `rgb` static method converts colors to that format and `.show` writes it out. In some cases this can result in a need for a large `FrameBuffer`, either diff --git a/drivers/st7789/st7789_4bit.py b/drivers/st7789/st7789_4bit.py index 85da1e3..4221f5b 100644 --- a/drivers/st7789/st7789_4bit.py +++ b/drivers/st7789/st7789_4bit.py @@ -8,7 +8,7 @@ # https://www.adafruit.com/product/4313 # TTGO T-Display # http://www.lilygo.cn/prod_view.aspx?TypeId=50044&Id=1126 - + # Based on # Adfruit https://github.com/adafruit/Adafruit_CircuitPython_ST7789/blob/master/adafruit_st7789.py # Also see st7735r_4bit.py for other source acknowledgements @@ -16,7 +16,7 @@ # SPI bus: default mode. Driver performs no read cycles. # Datasheet table 6 p44 scl write cycle 16ns == 62.5MHz -from time import sleep_ms #, ticks_us, ticks_diff +from time import sleep_ms # , ticks_us, ticks_diff import framebuf import gc import micropython @@ -32,19 +32,22 @@ PORTRAIT = 4 GENERIC = (0, 0, 0) TDISPLAY = (52, 40, 1) PI_PICO_LCD_2 = (0, 0, 1) # Waveshare Pico LCD 2 determined by Mike Wilson. -DFR0995 = (34, 0, 0) # DFR0995 Contributed by @EdgarKluge +DFR0995 = (34, 0, 0) # DFR0995 Contributed by @EdgarKluge +WAVESHARE_13 = (0, 0, 16) # Waveshare 1.3" 240x240 LCD contributed by Aaron Mittelmeier + @micropython.viper -def _lcopy(dest:ptr16, source:ptr8, lut:ptr16, length:int): +def _lcopy(dest: ptr16, source: ptr8, lut: ptr16, length: int): # rgb565 - 16bit/pixel n = 0 for x in range(length): c = source[x] dest[n] = lut[c >> 4] # current pixel n += 1 - dest[n] = lut[c & 0x0f] # next pixel + dest[n] = lut[c & 0x0F] # next pixel n += 1 + class ST7789(framebuf.FrameBuffer): lut = bytearray(0xFF for _ in range(32)) # set all colors to BLACK @@ -55,13 +58,23 @@ class ST7789(framebuf.FrameBuffer): # For some reason color must be inverted on this controller. @staticmethod def rgb(r, g, b): - return ((b & 0xf8) << 5 | (g & 0x1c) << 11 | (g & 0xe0) >> 5 | (r & 0xf8)) ^ 0xffff + return ((b & 0xF8) << 5 | (g & 0x1C) << 11 | (g & 0xE0) >> 5 | (r & 0xF8)) ^ 0xFFFF # rst and cs are active low, SPI is mode 0 - def __init__(self, spi, cs, dc, rst, height=240, width=240, - disp_mode=LANDSCAPE, init_spi=False, display=GENERIC): + def __init__( + self, + spi, + cs, + dc, + rst, + height=240, + width=240, + disp_mode=LANDSCAPE, + init_spi=False, + display=GENERIC, + ): if not 0 <= disp_mode <= 7: - raise ValueError('Invalid display mode:', disp_mode) + raise ValueError("Invalid display mode:", disp_mode) if not display in (GENERIC, TDISPLAY, PI_PICO_LCD_2): print("WARNING: unsupported display parameter value.") self._spi = spi # Clock cycle time for write 16ns 62.5MHz max (read is 150ns) @@ -121,16 +134,16 @@ class ST7789(framebuf.FrameBuffer): self._spi_init(self._spi) # Bus may be shared cmd = self._wcmd wcd = self._wcd - cmd(b'\x01') # SW reset datasheet specifies 120ms before SLPOUT + cmd(b"\x01") # SW reset datasheet specifies 120ms before SLPOUT sleep_ms(150) - cmd(b'\x11') # SLPOUT: exit sleep mode + cmd(b"\x11") # SLPOUT: exit sleep mode sleep_ms(10) # Adafruit delay 500ms (datsheet 5ms) - wcd(b'\x3a', b'\x55') # _COLMOD 16 bit/pixel, 65Kbit color space - cmd(b'\x20') # INVOFF Adafruit turn inversion on. This driver fixes .rgb - cmd(b'\x13') # NORON Normal display mode + wcd(b"\x3a", b"\x55") # _COLMOD 16 bit/pixel, 65Kbit color space + cmd(b"\x20") # INVOFF Adafruit turn inversion on. This driver fixes .rgb + cmd(b"\x13") # NORON Normal display mode # Table maps user request onto hardware values. index values: - # 0 Normal + # 0 Normal # 1 Reflect # 2 USD # 3 USD reflect @@ -146,11 +159,11 @@ class ST7789(framebuf.FrameBuffer): # PORTRAIT = 0x20 # REFLECT = 0x40 # USD = 0x80 - mode = (0x60, 0xe0, 0xa0, 0x20, 0, 0x40, 0xc0, 0x80)[user_mode] + mode = (0x60, 0xE0, 0xA0, 0x20, 0, 0x40, 0xC0, 0x80)[user_mode] # Set display window depending on mode, .height and .width. self.set_window(mode) - wcd(b'\x36', int.to_bytes(mode, 1, 'little')) - cmd(b'\x29') # DISPON. Adafruit then delay 500ms. + wcd(b"\x36", int.to_bytes(mode, 1, "little")) + cmd(b"\x29") # DISPON. Adafruit then delay 500ms. # Define the mapping between RAM and the display. # Datasheet section 8.12 p124. @@ -166,7 +179,7 @@ class ST7789(framebuf.FrameBuffer): xs = xoff xe = wwd + xoff - 1 ys = yoff # y start - ye = wht + yoff - 1 # y end + ye = wht + yoff - 1 # y end if mode & reflect: ys = rwd - wht - yoff ye = rwd - yoff - 1 @@ -179,7 +192,7 @@ class ST7789(framebuf.FrameBuffer): xs = xoff xe = wwd + xoff - 1 ys = yoff # y start - ye = wht + yoff - 1 # y end + ye = wht + yoff - 1 # y end if mode & usd: ys = rht - wht - yoff ye = rht - yoff - 1 @@ -188,15 +201,15 @@ class ST7789(framebuf.FrameBuffer): xe = rwd - xoff - 1 # Col address set. - self._wcd(b'\x2a', int.to_bytes((xs << 16) + xe, 4, 'big')) + self._wcd(b"\x2a", int.to_bytes((xs << 16) + xe, 4, "big")) # Row address set - self._wcd(b'\x2b', int.to_bytes((ys << 16) + ye, 4, 'big')) + self._wcd(b"\x2b", int.to_bytes((ys << 16) + ye, 4, "big")) - #@micropython.native # Made virtually no difference to timing. + # @micropython.native # Made virtually no difference to timing. def show(self): # Blocks for 83ms @60MHz SPI # Blocks for 60ms @30MHz SPI on TTGO in PORTRAIT mode # Blocks for 46ms @30MHz SPI on TTGO in LANDSCAPE mode - #ts = ticks_us() + # ts = ticks_us() clut = ST7789.lut wd = -(-self.width // 2) # Ceiling division for odd number widths end = self.height * wd @@ -206,20 +219,20 @@ class ST7789(framebuf.FrameBuffer): self._spi_init(self._spi) # Bus may be shared self._dc(0) self._cs(0) - self._spi.write(b'\x2c') # RAMWR + self._spi.write(b"\x2c") # RAMWR self._dc(1) for start in range(0, end, wd): _lcopy(lb, buf[start:], clut, wd) # Copy and map colors self._spi.write(lb) self._cs(1) - #print(ticks_diff(ticks_us(), ts)) + # print(ticks_diff(ticks_us(), ts)) # Asynchronous refresh with support for reducing blocking time. async def do_refresh(self, split=5): async with self._lock: lines, mod = divmod(self.height, split) # Lines per segment if mod: - raise ValueError('Invalid do_refresh arg.') + raise ValueError("Invalid do_refresh arg.") clut = ST7789.lut wd = -(-self.width // 2) lb = memoryview(self._linebuf) @@ -230,10 +243,10 @@ class ST7789(framebuf.FrameBuffer): self._spi_init(self._spi) # Bus may be shared self._dc(0) self._cs(0) - self._spi.write(b'\x3c' if n else b'\x2c') # RAMWR/Write memory continue + self._spi.write(b"\x3c" if n else b"\x2c") # RAMWR/Write memory continue self._dc(1) for start in range(wd * line, wd * (line + lines), wd): - _lcopy(lb, buf[start :], clut, wd) # Copy and map colors + _lcopy(lb, buf[start:], clut, wd) # Copy and map colors self._spi.write(lb) line += lines self._cs(1)