Beispiel #1
0
 def do_cmds(self):
     trans = esp.spi_transaction_t({
         "length": self.buflen * 8,
         "tx_buffer": self.txbuf,
         "rx_buffer": self.rxbuf
     })
     rxbuf = self.rxbuf.__dereference__(self.buflen)
     res = esp.spi_device_polling_transmit(self.spi, trans)
     # print("got:", res, ["%02x"%v for v in rxbuf])
     vals = [(rxbuf[1 + 2 * i] << 5) | (rxbuf[2 + 2 * i] >> 3)
             for i in range(self.buflen // 2)]
     # print("res:", res, "vals:", vals)
     res = [(vals[c * REP + REP - 2] + vals[c * REP + REP - 1]) // 2
            for c in range(len(self.CMDS))]
     return res
Beispiel #2
0
class ili9341:

    # Constants

    COLOR_MODE_RGB = const(0x00)
    COLOR_MODE_BGR = const(0x08)

    MADCTL_MH = const(0x04)
    MADCTL_ML = const(0x10)
    MADCTL_MV = const(0x20)
    MADCTL_MX = const(0x40)
    MADCTL_MY = const(0x80)

    PORTRAIT = MADCTL_MX
    LANDSCAPE = MADCTL_MV

    TRANS_BUFFER_LEN = const(16)

    # Default values of "power" and "backlight" are reversed logic! 0 means ON.
    # You can change this by setting backlight_on and power_on arguments.

    def __init__(self,
                 miso=5,
                 mosi=18,
                 clk=19,
                 cs=13,
                 dc=12,
                 rst=4,
                 power=14,
                 backlight=15,
                 backlight_on=0,
                 power_on=0,
                 spihost=esp.HSPI_HOST,
                 mhz=40,
                 factor=4,
                 hybrid=True,
                 width=240,
                 height=320,
                 colormode=COLOR_MODE_BGR,
                 rot=PORTRAIT,
                 invert=False,
                 double_buffer=True):

        # Make sure Micropython was built such that color won't require processing before DMA

        if lv.color_t.SIZE != 2:
            raise RuntimeError(
                'ili9341 micropython driver requires defining LV_COLOR_DEPTH=16'
            )
        if colormode == COLOR_MODE_BGR and not hasattr(lv.color_t().ch,
                                                       'green_l'):
            raise RuntimeError(
                'ili9341 BGR color mode requires defining LV_COLOR_16_SWAP=1')

        # Initializations

        self.width = width
        self.height = height

        self.miso = miso
        self.mosi = mosi
        self.clk = clk
        self.cs = cs
        self.dc = dc
        self.rst = rst
        self.power = power
        self.backlight = backlight
        self.backlight_on = backlight_on
        self.power_on = power_on
        self.spihost = spihost
        self.mhz = mhz
        self.factor = factor
        self.hybrid = hybrid

        self.buf_size = (self.width * self.height * lv.color_t.SIZE) // factor

        self.init_cmds = [
            {
                'cmd': 0xCF,
                'data': bytes([0x00, 0x83, 0X30])
            },
            {
                'cmd': 0xED,
                'data': bytes([0x64, 0x03, 0X12, 0X81])
            },
            {
                'cmd': 0xE8,
                'data': bytes([0x85, 0x01, 0x79])
            },
            {
                'cmd': 0xCB,
                'data': bytes([0x39, 0x2C, 0x00, 0x34, 0x02])
            },
            {
                'cmd': 0xF7,
                'data': bytes([0x20])
            },
            {
                'cmd': 0xEA,
                'data': bytes([0x00, 0x00])
            },
            {
                'cmd': 0xC0,
                'data': bytes([0x26])
            },  # Power control
            {
                'cmd': 0xC1,
                'data': bytes([0x11])
            },  # Power control
            {
                'cmd': 0xC5,
                'data': bytes([0x35, 0x3E])
            },  # VCOM control
            {
                'cmd': 0xC7,
                'data': bytes([0xBE])
            },  # VCOM control
            {
                'cmd': 0x36,
                'data': bytes([rot | colormode])
            },  # Memory Access Control
            {
                'cmd': 0x3A,
                'data': bytes([0x55])
            },  # Pixel Format Set
            {
                'cmd': 0xB1,
                'data': bytes([0x00, 0x1B])
            },
            {
                'cmd': 0xF2,
                'data': bytes([0x08])
            },
            {
                'cmd': 0x26,
                'data': bytes([0x01])
            },
            {
                'cmd':
                0xE0,
                'data':
                bytes([
                    0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A,
                    0x07, 0x02, 0x07, 0x05, 0x00
                ])
            },
            {
                'cmd':
                0XE1,
                'data':
                bytes([
                    0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05,
                    0x18, 0x0D, 0x38, 0x3A, 0x1F
                ])
            },
            {
                'cmd': 0x2A,
                'data': bytes([0x00, 0x00, 0x00, 0xEF])
            },
            {
                'cmd': 0x2B,
                'data': bytes([0x00, 0x00, 0x01, 0x3f])
            },
            {
                'cmd': 0x2C,
                'data': bytes([0])
            },
            {
                'cmd': 0xB7,
                'data': bytes([0x07])
            },
            {
                'cmd': 0xB6,
                'data': bytes([0x0A, 0x82, 0x27, 0x00])
            },
            {
                'cmd': 0x11,
                'data': bytes([0]),
                'delay': 100
            },
            {
                'cmd': 0x29,
                'data': bytes([0]),
                'delay': 100
            }
        ]

        if invert:
            self.init_cmds.append({'cmd': 0x21})

        self.init()

        # Register display driver

        self.buf1 = esp.heap_caps_malloc(self.buf_size, esp.MALLOC_CAP.DMA)
        self.buf2 = esp.heap_caps_malloc(
            self.buf_size, esp.MALLOC_CAP.DMA) if double_buffer else None

        if self.buf1 and self.buf2:
            print("Double buffer")
        elif self.buf1:
            print("Single buffer")
        else:
            raise RuntimeError(
                "Not enough DMA-able memory to allocate display buffer")

        self.disp_buf = lv.disp_buf_t()
        self.disp_drv = lv.disp_drv_t()

        lv.disp_buf_init(self.disp_buf, self.buf1, self.buf2,
                         self.buf_size // lv.color_t.SIZE)
        lv.disp_drv_init(self.disp_drv)

        self.disp_drv.user_data = {'dc': self.dc, 'spi': self.spi}
        self.disp_drv.buffer = self.disp_buf
        self.disp_drv.flush_cb = esp.ili9341_flush if self.hybrid and hasattr(
            esp, 'ili9341_flush') else self.flush
        self.disp_drv.monitor_cb = self.monitor
        self.disp_drv.hor_res = self.width
        self.disp_drv.ver_res = self.height

        lv.disp_drv_register(self.disp_drv)

    ######################################################

    def disp_spi_init(self):

        # Register finalizer callback to deinit SPI.
        # This would get called on soft reset.

        self.finalizer = lvesp32.cb_finalizer(self.deinit)
        lvesp32.init()

        buscfg = esp.spi_bus_config_t({
            "miso_io_num": self.miso,
            "mosi_io_num": self.mosi,
            "sclk_io_num": self.clk,
            "quadwp_io_num": -1,
            "quadhd_io_num": -1,
            "max_transfer_sz": self.buf_size,
        })

        devcfg = esp.spi_device_interface_config_t({
            "clock_speed_hz":
            self.mhz * 1000 * 1000,  # Clock out at DISP_SPI_MHZ MHz
            "mode":
            0,  # SPI mode 0
            "spics_io_num":
            self.cs,  # CS pin
            "queue_size":
            2,
            #"flags": esp.SPI_DEVICE.HALFDUPLEX,
            "duty_cycle_pos":
            128,
        })

        if self.hybrid and hasattr(esp, 'ili9341_post_cb_isr'):
            devcfg.pre_cb = None
            devcfg.post_cb = esp.ili9341_post_cb_isr
        else:
            devcfg.pre_cb = esp.spi_pre_cb_isr
            devcfg.post_cb = esp.spi_post_cb_isr

        esp.gpio_pad_select_gpio(self.cs)

        # Initialize the SPI bus, if needed.

        if buscfg.miso_io_num >= 0 and \
           buscfg.mosi_io_num >= 0 and \
           buscfg.sclk_io_num >= 0:

            esp.gpio_pad_select_gpio(self.miso)
            esp.gpio_pad_select_gpio(self.mosi)
            esp.gpio_pad_select_gpio(self.clk)

            esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT)
            esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY)
            esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT)
            esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT)

            ret = esp.spi_bus_initialize(self.spihost, buscfg, 1)
            if ret != 0: raise RuntimeError("Failed initializing SPI bus")

        self.trans_buffer = esp.heap_caps_malloc(TRANS_BUFFER_LEN,
                                                 esp.MALLOC_CAP.DMA)
        self.cmd_trans_data = self.trans_buffer.__dereference__(1)
        self.word_trans_data = self.trans_buffer.__dereference__(4)

        # Attach the LCD to the SPI bus

        ptr_to_spi = esp.C_Pointer()
        ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi)
        if ret != 0: raise RuntimeError("Failed adding SPI device")
        self.spi = ptr_to_spi.ptr_val

        self.bytes_transmitted = 0
        completed_spi_transaction = esp.spi_transaction_t()
        cast_spi_transaction_instance = esp.spi_transaction_t.cast_instance

        def post_isr(arg):
            reported_transmitted = self.bytes_transmitted
            if reported_transmitted > 0:
                print('- Completed DMA of %d bytes (mem_free=0x%X)' %
                      (reported_transmitted, gc.mem_free()))
                self.bytes_transmitted -= reported_transmitted

        # Called in ISR context!
        def flush_isr(spi_transaction_ptr):
            lv.disp_flush_ready(self.disp_drv)
            # esp.spi_device_release_bus(self.spi)
            esp.get_ccount(self.end_time_ptr)

            # cast_spi_transaction_instance(completed_spi_transaction, spi_transaction_ptr)
            # self.bytes_transmitted += completed_spi_transaction.length
            # try:
            #     micropython.schedule(post_isr, None)
            # except RuntimeError:
            #     pass

        self.spi_callbacks = esp.spi_transaction_set_cb(None, flush_isr)

    #
    # Deinitialize SPI device and bus, and free memory
    # This function is called from finilizer during gc sweep - therefore must not allocate memory!
    #

    trans_result_ptr = esp.C_Pointer()

    def deinit(self):

        print('Deinitializing ILI9341..')

        # Prevent callbacks to lvgl, which refer to the buffers we are about to delete

        lvesp32.deinit()

        if self.spi:

            # Pop all pending transaction results

            ret = 0
            while ret == 0:
                ret = esp.spi_device_get_trans_result(self.spi,
                                                      self.trans_result_ptr, 1)

            # Remove device

            esp.spi_bus_remove_device(self.spi)
            self.spi = None

            # Free SPI bus

            esp.spi_bus_free(self.spihost)
            self.spihost = None

        # Free RAM

        if self.buf1:
            esp.heap_caps_free(self.buf1)
            self.buf1 = None

        if self.buf2:
            esp.heap_caps_free(self.buf2)
            self.buf2 = None

        if self.trans_buffer:
            esp.heap_caps_free(self.trans_buffer)
            self.trans_buffer = None

    ######################################################

    trans = esp.spi_transaction_t()  # .cast(

    #                esp.heap_caps_malloc(
    #                    esp.spi_transaction_t.SIZE, esp.MALLOC_CAP.DMA))

    def spi_send(self, data):
        self.trans.length = len(
            data) * 8  # Length is in bytes, transaction length is in bits.
        self.trans.tx_buffer = data  # data should be allocated as DMA-able memory
        self.trans.user = None
        esp.spi_device_polling_transmit(self.spi, self.trans)

    def spi_send_dma(self, data):
        self.trans.length = len(
            data) * 8  # Length is in bytes, transaction length is in bits.
        self.trans.tx_buffer = data  # data should be allocated as DMA-able memory
        self.trans.user = self.spi_callbacks
        #esp.spi_device_transmit(self.spi, self.trans)
        esp.spi_device_queue_trans(self.spi, self.trans, -1)

    ######################################################
    ######################################################

    def send_cmd(self, cmd):
        esp.gpio_set_level(self.dc, 0)  # Command mode
        self.cmd_trans_data[0] = cmd
        self.spi_send(self.cmd_trans_data)

    def send_data(self, data):
        esp.gpio_set_level(self.dc, 1)  # Data mode
        if len(data) > TRANS_BUFFER_LEN:
            raise RuntimeError('Data too long, please use DMA!')
        trans_data = self.trans_buffer.__dereference__(len(data))
        trans_data[:] = data[:]
        self.spi_send(trans_data)

    def send_trans_word(self):
        esp.gpio_set_level(self.dc, 1)  # Data mode
        self.spi_send(self.word_trans_data)

    def send_data_dma(self,
                      data):  # data should be allocated as DMA-able memory
        esp.gpio_set_level(self.dc, 1)  # Data mode
        self.spi_send_dma(data)

    ######################################################

    def init(self):
        self.disp_spi_init()

        # Initialize non-SPI GPIOs

        esp.gpio_pad_select_gpio(self.dc)
        esp.gpio_pad_select_gpio(self.rst)
        if self.backlight != -1: esp.gpio_pad_select_gpio(self.backlight)
        if self.power != -1: esp.gpio_pad_select_gpio(self.power)

        esp.gpio_set_direction(self.dc, esp.GPIO_MODE.OUTPUT)
        esp.gpio_set_direction(self.rst, esp.GPIO_MODE.OUTPUT)
        if self.backlight != -1:
            esp.gpio_set_direction(self.backlight, esp.GPIO_MODE.OUTPUT)
        if self.power != -1:
            esp.gpio_set_direction(self.power, esp.GPIO_MODE.OUTPUT)

        # Power the display

        if self.power != -1:
            esp.gpio_set_level(self.power, self.power_on)
            sleep_ms(100)

