|  | 
|  | 1 | +# SPDX-FileCopyrightText: 2018 Dean Miller for Adafruit Industries | 
|  | 2 | +# | 
|  | 3 | +# SPDX-License-Identifier: MIT | 
|  | 4 | + | 
|  | 5 | +""" | 
|  | 6 | +`adafruit_epd.ssd1680` - Adafruit SSD1680 - ePaper display driver | 
|  | 7 | +==================================================================================== | 
|  | 8 | +CircuitPython driver for Adafruit SSD1680 display breakouts | 
|  | 9 | +* Author(s): Melissa LeBlanc-Williams | 
|  | 10 | +""" | 
|  | 11 | + | 
|  | 12 | +import time | 
|  | 13 | +from micropython import const | 
|  | 14 | +import adafruit_framebuf | 
|  | 15 | +from adafruit_epd.epd import Adafruit_EPD | 
|  | 16 | + | 
|  | 17 | +__version__ = "0.0.0-auto.0" | 
|  | 18 | +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_EPD.git" | 
|  | 19 | + | 
|  | 20 | +_SSD1680_DRIVER_CONTROL = const(0x01) | 
|  | 21 | +_SSD1680_GATE_VOLTAGE = const(0x03) | 
|  | 22 | +_SSD1680_SOURCE_VOLTAGE = const(0x04) | 
|  | 23 | +_SSD1680_INIT_SETTING = const(0x08) | 
|  | 24 | +_SSD1680_INIT_WRITE_REG = const(0x09) | 
|  | 25 | +_SSD1680_INIT_READ_REG = const(0x0A) | 
|  | 26 | +_SSD1680_BOOSTER_SOFT_START = const(0x0C) | 
|  | 27 | +_SSD1680_DEEP_SLEEP = const(0x10) | 
|  | 28 | +_SSD1680_DATA_MODE = const(0x11) | 
|  | 29 | +_SSD1680_SW_RESET = const(0x12) | 
|  | 30 | +_SSD1680_HV_DETECT = const(0x14) | 
|  | 31 | +_SSD1680_VCI_DETECT = const(0x15) | 
|  | 32 | +_SSD1680_TEMP_CONTROL = const(0x18) | 
|  | 33 | +_SSD1680_TEMP_WRITE = const(0x1A) | 
|  | 34 | +_SSD1680_TEMP_READ = const(0x1B) | 
|  | 35 | +_SSD1680_EXTTEMP_WRITE = const(0x1C) | 
|  | 36 | +_SSD1680_MASTER_ACTIVATE = const(0x20) | 
|  | 37 | +_SSD1680_DISP_CTRL1 = const(0x21) | 
|  | 38 | +_SSD1680_DISP_CTRL2 = const(0x22) | 
|  | 39 | +_SSD1680_WRITE_BWRAM = const(0x24) | 
|  | 40 | +_SSD1680_WRITE_REDRAM = const(0x26) | 
|  | 41 | +_SSD1680_READ_RAM = const(0x27) | 
|  | 42 | +_SSD1680_VCOM_SENSE = const(0x28) | 
|  | 43 | +_SSD1680_VCOM_DURATION = const(0x29) | 
|  | 44 | +_SSD1680_WRITE_VCOM_OTP = const(0x2A) | 
|  | 45 | +_SSD1680_WRITE_VCOM_CTRL = const(0x2B) | 
|  | 46 | +_SSD1680_WRITE_VCOM_REG = const(0x2C) | 
|  | 47 | +_SSD1680_READ_OTP = const(0x2D) | 
|  | 48 | +_SSD1680_READ_USERID = const(0x2E) | 
|  | 49 | +_SSD1680_READ_STATUS = const(0x2F) | 
|  | 50 | +_SSD1680_WRITE_WS_OTP = const(0x30) | 
|  | 51 | +_SSD1680_LOAD_WS_OTP = const(0x31) | 
|  | 52 | +_SSD1680_WRITE_LUT = const(0x32) | 
|  | 53 | +_SSD1680_CRC_CALC = const(0x34) | 
|  | 54 | +_SSD1680_CRC_READ = const(0x35) | 
|  | 55 | +_SSD1680_PROG_OTP = const(0x36) | 
|  | 56 | +_SSD1680_WRITE_DISPLAY_OPT = const(0x37) | 
|  | 57 | +_SSD1680_WRITE_USERID = const(0x38) | 
|  | 58 | +_SSD1680_OTP_PROGMODE = const(0x39) | 
|  | 59 | +_SSD1680_WRITE_BORDER = const(0x3C) | 
|  | 60 | +_SSD1680_END_OPTION = const(0x3F) | 
|  | 61 | +_SSD1680_SET_RAMXPOS = const(0x44) | 
|  | 62 | +_SSD1680_SET_RAMYPOS = const(0x45) | 
|  | 63 | +_SSD1680_AUTOWRITE_RED = const(0x46) | 
|  | 64 | +_SSD1680_AUTOWRITE_BW = const(0x47) | 
|  | 65 | +_SSD1680_SET_RAMXCOUNT = const(0x4E) | 
|  | 66 | +_SSD1680_SET_RAMYCOUNT = const(0x4F) | 
|  | 67 | +_SSD1680_NOP = const(0xFF) | 
|  | 68 | + | 
|  | 69 | + | 
|  | 70 | +class Adafruit_SSD1680(Adafruit_EPD): | 
|  | 71 | +    """driver class for Adafruit SSD1680 ePaper display breakouts""" | 
|  | 72 | + | 
|  | 73 | +    # pylint: disable=too-many-arguments | 
|  | 74 | +    def __init__( | 
|  | 75 | +        self, width, height, spi, *, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin | 
|  | 76 | +    ): | 
|  | 77 | +        super().__init__( | 
|  | 78 | +            width, height, spi, cs_pin, dc_pin, sramcs_pin, rst_pin, busy_pin | 
|  | 79 | +        ) | 
|  | 80 | + | 
|  | 81 | +        if width % 8 != 0: | 
|  | 82 | +            width += 8 - width % 8 | 
|  | 83 | + | 
|  | 84 | +        self._buffer1_size = int(width * height / 8) | 
|  | 85 | +        self._buffer2_size = self._buffer1_size | 
|  | 86 | + | 
|  | 87 | +        if sramcs_pin: | 
|  | 88 | +            self._buffer1 = self.sram.get_view(0) | 
|  | 89 | +            self._buffer2 = self.sram.get_view(self._buffer1_size) | 
|  | 90 | +        else: | 
|  | 91 | +            self._buffer1 = bytearray(self._buffer1_size) | 
|  | 92 | +            self._buffer2 = bytearray(self._buffer2_size) | 
|  | 93 | + | 
|  | 94 | +        self._framebuf1 = adafruit_framebuf.FrameBuffer( | 
|  | 95 | +            self._buffer1, width, height, buf_format=adafruit_framebuf.MHMSB | 
|  | 96 | +        ) | 
|  | 97 | +        self._framebuf2 = adafruit_framebuf.FrameBuffer( | 
|  | 98 | +            self._buffer2, width, height, buf_format=adafruit_framebuf.MHMSB | 
|  | 99 | +        ) | 
|  | 100 | +        self.set_black_buffer(0, True) | 
|  | 101 | +        self.set_color_buffer(1, False) | 
|  | 102 | +        # pylint: enable=too-many-arguments | 
|  | 103 | + | 
|  | 104 | +    def begin(self, reset=True): | 
|  | 105 | +        """Begin communication with the display and set basic settings""" | 
|  | 106 | +        if reset: | 
|  | 107 | +            self.hardware_reset() | 
|  | 108 | +        self.power_down() | 
|  | 109 | + | 
|  | 110 | +    def busy_wait(self): | 
|  | 111 | +        """Wait for display to be done with current task, either by polling the | 
|  | 112 | +        busy pin, or pausing""" | 
|  | 113 | +        if self._busy: | 
|  | 114 | +            while self._busy.value: | 
|  | 115 | +                time.sleep(0.01) | 
|  | 116 | +        else: | 
|  | 117 | +            time.sleep(0.5) | 
|  | 118 | + | 
|  | 119 | +    def power_up(self): | 
|  | 120 | +        """Power up the display in preparation for writing RAM and updating""" | 
|  | 121 | +        self.hardware_reset() | 
|  | 122 | +        self.busy_wait() | 
|  | 123 | +        self.command(_SSD1680_SW_RESET) | 
|  | 124 | +        self.busy_wait() | 
|  | 125 | +        # driver output control | 
|  | 126 | +        self.command( | 
|  | 127 | +            _SSD1680_DRIVER_CONTROL, | 
|  | 128 | +            bytearray([self._height - 1, (self._height - 1) >> 8, 0x00]), | 
|  | 129 | +        ) | 
|  | 130 | +        # data entry mode | 
|  | 131 | +        self.command(_SSD1680_DATA_MODE, bytearray([0x03])) | 
|  | 132 | + | 
|  | 133 | +        # Set voltages | 
|  | 134 | +        self.command(_SSD1680_WRITE_VCOM_REG, bytearray([0x36])) | 
|  | 135 | +        self.command(_SSD1680_GATE_VOLTAGE, bytearray([0x17])) | 
|  | 136 | +        self.command(_SSD1680_SOURCE_VOLTAGE, bytearray([0x41, 0x00, 0x32])) | 
|  | 137 | + | 
|  | 138 | +        # Set ram X start/end postion | 
|  | 139 | +        self.command(_SSD1680_SET_RAMXPOS, bytearray([0x00, 0x0F])) | 
|  | 140 | +        # Set ram Y start/end postion | 
|  | 141 | +        self.command( | 
|  | 142 | +            _SSD1680_SET_RAMYPOS, | 
|  | 143 | +            bytearray([0, 0, self._height - 1, (self._height - 1) >> 8]), | 
|  | 144 | +        ) | 
|  | 145 | +        # Set border waveform | 
|  | 146 | +        self.command(_SSD1680_WRITE_BORDER, bytearray([0x05])) | 
|  | 147 | + | 
|  | 148 | +        # Set ram X count | 
|  | 149 | +        self.command(_SSD1680_SET_RAMXCOUNT, bytearray([0x01])) | 
|  | 150 | +        # Set ram Y count | 
|  | 151 | +        self.command(_SSD1680_SET_RAMYCOUNT, bytearray([self._height - 1, 0])) | 
|  | 152 | +        self.busy_wait() | 
|  | 153 | + | 
|  | 154 | +    def power_down(self): | 
|  | 155 | +        """Power down the display - required when not actively displaying!""" | 
|  | 156 | +        self.command(_SSD1680_DEEP_SLEEP, bytearray([0x01])) | 
|  | 157 | +        time.sleep(0.1) | 
|  | 158 | + | 
|  | 159 | +    def update(self): | 
|  | 160 | +        """Update the display from internal memory""" | 
|  | 161 | +        self.command(_SSD1680_DISP_CTRL2, bytearray([0xF4])) | 
|  | 162 | +        self.command(_SSD1680_MASTER_ACTIVATE) | 
|  | 163 | +        self.busy_wait() | 
|  | 164 | +        if not self._busy: | 
|  | 165 | +            time.sleep(3)  # wait 3 seconds | 
|  | 166 | + | 
|  | 167 | +    def write_ram(self, index): | 
|  | 168 | +        """Send the one byte command for starting the RAM write process. Returns | 
|  | 169 | +        the byte read at the same time over SPI. index is the RAM buffer, can be | 
|  | 170 | +        0 or 1 for tri-color displays.""" | 
|  | 171 | +        if index == 0: | 
|  | 172 | +            return self.command(_SSD1680_WRITE_BWRAM, end=False) | 
|  | 173 | +        if index == 1: | 
|  | 174 | +            return self.command(_SSD1680_WRITE_REDRAM, end=False) | 
|  | 175 | +        raise RuntimeError("RAM index must be 0 or 1") | 
|  | 176 | + | 
|  | 177 | +    def set_ram_address(self, x, y):  # pylint: disable=unused-argument, no-self-use | 
|  | 178 | +        """Set the RAM address location, not used on this chipset but required by | 
|  | 179 | +        the superclass""" | 
|  | 180 | +        # Set RAM X address counter | 
|  | 181 | +        self.command(_SSD1680_SET_RAMXCOUNT, bytearray([x + 1])) | 
|  | 182 | +        # Set RAM Y address counter | 
|  | 183 | +        self.command(_SSD1680_SET_RAMYCOUNT, bytearray([y, y >> 8])) | 
0 commit comments