├── changesWereMade.md ├── changesWereMade ├── ft6x36.py ├── ili9XXX.py └── squarelineHelper.py ├── firmware └── wt32-sc01-firmware-fontsize14-16-30.bin └── micropython-lvgl-squareline-examples ├── easy-custom-font-size.py └── easy-toggle-button-and-message.py /changesWereMade.md: -------------------------------------------------------------------------------- 1 | In folder: **lv_micropython/ports/esp32/modules** 2 | 3 | Update 3 files before compile the firmware for ESP32: 4 | * ft6x36.py (driver for touch) 5 | * ili9XXX.py (add subclass st7796(ili9XXX), driver for display) 6 | * squarelineHelper.py (optional, used by Squareline exported code) 7 | 8 | => All needed files are in this gist 9 | -------------------------------------------------------------------------------- /changesWereMade/ft6x36.py: -------------------------------------------------------------------------------- 1 | # Pure Python LVGL indev driver for the FocalTech FT6X36 capacitive touch IC 2 | # 3 | # from ft6x36 import ft6x36 4 | # 5 | # touch = ft6x36(sda=, scl=) 6 | # 7 | # If you set the size of the touchpad, you have the option to invert each 8 | # axis, and you'll get some extra robustness against occasional read errors 9 | # as values outside the touchpad are quietly rejected. If you select to swap 10 | # the axes, width and height as well as the inversions refer to the situation 11 | # before the swap. 12 | # 13 | # The nice thing about this driver is that it allows access to the second 14 | # finger, as the FT6X36 is multi-touch. (Two fingers max, with caveats on 15 | # some boards.) 16 | # 17 | # The number of presses is in touch.presses, touch.points[0] and points[1] 18 | # hold the positions. LVGL is not (yet) multi-touch, so all it sees is the 19 | # position in points[0]. 20 | 21 | 22 | import lvgl as lv 23 | from machine import I2C, Pin 24 | 25 | class ft6x36: 26 | 27 | def __init__(self, i2c_dev=0, sda=18, scl=19, freq=400000, addr=0x38, width=320, height=480, 28 | inv_x=False, inv_y=True, swap_xy=True): 29 | 30 | if not lv.is_initialized(): 31 | lv.init() 32 | 33 | self.width, self.height = width, height 34 | self.inv_x, self.inv_y, self.swap_xy = inv_x, inv_y, swap_xy 35 | self.i2c = I2C(i2c_dev, sda=Pin(sda), scl=Pin(scl), freq=freq) 36 | self.addr = addr 37 | try: 38 | print("FT6X36 touch IC ready (fw id 0x{0:X} rel {1:d}, lib {2:X})".format( \ 39 | int.from_bytes(self.i2c.readfrom_mem(self.addr, 0xA6, 1), "big"), \ 40 | int.from_bytes(self.i2c.readfrom_mem(self.addr, 0xAF, 1), "big"), \ 41 | int.from_bytes(self.i2c.readfrom_mem(self.addr, 0xA1, 2), "big") \ 42 | )) 43 | except: 44 | print("FT6X36 touch IC not responding") 45 | return 46 | self.point = lv.point_t( {'x': 0, 'y': 0} ) 47 | self.points = [lv.point_t( {'x': 0, 'y': 0} ), lv.point_t( {'x': 0, 'y': 0} )] 48 | self.state = lv.INDEV_STATE.RELEASED 49 | self.indev_drv = lv.indev_drv_t() 50 | self.indev_drv.init() 51 | self.indev_drv.type = lv.INDEV_TYPE.POINTER 52 | self.indev_drv.read_cb = self.callback 53 | self.indev_drv.register() 54 | 55 | def callback(self, driver, data): 56 | 57 | def get_point(offset): 58 | x = (sensorbytes[offset ] << 8 | sensorbytes[offset + 1]) & 0x0fff 59 | y = (sensorbytes[offset + 2] << 8 | sensorbytes[offset + 3]) & 0x0fff 60 | if (self.width != -1 and x >= self.width) or (self.height != -1 and y >= self.height): 61 | raise ValueError 62 | x = self.width - x - 1 if self.inv_x else x 63 | y = self.height - y - 1 if self.inv_y else y 64 | (x, y) = (y, x) if self.swap_xy else (x, y) 65 | return { 'x': x, 'y': y } 66 | 67 | data.point = self.points[0] 68 | data.state = self.state 69 | sensorbytes = self.i2c.readfrom_mem(self.addr, 2, 11) 70 | self.presses = sensorbytes[0] 71 | if self.presses > 2: 72 | return False 73 | try: 74 | if self.presses: 75 | self.points[0] = get_point(1) 76 | if self.presses == 2: 77 | self.points[1] = get_point(7) 78 | except ValueError: 79 | return False 80 | if sensorbytes[3] >> 4: 81 | self.points[0], self.points[1] = self.points[1], self.points[0] 82 | data.point = self.points[0] 83 | data.state = self.state = lv.INDEV_STATE.PRESSED if self.presses else lv.INDEV_STATE.RELEASED 84 | return False 85 | 86 | -------------------------------------------------------------------------------- /changesWereMade/ili9XXX.py: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Pure/Hybrid micropython lvgl display driver for ili9341 and ili9488 on ESP32 3 | # 4 | # For ili9341 display: 5 | # 6 | # Build micropython with 7 | # LV_CFLAGS="-DLV_COLOR_DEPTH=16 -DLV_COLOR_16_SWAP=1" 8 | # (make parameter) to configure LVGL use the same color format as ili9341 9 | # and prevent the need to loop over all pixels to translate them. 10 | # 11 | # For ili9488 display: 12 | # 13 | # Build micropython with 14 | # LV_CFLAGS="-DLV_COLOR_DEPTH=32" 15 | # (make parameter) to configure LVGL use the ARGB color format, which can be 16 | # easily converted to RGB format used by ili9488 display. 17 | # 18 | # Default SPI freq is set to 40MHz. This means cca 9fps of full screen 19 | # redraw. to increase FPS, you can use 80MHz SPI - easily add parameter 20 | # mhz=80 in initialization of driver. 21 | # 22 | # For gc9a01 display: 23 | # 24 | # Build micropython with 25 | # LV_CFLAGS="-DLV_COLOR_DEPTH=16 -DLV_COLOR_16_SWAP=1" 26 | # (make parameter) to configure LVGL use the same color format as ili9341 27 | # and prevent the need to loop over all pixels to translate them. 28 | # 29 | # Default SPI freq is set to 60MHz as that is the maximum the tested dislay 30 | # would suport despite the datasheet suggesting that higher freqs would be 31 | # supported 32 | # 33 | # For st7789 display: 34 | # 35 | # Build micropython with 36 | # LV_CFLAGS="-DLV_COLOR_DEPTH=16 -DLV_COLOR_16_SWAP=1" 37 | # (make parameter) to configure LVGL use the same color format as ili9341 38 | # and prevent the need to loop over all pixels to translate them. 39 | # 40 | # Critical function for high FPS are flush and ISR. 41 | # when "hybrid=True", use C implementation for these functions instead of 42 | # pure python implementation. This improves each frame in about 15ms! 43 | # 44 | # When hybrid=False driver is pure micropython. 45 | # Pure Micropython could be viable when ESP32 supports Viper code emitter. 46 | # 47 | # ili9488 driver DO NOT support pure micropython now (because of required 48 | # color convert). Pure micropython is only supported the for ili9341 and 49 | # gc9a01 displays! 50 | ############################################################################## 51 | 52 | import espidf as esp 53 | import lvgl as lv 54 | import lv_utils 55 | import micropython 56 | import gc 57 | 58 | micropython.alloc_emergency_exception_buf(256) 59 | # gc.threshold(0x10000) # leave enough room for SPI master TX DMA buffers 60 | 61 | # Constants 62 | 63 | COLOR_MODE_RGB = const(0x00) 64 | COLOR_MODE_BGR = const(0x08) 65 | 66 | MADCTL_MH = const(0x04) # Refresh 0=Left to Right, 1=Right to Left 67 | MADCTL_ML = const(0x10) # Refresh 0=Top to Bottom, 1=Bottom to Top 68 | MADCTL_MV = const(0x20) # 0=Normal, 1=Row/column exchange 69 | MADCTL_MX = const(0x40) # 0=Left to Right, 1=Right to Left 70 | MADCTL_MY = const(0x80) # 0=Top to Bottom, 1=Bottom to Top 71 | 72 | # MADCTL values for each of the orientation constants for non-st7789 displays. 73 | ORIENTATION_TABLE = (MADCTL_MX, MADCTL_MV, MADCTL_MY, MADCTL_MY | MADCTL_MX | MADCTL_MV) 74 | 75 | # Negative orientation constants indicate the MADCTL value will come from the ORIENTATION_TABLE, 76 | # otherwise the rot value is used as the MADCTL value. 77 | PORTRAIT = const(-1) 78 | LANDSCAPE = const(-2) 79 | REVERSE_PORTRAIT = const(-3) 80 | REVERSE_LANDSCAPE = const(-4) 81 | 82 | DISPLAY_TYPE_ILI9341 = const(1) 83 | DISPLAY_TYPE_ILI9488 = const(2) 84 | DISPLAY_TYPE_GC9A01 = const(3) 85 | DISPLAY_TYPE_ST7789 = const(4) 86 | DISPLAY_TYPE_ST7735 = const(5) 87 | DISPLAY_TYPE_ST7796 = const(6) 88 | 89 | class ili9XXX: 90 | 91 | TRANS_BUFFER_LEN = const(16) 92 | display_name = 'ili9XXX' 93 | init_cmds = [ ] 94 | 95 | # Default values of "power" and "backlight" are reversed logic! 0 means ON. 96 | # You can change this by setting backlight_on and power_on arguments. 97 | 98 | def __init__(self, 99 | miso=5, mosi=18, clk=19, cs=13, dc=12, rst=4, power=14, backlight=15, backlight_on=0, power_on=0, 100 | spihost=esp.HSPI_HOST, spimode=0, mhz=40, factor=4, hybrid=True, width=240, height=320, start_x=0, start_y=0, 101 | invert=False, double_buffer=True, half_duplex=True, display_type=0, asynchronous=False, initialize=True, 102 | color_format=None 103 | ): 104 | 105 | # Initializations 106 | 107 | if not lv.is_initialized(): 108 | lv.init() 109 | 110 | self.asynchronous = asynchronous 111 | self.initialize = initialize 112 | 113 | self.width = width 114 | self.height = height 115 | self.start_x = start_x 116 | self.start_y = start_y 117 | 118 | self.miso = miso 119 | self.mosi = mosi 120 | self.clk = clk 121 | self.cs = cs 122 | self.dc = dc 123 | self.rst = rst 124 | self.power = power 125 | self.backlight = backlight 126 | self.backlight_on = backlight_on 127 | self.power_on = power_on 128 | self.spihost = spihost 129 | self.spimode = spimode 130 | self.mhz = mhz 131 | self.factor = factor 132 | self.hybrid = hybrid 133 | self.half_duplex = half_duplex 134 | self.display_type = display_type 135 | 136 | self.buf_size = (self.width * self.height * lv.color_t.__SIZE__) // factor 137 | 138 | if invert: 139 | self.init_cmds.append({'cmd': 0x21}) 140 | 141 | # Register display driver 142 | 143 | self.buf1 = esp.heap_caps_malloc(self.buf_size, esp.MALLOC_CAP.DMA) 144 | self.buf2 = esp.heap_caps_malloc(self.buf_size, esp.MALLOC_CAP.DMA) if double_buffer else None 145 | 146 | if self.buf1 and self.buf2: 147 | print("Double buffer") 148 | elif self.buf1: 149 | print("Single buffer") 150 | else: 151 | raise RuntimeError("Not enough DMA-able memory to allocate display buffer") 152 | 153 | self.disp_buf = lv.disp_draw_buf_t() 154 | self.disp_drv = lv.disp_drv_t() 155 | 156 | self.disp_buf.init(self.buf1, self.buf2, self.buf_size // lv.color_t.__SIZE__) 157 | self.disp_drv.init() 158 | self.disp_spi_init() 159 | 160 | self.disp_drv.user_data = { 161 | 'dc': self.dc, 162 | 'spi': self.spi, 163 | 'dt': self.display_type, 164 | 'start_x': self.start_x, 165 | 'start_y': self.start_y} 166 | 167 | self.disp_drv.draw_buf = self.disp_buf 168 | self.disp_drv.flush_cb = esp.ili9xxx_flush if hybrid and hasattr(esp, 'ili9xxx_flush') else self.flush 169 | self.disp_drv.monitor_cb = self.monitor 170 | self.disp_drv.hor_res = self.width 171 | self.disp_drv.ver_res = self.height 172 | if color_format: 173 | self.disp_drv.color_format = color_format 174 | 175 | if self.initialize: 176 | self.init() 177 | 178 | if not lv_utils.event_loop.is_running(): 179 | self.event_loop = lv_utils.event_loop(asynchronous=self.asynchronous) 180 | 181 | 182 | ###################################################### 183 | 184 | def disp_spi_init(self): 185 | 186 | # TODO: Register finalizer callback to deinit SPI. 187 | # That would get called on soft reset. 188 | 189 | buscfg = esp.spi_bus_config_t({ 190 | "miso_io_num": self.miso, 191 | "mosi_io_num": self.mosi, 192 | "sclk_io_num": self.clk, 193 | "quadwp_io_num": -1, 194 | "quadhd_io_num": -1, 195 | "max_transfer_sz": self.buf_size, 196 | }) 197 | 198 | devcfg_flags = esp.SPI_DEVICE.NO_DUMMY 199 | if self.half_duplex: 200 | devcfg_flags |= esp.SPI_DEVICE.HALFDUPLEX 201 | 202 | devcfg = esp.spi_device_interface_config_t({ 203 | "clock_speed_hz": self.mhz*1000*1000, # Clock out at DISP_SPI_MHZ MHz 204 | "mode": self.spimode, # SPI mode 0 by default 205 | "spics_io_num": self.cs, # CS pin 206 | "queue_size": 2, 207 | "flags": devcfg_flags, 208 | "duty_cycle_pos": 128, 209 | }) 210 | 211 | if self.hybrid and hasattr(esp, 'ili9xxx_post_cb_isr'): 212 | devcfg.pre_cb = None 213 | devcfg.post_cb = esp.ili9xxx_post_cb_isr 214 | else: 215 | devcfg.pre_cb = esp.ex_spi_pre_cb_isr 216 | devcfg.post_cb = esp.ex_spi_post_cb_isr 217 | 218 | esp.gpio_pad_select_gpio(self.cs) 219 | 220 | # Initialize the SPI bus, if needed. 221 | 222 | if buscfg.mosi_io_num >= 0 and \ 223 | buscfg.sclk_io_num >= 0: 224 | 225 | if buscfg.miso_io_num >= 0: 226 | esp.gpio_pad_select_gpio(self.miso) 227 | esp.gpio_set_direction(self.miso, esp.GPIO_MODE.INPUT) 228 | esp.gpio_set_pull_mode(self.miso, esp.GPIO.PULLUP_ONLY) 229 | 230 | esp.gpio_pad_select_gpio(self.mosi) 231 | esp.gpio_pad_select_gpio(self.clk) 232 | esp.gpio_set_direction(self.mosi, esp.GPIO_MODE.OUTPUT) 233 | esp.gpio_set_direction(self.clk, esp.GPIO_MODE.OUTPUT) 234 | 235 | ret = esp.spi_bus_initialize(self.spihost, buscfg, 1) 236 | if ret != 0: raise RuntimeError("Failed initializing SPI bus") 237 | 238 | self.trans_buffer = esp.heap_caps_malloc(TRANS_BUFFER_LEN, esp.MALLOC_CAP.DMA) 239 | self.cmd_trans_data = self.trans_buffer.__dereference__(1) 240 | self.word_trans_data = self.trans_buffer.__dereference__(4) 241 | 242 | # Attach the LCD to the SPI bus 243 | 244 | ptr_to_spi = esp.C_Pointer() 245 | ret = esp.spi_bus_add_device(self.spihost, devcfg, ptr_to_spi) 246 | if ret != 0: raise RuntimeError("Failed adding SPI device") 247 | self.spi = ptr_to_spi.ptr_val 248 | 249 | self.bytes_transmitted = 0 250 | completed_spi_transaction = esp.spi_transaction_t() 251 | cast_spi_transaction_instance = esp.spi_transaction_t.__cast_instance__ 252 | 253 | def post_isr(arg): 254 | reported_transmitted = self.bytes_transmitted 255 | if reported_transmitted > 0: 256 | print('- Completed DMA of %d bytes (mem_free=0x%X)' % (reported_transmitted , gc.mem_free())) 257 | self.bytes_transmitted -= reported_transmitted 258 | 259 | # Called in ISR context! 260 | def flush_isr(spi_transaction_ptr): 261 | self.disp_drv.flush_ready() 262 | # esp.spi_device_release_bus(self.spi) 263 | esp.get_ccount(self.end_time_ptr) 264 | 265 | # cast_spi_transaction_instance(completed_spi_transaction, spi_transaction_ptr) 266 | # self.bytes_transmitted += completed_spi_transaction.length 267 | # try: 268 | # micropython.schedule(post_isr, None) 269 | # except RuntimeError: 270 | # pass 271 | 272 | self.spi_callbacks = esp.spi_transaction_set_cb(None, flush_isr) 273 | 274 | # 275 | # Deinitialize SPI device and bus, and free memory 276 | # This function is called from finilizer during gc sweep - therefore must not allocate memory! 277 | # 278 | 279 | trans_result_ptr = esp.C_Pointer() 280 | 281 | def deinit(self): 282 | 283 | print('Deinitializing {}..'.format(self.display_name)) 284 | 285 | # Prevent callbacks to lvgl, which refer to the buffers we are about to delete 286 | 287 | if lv_utils.event_loop.is_running(): 288 | self.event_loop.deinit() 289 | 290 | self.disp.remove() 291 | 292 | if self.spi: 293 | 294 | # Pop all pending transaction results 295 | 296 | ret = 0 297 | while ret == 0: 298 | ret = esp.spi_device_get_trans_result(self.spi, self.trans_result_ptr , 1) 299 | 300 | # Remove device 301 | 302 | esp.spi_bus_remove_device(self.spi) 303 | self.spi = None 304 | 305 | # Free SPI bus 306 | 307 | esp.spi_bus_free(self.spihost) 308 | self.spihost = None 309 | 310 | # Free RAM 311 | 312 | if self.buf1: 313 | esp.heap_caps_free(self.buf1) 314 | self.buf1 = None 315 | 316 | if self.buf2: 317 | esp.heap_caps_free(self.buf2) 318 | self.buf2 = None 319 | 320 | if self.trans_buffer: 321 | esp.heap_caps_free(self.trans_buffer) 322 | self.trans_buffer = None 323 | 324 | 325 | ###################################################### 326 | 327 | trans = esp.spi_transaction_t() # .__cast__( 328 | # esp.heap_caps_malloc( 329 | # esp.spi_transaction_t.__SIZE__, esp.MALLOC_CAP.DMA)) 330 | 331 | def spi_send(self, data): 332 | self.trans.length = len(data) * 8 # Length is in bytes, transaction length is in bits. 333 | self.trans.tx_buffer = data # data should be allocated as DMA-able memory 334 | self.trans.user = None 335 | esp.spi_device_polling_transmit(self.spi, self.trans) 336 | 337 | def spi_send_dma(self, data): 338 | self.trans.length = len(data) * 8 # Length is in bytes, transaction length is in bits. 339 | self.trans.tx_buffer = data # data should be allocated as DMA-able memory 340 | self.trans.user = self.spi_callbacks 341 | esp.spi_device_queue_trans(self.spi, self.trans, -1) 342 | 343 | ###################################################### 344 | ###################################################### 345 | 346 | def send_cmd(self, cmd): 347 | esp.gpio_set_level(self.dc, 0) # Command mode 348 | self.cmd_trans_data[0] = cmd 349 | self.spi_send(self.cmd_trans_data) 350 | 351 | def send_data(self, data): 352 | esp.gpio_set_level(self.dc, 1) # Data mode 353 | if len(data) > TRANS_BUFFER_LEN: raise RuntimeError('Data too long, please use DMA!') 354 | trans_data = self.trans_buffer.__dereference__(len(data)) 355 | trans_data[:] = data[:] 356 | self.spi_send(trans_data) 357 | 358 | def send_trans_word(self): 359 | esp.gpio_set_level(self.dc, 1) # Data mode 360 | self.spi_send(self.word_trans_data) 361 | 362 | def send_data_dma(self, data): # data should be allocated as DMA-able memory 363 | esp.gpio_set_level(self.dc, 1) # Data mode 364 | self.spi_send_dma(data) 365 | 366 | ###################################################### 367 | 368 | async def _init(self, sleep_func): 369 | 370 | # Initialize non-SPI GPIOs 371 | 372 | esp.gpio_pad_select_gpio(self.dc) 373 | if self.rst != -1: esp.gpio_pad_select_gpio(self.rst) 374 | if self.backlight != -1: esp.gpio_pad_select_gpio(self.backlight) 375 | if self.power != -1: esp.gpio_pad_select_gpio(self.power) 376 | 377 | esp.gpio_set_direction(self.dc, esp.GPIO_MODE.OUTPUT) 378 | if self.rst != -1: esp.gpio_set_direction(self.rst, esp.GPIO_MODE.OUTPUT) 379 | if self.backlight != -1: esp.gpio_set_direction(self.backlight, esp.GPIO_MODE.OUTPUT) 380 | if self.power != -1: esp.gpio_set_direction(self.power, esp.GPIO_MODE.OUTPUT) 381 | 382 | # Power the display 383 | 384 | if self.power != -1: 385 | esp.gpio_set_level(self.power, self.power_on) 386 | await sleep_func(100) 387 | 388 | # Reset the display 389 | 390 | if self.rst != -1: 391 | esp.gpio_set_level(self.rst, 0) 392 | await sleep_func(100) 393 | esp.gpio_set_level(self.rst, 1) 394 | await sleep_func(100) 395 | 396 | # Send all the commands 397 | 398 | for cmd in self.init_cmds: 399 | self.send_cmd(cmd['cmd']) 400 | if 'data' in cmd: 401 | self.send_data(cmd['data']) 402 | if 'delay' in cmd: 403 | await sleep_func(cmd['delay']) 404 | 405 | print("{} initialization completed".format(self.display_name)) 406 | 407 | # Enable backlight 408 | 409 | if self.backlight != -1: 410 | print("Enable backlight") 411 | esp.gpio_set_level(self.backlight, self.backlight_on) 412 | 413 | # Register the driver 414 | self.disp = self.disp_drv.register() 415 | 416 | 417 | def init(self): 418 | import utime 419 | generator = self._init(lambda ms:(yield ms)) 420 | try: 421 | while True: 422 | ms = next(generator) 423 | utime.sleep_ms(ms) 424 | except StopIteration: 425 | pass 426 | 427 | async def init_async(self): 428 | import uasyncio 429 | await self._init(uasyncio.sleep_ms) 430 | 431 | def power_down(self): 432 | 433 | if self.power != -1: 434 | esp.gpio_set_level(self.power, 1 - self.power_on) 435 | 436 | if self.backlight != -1: 437 | esp.gpio_set_level(self.backlight, 1 - self.backlight_on) 438 | 439 | 440 | ###################################################### 441 | 442 | start_time_ptr = esp.C_Pointer() 443 | end_time_ptr = esp.C_Pointer() 444 | flush_acc_setup_cycles = 0 445 | flush_acc_dma_cycles = 0 446 | 447 | def flush(self, disp_drv, area, color_p): 448 | 449 | if self.end_time_ptr.int_val and self.end_time_ptr.int_val > self.start_time_ptr.int_val: 450 | self.flush_acc_dma_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val 451 | 452 | esp.get_ccount(self.start_time_ptr) 453 | 454 | # esp.spi_device_acquire_bus(self.spi, esp.ESP.MAX_DELAY) 455 | 456 | # Column addresses 457 | self.send_cmd(0x2A) 458 | 459 | x1 = area.x1 + self.start_x 460 | x2 = area.x2 + self.start_x 461 | 462 | self.word_trans_data[0] = (x1 >> 8) & 0xFF 463 | self.word_trans_data[1] = x1 & 0xFF 464 | self.word_trans_data[2] = (x2 >> 8) & 0xFF 465 | self.word_trans_data[3] = x2 & 0xFF 466 | self.send_trans_word() 467 | 468 | # Page addresses 469 | 470 | self.send_cmd(0x2B) 471 | 472 | y1 = area.y1 + self.start_y 473 | y2 = area.y2 + self.start_y 474 | 475 | self.word_trans_data[0] = (y1 >> 8) & 0xFF 476 | self.word_trans_data[1] = y1 & 0xFF 477 | self.word_trans_data[2] = (y2 >> 8) & 0xFF 478 | self.word_trans_data[3] = y2 & 0xFF 479 | self.send_trans_word() 480 | 481 | # Memory write by DMA, disp_flush_ready when finished 482 | 483 | self.send_cmd(0x2C) 484 | 485 | size = (x2 - x1 + 1) * (y2 - y1 + 1) 486 | data_view = color_p.__dereference__(size * lv.color_t.__SIZE__) 487 | 488 | esp.get_ccount(self.end_time_ptr) 489 | if self.end_time_ptr.int_val > self.start_time_ptr.int_val: 490 | self.flush_acc_setup_cycles += self.end_time_ptr.int_val - self.start_time_ptr.int_val 491 | esp.get_ccount(self.start_time_ptr) 492 | 493 | self.send_data_dma(data_view) 494 | 495 | ###################################################### 496 | 497 | monitor_acc_time = 0 498 | monitor_acc_px = 0 499 | monitor_count = 0 500 | 501 | cycles_in_ms = esp.esp_clk_cpu_freq() // 1000 502 | 503 | def monitor(self, disp_drv, time, px): 504 | self.monitor_acc_time += time 505 | self.monitor_acc_px += px 506 | self.monitor_count += 1 507 | 508 | def stat(self): 509 | if self.monitor_count == 0: 510 | return None 511 | 512 | time = self.monitor_acc_time // self.monitor_count 513 | setup = self.flush_acc_setup_cycles // (self.monitor_count * self.cycles_in_ms) 514 | dma = self.flush_acc_dma_cycles // (self.monitor_count * self.cycles_in_ms) 515 | px = self.monitor_acc_px // self.monitor_count 516 | 517 | self.monitor_acc_time = 0 518 | self.monitor_acc_px = 0 519 | self.monitor_count = 0 520 | self.flush_acc_setup_cycles = 0 521 | self.flush_acc_dma_cycles = 0 522 | 523 | return time, setup, dma, px 524 | 525 | def madctl(self, colormode, rotation, rotations): 526 | 527 | # if rotation is 0 or positive use the value as is. 528 | 529 | if rotation >= 0: 530 | return rotation | colormode 531 | 532 | # otherwise use abs(rotation)-1 as index to retreive value from rotations set 533 | 534 | index = abs(rotation) - 1 535 | if index > len(rotations): 536 | RuntimeError('Invalid display rot value specified during init.') 537 | 538 | return rotations[index] | colormode 539 | 540 | class ili9341(ili9XXX): 541 | 542 | def __init__(self, 543 | miso=5, mosi=18, clk=19, cs=13, dc=12, rst=4, power=14, backlight=15, backlight_on=0, power_on=0, 544 | spihost=esp.HSPI_HOST, spimode=0, mhz=40, factor=4, hybrid=True, width=240, height=320, start_x=0, start_y=0, 545 | colormode=COLOR_MODE_BGR, rot=PORTRAIT, invert=False, double_buffer=True, half_duplex=True, 546 | asynchronous=False, initialize=True, color_format=lv.COLOR_FORMAT.NATIVE_REVERSE 547 | ): 548 | 549 | # Make sure Micropython was built such that color won't require processing before DMA 550 | 551 | if lv.color_t.__SIZE__ != 2: 552 | raise RuntimeError('ili9341 micropython driver requires defining LV_COLOR_DEPTH=16') 553 | 554 | self.display_name = 'ILI9341' 555 | 556 | self.init_cmds = [ 557 | {'cmd': 0xCF, 'data': bytes([0x00, 0x83, 0X30])}, 558 | {'cmd': 0xED, 'data': bytes([0x64, 0x03, 0X12, 0X81])}, 559 | {'cmd': 0xE8, 'data': bytes([0x85, 0x01, 0x79])}, 560 | {'cmd': 0xCB, 'data': bytes([0x39, 0x2C, 0x00, 0x34, 0x02])}, 561 | {'cmd': 0xF7, 'data': bytes([0x20])}, 562 | {'cmd': 0xEA, 'data': bytes([0x00, 0x00])}, 563 | {'cmd': 0xC0, 'data': bytes([0x26])}, # Power control 564 | {'cmd': 0xC1, 'data': bytes([0x11])}, # Power control 565 | {'cmd': 0xC5, 'data': bytes([0x35, 0x3E])}, # VCOM control 566 | {'cmd': 0xC7, 'data': bytes([0xBE])}, # VCOM control 567 | 568 | {'cmd': 0x36, 'data': bytes([ 569 | self.madctl(colormode, rot, ORIENTATION_TABLE)])}, # MADCTL 570 | 571 | {'cmd': 0x3A, 'data': bytes([0x55])}, # Pixel Format Set 572 | {'cmd': 0xB1, 'data': bytes([0x00, 0x1B])}, 573 | {'cmd': 0xF2, 'data': bytes([0x08])}, 574 | {'cmd': 0x26, 'data': bytes([0x01])}, 575 | {'cmd': 0xE0, 'data': bytes([0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00])}, 576 | {'cmd': 0XE1, 'data': bytes([0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F])}, 577 | {'cmd': 0x2A, 'data': bytes([0x00, 0x00, 0x00, 0xEF])}, 578 | {'cmd': 0x2B, 'data': bytes([0x00, 0x00, 0x01, 0x3f])}, 579 | {'cmd': 0x2C, 'data': bytes([0])}, 580 | {'cmd': 0xB7, 'data': bytes([0x07])}, 581 | {'cmd': 0xB6, 'data': bytes([0x0A, 0x82, 0x27, 0x00])}, 582 | {'cmd': 0x11, 'data': bytes([0]), 'delay':100}, 583 | {'cmd': 0x29, 'data': bytes([0]), 'delay':100} 584 | ] 585 | 586 | super().__init__(miso=miso, mosi=mosi, clk=clk, cs=cs, dc=dc, rst=rst, power=power, backlight=backlight, 587 | backlight_on=backlight_on, power_on=power_on, spihost=spihost, spimode=spimode, mhz=mhz, factor=factor, hybrid=hybrid, 588 | width=width, height=height, start_x=start_x, start_y=start_y, invert=invert, double_buffer=double_buffer, 589 | half_duplex=half_duplex, display_type=DISPLAY_TYPE_ILI9341, asynchronous=asynchronous, initialize=initialize, 590 | color_format=color_format) 591 | 592 | class ili9488(ili9XXX): 593 | 594 | def __init__(self, 595 | miso=5, mosi=18, clk=19, cs=13, dc=12, rst=4, power=14, backlight=15, backlight_on=0, power_on=0, 596 | spihost=esp.HSPI_HOST, spimode=0, mhz=40, factor=8, hybrid=True, width=320, height=480, colormode=COLOR_MODE_RGB, 597 | rot=PORTRAIT, invert=False, double_buffer=True, half_duplex=True, asynchronous=False, initialize=True, 598 | color_format=None 599 | ): 600 | 601 | if lv.color_t.__SIZE__ != 4: 602 | raise RuntimeError('ili9488 micropython driver requires defining LV_COLOR_DEPTH=32') 603 | if not hybrid: 604 | raise RuntimeError('ili9488 micropython driver do not support non-hybrid driver') 605 | 606 | self.display_name = 'ILI9488' 607 | 608 | self.init_cmds = [ 609 | {'cmd': 0x01, 'data': bytes([0]), 'delay': 200}, 610 | {'cmd': 0x11, 'data': bytes([0]), 'delay': 120}, 611 | {'cmd': 0xE0, 'data': bytes([0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F])}, 612 | {'cmd': 0xE1, 'data': bytes([0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F])}, 613 | {'cmd': 0xC0, 'data': bytes([0x17, 0x15])}, ### 0x13, 0x13 614 | {'cmd': 0xC1, 'data': bytes([0x41])}, ### 615 | {'cmd': 0xC2, 'data': bytes([0x44])}, ### 616 | {'cmd': 0xC5, 'data': bytes([0x00, 0x12, 0x80])}, 617 | #{'cmd': 0xC5, 'data': bytes([0x00, 0x0, 0x0, 0x0])}, 618 | 619 | {'cmd': 0x36, 'data': bytes([ 620 | self.madctl(colormode, rot, ORIENTATION_TABLE)])}, # MADCTL 621 | 622 | {'cmd': 0x3A, 'data': bytes([0x66])}, 623 | {'cmd': 0xB0, 'data': bytes([0x00])}, 624 | {'cmd': 0xB1, 'data': bytes([0xA0])}, 625 | {'cmd': 0xB4, 'data': bytes([0x02])}, 626 | {'cmd': 0xB6, 'data': bytes([0x02, 0x02])}, 627 | {'cmd': 0xE9, 'data': bytes([0x00])}, 628 | {'cmd': 0x53, 'data': bytes([0x28])}, 629 | {'cmd': 0x51, 'data': bytes([0x7F])}, 630 | {'cmd': 0xF7, 'data': bytes([0xA9, 0x51, 0x2C, 0x02])}, 631 | {'cmd': 0x29, 'data': bytes([0]), 'delay': 120} 632 | ] 633 | 634 | super().__init__(miso=miso, mosi=mosi, clk=clk, cs=cs, dc=dc, rst=rst, power=power, backlight=backlight, 635 | backlight_on=backlight_on, power_on=power_on, spihost=spihost, spimode=spimode, mhz=mhz, factor=factor, hybrid=hybrid, 636 | width=width, height=height, invert=invert, double_buffer=double_buffer, half_duplex=half_duplex, 637 | display_type=DISPLAY_TYPE_ILI9488, asynchronous=asynchronous, initialize=initialize, color_format=color_format) 638 | 639 | class gc9a01(ili9XXX): 640 | 641 | # On the tested display the write direction and colormode appear to be 642 | # reversed from how they are presented in the datasheet 643 | 644 | def __init__(self, 645 | miso=5, mosi=18, clk=19, cs=13, dc=12, rst=4, power=14, backlight=15, backlight_on=0, power_on=0, 646 | spihost=esp.HSPI_HOST, spimode=0, mhz=60, factor=4, hybrid=True, width=240, height=240, colormode=COLOR_MODE_RGB, 647 | rot=PORTRAIT, invert=False, double_buffer=True, half_duplex=True, asynchronous=False, initialize=True, 648 | color_format=None 649 | ): 650 | 651 | if lv.color_t.__SIZE__ != 2: 652 | raise RuntimeError('gc9a01 micropython driver requires defining LV_COLOR_DEPTH=16') 653 | 654 | # This is included as the color mode appears to be reversed from the 655 | # datasheet and the ili9XXX driver values 656 | 657 | if colormode == COLOR_MODE_RGB: 658 | self.colormode = COLOR_MODE_BGR 659 | elif colormode == COLOR_MODE_BGR: 660 | self.colormode = COLOR_MODE_RGB 661 | 662 | self.display_name = 'GC9A01' 663 | 664 | self.init_cmds = [ 665 | {'cmd': 0xEF, 'data': bytes([0])}, 666 | {'cmd': 0xEB, 'data': bytes([0x14])}, 667 | {'cmd': 0xFE, 'data': bytes([0])}, 668 | {'cmd': 0xEF, 'data': bytes([0])}, 669 | {'cmd': 0xEB, 'data': bytes([0x14])}, 670 | {'cmd': 0x84, 'data': bytes([0x40])}, 671 | {'cmd': 0x85, 'data': bytes([0xFF])}, 672 | {'cmd': 0x86, 'data': bytes([0xFF])}, 673 | {'cmd': 0x87, 'data': bytes([0xFF])}, 674 | {'cmd': 0x88, 'data': bytes([0x0A])}, 675 | {'cmd': 0x89, 'data': bytes([0x21])}, 676 | {'cmd': 0x8A, 'data': bytes([0x00])}, 677 | {'cmd': 0x8B, 'data': bytes([0x80])}, 678 | {'cmd': 0x8C, 'data': bytes([0x01])}, 679 | {'cmd': 0x8D, 'data': bytes([0x01])}, 680 | {'cmd': 0x8E, 'data': bytes([0xFF])}, 681 | {'cmd': 0x8F, 'data': bytes([0xFF])}, 682 | {'cmd': 0xB6, 'data': bytes([0x00, 0x00])}, 683 | 684 | {'cmd': 0x36, 'data': bytes([ 685 | self.madctl(colormode, rot, ORIENTATION_TABLE)])}, # MADCTL 686 | 687 | {'cmd': 0x3A, 'data': bytes([0x05])}, 688 | {'cmd': 0x90, 'data': bytes([0x08, 0x08, 0x08, 0x08])}, 689 | {'cmd': 0xBD, 'data': bytes([0x06])}, 690 | {'cmd': 0xBC, 'data': bytes([0x00])}, 691 | {'cmd': 0xFF, 'data': bytes([0x60, 0x01, 0x04])}, 692 | {'cmd': 0xC3, 'data': bytes([0x13])}, 693 | {'cmd': 0xC4, 'data': bytes([0x13])}, 694 | {'cmd': 0xC9, 'data': bytes([0x22])}, 695 | {'cmd': 0xBE, 'data': bytes([0x11])}, 696 | {'cmd': 0xE1, 'data': bytes([0x10, 0x0E])}, 697 | {'cmd': 0xDF, 'data': bytes([0x21, 0x0c, 0x02])}, 698 | {'cmd': 0xF0, 'data': bytes([0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])}, 699 | {'cmd': 0xF1, 'data': bytes([0x43, 0x70, 0x72, 0x36, 0x37, 0x6F])}, 700 | {'cmd': 0xF2, 'data': bytes([0x45, 0x09, 0x08, 0x08, 0x26, 0x2A])}, 701 | {'cmd': 0xF3, 'data': bytes([0x43, 0x70, 0x72, 0x36, 0x37, 0x6F])}, 702 | {'cmd': 0xED, 'data': bytes([0x1B, 0x0B])}, 703 | {'cmd': 0xAE, 'data': bytes([0x77])}, 704 | {'cmd': 0xCD, 'data': bytes([0x63])}, 705 | {'cmd': 0x70, 'data': bytes([0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03])}, 706 | {'cmd': 0xE8, 'data': bytes([0x34])}, 707 | {'cmd': 0x62, 'data': bytes([0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70])}, 708 | {'cmd': 0x63, 'data': bytes([0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70])}, 709 | {'cmd': 0x64, 'data': bytes([0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07])}, 710 | {'cmd': 0x66, 'data': bytes([0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00])}, 711 | {'cmd': 0x67, 'data': bytes([0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98])}, 712 | {'cmd': 0x74, 'data': bytes([0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00])}, 713 | {'cmd': 0x98, 'data': bytes([0x3e, 0x07])}, 714 | {'cmd': 0x35, 'data': bytes([0])}, 715 | {'cmd': 0x21, 'data': bytes([0])}, 716 | {'cmd': 0x11, 'data': bytes([0]), 'delay': 20}, 717 | {'cmd': 0x29, 'data': bytes([0]), 'delay': 120} 718 | ] 719 | 720 | super().__init__(miso=miso, mosi=mosi, clk=clk, cs=cs, dc=dc, rst=rst, power=power, backlight=backlight, 721 | backlight_on=backlight_on, power_on=power_on, spihost=spihost, spimode=spimode, mhz=mhz, factor=factor, hybrid=hybrid, 722 | width=width, height=height, invert=invert, double_buffer=double_buffer, half_duplex=half_duplex, 723 | display_type=DISPLAY_TYPE_GC9A01, asynchronous=asynchronous, initialize=initialize, color_format=color_format) 724 | 725 | class st7789(ili9XXX): 726 | 727 | # The st7789 display controller has an internal framebuffer arranged in a 320x240 pixel 728 | # configuration. Physical displays with pixel sizes less than 320x240 must supply a start_x and 729 | # start_y argument to indicate where the physical display begins relative to the start of the 730 | # display controllers internal framebuffer. 731 | 732 | def __init__(self, 733 | miso=-1, mosi=19, clk=18, cs=5, dc=16, rst=23, power=-1, backlight=4, backlight_on=1, power_on=0, 734 | spihost=esp.HSPI_HOST, spimode=0, mhz=40, factor=4, hybrid=True, width=320, height=240, start_x=0, start_y=0, 735 | colormode=COLOR_MODE_BGR, rot=PORTRAIT, invert=True, double_buffer=True, half_duplex=True, 736 | asynchronous=False, initialize=True, color_format=lv.COLOR_FORMAT.NATIVE_REVERSE): 737 | 738 | # Make sure Micropython was built such that color won't require processing before DMA 739 | 740 | if lv.color_t.__SIZE__ != 2: 741 | raise RuntimeError('st7789 micropython driver requires defining LV_COLOR_DEPTH=16') 742 | 743 | self.display_name = 'ST7789' 744 | 745 | self.init_cmds = [ 746 | {'cmd': 0x11, 'data': bytes([0x0]), 'delay': 120}, 747 | {'cmd': 0x13, 'data': bytes([0x0])}, 748 | 749 | {'cmd': 0x36, 'data': bytes([ 750 | self.madctl(colormode, rot, (0x0, MADCTL_MX | MADCTL_MV, MADCTL_MY | MADCTL_MX, MADCTL_MY | MADCTL_MV))])}, # MADCTL 751 | 752 | {'cmd': 0xb6, 'data': bytes([0xa, 0x82])}, 753 | {'cmd': 0x3a, 'data': bytes([0x55]),'delay': 10}, 754 | {'cmd': 0xb2, 'data': bytes([0xc, 0xc, 0x0, 0x33, 0x33])}, 755 | {'cmd': 0xb7, 'data': bytes([0x35])}, 756 | {'cmd': 0xbb, 'data': bytes([0x28])}, 757 | {'cmd': 0xc0, 'data': bytes([0xc])}, 758 | {'cmd': 0xc2, 'data': bytes([0x1, 0xff])}, 759 | {'cmd': 0xc3, 'data': bytes([0x10])}, 760 | {'cmd': 0xc4, 'data': bytes([0x20])}, 761 | {'cmd': 0xc6, 'data': bytes([0xf])}, 762 | {'cmd': 0xd0, 'data': bytes([0xa4, 0xa1])}, 763 | {'cmd': 0xe0, 'data': bytes([0xd0, 0x0, 0x2, 0x7, 0xa, 0x28, 0x32, 0x44, 0x42, 0x6, 0xe, 0x12, 0x14, 0x17])}, 764 | {'cmd': 0xe1, 'data': bytes([0xd0, 0x0, 0x2, 0x7, 0xa, 0x28, 0x31, 0x54, 0x47, 0xe, 0x1c, 0x17, 0x1b, 0x1e])}, 765 | {'cmd': 0x21, 'data': bytes([0x0])}, 766 | {'cmd': 0x2a, 'data': bytes([0x0, 0x0, 0x0, 0xe5])}, 767 | {'cmd': 0x2b, 'data': bytes([0x0, 0x0, 0x1, 0x3f]), 'delay': 120}, 768 | {'cmd': 0x29, 'data': bytes([0x0]), 'delay': 120} 769 | ] 770 | 771 | super().__init__(miso=miso, mosi=mosi, clk=clk, cs=cs, dc=dc, rst=rst, power=power, backlight=backlight, 772 | backlight_on=backlight_on, power_on=power_on, spihost=spihost, spimode=spimode, mhz=mhz, factor=factor, hybrid=hybrid, 773 | width=width, height=height, start_x=start_x, start_y=start_y, invert=invert, double_buffer=double_buffer, 774 | half_duplex=half_duplex, display_type=DISPLAY_TYPE_ST7789, asynchronous=asynchronous, 775 | initialize=initialize, color_format=color_format) 776 | 777 | class st7735(ili9XXX): 778 | 779 | # The st7735 display controller has an internal framebuffer arranged in 132x162 pixel 780 | # configuration. Physical displays with pixel sizes less than 132x162 must supply a start_x and 781 | # start_y argument to indicate where the physical display begins relative to the start of the 782 | # display controllers internal framebuffer. 783 | 784 | def __init__(self, 785 | miso=-1, mosi=19, clk=18, cs=13, dc=12, rst=4, power=-1, backlight=15, backlight_on=1, power_on=0, 786 | spihost=esp.HSPI_HOST, spimode=0, mhz=40, factor=4, hybrid=True, width=128, height=160, start_x=0, start_y=0, 787 | colormode=COLOR_MODE_RGB, rot=PORTRAIT, invert=False, double_buffer=True, half_duplex=True, 788 | asynchronous=False, initialize=True, color_format=lv.COLOR_FORMAT.NATIVE_REVERSE): 789 | 790 | # Make sure Micropython was built such that color won't require processing before DMA 791 | 792 | if lv.color_t.__SIZE__ != 2: 793 | raise RuntimeError('st7735 micropython driver requires defining LV_COLOR_DEPTH=16') 794 | 795 | self.display_name = 'ST7735' 796 | 797 | self.init_cmds = [ 798 | {'cmd': 0xCF, 'data': bytes([0x00, 0x83, 0X30])}, 799 | {'cmd': 0xED, 'data': bytes([0x64, 0x03, 0X12, 0X81])}, 800 | {'cmd': 0xE8, 'data': bytes([0x85, 0x01, 0x79])}, 801 | {'cmd': 0xCB, 'data': bytes([0x39, 0x2C, 0x00, 0x34, 0x02])}, 802 | {'cmd': 0xF7, 'data': bytes([0x20])}, 803 | {'cmd': 0xEA, 'data': bytes([0x00, 0x00])}, 804 | {'cmd': 0xC0, 'data': bytes([0x26])}, # Power control 805 | {'cmd': 0xC1, 'data': bytes([0x11])}, # Power control 806 | {'cmd': 0xC5, 'data': bytes([0x35, 0x3E])}, # VCOM control 807 | {'cmd': 0xC7, 'data': bytes([0xBE])}, # VCOM control 808 | 809 | {'cmd': 0x36, 'data': bytes([ 810 | self.madctl(colormode, rot, (MADCTL_MX | MADCTL_MY, MADCTL_MV | MADCTL_MY, 0, MADCTL_MX | MADCTL_MV))])}, # MADCTL 811 | 812 | {'cmd': 0x3A, 'data': bytes([0x55])}, # Pixel Format Set 813 | {'cmd': 0xB1, 'data': bytes([0x00, 0x1B])}, 814 | {'cmd': 0xF2, 'data': bytes([0x08])}, 815 | {'cmd': 0x26, 'data': bytes([0x01])}, 816 | {'cmd': 0xE0, 'data': bytes([0x1F, 0x1A, 0x18, 0x0A, 0x0F, 0x06, 0x45, 0X87, 0x32, 0x0A, 0x07, 0x02, 0x07, 0x05, 0x00])}, 817 | {'cmd': 0XE1, 'data': bytes([0x00, 0x25, 0x27, 0x05, 0x10, 0x09, 0x3A, 0x78, 0x4D, 0x05, 0x18, 0x0D, 0x38, 0x3A, 0x1F])}, 818 | {'cmd': 0x2A, 'data': bytes([0x00, 0x00, 0x00, 0xEF])}, 819 | {'cmd': 0x2B, 'data': bytes([0x00, 0x00, 0x01, 0x3f])}, 820 | {'cmd': 0x2C, 'data': bytes([0])}, 821 | {'cmd': 0xB7, 'data': bytes([0x07])}, 822 | {'cmd': 0xB6, 'data': bytes([0x0A, 0x82, 0x27, 0x00])}, 823 | {'cmd': 0x11, 'data': bytes([0]), 'delay':100}, 824 | {'cmd': 0x29, 'data': bytes([0]), 'delay':100} 825 | ] 826 | 827 | super().__init__(miso=miso, mosi=mosi, clk=clk, cs=cs, dc=dc, rst=rst, power=power, backlight=backlight, 828 | backlight_on=backlight_on, power_on=power_on, spihost=spihost, spimode=spimode, mhz=mhz, factor=factor, hybrid=hybrid, 829 | width=width, height=height, start_x=start_x, start_y=start_y, invert=invert, double_buffer=double_buffer, 830 | half_duplex=half_duplex, display_type=DISPLAY_TYPE_ST7735, asynchronous=asynchronous, 831 | initialize=initialize, color_format=color_format) 832 | 833 | class st7796(ili9XXX): 834 | 835 | # The st7796 display controller has an internal framebuffer arranged in a 480x320 pixel 836 | # configuration. Physical displays with pixel sizes less than 480x320 must supply a start_x and 837 | # start_y argument to indicate where the physical display begins relative to the start of the 838 | # display controllers internal framebuffer. 839 | 840 | def __init__(self, 841 | miso=-1, mosi=13, clk=14, cs=15, dc=21, rst=22, power=-1, backlight=23, backlight_on=1, power_on=0, 842 | spihost=esp.HSPI_HOST, spimode=0, mhz=80, factor=16, hybrid=True, width=480, height=320, start_x=0, start_y=0, 843 | colormode=COLOR_MODE_RGB, rot=REVERSE_LANDSCAPE, invert=True, double_buffer=False, half_duplex=True, 844 | asynchronous=False, initialize=True, color_format=lv.COLOR_FORMAT.NATIVE_REVERSE): 845 | 846 | # Make sure Micropython was built such that color won't require processing before DMA 847 | 848 | if lv.color_t.__SIZE__ != 2: 849 | raise RuntimeError('st7796 micropython driver requires defining LV_COLOR_DEPTH=16') 850 | 851 | self.display_name = 'ST7796' 852 | 853 | self.init_cmds = [ 854 | {'cmd': 0x01, 'data': bytes([0x0]), 'delay': 120}, 855 | {'cmd': 0x11, 'data': bytes([0x0]), 'delay': 120}, 856 | {'cmd': 0xf0, 'data': bytes([0xc3])}, 857 | {'cmd': 0xf0, 'data': bytes([0x96])}, 858 | # {'cmd': 0x36, 'data': bytes([0x48])}, 859 | # {'cmd': 0x36, 'data': bytes([ 860 | # self.madctl(colormode, rot, (0x0, MADCTL_MX | MADCTL_MV, MADCTL_MY | MADCTL_MX, MADCTL_MY | MADCTL_MV))])}, # MADCTL 861 | {'cmd': 0x36, 'data': bytes([ 862 | self.madctl(colormode, rot, ORIENTATION_TABLE)])}, # MADCTL 863 | {'cmd': 0x3a, 'data': bytes([0x55]),'delay': 10}, 864 | {'cmd': 0xb4, 'data': bytes([0x01])}, 865 | {'cmd': 0xb6, 'data': bytes([0x80, 0x02, 0x3b])}, 866 | {'cmd': 0xe8, 'data': bytes([0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33])}, 867 | {'cmd': 0xc1, 'data': bytes([0x06])}, 868 | {'cmd': 0xc2, 'data': bytes([0xa7])}, 869 | {'cmd': 0xc5, 'data': bytes([0x18]),'delay': 120}, 870 | {'cmd': 0xe0, 'data': bytes([0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x18])}, 871 | {'cmd': 0xe1, 'data': bytes([0xe0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2b, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x18]),'delay': 120}, 872 | {'cmd': 0xf0, 'data': bytes([0x3c])}, 873 | {'cmd': 0xf0, 'data': bytes([0x69]),'delay': 120}, 874 | {'cmd': 0x29, 'data': bytes([0x0])} 875 | 876 | ] 877 | 878 | super().__init__(miso=miso, mosi=mosi, clk=clk, cs=cs, dc=dc, rst=rst, power=power, backlight=backlight, 879 | backlight_on=backlight_on, power_on=power_on, spihost=spihost, spimode=spimode, mhz=mhz, factor=factor, hybrid=hybrid, 880 | width=width, height=height, start_x=start_x, start_y=start_y, invert=invert, double_buffer=double_buffer, 881 | half_duplex=half_duplex, display_type=DISPLAY_TYPE_ST7796, asynchronous=asynchronous, 882 | initialize=initialize, color_format=color_format) 883 | -------------------------------------------------------------------------------- /changesWereMade/squarelineHelper.py: -------------------------------------------------------------------------------- 1 | def SetFlag( obj, flag, value): 2 | if (value): 3 | obj.add_flag(flag) 4 | else: 5 | obj.clear_flag(flag) 6 | return 7 | 8 | _ui_comp_table = {} 9 | _ui_comp_prev = None 10 | _ui_name_prev = None 11 | _ui_child_prev = None 12 | _ui_comp_table.clear() 13 | 14 | def _ui_comp_del_event(e): 15 | target = e.get_target() 16 | _ui_comp_table[id(target)].remove() 17 | 18 | def ui_comp_get_child(comp, child_name): 19 | return _ui_comp_table[id(comp)][child_name] 20 | 21 | def ui_comp_get_root_from_child(child, compname): 22 | for component in _ui_comp_table: 23 | if _ui_comp_table[component]["_CompName"]==compname: 24 | for part in _ui_comp_table[component]: 25 | if id(_ui_comp_table[component][part]) == id(child): 26 | return _ui_comp_table[component] 27 | return None 28 | def SetBarProperty(target, id, val): 29 | if id == 'Value_with_anim': target.set_value(val, lv.ANIM.ON) 30 | if id == 'Value': target.set_value(val, lv.ANIM.OFF) 31 | return 32 | 33 | def SetPanelProperty(target, id, val): 34 | if id == 'Position_X': target.set_x(val) 35 | if id == 'Position_Y': target.set_y(val) 36 | if id == 'Width': target.set_width(val) 37 | if id == 'Height': target.set_height(val) 38 | return 39 | 40 | def SetDropdownProperty(target, id, val): 41 | if id == 'Selected': 42 | target.set_selected(val) 43 | return 44 | 45 | def SetImageProperty(target, id, val, val2): 46 | if id == 'Image': target.set_src(val) 47 | if id == 'Angle': target.set_angle(val2) 48 | if id == 'Zoom': target.set_zoom(val2) 49 | return 50 | 51 | def SetLabelProperty(target, id, val): 52 | if id == 'Text': target.set_text(val) 53 | return 54 | 55 | def SetRollerProperty(target, id, val): 56 | if id == 'Selected': 57 | target.set_selected(val, lv.ANIM.OFF) 58 | if id == 'Selected_with_anim': 59 | target.set_selected(val, lv.ANIM.ON) 60 | return 61 | 62 | def SetSliderProperty(target, id, val): 63 | if id == 'Value_with_anim': target.set_value(val, lv.ANIM.ON) 64 | if id == 'Value': target.set_value(val, lv.ANIM.OFF) 65 | return 66 | 67 | def ChangeScreen( src, fademode, speed, delay): 68 | lv.scr_load_anim(src, fademode, speed, delay, False) 69 | return 70 | 71 | def IncrementArc( trg, val): 72 | trg.set_value(trg.get_value()+val) 73 | return 74 | 75 | def IncrementBar( trg, val, anim): 76 | trg.set_value(trg.get_value()+val,anim) 77 | return 78 | 79 | def IncrementSlider( trg, val, anim): 80 | trg.set_value(trg.get_value()+val,anim) 81 | return 82 | 83 | def ModifyFlag( obj, flag, value): 84 | if (value=="TOGGLE"): 85 | if ( obj.has_flag(flag) ): 86 | obj.clear_flag(flag) 87 | else: 88 | obj.add_flag(flag) 89 | return 90 | 91 | if (value=="ADD"): 92 | obj.add_flag(flag) 93 | else: 94 | obj.clear_flag(flag) 95 | return 96 | 97 | def ModifyState( obj, state, value): 98 | if (value=="TOGGLE"): 99 | if ( obj.has_state(state) ): 100 | obj.clear_state(state) 101 | else: 102 | obj.add_state(state) 103 | return 104 | 105 | if (value=="ADD"): 106 | obj.add_state(state) 107 | else: 108 | obj.clear_state(state) 109 | return 110 | 111 | def set_opacity(obj, v): 112 | obj.set_style_opa(v, lv.STATE.DEFAULT|lv.PART.MAIN) 113 | return 114 | 115 | def SetTextValueArc( trg, src, prefix, postfix): 116 | trg.set_text(prefix+str(src.get_value())+postfix) 117 | return 118 | 119 | def SetTextValueSlider( trg, src, prefix, postfix): 120 | trg.set_text(prefix+str(src.get_value())+postfix) 121 | return 122 | 123 | def SetTextValueChecked( trg, src, txton, txtoff): 124 | if src.has_state(lv.STATE.CHECKED): 125 | trg.set_text(txton) 126 | else: 127 | trg.set_text(txtoff) 128 | return 129 | -------------------------------------------------------------------------------- /firmware/wt32-sc01-firmware-fontsize14-16-30.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thanh01pmt/esp32-wt32-sc01-micropython-lvgl-squareline/31d85c1e7e04e2c06796da16a72daf52b464c31c/firmware/wt32-sc01-firmware-fontsize14-16-30.bin -------------------------------------------------------------------------------- /micropython-lvgl-squareline-examples/easy-custom-font-size.py: -------------------------------------------------------------------------------- 1 | import lvgl as lv 2 | 3 | from ili9XXX import * 4 | from ft6x36 import ft6x36 5 | 6 | disp = st7796(invert=True, hybrid=True, double_buffer=True, rot=REVERSE_LANDSCAPE) #Dark Theme 7 | #disp = st7796(invert=False) #Light Theme 8 | #disp.init() 9 | 10 | touch = ft6x36(sda=18, scl=19, width=320, height=480, inv_x=False, inv_y=True, swap_xy=True) 11 | 12 | 13 | # screen1 = lv.obj(lv.scr_act()) 14 | # screen1.set_size(480, 320) 15 | # label1 = lv.label(screen1) 16 | # label1.align(lv.ALIGN.CENTER,0,0) 17 | # label1.set_text("Hello LVGL") 18 | 19 | 20 | dispp = lv.disp_get_default() 21 | theme = lv.theme_default_init(dispp, lv.palette_main(lv.PALETTE.BLUE), lv.palette_main(lv.PALETTE.RED), True, lv.font_default()) 22 | dispp.set_theme(theme) 23 | 24 | def SetFlag( obj, flag, value): 25 | if (value): 26 | obj.add_flag(flag) 27 | else: 28 | obj.clear_flag(flag) 29 | return 30 | 31 | _ui_comp_table = {} 32 | _ui_comp_prev = None 33 | _ui_name_prev = None 34 | _ui_child_prev = None 35 | _ui_comp_table.clear() 36 | 37 | def _ui_comp_del_event(e): 38 | target = e.get_target() 39 | _ui_comp_table[id(target)].remove() 40 | 41 | def ui_comp_get_child(comp, child_name): 42 | return _ui_comp_table[id(comp)][child_name] 43 | 44 | def ui_comp_get_root_from_child(child, compname): 45 | for component in _ui_comp_table: 46 | if _ui_comp_table[component]["_CompName"]==compname: 47 | for part in _ui_comp_table[component]: 48 | if id(_ui_comp_table[component][part]) == id(child): 49 | return _ui_comp_table[component] 50 | return None 51 | def SetBarProperty(target, id, val): 52 | if id == 'Value_with_anim': target.set_value(val, lv.ANIM.ON) 53 | if id == 'Value': target.set_value(val, lv.ANIM.OFF) 54 | return 55 | 56 | def SetPanelProperty(target, id, val): 57 | if id == 'Position_X': target.set_x(val) 58 | if id == 'Position_Y': target.set_y(val) 59 | if id == 'Width': target.set_width(val) 60 | if id == 'Height': target.set_height(val) 61 | return 62 | 63 | def SetDropdownProperty(target, id, val): 64 | if id == 'Selected': 65 | target.set_selected(val) 66 | return 67 | 68 | def SetImageProperty(target, id, val, val2): 69 | if id == 'Image': target.set_src(val) 70 | if id == 'Angle': target.set_angle(val2) 71 | if id == 'Zoom': target.set_zoom(val2) 72 | return 73 | 74 | def SetLabelProperty(target, id, val): 75 | if id == 'Text': target.set_text(val) 76 | return 77 | 78 | def SetRollerProperty(target, id, val): 79 | if id == 'Selected': 80 | target.set_selected(val, lv.ANIM.OFF) 81 | if id == 'Selected_with_anim': 82 | target.set_selected(val, lv.ANIM.ON) 83 | return 84 | 85 | def SetSliderProperty(target, id, val): 86 | if id == 'Value_with_anim': target.set_value(val, lv.ANIM.ON) 87 | if id == 'Value': target.set_value(val, lv.ANIM.OFF) 88 | return 89 | 90 | def ChangeScreen( src, fademode, speed, delay): 91 | lv.scr_load_anim(src, fademode, speed, delay, False) 92 | return 93 | 94 | def IncrementArc( trg, val): 95 | trg.set_value(trg.get_value()+val) 96 | return 97 | 98 | def IncrementBar( trg, val, anim): 99 | trg.set_value(trg.get_value()+val,anim) 100 | return 101 | 102 | def IncrementSlider( trg, val, anim): 103 | trg.set_value(trg.get_value()+val,anim) 104 | return 105 | 106 | def ModifyFlag( obj, flag, value): 107 | if (value=="TOGGLE"): 108 | if ( obj.has_flag(flag) ): 109 | obj.clear_flag(flag) 110 | else: 111 | obj.add_flag(flag) 112 | return 113 | 114 | if (value=="ADD"): 115 | obj.add_flag(flag) 116 | else: 117 | obj.clear_flag(flag) 118 | return 119 | 120 | def ModifyState( obj, state, value): 121 | if (value=="TOGGLE"): 122 | if ( obj.has_state(state) ): 123 | obj.clear_state(state) 124 | else: 125 | obj.add_state(state) 126 | return 127 | 128 | if (value=="ADD"): 129 | obj.add_state(state) 130 | else: 131 | obj.clear_state(state) 132 | return 133 | 134 | def set_opacity(obj, v): 135 | obj.set_style_opa(v, lv.STATE.DEFAULT|lv.PART.MAIN) 136 | return 137 | 138 | def SetTextValueArc( trg, src, prefix, postfix): 139 | trg.set_text(prefix+str(src.get_value())+postfix) 140 | return 141 | 142 | def SetTextValueSlider( trg, src, prefix, postfix): 143 | trg.set_text(prefix+str(src.get_value())+postfix) 144 | return 145 | 146 | def SetTextValueChecked( trg, src, txton, txtoff): 147 | if src.has_state(lv.STATE.CHECKED): 148 | trg.set_text(txton) 149 | else: 150 | trg.set_text(txtoff) 151 | return 152 | 153 | # COMPONENTS 154 | 155 | # COMPONENT ButtonClickMe 156 | def ui_ButtonClickMe_create(comp_parent): 157 | cui_ButtonClickMe = lv.btn(comp_parent) 158 | cui_ButtonClickMe.set_width(100) 159 | cui_ButtonClickMe.set_height(50) 160 | cui_ButtonClickMe.set_x(3) 161 | cui_ButtonClickMe.set_y(45) 162 | cui_ButtonClickMe.set_align( lv.ALIGN.CENTER) 163 | SetFlag(cui_ButtonClickMe, lv.obj.FLAG.SCROLLABLE, False) 164 | SetFlag(cui_ButtonClickMe, lv.obj.FLAG.SCROLL_ON_FOCUS, True) 165 | _ui_comp_table[id(cui_ButtonClickMe)]= {"ButtonClickMe" : cui_ButtonClickMe, "_CompName" : "ButtonClickMe"} 166 | return cui_ButtonClickMe 167 | 168 | def ButtonToggleMsg_eventhandler(event_struct): 169 | event = event_struct.code 170 | if event == lv.EVENT.CLICKED and True: 171 | SetLabelProperty(ui_LabelMsg, 'Text', 'SquareLine') 172 | return 173 | 174 | ui_Screen1 = lv.obj() 175 | SetFlag(ui_Screen1, lv.obj.FLAG.SCROLLABLE, False) 176 | 177 | ui_LabelMsg = lv.label(ui_Screen1) 178 | ui_LabelMsg.set_text("Hello MicroPython") 179 | ui_LabelMsg.set_width(lv.SIZE_CONTENT) # 1 180 | ui_LabelMsg.set_height(lv.SIZE_CONTENT) # 1 181 | ui_LabelMsg.set_x(0) 182 | ui_LabelMsg.set_y(-20) 183 | ui_LabelMsg.set_align( lv.ALIGN.CENTER) 184 | ui_LabelMsg.set_style_text_font( lv.font_montserrat_14, lv.PART.MAIN | lv.STATE.DEFAULT ) 185 | 186 | ui_ButtonToggleMsg = lv.btn(ui_Screen1) 187 | ui_ButtonToggleMsg.set_width(100) 188 | ui_ButtonToggleMsg.set_height(50) 189 | ui_ButtonToggleMsg.set_x(-9) 190 | ui_ButtonToggleMsg.set_y(47) 191 | ui_ButtonToggleMsg.set_align( lv.ALIGN.CENTER) 192 | SetFlag(ui_ButtonToggleMsg, lv.obj.FLAG.SCROLLABLE, False) 193 | SetFlag(ui_ButtonToggleMsg, lv.obj.FLAG.SCROLL_ON_FOCUS, True) 194 | 195 | ui_LabelToggleMsg = lv.label(ui_ButtonToggleMsg) 196 | ui_LabelToggleMsg.set_text("Next") 197 | ui_LabelToggleMsg.set_width(lv.SIZE_CONTENT) # 1 198 | ui_LabelToggleMsg.set_height(lv.SIZE_CONTENT) # 1 199 | ui_LabelToggleMsg.set_align( lv.ALIGN.CENTER) 200 | 201 | ui_ButtonToggleMsg.add_event_cb(ButtonToggleMsg_eventhandler, lv.EVENT.ALL, None) 202 | ui_Switch1 = lv.switch(ui_Screen1) 203 | ui_Switch1.set_width(50) 204 | ui_Switch1.set_height(25) 205 | ui_Switch1.set_x(-197) 206 | ui_Switch1.set_y(-136) 207 | ui_Switch1.set_align( lv.ALIGN.CENTER) 208 | 209 | lv.scr_load(ui_Screen1) 210 | -------------------------------------------------------------------------------- /micropython-lvgl-squareline-examples/easy-toggle-button-and-message.py: -------------------------------------------------------------------------------- 1 | import lvgl as lv 2 | 3 | from ili9XXX import * 4 | from ft6x36 import ft6x36 5 | 6 | disp = st7796(invert=True, hybrid=True, double_buffer=True, rot=REVERSE_LANDSCAPE) #Dark Theme 7 | #disp = st7796(invert=False) #Light Theme 8 | #disp.init() 9 | 10 | touch = ft6x36(sda=18, scl=19, width=320, height=480, inv_x=False, inv_y=True, swap_xy=True) 11 | 12 | 13 | # screen1 = lv.obj(lv.scr_act()) 14 | # screen1.set_size(480, 320) 15 | # label1 = lv.label(screen1) 16 | # label1.align(lv.ALIGN.CENTER,0,0) 17 | # label1.set_text("Hello LVGL") 18 | 19 | 20 | dispp = lv.disp_get_default() 21 | theme = lv.theme_default_init(dispp, lv.palette_main(lv.PALETTE.RED), lv.palette_main(lv.PALETTE.BLUE), True, lv.font_default()) 22 | dispp.set_theme(theme) 23 | 24 | def SetFlag( obj, flag, value): 25 | if (value): 26 | obj.add_flag(flag) 27 | else: 28 | obj.clear_flag(flag) 29 | return 30 | 31 | _ui_comp_table = {} 32 | _ui_comp_prev = None 33 | _ui_name_prev = None 34 | _ui_child_prev = None 35 | _ui_comp_table.clear() 36 | 37 | def _ui_comp_del_event(e): 38 | target = e.get_target() 39 | _ui_comp_table[id(target)].remove() 40 | 41 | def ui_comp_get_child(comp, child_name): 42 | return _ui_comp_table[id(comp)][child_name] 43 | 44 | def ui_comp_get_root_from_child(child, compname): 45 | for component in _ui_comp_table: 46 | if _ui_comp_table[component]["_CompName"]==compname: 47 | for part in _ui_comp_table[component]: 48 | if id(_ui_comp_table[component][part]) == id(child): 49 | return _ui_comp_table[component] 50 | return None 51 | def SetBarProperty(target, id, val): 52 | if id == 'Value_with_anim': target.set_value(val, lv.ANIM.ON) 53 | if id == 'Value': target.set_value(val, lv.ANIM.OFF) 54 | return 55 | 56 | def SetPanelProperty(target, id, val): 57 | if id == 'Position_X': target.set_x(val) 58 | if id == 'Position_Y': target.set_y(val) 59 | if id == 'Width': target.set_width(val) 60 | if id == 'Height': target.set_height(val) 61 | return 62 | 63 | def SetDropdownProperty(target, id, val): 64 | if id == 'Selected': 65 | target.set_selected(val) 66 | return 67 | 68 | def SetImageProperty(target, id, val, val2): 69 | if id == 'Image': target.set_src(val) 70 | if id == 'Angle': target.set_angle(val2) 71 | if id == 'Zoom': target.set_zoom(val2) 72 | return 73 | 74 | def SetLabelProperty(target, id, val): 75 | if id == 'Text': target.set_text(val) 76 | return 77 | 78 | def SetRollerProperty(target, id, val): 79 | if id == 'Selected': 80 | target.set_selected(val, lv.ANIM.OFF) 81 | if id == 'Selected_with_anim': 82 | target.set_selected(val, lv.ANIM.ON) 83 | return 84 | 85 | def SetSliderProperty(target, id, val): 86 | if id == 'Value_with_anim': target.set_value(val, lv.ANIM.ON) 87 | if id == 'Value': target.set_value(val, lv.ANIM.OFF) 88 | return 89 | 90 | def ChangeScreen( src, fademode, speed, delay): 91 | lv.scr_load_anim(src, fademode, speed, delay, False) 92 | return 93 | 94 | def IncrementArc( trg, val): 95 | trg.set_value(trg.get_value()+val) 96 | return 97 | 98 | def IncrementBar( trg, val, anim): 99 | trg.set_value(trg.get_value()+val,anim) 100 | return 101 | 102 | def IncrementSlider( trg, val, anim): 103 | trg.set_value(trg.get_value()+val,anim) 104 | return 105 | 106 | def ModifyFlag( obj, flag, value): 107 | if (value=="TOGGLE"): 108 | if ( obj.has_flag(flag) ): 109 | obj.clear_flag(flag) 110 | else: 111 | obj.add_flag(flag) 112 | return 113 | 114 | if (value=="ADD"): 115 | obj.add_flag(flag) 116 | else: 117 | obj.clear_flag(flag) 118 | return 119 | 120 | def ModifyState( obj, state, value): 121 | if (value=="TOGGLE"): 122 | if ( obj.has_state(state) ): 123 | obj.clear_state(state) 124 | else: 125 | obj.add_state(state) 126 | return 127 | 128 | if (value=="ADD"): 129 | obj.add_state(state) 130 | else: 131 | obj.clear_state(state) 132 | return 133 | 134 | def set_opacity(obj, v): 135 | obj.set_style_opa(v, lv.STATE.DEFAULT|lv.PART.MAIN) 136 | return 137 | 138 | def SetTextValueArc( trg, src, prefix, postfix): 139 | trg.set_text(prefix+str(src.get_value())+postfix) 140 | return 141 | 142 | def SetTextValueSlider( trg, src, prefix, postfix): 143 | trg.set_text(prefix+str(src.get_value())+postfix) 144 | return 145 | 146 | def SetTextValueChecked( trg, src, txton, txtoff): 147 | if src.has_state(lv.STATE.CHECKED): 148 | trg.set_text(txton) 149 | else: 150 | trg.set_text(txtoff) 151 | return 152 | 153 | # COMPONENTS 154 | 155 | # COMPONENT ButtonClickMe 156 | def ui_ButtonClickMe_create(comp_parent): 157 | cui_ButtonClickMe = lv.btn(comp_parent) 158 | cui_ButtonClickMe.set_width(100) 159 | cui_ButtonClickMe.set_height(50) 160 | cui_ButtonClickMe.set_x(3) 161 | cui_ButtonClickMe.set_y(45) 162 | cui_ButtonClickMe.set_align( lv.ALIGN.CENTER) 163 | SetFlag(cui_ButtonClickMe, lv.obj.FLAG.SCROLLABLE, False) 164 | SetFlag(cui_ButtonClickMe, lv.obj.FLAG.SCROLL_ON_FOCUS, True) 165 | _ui_comp_table[id(cui_ButtonClickMe)]= {"ButtonClickMe" : cui_ButtonClickMe, "_CompName" : "ButtonClickMe"} 166 | return cui_ButtonClickMe 167 | 168 | def ButtonToggleMsg_eventhandler(event_struct): 169 | event = event_struct.code 170 | if (event == lv.EVENT.CLICKED) and True: 171 | if (ui_LabelMsg.get_text() == 'Hello Arduino'): 172 | SetLabelProperty(ui_LabelMsg, 'Text', 'Hello SquareLine') 173 | else: 174 | SetLabelProperty(ui_LabelMsg, 'Text', 'Hello Arduino') 175 | return 176 | 177 | ui_Screen1 = lv.obj() 178 | SetFlag(ui_Screen1, lv.obj.FLAG.SCROLLABLE, False) 179 | 180 | ui_LabelMsg = lv.label(ui_Screen1) 181 | ui_LabelMsg.set_text("Hello MicroPython") 182 | ui_LabelMsg.set_width(lv.SIZE_CONTENT) # 1 183 | ui_LabelMsg.set_height(lv.SIZE_CONTENT) # 1 184 | ui_LabelMsg.set_x(0) 185 | ui_LabelMsg.set_y(-20) 186 | ui_LabelMsg.set_align( lv.ALIGN.CENTER) 187 | ui_LabelMsg.set_style_text_font( lv.font_montserrat_16, lv.PART.MAIN | lv.STATE.DEFAULT ) 188 | 189 | ui_ButtonToggleMsg = lv.btn(ui_Screen1) 190 | ui_ButtonToggleMsg.set_width(100) 191 | ui_ButtonToggleMsg.set_height(50) 192 | ui_ButtonToggleMsg.set_x(-9) 193 | ui_ButtonToggleMsg.set_y(47) 194 | ui_ButtonToggleMsg.set_align( lv.ALIGN.CENTER) 195 | SetFlag(ui_ButtonToggleMsg, lv.obj.FLAG.SCROLLABLE, False) 196 | SetFlag(ui_ButtonToggleMsg, lv.obj.FLAG.SCROLL_ON_FOCUS, True) 197 | 198 | ui_LabelToggleMsg = lv.label(ui_ButtonToggleMsg) 199 | ui_LabelToggleMsg.set_text("Next") 200 | ui_LabelToggleMsg.set_width(lv.SIZE_CONTENT) # 1 201 | ui_LabelToggleMsg.set_height(lv.SIZE_CONTENT) # 1 202 | ui_LabelToggleMsg.set_align( lv.ALIGN.CENTER) 203 | 204 | ui_ButtonToggleMsg.add_event_cb(ButtonToggleMsg_eventhandler, lv.EVENT.ALL, None) 205 | 206 | lv.scr_load(ui_Screen1) 207 | 208 | 209 | --------------------------------------------------------------------------------