# Reset the display

        esp.gpio_set_level(self.rst, 0)
        sleep_ms(100)
        esp.gpio_set_level(self.rst, 1)
        sleep_ms(100)

        # Send all the commands

        for cmd in self.init_cmds:
            self.send_cmd(cmd['cmd'])
            if 'data' in cmd:
                self.send_data(cmd['data'])
            if 'delay' in cmd:
                sleep_ms(cmd['delay'])

        print("ILI9341 initialization completed")

        # Enable backlight

        if self.backlight != -1:
            print("Enable backlight")
            esp.gpio_set_level(self.backlight, self.backlight_on)

    ######################################################

    start_time_ptr = esp.C_Pointer()
    end_time_ptr = esp.C_Pointer()
    flush_acc_setup_cycles = 0
    flush_acc_dma_cycles = 0

    def flush(self, disp_drv, area, color_p):

        if self.end_time_ptr.int_val and self.end_time_ptr.int_val > self.start_time_ptr.int_val:
            self.flush_acc_dma_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val

        esp.get_ccount(self.start_time_ptr)

        # esp.spi_device_acquire_bus(self.spi, esp.ESP.MAX_DELAY)

        # Column addresses

        self.send_cmd(0x2A)

        self.word_trans_data[0] = (area.x1 >> 8) & 0xFF
        self.word_trans_data[1] = area.x1 & 0xFF
        self.word_trans_data[2] = (area.x2 >> 8) & 0xFF
        self.word_trans_data[3] = area.x2 & 0xFF
        self.send_trans_word()

        # Page addresses

        self.send_cmd(0x2B)

        self.word_trans_data[0] = (area.y1 >> 8) & 0xFF
        self.word_trans_data[1] = area.y1 & 0xFF
        self.word_trans_data[2] = (area.y2 >> 8) & 0xFF
        self.word_trans_data[3] = area.y2 & 0xFF
        self.send_trans_word()

        # Memory write by DMA, disp_flush_ready when finished

        self.send_cmd(0x2C)

        size = (area.x2 - area.x1 + 1) * (area.y2 - area.y1 + 1)
        data_view = color_p.__dereference__(size * lv.color_t.SIZE)

        esp.get_ccount(self.end_time_ptr)
        if self.end_time_ptr.int_val > self.start_time_ptr.int_val:
            self.flush_acc_setup_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val
        esp.get_ccount(self.start_time_ptr)

        self.send_data_dma(data_view)

    ######################################################

    monitor_acc_time = 0
    monitor_acc_px = 0
    monitor_count = 0

    cycles_in_ms = esp.esp_clk_cpu_freq() // 1000

    def monitor(self, disp_drv, time, px):
        self.monitor_acc_time += time
        self.monitor_acc_px += px
        self.monitor_count += 1

    def stat(self):
        if self.monitor_count == 0:
            return None

        time = self.monitor_acc_time // self.monitor_count
        setup = self.flush_acc_setup_cycles // (self.monitor_count *
                                                self.cycles_in_ms)
        dma = self.flush_acc_dma_cycles // (self.monitor_count *
                                            self.cycles_in_ms)
        px = self.monitor_acc_px // self.monitor_count

        self.monitor_acc_time = 0
        self.monitor_acc_px = 0
        self.monitor_count = 0
        self.flush_acc_setup_cycles = 0
        self.flush_acc_dma_cycles = 0

        return time, setup, dma, px
