changeset 0:4eae06eeba7c

Add initial set of display drivers
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sun, 28 Aug 2016 10:49:29 +0200
parents
children 085126ee39f9
files display.py hx8353.py ili9341.py s6d02a1.py ssd1331.py ssd1351.py st7735.py
diffstat 7 files changed, 536 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/display.py	Sun Aug 28 10:49:29 2016 +0200
@@ -0,0 +1,139 @@
+import time
+import ustruct
+
+
+def color565(r, g, b):
+    return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3
+
+
+class DummyPin:
+    """A fake gpio pin for when you want to skip pins."""
+    def init(self, *args, **kwargs):
+        pass
+
+    def low(self):
+        pass
+
+    def high(self):
+        pass
+
+
+class Display:
+    _PAGE_SET = None
+    _COLUMN_SET = None
+    _RAM_WRITE = None
+    _RAM_READ = None
+    _INIT = ()
+    _ENCODE_PIXEL = ">H"
+    _ENCODE_POS = ">HH"
+    _DECODE_PIXEL = ">BBB"
+
+    def __init__(self, width, height):
+        self.width = width
+        self.height = height
+        self.init()
+
+    def init(self):
+        """Run the initialization commands."""
+        for command, data in self._INIT:
+            self._write(command, data)
+
+    def _block(self, x0, y0, x1, y1, data=None):
+        """Read or write a block of data."""
+        self._write(self._COLUMN_SET, self._encode_pos(x0, x1))
+        self._write(self._PAGE_SET, self._encode_pos(y0, y1))
+        if data is None:
+            size = ustruct.calcsize(self._DECODE_PIXEL)
+            return self._read(self._RAM_READ,
+                              (x1 - x0 + 1) * (y1 - y0 + 1) * size)
+        self._write(self._RAM_WRITE, data)
+
+    def _encode_pos(self, a, b):
+        """Encode a postion into bytes."""
+        return ustruct.pack(self._ENCODE_POS, a, b)
+
+    def _encode_pixel(self, color):
+        """Encode a pixel color into bytes."""
+        return ustruct.pack(self._ENCODE_PIXEL, color)
+
+    def _decode_pixel(self, data):
+        """Decode bytes into a pixel color."""
+        return color565(*ustruct.unpack(self._DECODE_PIXEL, data))
+
+    def pixel(self, x, y, color=None):
+        """Read or write a pixel."""
+        if color is None:
+            return self._decode_pixel(self._block(x, y, x, y))
+        if not 0 <= x < self.width or not 0 <= y < self.height:
+            return
+        self._block(x, y, x, y, self._encode_pixel(color))
+
+    def fill_rectangle(self, x, y, w, h, color):
+        """Draw a filled rectangle."""
+        x = min(self.width - 1, max(0, x))
+        y = min(self.height - 1, max(0, y))
+        w = min(self.width - x, max(1, w))
+        h = min(self.height - y, max(1, h))
+        self._block(x, y, x + w - 1, y + h - 1, b'')
+        chunks, rest = divmod(w * h, 512)
+        pixel = self._encode_pixel(color)
+        if chunks:
+            data = pixel * 512
+            for count in range(chunks):
+                self._write(None, data)
+        self._write(None, pixel * rest)
+
+    def fill(self, color=0):
+        """Fill whole screen."""
+        self.fill_rectangle(0, 0, self.width, self.height, color)
+
+    def hline(self, x, y, width, color):
+        """Draw a horizontal line."""
+        self.fill_rectangle(x, y, width, 1, color)
+
+    def vline(self, x, y, height, color):
+        """Draw a vertical line."""
+        self.fill_rectangle(x, y, 1, height, color)
+
+
+class DisplaySPI(Display):
+    def __init__(self, spi, dc, cs, rst=None, width=1, height=1):
+        self.spi = spi
+        self.cs = cs
+        self.dc = dc
+        self.rst = rst
+        self.cs.init(self.cs.OUT, value=1)
+        self.dc.init(self.dc.OUT, value=0)
+        if self.rst:
+            self.rst.init(self.rst.OUT, value=0)
+            self.reset()
+        super().__init__(width, height)
+
+    def reset(self):
+        self.rst.low()
+        time.sleep_ms(50)
+        self.rst.high()
+        time.sleep_ms(50)
+
+    def _write(self, command=None, data=None):
+        if command is not None:
+            self.dc.low()
+            self.cs.low()
+            self.spi.write(bytearray([command]))
+            self.cs.high()
+        if data is not None:
+            self.dc.high()
+            self.cs.low()
+            self.spi.write(data)
+            self.cs.high()
+
+    def _read(self, command=None, count=0):
+        self.dc.low()
+        self.cs.low()
+        if command is not None:
+            self.spi.write(bytearray([command]))
+        if count:
+            data = self.spi.read(count)
+        self.cs.high()
+        return data
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hx8353.py	Sun Aug 28 10:49:29 2016 +0200
@@ -0,0 +1,40 @@
+from display import DisplaySPI, color565
+
+_SWRESET = const(0x01)
+_NORON = const(0x13)
+_INVOFF = const(0x20)
+_INVON = const(0x21)
+_DISPOFF = const(0x28)
+_DISPON = const(0x29)
+_CASET = const(0x2a)
+_PASET = const(0x2b)
+_RAMWR = const(0x2c)
+_RAMRD = const(0x2e)
+_MADCTL = const(0x36)
+_COLMOD = const(0x3a)
+
+class HX8353(DisplaySPI):
+    """
+    A simple driver for the ST7735-based displays.
+
+    >>> from machine import Pin, SPI
+    >>> import hx8353
+    >>> spi = SPI(0)
+    >>> display = hx8353.HX8383(spi, dc=Pin(0), cs=Pin(15), rst=Pin(16))
+    >>> display.fill(0x7521)
+    >>> display.pixel(64, 64, 0)
+
+    """
+    _COLUMN_SET = _CASET
+    _PAGE_SET = _PASET
+    _RAM_WRITE = _RAMWR
+    _RAM_READ = _RAMRD
+    _INIT = (
+        (_SWRESET, None),
+        (_DISPON, None),
+    )
+    _ENCODE_PIXEL = ">H"
+    _ENCODE_POS = ">HH"
+
+    def __init__(self, spi, dc, cs, rst=None, width=128, height=128):
+        super().__init__(spi, dc, cs, rst, width, height)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ili9341.py	Sun Aug 28 10:49:29 2016 +0200
@@ -0,0 +1,59 @@
+import ustruct
+from display import DisplaySPI, color565
+
+
+class ILI9341(DisplaySPI):
+    """
+    A simple driver for the ILI9341/ILI9340-based displays.
+
+
+    >>> import ili9341
+    >>> from machine import Pin, SPI
+    >>> spi = SPI(mosi=Pin(13), sck=Pin(14))
+    >>> display = ili9341.ILI9341(spi, cs=Pin(15), dc=Pin(12), rst=Pin(16))
+    >>> display.fill(ili9341.color565(0xff, 0x11, 0x22))
+    >>> display.pixel(120, 160, 0)
+    """
+
+    _COLUMN_SET = 0x2a
+    _PAGE_SET = 0x2b
+    _RAM_WRITE = 0x2c
+    _RAM_READ = 0x2e
+    _INIT = (
+        (0xef, b'\x03\x80\x02'),
+        (0xcf, b'\x00\xc1\x30'),
+        (0xed, b'\x64\x03\x12\x81'),
+        (0xe8, b'\x85\x00\x78'),
+        (0xcb, b'\x39\x2c\x00\x34\x02'),
+        (0xf7, b'\x20'),
+        (0xea, b'\x00\x00'),
+        (0xc0, b'\x23'),  # Power Control 1, VRH[5:0]
+        (0xc1, b'\x10'),  # Power Control 2, SAP[2:0], BT[3:0]
+        (0xc5, b'\x3e\x28'),  # VCM Control 1
+        (0xc7, b'\x86'),  # VCM Control 2
+        (0x36, b'\x48'),  # Memory Access Control
+        (0x3a, b'\x55'),  # Pixel Format
+        (0xb1, b'\x00\x18'),  # FRMCTR1
+        (0xb6, b'\x08\x82\x27'),  # Display Function Control
+        (0xf2, b'\x00'),  # 3Gamma Function Disable
+        (0x26, b'\x01'),  # Gamma Curve Selected
+        (0xe0,  # Set Gamma
+         b'\x0f\x31\x2b\x0c\x0e\x08\x4e\xf1\x37\x07\x10\x03\x0e\x09\x00'),
+        (0xe1,  # Set Gamma
+         b'\x00\x0e\x14\x03\x11\x07\x31\xc1\x48\x08\x0f\x0c\x31\x36\x0f'),
+        (0x11, None),
+        (0x29, None),
+    )
+    _ENCODE_PIXEL = ">H"
+    _ENCODE_POS = ">HH"
+    _DECODE_PIXEL = ">BBB"
+
+    def __init__(self, spi, dc, cs, rst=None, width=240, height=320):
+        super().__init__(spi, dc, cs, rst, width, height)
+        self._scroll = 0
+
+    def scroll(self, dy=None):
+        if dy is None:
+            return self._scroll
+        self._scroll = (self._scroll + dy) % self.height
+        self._write(0x37, ustruct.pack('>H', self._scroll))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/s6d02a1.py	Sun Aug 28 10:49:29 2016 +0200
@@ -0,0 +1,40 @@
+from display import DisplaySPI, color565
+
+_SWRESET = const(0x01)
+_DISPON = const(0x29)
+_SLEEPOUT = const(0x11)
+_CASET = const(0x2a)
+_PASET = const(0x2b)
+_RAMWR = const(0x2c)
+_RAMRD = const(0x2e)
+_COLMOD = const(0x3a)
+_MADCTL = const(0x36)
+
+class S6D02A1(DisplaySPI):
+    """
+    A simple driver for the ST7735-based displays.
+
+    >>> from machine import Pin, SPI
+    >>> import s6d02a1
+    >>> spi = SPI(0, baudrate=40000000)
+    >>> display = s6d02a1.S6D02A1(spi, dc=Pin(0), cs=Pin(15), rst=Pin(16))
+    >>> display.fill(0x7521)
+    >>> display.pixel(64, 64, 0)
+
+    """
+    _COLUMN_SET = _CASET
+    _PAGE_SET = _PASET
+    _RAM_WRITE = _RAMWR
+    _RAM_READ = _RAMRD
+    _INIT = (
+        (_SWRESET, None),
+        (_SLEEPOUT, None),
+        (_MADCTL, b'\x10'), # bottom-top
+        (_COLMOD, b'\x05'), # RGB565 pixel format
+        (_DISPON, None),
+    )
+    _ENCODE_PIXEL = ">H"
+    _ENCODE_POS = ">HH"
+
+    def __init__(self, spi, dc, cs, rst=None, width=128, height=160):
+        super().__init__(spi, dc, cs, rst, width, height)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ssd1331.py	Sun Aug 28 10:49:29 2016 +0200
@@ -0,0 +1,81 @@
+from display import DisplaySPI, color565
+
+
+_DRAWLINE = const(0x21)
+_DRAWRECT = const(0x22)
+_FILL = const(0x26)
+_PHASEPERIOD = const(0x12)
+_SETCOLUMN = const(0x15)
+_SETROW = const(0x75)
+_CONTRASTA = const(0x81)
+_CONTRASTB = const(0x82)
+_CONTRASTC = const(0x83)
+_MASTERCURRENT = const(0x87)
+_SETREMAP = const(0xA0)
+_STARTLINE = const(0xA1)
+_DISPLAYOFFSET = const(0xA2)
+_NORMALDISPLAY = const(0xA4)
+_DISPLAYALLON = const(0xA5)
+_DISPLAYALLOFF = const(0xA6)
+_INVERTDISPLAY = const(0xA7)
+_SETMULTIPLEX = const(0xA8)
+_SETMASTER = const(0xAD)
+_DISPLAYOFF = const(0xAE)
+_DISPLAYON = const(0xAF)
+_POWERMODE = const(0xB0)
+_PRECHARGE = const(0xB1)
+_CLOCKDIV = const(0xB3)
+_PRECHARGEA = const(0x8A)
+_PRECHARGEB = const(0x8B)
+_PRECHARGEC = const(0x8C)
+_PRECHARGELEVEL = const(0xBB)
+_VCOMH = const(0xBE)
+_LOCK = const(0xfd)
+
+
+class SSD1331(DisplaySPI):
+    """
+>>>
+from machine import Pin, HSPI
+import ssd1331
+#spi = SPI(mosi=Pin(13), sck=Pin(14), polarity=1, phase=1)
+spi = HSPI(polarity=1, phase=1)
+display = ssd1331.SSD1331(spi, dc=Pin(2), cs=Pin(15), rst=Pin(16))
+display.fill(0x7521)
+display.pixel(32, 32, 0)
+    """
+    _COLUMN_SET = _SETCOLUMN
+    _PAGE_SET = _SETROW
+    _RAM_WRITE = None
+    _RAM_READ = None
+    _INIT = (
+        (_DISPLAYOFF, b''),
+        (_LOCK, b'\x0b'),
+        (_SETREMAP, b'\x72'), # RGB Color
+        (_STARTLINE, b'\x00'),
+        (_DISPLAYOFFSET, b'\x00'),
+        (_NORMALDISPLAY, b''),
+#        (_FILL, b'\x01'),
+
+#	(_PHASEPERIOD, b'\x31'),
+#	(_SETMULTIPLEX, b'\x3f'),
+#	(_SETMASTER, b'\x8e'),
+#	(_POWERMODE,b'\x0b'),
+#	(_PRECHARGE, b'\x31'), #;//0x1F - 0x31
+#	(_CLOCKDIV, b'\xf0'),
+#	(_VCOMH, b'\x3e'), #;//0x3E - 0x3F
+#	(_MASTERCURRENT, b'\x06'), # ;//0x06 - 0x0F
+#	(_PRECHARGEA, b'\x64'),
+#	(_PRECHARGEB, b'\x78'),
+#	(_PRECHARGEC, b'\x64'),
+#	(_PRECHARGELEVEL, b'\x3a'), # 0x3A - 0x00
+#	(_CONTRASTA, b'\x91'), #//0xEF - 0x91
+#	(_CONTRASTB, b'\x50'), #;//0x11 - 0x50
+#	(_CONTRASTC, b'\x7d'), #;//0x48 - 0x7D
+        (_DISPLAYON, b''),
+    )
+    _ENCODE_PIXEL = ">H"
+    _ENCODE_POS = ">BB"
+
+    def __init__(self, spi, dc, cs, rst=None, width=96, height=64):
+        super().__init__(spi, dc, cs, rst, width, height)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ssd1351.py	Sun Aug 28 10:49:29 2016 +0200
@@ -0,0 +1,81 @@
+from display import DisplaySPI, color565
+
+
+_SETCOLUMN = const(0x15)
+_SETROW = const(0x75)
+_WRITERAM = const(0x5C)
+_READRAM = const(0x5D)
+_SETREMAP = const(0xA0)
+_STARTLINE = const(0xA1)
+_DISPLAYOFFSET = const(0xA2)
+_DISPLAYALLOFF = const(0xA4)
+_DISPLAYALLON = const(0xA5)
+_NORMALDISPLAY = const(0xA6)
+_INVERTDISPLAY = const(0xA7)
+_FUNCTIONSELECT = const(0xAB)
+_DISPLAYOFF = const(0xAE)
+_DISPLAYON = const(0xAF)
+_PRECHARGE = const(0xB1)
+_DISPLAYENHANCE = const(0xB2)
+_CLOCKDIV = const(0xB3)
+_SETVSL = const(0xB4)
+_SETGPIO = const(0xB5)
+_PRECHARGE2 = const(0xB6)
+_SETGRAY = const(0xB8)
+_USELUT = const(0xB9)
+_PRECHARGELEVEL = const(0xBB)
+_VCOMH = const(0xBE)
+_CONTRASTABC = const(0xC1)
+_CONTRASTMASTER = const(0xC7)
+_MUXRATIO = const(0xCA)
+_COMMANDLOCK = const(0xFD)
+_HORIZSCROLL = const(0x96)
+_STOPSCROLL = const(0x9E)
+_STARTSCROLL = const(0x9F)
+
+
+class SSD1351(DisplaySPI):
+    """
+>>>
+from machine import Pin, SPI
+import ssd1351
+spi = SPI(mosi=Pin(13), sck=Pin(14))
+display = ssd1351.SSD1351(spi, dc=Pin(12), cs=Pin(15), rst=Pin(16))
+display.fill(0x7521)
+display.pixel(32, 32, 0)
+    """
+
+    _COLUMN_SET = _SETCOLUMN
+    _PAGE_SET = _SETROW
+    _RAM_WRITE = _WRITERAM
+    _RAM_READ = _READRAM
+    _INIT = (
+        (_COMMANDLOCK, b'\x12'),
+        (_COMMANDLOCK, b'\xb1'),
+        (_DISPLAYOFF, b''),
+        (_DISPLAYENHANCE, b'\xa4\x00\x00'),
+        # 7:4 = Oscillator Frequency,
+        # 3:0 = CLK Div Ratio (A[3:0]+1 = 1..16)
+        (_CLOCKDIV, b'\xf0'),
+        (_MUXRATIO, b'\x7f'), # 127
+        (_SETREMAP, b'\x74'),
+        (_STARTLINE, b'\x00'),
+        (_DISPLAYOFFSET, b'\x00'),
+        (_SETGPIO, b'\x00'),
+        (_FUNCTIONSELECT, b'\x01'),
+        (_PRECHARGE, b'\x32'),
+        (_PRECHARGELEVEL, b'\x1f'),
+        (_VCOMH, b'\x05'),
+        (_NORMALDISPLAY, b''),
+        (_CONTRASTABC, b'\xc8\x80\xc8'),
+        (_CONTRASTMASTER, b'\x0a'),
+        (_SETVSL, b'\xa0\xb5\x55'),
+        (_PRECHARGE2, b'\x01'),
+        (_DISPLAYON, b''),
+    )
+    _ENCODE_PIXEL = ">H"
+    _ENCODE_POS = ">BB"
+
+    def __init__(self, spi, dc, cs, rst=None, width=128, height=128):
+        super().__init__(spi, dc, cs, rst, width, height)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/st7735.py	Sun Aug 28 10:49:29 2016 +0200
@@ -0,0 +1,96 @@
+from display import DisplaySPI, color565
+
+
+_NOP=const(0x00)
+_SWRESET=const(0x01)
+_RDDID=const(0x04)
+_RDDST=const(0x09)
+
+_SLPIN=const(0x10)
+_SLPOUT=const(0x11)
+_PTLON=const(0x12)
+_NORON=const(0x13)
+
+_INVOFF=const(0x20)
+_INVON=const(0x21)
+_DISPOFF=const(0x28)
+_DISPON=const(0x29)
+_CASET=const(0x2A)
+_RASET=const(0x2B)
+_RAMWR=const(0x2C)
+_RAMRD=const(0x2E)
+
+_PTLAR=const(0x30)
+_COLMOD=const(0x3A)
+_MADCTL=const(0x36)
+
+_FRMCTR1=const(0xB1)
+_FRMCTR2=const(0xB2)
+_FRMCTR3=const(0xB3)
+_INVCTR=const(0xB4)
+_DISSET5=const(0xB6)
+
+_PWCTR1=const(0xC0)
+_PWCTR2=const(0xC1)
+_PWCTR3=const(0xC2)
+_PWCTR4=const(0xC3)
+_PWCTR5=const(0xC4)
+_VMCTR1=const(0xC5)
+
+_RDID1=const(0xDA)
+_RDID2=const(0xDB)
+_RDID3=const(0xDC)
+_RDID4=const(0xDD)
+
+_PWCTR6=const(0xFC)
+
+_GMCTRP1=const(0xE0)
+_GMCTRN1=const(0xE1)
+
+
+class ST7735(DisplaySPI):
+    """
+    A simple driver for the ST7735-based displays.
+
+    >>> from machine import Pin, SPI
+    >>> import st7735
+    >>> spi = SPI(mosi=Pin(13), sck=Pin(14))
+    >>> display = st7735.ST7735(spi, dc=Pin(12), cs=Pin(15), rst=Pin(16))
+    >>> display.fill(0x7521)
+    >>> display.pixel(64, 64, 0)
+
+    """
+    _COLUMN_SET = _CASET
+    _PAGE_SET = _RASET
+    _RAM_WRITE = _RAMWR
+    _RAM_READ = _RAMRD
+    _INIT = (
+        (_SWRESET, None),
+        (_SLPOUT, None),
+        (_COLMOD, b'\x05'), # 16bit color
+        # fastest refresh, 6 lines front porch, 3 line back porch
+        (_FRMCTR1, b'\x00\x06\x03'),
+        (_MADCTL, b'\x08'), # bottom to top refresh
+        # 1 clk cycle nonoverlap, 2 cycle gate rise, 3 sycle osc equalie,
+        # fix on VTL
+        (_DISSET5, b'\x15\x02'),
+        (_INVCTR, b'0x00'), # line inversion
+        (_PWCTR1, b'\x02\x70'), # GVDD = 4.7V, 1.0uA
+        (_PWCTR2, b'\x05'), # VGH=14.7V, VGL=-7.35V
+        (_PWCTR3, b'\x01\x02'), # Opamp current small, Boost frequency
+        (_VMCTR1, b'\x3c\x38'), # VCOMH = 4V, VOML = -1.1V
+        (_PWCTR6, b'\x11\x15'),
+        (_GMCTRP1, b'\x09\x16\x09\x20\x21\x1b\x13\x19'
+                   b'\x17\x15\x1e\x2b\x04\x05\x02\x0e'), # Gamma
+        (_GMCTRN1, b'\x08\x14\x08\x1e\x22\x1d\x18\x1e'
+                   b'\x18\x1a\x24\x2b\x06\x06\x02\x0f'),
+        (_CASET, b'\x00\x02\x00\x81'), # XSTART = 2, XEND = 129
+        (_RASET, b'\x00\x02\x00\x81'), # XSTART = 2, XEND = 129
+        (_NORON, None),
+        (_DISPON, None),
+    )
+    _ENCODE_PIXEL = ">H"
+    _ENCODE_POS = ">HH"
+
+    def __init__(self, spi, dc, cs, rst=None, width=128, height=128):
+        super().__init__(spi, dc, cs, rst, width, height)