Beispiel #3
0
    def disp_spi_init(self):

        # Register finalizer callback to deinit SPI.
        # This would get called on soft reset.

        self.finalizer = lvesp32.cb_finalizer(self.deinit)
        lvesp32.init()

        buscfg = esp.spi_bus_config_t({
            "miso_io_num": self.miso,
            "mosi_io_num": self.mosi,
            "sclk_io_num": self.clk,
            "quadwp_io_num": -1,
            "quadhd_io_num": -1,
            "max_transfer_sz": self.buf_size,
        })

        devcfg = esp.spi_device_interface_config_t({
            "clock_speed_hz":
            self.mhz * 1000 * 1000,  # Clock out at DISP_SPI_MHZ MHz
            "mode":
            0,  # SPI mode 0
            "spics_io_num":
            self.cs,  # CS pin
            "queue_size":
            2,
            #"flags": esp.SPI_DEVICE.HALFDUPLEX,
            "duty_cycle_pos":
            128,
        })

        if self.hybrid and hasattr(esp, 'ili9341_post_cb_isr'):
            devcfg.pre_cb = None
            devcfg.post_cb = esp.ili9341_post_cb_isr
        else:
            devcfg.pre_cb = esp.spi_pre_cb_isr
            devcfg.post_cb = esp.spi_post_cb_isr

        esp.gpio_pad_select_gpio(self.cs)

        # Initialize the SPI bus, if needed.

        if buscfg.miso_io_num >= 0 and \
           buscfg.mosi_io_num >= 0 and \
           buscfg.sclk_io_num >= 0:

            esp.gpio_pad_select_gpio(self.miso)
            esp.gpio_pad_select_gpio(self.mosi)
            esp.gpio_pad_select_gpio(self.clk)

            esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT)
            esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY)
            esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT)
            esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT)

            ret = esp.spi_bus_initialize(self.spihost, buscfg, 1)
            if ret != 0: raise RuntimeError("Failed initializing SPI bus")

        self.trans_buffer = esp.heap_caps_malloc(TRANS_BUFFER_LEN,
                                                 esp.MALLOC_CAP.DMA)
        self.cmd_trans_data = self.trans_buffer.__dereference__(1)
        self.word_trans_data = self.trans_buffer.__dereference__(4)

        # Attach the LCD to the SPI bus

        ptr_to_spi = esp.C_Pointer()
        ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi)
        if ret != 0: raise RuntimeError("Failed adding SPI device")
        self.spi = ptr_to_spi.ptr_val

        self.bytes_transmitted = 0
        completed_spi_transaction = esp.spi_transaction_t()
        cast_spi_transaction_instance = esp.spi_transaction_t.cast_instance

        def post_isr(arg):
            reported_transmitted = self.bytes_transmitted
            if reported_transmitted > 0:
                print('- Completed DMA of %d bytes (mem_free=0x%X)' %
                      (reported_transmitted, gc.mem_free()))
                self.bytes_transmitted -= reported_transmitted

        # Called in ISR context!
        def flush_isr(spi_transaction_ptr):
            lv.disp_flush_ready(self.disp_drv)
            # esp.spi_device_release_bus(self.spi)
            esp.get_ccount(self.end_time_ptr)

            # cast_spi_transaction_instance(completed_spi_transaction, spi_transaction_ptr)
            # self.bytes_transmitted += completed_spi_transaction.length
            # try:
            #     micropython.schedule(post_isr, None)
            # except RuntimeError:
            #     pass

        self.spi_callbacks = esp.spi_transaction_set_cb(None, flush_isr)
Beispiel #4
0
    def spi_init(self):
        buscfg = esp.spi_bus_config_t({
            "miso_io_num": self.miso,
            "mosi_io_num": self.mosi,
            "sclk_io_num": self.clk,
            "quadwp_io_num": -1,
            "quadhd_io_num": -1,
            "max_transfer_sz": 4,
        })

        devcfg = esp.spi_device_interface_config_t({
            "command_bits":
            9,  # Actually 8, but need another cycle before xpt starts transmitting response, see Figure 12 on the datasheet.
            "clock_speed_hz":
            self.mhz * 1000 * 1000,
            "mode":
            0,  # SPI mode 0
            "spics_io_num":
            self.cs,  # CS pin
            "queue_size":
            self.max_cmds,
            "flags":
            esp.SPI_DEVICE.HALFDUPLEX,
            "duty_cycle_pos":
            128,
        })

        esp.gpio_pad_select_gpio(self.cs)

        # Initialize the SPI bus, if needed

        if buscfg.miso_io_num >= 0 and \
           buscfg.mosi_io_num >= 0 and \
           buscfg.sclk_io_num >= 0:

            esp.gpio_pad_select_gpio(self.miso)
            esp.gpio_pad_select_gpio(self.mosi)
            esp.gpio_pad_select_gpio(self.clk)

            esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT)
            esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY)
            esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT)
            esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT)

            ret = esp.spi_bus_initialize(self.spihost, buscfg, 1)
            if ret != 0: raise RuntimeError("Failed initializing SPI bus")

    # Register finalizer callback to deinit SPI.
    # This would get called on soft reset.

        self.finalizer = lvesp32.cb_finalizer(self.deinit)

        # Attach the xpt2046 to the SPI bus

        ptr_to_spi = esp.C_Pointer()
        ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi)
        if ret != 0: raise RuntimeError("Failed adding SPI device")
        self.spi = ptr_to_spi.ptr_val

        # Prepare transactions. Each response is 16bit long

        self.trans = [
            esp.spi_transaction_t({
                'rx_buffer': bytearray(2),
                'rxlength': 16
            }) for i in range(0, self.max_cmds)
        ]
Beispiel #5
0
class ILI9XXX:

    display_name = "ili9XXX"
    display_type = 0

    init_cmds = []

    # Default values of "power" and "backlight" are reversed logic! 0 means ON.
    # You can change this by setting backlight_on and power_on arguments.

    def __init__(
        self,
        m5stack,
        miso=5,
        mosi=18,
        clk=19,
        cs=13,
        dc=12,
        backlight_on=0,
        power_on=0,
        spihost=esp.HSPI_HOST,
        mhz=40,
        factor=4,
        hybrid=True,
        width=320,
        height=240,
        colormode=COLOR_MODE_BGR,
        rot=LANDSCAPE,
        invert=False,
        double_buffer=True,
        half_duplex=True,
        display_type=0,
        debug=False,
    ):

        # Initializations
        self.debug = debug

        self.width = width
        self.height = height

        self.m5stack = m5stack
        self.miso = miso
        self.mosi = mosi
        self.clk = clk
        self.cs = cs
        self.dc = dc
        self.backlight_on = backlight_on
        self.power_on = power_on
        self.spihost = spihost
        self.mhz = mhz
        self.factor = factor
        self.hybrid = hybrid
        self.half_duplex = half_duplex

        self.buf_size = (self.width * self.height * lv.color_t.SIZE) // factor

        if invert:
            self.init_cmds.append({"cmd": 0x21})

        self.init()

        # Register display driver

        self.buf1 = esp.heap_caps_malloc(self.buf_size, esp.MALLOC_CAP.DMA)
        self.buf2 = (esp.heap_caps_malloc(self.buf_size, esp.MALLOC_CAP.DMA)
                     if double_buffer else None)

        if self.buf1 and self.buf2:
            if self.debug:
                print("Double buffer")
        elif self.buf1:
            if self.debug:
                print("Single buffer")
        else:
            raise RuntimeError(
                "Not enough DMA-able memory to allocate display buffer")

        self.disp_buf = lv.disp_buf_t()
        self.disp_drv = lv.disp_drv_t()

        self.disp_buf.init(self.buf1, self.buf2,
                           self.buf_size // lv.color_t.SIZE)
        self.disp_drv.init()

        self.disp_drv.user_data = {
            "dc": self.dc,
            "spi": self.spi,
            "dt": self.display_type
        }
        self.disp_drv.buffer = self.disp_buf
        self.disp_drv.flush_cb = (esp.ili9xxx_flush
                                  if hybrid and hasattr(esp, "ili9xxx_flush")
                                  else self.flush)
        self.disp_drv.monitor_cb = self.monitor
        self.disp_drv.hor_res = self.width
        self.disp_drv.ver_res = self.height

        self.disp_drv.register()

    ######################################################

    def disp_spi_init(self):

        # Register finalizer callback to deinit SPI.
        # This would get called on soft reset.

        self.finalizer = lvesp32.cb_finalizer(self.deinit)
        lvesp32.init()

        buscfg = esp.spi_bus_config_t({
            "miso_io_num": self.miso,
            "mosi_io_num": self.mosi,
            "sclk_io_num": self.clk,
            "quadwp_io_num": -1,
            "quadhd_io_num": -1,
            "max_transfer_sz": self.buf_size,
        })

        devcfg_flags = esp.SPI_DEVICE.NO_DUMMY
        if self.half_duplex:
            devcfg_flags |= esp.SPI_DEVICE.HALFDUPLEX

        devcfg = esp.spi_device_interface_config_t({
            "clock_speed_hz":
            self.mhz * 1000 * 1000,  # Clock out at DISP_SPI_MHZ MHz
            "mode":
            0,  # SPI mode 0
            "spics_io_num":
            self.cs,  # CS pin
            "queue_size":
            2,
            "flags":
            devcfg_flags,
            "duty_cycle_pos":
            128,
        })

        if self.hybrid and hasattr(esp, "ili9xxx_post_cb_isr"):
            devcfg.pre_cb = None
            devcfg.post_cb = esp.ili9xxx_post_cb_isr
        else:
            devcfg.pre_cb = esp.spi_pre_cb_isr
            devcfg.post_cb = esp.spi_post_cb_isr

        esp.gpio_pad_select_gpio(self.cs)

        # Initialize the SPI bus, if needed.

        if buscfg.miso_io_num >= 0 and buscfg.mosi_io_num >= 0 and buscfg.sclk_io_num >= 0:

            esp.gpio_pad_select_gpio(self.miso)
            esp.gpio_pad_select_gpio(self.mosi)
            esp.gpio_pad_select_gpio(self.clk)

            esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT)
            esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY)
            esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT)
            esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT)

            ret = esp.spi_bus_initialize(self.spihost, buscfg, 1)
            if ret != 0:
                raise RuntimeError("Failed initializing SPI bus")

        self.trans_buffer = esp.heap_caps_malloc(TRANS_BUFFER_LEN,
                                                 esp.MALLOC_CAP.DMA)
        self.cmd_trans_data = self.trans_buffer.__dereference__(1)
        self.word_trans_data = self.trans_buffer.__dereference__(4)

        # Attach the LCD to the SPI bus

        ptr_to_spi = esp.C_Pointer()
        ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi)
        if ret != 0:
            raise RuntimeError("Failed adding SPI device")
        self.spi = ptr_to_spi.ptr_val

        self.bytes_transmitted = 0
        completed_spi_transaction = esp.spi_transaction_t()
        cast_spi_transaction_instance = esp.spi_transaction_t.cast_instance

        def post_isr(arg):
            reported_transmitted = self.bytes_transmitted
            if reported_transmitted > 0:
                print("- Completed DMA of %d bytes (mem_free=0x%X)" %
                      (reported_transmitted, gc.mem_free()))
                self.bytes_transmitted -= reported_transmitted

        # Called in ISR context!
        def flush_isr(spi_transaction_ptr):
            lv.disp_flush_ready(self.disp_drv)
            # esp.spi_device_release_bus(self.spi)
            esp.get_ccount(self.end_time_ptr)

            # cast_spi_transaction_instance(completed_spi_transaction, spi_transaction_ptr)
            # self.bytes_transmitted += completed_spi_transaction.length
            # try:
            #     micropython.schedule(post_isr, None)
            # except RuntimeError:
            #     pass

        self.spi_callbacks = esp.spi_transaction_set_cb(None, flush_isr)

    #
    # Deinitialize SPI device and bus, and free memory
    # This function is called from finilizer during gc sweep - therefore must not allocate memory!
    #

    trans_result_ptr = esp.C_Pointer()

    def deinit(self):
        if self.debug:
            print("Deinitializing {}..".format(self.display_name))

        # Prevent callbacks to lvgl, which refer to the buffers we are about to delete

        lvesp32.deinit()

        if self.spi:

            # Pop all pending transaction results

            ret = 0
            while ret == 0:
                ret = esp.spi_device_get_trans_result(self.spi,
                                                      self.trans_result_ptr, 1)

            # Remove device

            esp.spi_bus_remove_device(self.spi)
            self.spi = None

            # Free SPI bus

            esp.spi_bus_free(self.spihost)
            self.spihost = None

        # Free RAM

        if self.buf1:
            esp.heap_caps_free(self.buf1)
            self.buf1 = None

        if self.buf2:
            esp.heap_caps_free(self.buf2)
            self.buf2 = None

        if self.trans_buffer:
            esp.heap_caps_free(self.trans_buffer)
            self.trans_buffer = None

    ######################################################

    trans = esp.spi_transaction_t()  # .cast(

    #                esp.heap_caps_malloc(
    #                    esp.spi_transaction_t.SIZE, esp.MALLOC_CAP.DMA))

    def spi_send(self, data):
        # Length is in bytes, transaction length is in bits.
        self.trans.length = len(data) * 8
        self.trans.tx_buffer = data  # data should be allocated as DMA-able memory
        self.trans.user = None
        esp.spi_device_polling_transmit(self.spi, self.trans)

    def spi_send_dma(self, data):
        # Length is in bytes, transaction length is in bits.
        self.trans.length = len(data) * 8
        self.trans.tx_buffer = data  # data should be allocated as DMA-able memory
        self.trans.user = self.spi_callbacks
        esp.spi_device_queue_trans(self.spi, self.trans, -1)

    ######################################################
    ######################################################

    def send_cmd(self, cmd):
        esp.gpio_set_level(self.dc, 0)  # Command mode
        self.cmd_trans_data[0] = cmd
        self.spi_send(self.cmd_trans_data)

    def send_data(self, data):
        esp.gpio_set_level(self.dc, 1)  # Data mode
        if len(data) > TRANS_BUFFER_LEN:
            raise RuntimeError("Data too long, please use DMA!")
        trans_data = self.trans_buffer.__dereference__(len(data))
        trans_data[:] = data[:]
        self.spi_send(trans_data)

    def send_trans_word(self):
        esp.gpio_set_level(self.dc, 1)  # Data mode
        self.spi_send(self.word_trans_data)

    def send_data_dma(self,
                      data):  # data should be allocated as DMA-able memory
        esp.gpio_set_level(self.dc, 1)  # Data mode
        self.spi_send_dma(data)

    ######################################################

    def init(self):
        self.disp_spi_init()

        # Initialize non-SPI GPIOs

        esp.gpio_pad_select_gpio(self.dc)

        esp.gpio_set_direction(self.dc, esp.GPIO_MODE.OUTPUT)

        # Power the display
        # Gets powered on M5Stack initialization

        # Reset the display

        self.m5stack.lcd_rst(0)
        sleep_ms(100)
        self.m5stack.lcd_rst(1)
        sleep_ms(100)

        # Send all the commands

        for cmd in self.init_cmds:
            self.send_cmd(cmd["cmd"])
            if "data" in cmd:
                self.send_data(cmd["data"])
            if "delay" in cmd:
                sleep_ms(cmd["delay"])
        if self.debug:
            print("{} initialization completed".format(self.display_name))

        # Enable backlight

        self.m5stack.lcd_brightness(100)

    ######################################################

    start_time_ptr = esp.C_Pointer()
    end_time_ptr = esp.C_Pointer()
    flush_acc_setup_cycles = 0
    flush_acc_dma_cycles = 0

    def flush(self, disp_drv, area, color_p):

        if self.end_time_ptr.int_val and self.end_time_ptr.int_val > self.start_time_ptr.int_val:
            self.flush_acc_dma_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val

        esp.get_ccount(self.start_time_ptr)

        # esp.spi_device_acquire_bus(self.spi, esp.ESP.MAX_DELAY)

        # Column addresses

        self.send_cmd(0x2A)

        self.word_trans_data[0] = (area.x1 >> 8) & 0xFF
        self.word_trans_data[1] = area.x1 & 0xFF
        self.word_trans_data[2] = (area.x2 >> 8) & 0xFF
        self.word_trans_data[3] = area.x2 & 0xFF
        self.send_trans_word()

        # Page addresses

        self.send_cmd(0x2B)

        self.word_trans_data[0] = (area.y1 >> 8) & 0xFF
        self.word_trans_data[1] = area.y1 & 0xFF
        self.word_trans_data[2] = (area.y2 >> 8) & 0xFF
        self.word_trans_data[3] = area.y2 & 0xFF
        self.send_trans_word()

        # Memory write by DMA, disp_flush_ready when finished

        self.send_cmd(0x2C)

        size = (area.x2 - area.x1 + 1) * (area.y2 - area.y1 + 1)
        data_view = color_p.__dereference__(size * lv.color_t.SIZE)

        esp.get_ccount(self.end_time_ptr)
        if self.end_time_ptr.int_val > self.start_time_ptr.int_val:
            self.flush_acc_setup_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val
        esp.get_ccount(self.start_time_ptr)

        self.send_data_dma(data_view)

    ######################################################

    monitor_acc_time = 0
    monitor_acc_px = 0
    monitor_count = 0

    cycles_in_ms = esp.esp_clk_cpu_freq() // 1000

    def monitor(self, disp_drv, time, px):
        self.monitor_acc_time += time
        self.monitor_acc_px += px
        self.monitor_count += 1

    def stat(self):
        if self.monitor_count == 0:
            return None

        time = self.monitor_acc_time // self.monitor_count
        setup = self.flush_acc_setup_cycles // (self.monitor_count *
                                                self.cycles_in_ms)
        dma = self.flush_acc_dma_cycles // (self.monitor_count *
                                            self.cycles_in_ms)
        px = self.monitor_acc_px // self.monitor_count

        self.monitor_acc_time = 0
        self.monitor_acc_px = 0
        self.monitor_count = 0
        self.flush_acc_setup_cycles = 0
        self.flush_acc_dma_cycles = 0

        return time, setup, dma, px
    def disp_spi_init(self):
        buscfg = esp.spi_bus_config_t({
            "miso_io_num": self.miso,
            "mosi_io_num": self.mosi,
            "sclk_io_num": self.clk,
            "quadwp_io_num": -1,
            "quadhd_io_num": -1,
            "max_transfer_sz": 128 * 1024,
        })

        devcfg = esp.spi_device_interface_config_t({
            "clock_speed_hz":
            self.mhz * 1000 * 1000,  # Clock out at DISP_SPI_MHZ MHz
            "mode":
            0,  # SPI mode 0
            "spics_io_num":
            self.cs,  # CS pin
            "queue_size":
            2,
            "pre_cb":
            esp.spi_pre_cb_isr,
            "post_cb":
            esp.spi_post_cb_isr,
            "flags":
            1 << 4,  # SPI_DEVICE_HALFDUPLEX
            "duty_cycle_pos":
            128,
        })

        esp.gpio_pad_select_gpio(self.miso)
        esp.gpio_pad_select_gpio(self.mosi)
        esp.gpio_pad_select_gpio(self.clk)

        esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT)
        esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY)
        esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT)
        esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT)

        esp.gpio_pad_select_gpio(self.cs)

        # Initialize the SPI bus

        ret = esp.spi_bus_initialize(self.spihost, buscfg, 1)
        if ret != 0: raise RuntimeError("Failed initializing SPI bus")

        # Attach the LCD to the SPI bus

        ptr_to_spi = esp.C_Pointer()
        ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi)
        if ret != 0: raise RuntimeError("Failed adding SPI device")
        self.spi = ptr_to_spi.ptr_val

        self.bytes_transmitted = 0
        completed_spi_transaction = esp.spi_transaction_t()
        cast_spi_transaction_instance = esp.spi_transaction_t.cast_instance

        def post_isr(arg):
            reported_transmitted = self.bytes_transmitted
            if reported_transmitted > 0:
                print('- Completed DMA of %d bytes (mem_free=0x%X)' %
                      (reported_transmitted, gc.mem_free()))
                self.bytes_transmitted -= reported_transmitted

    # Called in ISR context!

        def flush_isr(spi_transaction_ptr):
            lv.disp_flush_ready(self.disp_drv)
            # cast_spi_transaction_instance(completed_spi_transaction, spi_transaction_ptr)
            # self.bytes_transmitted += completed_spi_transaction.length
            # try:
            #     micropython.schedule(post_isr, None)
            # except RuntimeError:
            #     pass

        self.spi_callbacks = esp.spi_transaction_set_cb(None, flush_isr)
class ili9341:

    width = const(240)
    height = const(320)

    ######################################################

    disp_buf = lv.disp_buf_t()
    disp_drv = lv.disp_drv_t()

    def __init__(self,
                 miso=5,
                 mosi=18,
                 clk=19,
                 cs=13,
                 dc=12,
                 rst=4,
                 backlight=2,
                 spihost=esp.enum.HSPI_HOST,
                 mhz=40,
                 factor=5):

        # Make sure Micropython was built such that color won't require processing before DMA

        if lv.color_t.SIZE != 2:
            raise RuntimeError(
                'ili9341 micropython driver requires defining LV_COLOR_DEPTH=16'
            )
        if not hasattr(lv.color_t().ch, 'green_l'):
            raise RuntimeError(
                'ili9341 micropython driver requires defining LV_COLOR_16_SWAP=1'
            )

        # Initializations

        self.miso = miso
        self.mosi = mosi
        self.clk = clk
        self.cs = cs
        self.dc = dc
        self.rst = rst
        self.backlight = backlight
        self.spihost = spihost
        self.mhz = mhz

        self.init()

        # Register display driver

        buf_size = (self.width * self.height * lv.color_t.SIZE) // factor

        lv.disp_buf_init(self.disp_buf, bytearray(buf_size),
                         bytearray(buf_size), buf_size // lv.color_t.SIZE)
        lv.disp_drv_init(self.disp_drv)

        self.disp_drv.buffer = self.disp_buf
        self.disp_drv.flush_cb = self.flush
        self.disp_drv.monitor_cb = self.monitor
        self.disp_drv.hor_res = self.width
        self.disp_drv.ver_res = self.height

        lv.disp_drv_register(self.disp_drv)

    ######################################################

    init_cmds = [
        {
            'cmd': 0xCF,
            'data': bytes([0x00, 0x83, 0X30])
        },
        {
            'cmd': 0xED,
            'data': bytes([0x64, 0x03, 0X12, 0X81])
        },
        {
            'cmd': 0xE8,
            'data': bytes([0x85, 0x01, 0x79])
        },
        {
            'cmd': 0xCB,
            'data': bytes([0x39, 0x2C, 0x00, 0x34, 0x02])
        },
        {
            'cmd': 0xF7,
            'data': bytes([0x20])
        },
        {
            'cmd': 0xEA,
            'data': bytes([0x00, 0x00])
        },
        {
            'cmd': 0xC0,
            'data': bytes([0x26])
        },  # Power control
        {
            'cmd': 0xC1,
            'data': bytes([0x11])
        },  # Power control
        {
            'cmd': 0xC5,
            'data': bytes([0x35, 0x3E])
        },  # VCOM control
        {
            'cmd': 0xC7,
            'data': bytes([0xBE])
        },  # VCOM control
        {
            'cmd': 0x36,
            'data': bytes([0x48])
        },  # Memory Access Control
        {
            'cmd': 0x3A,
            'data': bytes([0x55])
        },  # Pixel Format Set
        {
            'cmd': 0xB1,
            'data': bytes([0x00, 0x1B])
        },
        {
            'cmd': 0xF2,
            'data': bytes([0x08])
        },
        {
            'cmd': 0x26,
            'data': bytes([0x01])
        },
        {
            'cmd':
            0xE0,
            'data':
            bytes([
                0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A,
                0x07, 0x02, 0x07, 0x05, 0x00
            ])
        },
        {
            'cmd':
            0XE1,
            'data':
            bytes([
                0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05,
                0x18, 0x0D, 0x38, 0x3A, 0x1F
            ])
        },
        {
            'cmd': 0x2A,
            'data': bytes([0x00, 0x00, 0x00, 0xEF])
        },
        {
            'cmd': 0x2B,
            'data': bytes([0x00, 0x00, 0x01, 0x3f])
        },
        {
            'cmd': 0x2C,
            'data': bytes([0])
        },
        {
            'cmd': 0xB7,
            'data': bytes([0x07])
        },
        {
            'cmd': 0xB6,
            'data': bytes([0x0A, 0x82, 0x27, 0x00])
        },
        {
            'cmd': 0x11,
            'data': bytes([0]),
            'delay': 100
        },
        {
            'cmd': 0x29,
            'data': bytes([0]),
            'delay': 100
        }
    ]

    ######################################################

    def disp_spi_init(self):
        buscfg = esp.spi_bus_config_t({
            "miso_io_num": self.miso,
            "mosi_io_num": self.mosi,
            "sclk_io_num": self.clk,
            "quadwp_io_num": -1,
            "quadhd_io_num": -1,
            "max_transfer_sz": 128 * 1024,
        })

        devcfg = esp.spi_device_interface_config_t({
            "clock_speed_hz":
            self.mhz * 1000 * 1000,  # Clock out at DISP_SPI_MHZ MHz
            "mode":
            0,  # SPI mode 0
            "spics_io_num":
            self.cs,  # CS pin
            "queue_size":
            2,
            "pre_cb":
            esp.spi_pre_cb_isr,
            "post_cb":
            esp.spi_post_cb_isr,
            "flags":
            1 << 4,  # SPI_DEVICE_HALFDUPLEX
            "duty_cycle_pos":
            128,
        })

        esp.gpio_pad_select_gpio(self.miso)
        esp.gpio_pad_select_gpio(self.mosi)
        esp.gpio_pad_select_gpio(self.clk)

        esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT)
        esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY)
        esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT)
        esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT)

        esp.gpio_pad_select_gpio(self.cs)

        # Initialize the SPI bus

        ret = esp.spi_bus_initialize(self.spihost, buscfg, 1)
        if ret != 0: raise RuntimeError("Failed initializing SPI bus")

        # Attach the LCD to the SPI bus

        ptr_to_spi = esp.C_Pointer()
        ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi)
        if ret != 0: raise RuntimeError("Failed adding SPI device")
        self.spi = ptr_to_spi.ptr_val

        self.bytes_transmitted = 0
        completed_spi_transaction = esp.spi_transaction_t()
        cast_spi_transaction_instance = esp.spi_transaction_t.cast_instance

        def post_isr(arg):
            reported_transmitted = self.bytes_transmitted
            if reported_transmitted > 0:
                print('- Completed DMA of %d bytes (mem_free=0x%X)' %
                      (reported_transmitted, gc.mem_free()))
                self.bytes_transmitted -= reported_transmitted

    # Called in ISR context!

        def flush_isr(spi_transaction_ptr):
            lv.disp_flush_ready(self.disp_drv)
            # cast_spi_transaction_instance(completed_spi_transaction, spi_transaction_ptr)
            # self.bytes_transmitted += completed_spi_transaction.length
            # try:
            #     micropython.schedule(post_isr, None)
            # except RuntimeError:
            #     pass

        self.spi_callbacks = esp.spi_transaction_set_cb(None, flush_isr)

    ######################################################

    trans = esp.spi_transaction_t()
    trans_res = esp.spi_transaction_t()

    def disp_spi_send(self, data, dma=False):
        if len(data) == 0: return  # no need to send anything

        self.trans.length = len(
            data) * 8  # Length is in bytes, transaction length is in bits.
        self.trans.tx_buffer = data

        if dma:
            self.trans.user = self.spi_callbacks
            esp.spi_device_queue_trans(self.spi, self.trans, -1)
        else:
            self.trans.user = None
            esp.spi_device_polling_transmit(self.spi, self.trans)

    ######################################################

    cmd_data = bytearray(1)

    def send_cmd(self, cmd):
        self.cmd_data[0] = cmd
        esp.gpio_set_level(self.dc, 0)  # Command mode
        self.disp_spi_send(self.cmd_data)

    def send_data(self, data, dma=False):
        esp.gpio_set_level(self.dc, 1)  # Data mode
        self.disp_spi_send(data, dma=dma)

    ######################################################

    def init(self):
        self.disp_spi_init()
        esp.gpio_pad_select_gpio(self.dc)

        # Initialize non-SPI GPIOs

        esp.gpio_set_direction(self.dc, esp.GPIO_MODE.OUTPUT)
        esp.gpio_set_direction(self.rst, esp.GPIO_MODE.OUTPUT)
        if self.backlight != -1:
            esp.gpio_set_direction(self.backlight, esp.GPIO_MODE.OUTPUT)

        # Reset the display

        esp.gpio_set_level(self.rst, 0)
        esp.task_delay_ms(100)
        esp.gpio_set_level(self.rst, 1)
        esp.task_delay_ms(100)

        # Send all the commands

        for cmd in self.init_cmds:
            self.send_cmd(cmd['cmd'])
            self.send_data(cmd['data'])
            if 'delay' in cmd:
                esp.task_delay_ms(cmd['delay'])

        print("ILI9341 initialization completed")

        # Enable backlight

        if self.backlight != -1:
            print("Enable backlight")
            esp.gpio_set_level(self.backlight, 1)

    ######################################################

    flush_data = bytearray(4)

    def flush(self, disp_drv, area, color_p):

        # Column addresses

        self.send_cmd(0x2A)
        self.flush_data[0] = (area.x1 >> 8) & 0xFF
        self.flush_data[1] = area.x1 & 0xFF
        self.flush_data[2] = (area.x2 >> 8) & 0xFF
        self.flush_data[3] = area.x2 & 0xFF
        self.send_data(self.flush_data)

        # Page addresses

        self.send_cmd(0x2B)
        self.flush_data[0] = (area.y1 >> 8) & 0xFF
        self.flush_data[1] = area.y1 & 0xFF
        self.flush_data[2] = (area.y2 >> 8) & 0xFF
        self.flush_data[3] = area.y2 & 0xFF
        self.send_data(self.flush_data)

        # Memory write by DMA, disp_flush_ready when finished

        self.send_cmd(0x2C)

        size = (area.x2 - area.x1 + 1) * (area.y2 - area.y1 + 1)
        data_view = color_p.__dereference__(size * lv.color_t.SIZE)

        self.send_data(data_view, dma=True)

    ######################################################

    monitor_acc_time = 0
    monitor_acc_px = 0
    monitor_count = 0

    def monitor(self, disp_drv, time, px):
        self.monitor_acc_time += time
        self.monitor_acc_px += px
        self.monitor_count += 1

    def stat(self):
        if self.monitor_count == 0:
            return None

        time = self.monitor_acc_time // self.monitor_count
        px = self.monitor_acc_px // self.monitor_count
        self.monitor_acc_time = 0
        self.monitor_acc_px = 0
        self.monitor_count = 0

        return time, px
Beispiel #8
0
class ili9XXX:

    TRANS_BUFFER_LEN = const(16)
    display_name = 'ili9XXX'
    init_cmds = []

    # Default values of "power" and "backlight" are reversed logic! 0 means ON.
    # You can change this by setting backlight_on and power_on arguments.

    def __init__(self,
                 m5stack=M5Stack(),
                 miso=5,
                 mosi=18,
                 clk=19,
                 cs=13,
                 dc=12,
                 backlight=100,
                 backlight_on=1,
                 power_on=1,
                 spihost=esp.HSPI_HOST,
                 mhz=40,
                 factor=4,
                 hybrid=True,
                 width=240,
                 height=320,
                 start_x=0,
                 start_y=0,
                 invert=False,
                 double_buffer=True,
                 half_duplex=True,
                 display_type=0,
                 asynchronous=False,
                 initialize=True):

        # Initializations

        if not lv.is_initialized():
            lv.init()

        self.asynchronous = asynchronous
        self.initialize = initialize

        self.m5stack = m5stack

        self.width = width
        self.height = height
        self.start_x = start_x
        self.start_y = start_y

        self.miso = miso
        self.mosi = mosi
        self.clk = clk
        self.cs = cs
        self.dc = dc
        self.backlight = backlight
        self.backlight_on = backlight_on
        self.power_on = power_on
        self.spihost = spihost
        self.mhz = mhz
        self.factor = factor
        self.hybrid = hybrid
        self.half_duplex = half_duplex
        self.display_type = display_type

        self.buf_size = (self.width * self.height *
                         lv.color_t.__SIZE__) // factor

        if invert:
            self.init_cmds.append({'cmd': 0x21})

        # Register display driver

        self.buf1 = esp.heap_caps_malloc(self.buf_size, esp.MALLOC_CAP.DMA)
        self.buf2 = esp.heap_caps_malloc(
            self.buf_size, esp.MALLOC_CAP.DMA) if double_buffer else None

        if self.buf1 and self.buf2:
            print("Double buffer")
        elif self.buf1:
            print("Single buffer")
        else:
            raise RuntimeError(
                "Not enough DMA-able memory to allocate display buffer")

        self.disp_buf = lv.disp_draw_buf_t()
        self.disp_drv = lv.disp_drv_t()

        self.disp_buf.init(self.buf1, self.buf2,
                           self.buf_size // lv.color_t.__SIZE__)
        self.disp_drv.init()
        self.disp_spi_init()

        self.disp_drv.user_data = {
            'dc': self.dc,
            'spi': self.spi,
            'dt': self.display_type,
            'start_x': self.start_x,
            'start_y': self.start_y
        }

        self.disp_drv.draw_buf = self.disp_buf
        self.disp_drv.flush_cb = esp.ili9xxx_flush if hybrid and hasattr(
            esp, 'ili9xxx_flush') else self.flush
        self.disp_drv.monitor_cb = self.monitor
        self.disp_drv.hor_res = self.width
        self.disp_drv.ver_res = self.height

        if self.initialize:
            self.init()

        if not lv_utils.event_loop.is_running():
            self.event_loop = lv_utils.event_loop(
                asynchronous=self.asynchronous)

    ######################################################

    def disp_spi_init(self):

        # TODO: Register finalizer callback to deinit SPI.
        # That would get called on soft reset.

        buscfg = esp.spi_bus_config_t({
            "miso_io_num": self.miso,
            "mosi_io_num": self.mosi,
            "sclk_io_num": self.clk,
            "quadwp_io_num": -1,
            "quadhd_io_num": -1,
            "max_transfer_sz": self.buf_size,
        })

        devcfg_flags = esp.SPI_DEVICE.NO_DUMMY
        if self.half_duplex:
            devcfg_flags |= esp.SPI_DEVICE.HALFDUPLEX

        devcfg = esp.spi_device_interface_config_t({
            "clock_speed_hz":
            self.mhz * 1000 * 1000,  # Clock out at DISP_SPI_MHZ MHz
            "mode":
            0,  # SPI mode 0
            "spics_io_num":
            self.cs,  # CS pin
            "queue_size":
            2,
            "flags":
            devcfg_flags,
            "duty_cycle_pos":
            128,
        })

        if self.hybrid and hasattr(esp, 'ili9xxx_post_cb_isr'):
            devcfg.pre_cb = None
            devcfg.post_cb = esp.ili9xxx_post_cb_isr
        else:
            devcfg.pre_cb = esp.ex_spi_pre_cb_isr
            devcfg.post_cb = esp.ex_spi_post_cb_isr

        esp.gpio_pad_select_gpio(self.cs)

        # Initialize the SPI bus, if needed.

        if buscfg.mosi_io_num >= 0 and \
           buscfg.sclk_io_num >= 0:

            if buscfg.miso_io_num >= 0:
                esp.gpio_pad_select_gpio(self.miso)
                esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT)
                esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY)

            esp.gpio_pad_select_gpio(self.mosi)
            esp.gpio_pad_select_gpio(self.clk)
            esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT)
            esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT)

            ret = esp.spi_bus_initialize(self.spihost, buscfg, 1)
            if ret != 0: raise RuntimeError("Failed initializing SPI bus")

        self.trans_buffer = esp.heap_caps_malloc(TRANS_BUFFER_LEN,
                                                 esp.MALLOC_CAP.DMA)
        self.cmd_trans_data = self.trans_buffer.__dereference__(1)
        self.word_trans_data = self.trans_buffer.__dereference__(4)

        # Attach the LCD to the SPI bus

        ptr_to_spi = esp.C_Pointer()
        ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi)
        if ret != 0: raise RuntimeError("Failed adding SPI device")
        self.spi = ptr_to_spi.ptr_val

        self.bytes_transmitted = 0
        completed_spi_transaction = esp.spi_transaction_t()
        cast_spi_transaction_instance = esp.spi_transaction_t.__cast_instance__

        def post_isr(arg):
            reported_transmitted = self.bytes_transmitted
            if reported_transmitted > 0:
                print('- Completed DMA of %d bytes (mem_free=0x%X)' %
                      (reported_transmitted, gc.mem_free()))
                self.bytes_transmitted -= reported_transmitted

        # Called in ISR context!
        def flush_isr(spi_transaction_ptr):
            self.disp_drv.flush_ready()
            # esp.spi_device_release_bus(self.spi)
            esp.get_ccount(self.end_time_ptr)

            # cast_spi_transaction_instance(completed_spi_transaction, spi_transaction_ptr)
            # self.bytes_transmitted += completed_spi_transaction.length
            # try:
            #     micropython.schedule(post_isr, None)
            # except RuntimeError:
            #     pass

        self.spi_callbacks = esp.spi_transaction_set_cb(None, flush_isr)

    #
    # Deinitialize SPI device and bus, and free memory
    # This function is called from finilizer during gc sweep - therefore must not allocate memory!
    #

    trans_result_ptr = esp.C_Pointer()

    def deinit(self):

        print('Deinitializing {}..'.format(self.display_name))

        # Prevent callbacks to lvgl, which refer to the buffers we are about to delete

        if lv_utils.event_loop.is_running():
            self.event_loop.deinit()

        self.disp_drv.remove()

        if self.spi:

            # Pop all pending transaction results

            ret = 0
            while ret == 0:
                ret = esp.spi_device_get_trans_result(self.spi,
                                                      self.trans_result_ptr, 1)

            # Remove device

            esp.spi_bus_remove_device(self.spi)
            self.spi = None

            # Free SPI bus

            esp.spi_bus_free(self.spihost)
            self.spihost = None

        # Free RAM

        if self.buf1:
            esp.heap_caps_free(self.buf1)
            self.buf1 = None

        if self.buf2:
            esp.heap_caps_free(self.buf2)
            self.buf2 = None

        if self.trans_buffer:
            esp.heap_caps_free(self.trans_buffer)
            self.trans_buffer = None

    ######################################################

    trans = esp.spi_transaction_t()  # .__cast__(

    #                esp.heap_caps_malloc(
    #                    esp.spi_transaction_t.__SIZE__, esp.MALLOC_CAP.DMA))

    def spi_send(self, data):
        self.trans.length = len(
            data) * 8  # Length is in bytes, transaction length is in bits.
        self.trans.tx_buffer = data  # data should be allocated as DMA-able memory
        self.trans.user = None
        esp.spi_device_polling_transmit(self.spi, self.trans)

    def spi_send_dma(self, data):
        self.trans.length = len(
            data) * 8  # Length is in bytes, transaction length is in bits.
        self.trans.tx_buffer = data  # data should be allocated as DMA-able memory
        self.trans.user = self.spi_callbacks
        esp.spi_device_queue_trans(self.spi, self.trans, -1)

    ######################################################
    ######################################################

    def send_cmd(self, cmd):
        esp.gpio_set_level(self.dc, 0)  # Command mode
        self.cmd_trans_data[0] = cmd
        self.spi_send(self.cmd_trans_data)

    def send_data(self, data):
        esp.gpio_set_level(self.dc, 1)  # Data mode
        if len(data) > TRANS_BUFFER_LEN:
            raise RuntimeError('Data too long, please use DMA!')
        trans_data = self.trans_buffer.__dereference__(len(data))
        trans_data[:] = data[:]
        self.spi_send(trans_data)

    def send_trans_word(self):
        esp.gpio_set_level(self.dc, 1)  # Data mode
        self.spi_send(self.word_trans_data)

    def send_data_dma(self,
                      data):  # data should be allocated as DMA-able memory
        esp.gpio_set_level(self.dc, 1)  # Data mode
        self.spi_send_dma(data)

    ######################################################

    async def _init(self, sleep_func):

        # Initialize non-SPI GPIOs

        esp.gpio_pad_select_gpio(self.dc)
        esp.gpio_set_direction(self.dc, esp.GPIO_MODE.OUTPUT)

        # Power the display

        # Reset the display

        self.m5stack.lcd_rst(0)
        await sleep_func(100)
        self.m5stack.lcd_rst(1)
        await sleep_func(100)

        # Send all the commands

        for cmd in self.init_cmds:
            self.send_cmd(cmd['cmd'])
            if 'data' in cmd:
                self.send_data(cmd['data'])
            if 'delay' in cmd:
                await sleep_func(cmd['delay'])

        print("{} initialization completed".format(self.display_name))

        # Enable backlight

        if self.backlight != -1:
            print("Enable backlight")
            self.m5stack.lcd_backlight(self.backlight_on)
            self.m5stack.lcd_brightness(self.backlight)

        # Register the driver
        self.disp_drv.register()

    def init(self):
        import utime
        generator = self._init(lambda ms: (yield ms))
        try:
            while True:
                ms = next(generator)
                utime.sleep_ms(ms)
        except StopIteration:
            pass

    async def init_async(self):
        import uasyncio
        await self._init(uasyncio.sleep_ms)


#    def power_down(self):

######################################################

    start_time_ptr = esp.C_Pointer()
    end_time_ptr = esp.C_Pointer()
    flush_acc_setup_cycles = 0
    flush_acc_dma_cycles = 0

    def flush(self, disp_drv, area, color_p):

        if self.end_time_ptr.int_val and self.end_time_ptr.int_val > self.start_time_ptr.int_val:
            self.flush_acc_dma_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val

        esp.get_ccount(self.start_time_ptr)

        # esp.spi_device_acquire_bus(self.spi, esp.ESP.MAX_DELAY)

        # Column addresses
        self.send_cmd(0x2A)

        x1 = area.x1 + self.start_x
        x2 = area.x2 + self.start_x

        self.word_trans_data[0] = (x1 >> 8) & 0xFF
        self.word_trans_data[1] = x1 & 0xFF
        self.word_trans_data[2] = (x2 >> 8) & 0xFF
        self.word_trans_data[3] = x2 & 0xFF
        self.send_trans_word()

        # Page addresses

        self.send_cmd(0x2B)

        y1 = area.y1 + self.start_y
        y2 = area.y2 + self.start_y

        self.word_trans_data[0] = (y1 >> 8) & 0xFF
        self.word_trans_data[1] = y1 & 0xFF
        self.word_trans_data[2] = (y2 >> 8) & 0xFF
        self.word_trans_data[3] = y2 & 0xFF
        self.send_trans_word()

        # Memory write by DMA, disp_flush_ready when finished

        self.send_cmd(0x2C)

        size = (x2 - x1 + 1) * (y2 - y1 + 1)
        data_view = color_p.__dereference__(size * lv.color_t.__SIZE__)

        esp.get_ccount(self.end_time_ptr)
        if self.end_time_ptr.int_val > self.start_time_ptr.int_val:
            self.flush_acc_setup_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val
        esp.get_ccount(self.start_time_ptr)

        self.send_data_dma(data_view)

    ######################################################

    monitor_acc_time = 0
    monitor_acc_px = 0
    monitor_count = 0

    cycles_in_ms = esp.esp_clk_cpu_freq() // 1000

    def monitor(self, disp_drv, time, px):
        self.monitor_acc_time += time
        self.monitor_acc_px += px
        self.monitor_count += 1

    def stat(self):
        if self.monitor_count == 0:
            return None

        time = self.monitor_acc_time // self.monitor_count
        setup = self.flush_acc_setup_cycles // (self.monitor_count *
                                                self.cycles_in_ms)
        dma = self.flush_acc_dma_cycles // (self.monitor_count *
                                            self.cycles_in_ms)
        px = self.monitor_acc_px // self.monitor_count

        self.monitor_acc_time = 0
        self.monitor_acc_px = 0
        self.monitor_count = 0
        self.flush_acc_setup_cycles = 0
        self.flush_acc_dma_cycles = 0

        return time, setup, dma, px

    def madctl(self, colormode, rotation, rotations):

        # if rotation is 0 or positive use the value as is.

        if rotation >= 0:
            return rotation | colormode

        # otherwise use abs(rotation)-1 as index to retreive value from rotations set

        index = abs(rotation) - 1
        if index > len(rotations):
            RuntimeError('Invalid display rot value specified during init.')

        return rotations[index] | colormode
class ili9341:

    width = const(240)
    height = const(320)

    ######################################################

    disp_buf1 = lv.disp_buf_t()
    disp_drv = lv.disp_drv_t()

    def __init__(self,
                 miso=5,
                 mosi=18,
                 clk=19,
                 cs=13,
                 dc=12,
                 rst=4,
                 backlight=2,
                 spihost=esp.enum.HSPI_HOST,
                 mhz=40,
                 factor=10):

        # Make sure Micropython was built such that color won't require processing before DMA

        if lv.color_t.SIZE != 2:
            raise RuntimeError(
                'ili9341 micropython driver requires defining LV_COLOR_DEPTH=16'
            )
        if not hasattr(lv.color_t().ch, 'green_l'):
            raise RuntimeError(
                'ili9341 micropython driver requires defining LV_COLOR_16_SWAP=1'
            )

        # Initializations

        self.buf1_1 = bytearray(
            (self.width * self.height * lv.color_t.SIZE) // factor)

        self.miso = miso
        self.mosi = mosi
        self.clk = clk
        self.cs = cs
        self.dc = dc
        self.rst = rst
        self.backlight = backlight
        self.spihost = spihost
        self.mhz = mhz

        self.init()

        # Register display driver

        lv.disp_buf_init(self.disp_buf1, self.buf1_1, None,
                         len(self.buf1_1) // lv.color_t.SIZE)
        lv.disp_drv_init(self.disp_drv)
        self.disp_drv.buffer = self.disp_buf1
        self.disp_drv.flush_cb = self.flush
        self.disp_drv.hor_res = self.width
        self.disp_drv.ver_res = self.height
        lv.disp_drv_register(self.disp_drv)

    ######################################################

    init_cmds = [
        {
            'cmd': 0xCF,
            'data': bytearray([0x00, 0x83, 0X30])
        },
        {
            'cmd': 0xED,
            'data': bytearray([0x64, 0x03, 0X12, 0X81])
        },
        {
            'cmd': 0xE8,
            'data': bytearray([0x85, 0x01, 0x79])
        },
        {
            'cmd': 0xCB,
            'data': bytearray([0x39, 0x2C, 0x00, 0x34, 0x02])
        },
        {
            'cmd': 0xF7,
            'data': bytearray([0x20])
        },
        {
            'cmd': 0xEA,
            'data': bytearray([0x00, 0x00])
        },
        {
            'cmd': 0xC0,
            'data': bytearray([0x26])
        },  # Power control
        {
            'cmd': 0xC1,
            'data': bytearray([0x11])
        },  # Power control
        {
            'cmd': 0xC5,
            'data': bytearray([0x35, 0x3E])
        },  # VCOM control
        {
            'cmd': 0xC7,
            'data': bytearray([0xBE])
        },  # VCOM control
        {
            'cmd': 0x36,
            'data': bytearray([0x48])
        },  # Memory Access Control
        {
            'cmd': 0x3A,
            'data': bytearray([0x55])
        },  # Pixel Format Set
        {
            'cmd': 0xB1,
            'data': bytearray([0x00, 0x1B])
        },
        {
            'cmd': 0xF2,
            'data': bytearray([0x08])
        },
        {
            'cmd': 0x26,
            'data': bytearray([0x01])
        },
        {
            'cmd':
            0xE0,
            'data':
            bytearray([
                0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A,
                0x07, 0x02, 0x07, 0x05, 0x00
            ])
        },
        {
            'cmd':
            0XE1,
            'data':
            bytearray([
                0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05,
                0x18, 0x0D, 0x38, 0x3A, 0x1F
            ])
        },
        {
            'cmd': 0x2A,
            'data': bytearray([0x00, 0x00, 0x00, 0xEF])
        },
        {
            'cmd': 0x2B,
            'data': bytearray([0x00, 0x00, 0x01, 0x3f])
        },
        {
            'cmd': 0x2C,
            'data': bytearray([0])
        },
        {
            'cmd': 0xB7,
            'data': bytearray([0x07])
        },
        {
            'cmd': 0xB6,
            'data': bytearray([0x0A, 0x82, 0x27, 0x00])
        },
        {
            'cmd': 0x11,
            'data': bytearray([0]),
            'delay': 100
        },
        {
            'cmd': 0x29,
            'data': bytearray([0]),
            'delay': 100
        }
    ]

    ######################################################

    def disp_spi_init(self):
        buscfg = esp.spi_bus_config_t({
            "miso_io_num": self.miso,
            "mosi_io_num": self.mosi,
            "sclk_io_num": self.clk,
            "quadwp_io_num": -1,
            "quadhd_io_num": -1,
            "max_transfer_sz": 128 * 1024,
        })

        devcfg = esp.spi_device_interface_config_t({
            "clock_speed_hz":
            self.mhz * 1000 * 1000,  # Clock out at DISP_SPI_MHZ MHz
            "mode":
            0,  # SPI mode 0
            "spics_io_num":
            self.cs,  # CS pin
            "queue_size":
            1,
            "pre_cb":
            None,
            "post_cb":
            None,
            "flags":
            1 << 4,  # SPI_DEVICE_HALFDUPLEX
            "duty_cycle_pos":
            128,
        })

        esp.gpio_pad_select_gpio(self.miso)
        esp.gpio_pad_select_gpio(self.mosi)
        esp.gpio_pad_select_gpio(self.clk)

        esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT)
        esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY)
        esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT)
        esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT)

        esp.gpio_pad_select_gpio(self.cs)

        # Initialize the SPI bus

        ret = esp.spi_bus_initialize(self.spihost, buscfg, 1)
        if ret != 0: raise RuntimeError("Failed initializing SPI bus")

        # Attach the LCD to the SPI bus

        ptr_to_spi = esp.C_Pointer()
        ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi)
        if ret != 0: raise RuntimeError("Failed adding SPI device")
        self.spi = ptr_to_spi.ptr_val

    ######################################################

    trans = esp.spi_transaction_t()
    trans_res = esp.spi_transaction_t()

    def disp_spi_send(self, data):
        if len(data) == 0: return  # no need to send anything

        self.trans.length = len(
            data) * 8  # Length is in bytes, transaction length is in bits.
        self.trans.tx_buffer = data

        esp.spi_device_queue_trans(self.spi, self.trans, -1)
        esp.spi_device_get_trans_result(self.spi, self.trans_res, -1)

    ######################################################

    cmd_data = bytearray(1)

    def send_cmd(self, cmd):
        self.cmd_data[0] = cmd
        esp.gpio_set_level(self.dc, 0)  # Command mode
        self.disp_spi_send(self.cmd_data)

    def send_data(self, data):
        esp.gpio_set_level(self.dc, 1)  # Data mode
        self.disp_spi_send(data)

    ######################################################

    def init(self):
        self.disp_spi_init()
        esp.gpio_pad_select_gpio(self.dc)

        # Initialize non-SPI GPIOs

        esp.gpio_set_direction(self.dc, esp.GPIO_MODE.OUTPUT)
        esp.gpio_set_direction(self.rst, esp.GPIO_MODE.OUTPUT)
        if self.backlight != -1:
            esp.gpio_set_direction(self.backlight, esp.GPIO_MODE.OUTPUT)

        # Reset the display

        esp.gpio_set_level(self.rst, 0)
        esp.task_delay_ms(100)
        esp.gpio_set_level(self.rst, 1)
        esp.task_delay_ms(100)

        # Send all the commands

        for cmd in self.init_cmds:
            self.send_cmd(cmd['cmd'])
            self.send_data(cmd['data'])
            if hasattr(cmd, 'delay'):
                task_delay_ms(cmd['delay'])

        print("ILI9341 initialization completed")

        # Enable backlight

        if self.backlight != -1:
            print("Enable backlight")
            esp.gpio_set_level(self.backlight, 1)

    ######################################################

    flush_data = bytearray(4)

    def flush(self, disp_drv, area, color_p):

        # Column addresses

        self.send_cmd(0x2A)
        self.flush_data[0] = (area.x1 >> 8) & 0xFF
        self.flush_data[1] = area.x1 & 0xFF
        self.flush_data[2] = (area.x2 >> 8) & 0xFF
        self.flush_data[3] = area.x2 & 0xFF
        self.send_data(self.flush_data)

        # Page addresses

        self.send_cmd(0x2B)
        self.flush_data[0] = (area.y1 >> 8) & 0xFF
        self.flush_data[1] = area.y1 & 0xFF
        self.flush_data[2] = (area.y2 >> 8) & 0xFF
        self.flush_data[3] = area.y2 & 0xFF
        self.send_data(self.flush_data)

        # Memory write
        self.send_cmd(0x2C)

        size = (area.x2 - area.x1 + 1) * (area.y2 - area.y1 + 1)

        data_view = color_p.__dereference__(size * 2)

        self.send_data(data_view)
        lv.disp_flush_ready(disp_drv)