Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OK folks, I need an accounting of what problems still exist. #65

Open
kdschlosser opened this issue Jun 23, 2024 · 83 comments
Open

OK folks, I need an accounting of what problems still exist. #65

kdschlosser opened this issue Jun 23, 2024 · 83 comments

Comments

@kdschlosser
Copy link
Collaborator

This is what I know still has an issue.

  • ST display drivers have issue with the rotation being out of sorts. This is causing the text to display backwards. I have to do a little bit more research on this issue. as per the data sheet the rotation I have set for it should be correct but for some reason it is not.

  • There were reports of the REPL not working, This is not happening for everyone and the people that are having the issue have a common thing. They are using Thonny. Thonny is known to cause problems and I recommend not using it. Download a program called Putty if you are running on Windows. I know this work properly when connecting to the ESP32. The other possibility could be with MicroPython and using the built in USB for the USB instead of the UART. I would need more information to confirm what MCU is being used and what the MicroPython default settings are to nail down what the issue might be.

  • There was a report of the TaskHandler causing the MCU to freeze. I want to say this was with the RGBBus. The Freeze should now be fixed but the RGBBus driver is still not working. I am in the process of sorting this one out. I am really wanting a way for LVGL to exit the refresh instead of sitting there spinning it's wheels waiting for the buffer to finish flushing. This is a complex issue I am trying to come up with the best way to handle it.

  • The touch screen calibration is not working. I have not done anything as of yet to get this sorted out. It is on my TODO list.

  • The Unix port crashes if the display window is resized. This is a work in progress. I made some changes locally but I have not tested them yet. I was attempting to reallocate the buffers and that would cause a crash. I don't recall what the exact error was but it some something that didn't help to isolate the issue. What my local code does is it frees the existing frame buffer and then allocates a new buffer. I am going to change the default heap size to 8mb. Right now it is at 4mb which is not going to be anywhere near enough if a display is created that is larger than 800x600x24 using double buffering. A single buffer for a display size of 1024x768x24 is 2,334,720 bytes.

@ste7anste7an
Copy link

I just pulled the latest version (commit ee1bdbc ) from github. For non SPIRAM it comples fine, but when using SPIRAM I get the IRAM error:

 python3 make.py esp32 submodules clean mpy_cross BOARD=ESP32_GENERIC BOARD_VARIANT=SPIRAM DISPLAY=ili9341 INDEV=xpt2046 LV_CFLAGS="-DLV_COLOR_DEPTH=16"

with this error:

 cat /home/stefan/projects/esp32/lvgl_micropython/lib/micropython/ports/esp32/build-ESP32_GENERIC-SPIRAM/log/idf_py_stderr_output_14003
make[1]: warning: -j6 forced in submake: resetting jobserver mode.
/home/stefan/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld: micropython.elf section `.iram0.text' will not fit in region `iram0_0_seg'
/home/stefan/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld: IRAM0 segment data does not fit.
/home/stefan/.espressif/tools/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin/../lib/gcc/xtensa-esp-elf/13.2.0/../../../../xtensa-esp-elf/bin/ld: region `iram0_0_seg' overflowed by 3128 bytes
collect2: error: ld returned 1 exit status
make[3]: *** [CMakeFiles/micropython.elf.dir/build.make:5218: micropython.elf] Error 1
make[2]: *** [CMakeFiles/Makefile2:2065: CMakeFiles/micropython.elf.dir/all] Error 2
make[1]: *** [Makefile:136: all] Error 2

Is there a simple fix for this IRAM overflow error?

@kdschlosser
Copy link
Collaborator Author

do you get this error if you compile vanilla MicroPython 1.23 as well?? I think this issue might be upstream and if it is I will open a PR there.

I will clone MicroPython and see if there is an issue. Not a big deal to do,

@ste7anste7an
Copy link

That would be great. I just compiled vanilla python 1.24. Works fine with native installed esp-idf 5.0.4

@kdschlosser
Copy link
Collaborator Author

OK so it does compile for vanilla MicroPython. I wonder if there is not enough IRAM for the esp_lcd component to run. I will ask the folks over at MicroPython about squeezing more space. I am pretty sure that is what the issue is. It may end up being a case of the ESP32 not being supported or I am going to have to write custom drivers possibly. we will see what they say over there.

@ste7anste7an
Copy link

Have you tried with SPIRAM support. Without SPIRAM support your code compiles fine.

@kdschlosser
Copy link
Collaborator Author

with SPIRAM the code size is larger and there is more that is placed into IRAM.

I just pushed a commit which takes the keyboard exception and scheduling exceptions and places them into flash instead of IRAM. See if that does the trick for ya.

@kdschlosser
Copy link
Collaborator Author

kdschlosser commented Jun 23, 2024

3k in space is not a whole lot of space to try and free up. I don't know how much IRAM a function uses up and I am hoping that between the 2 things I moved it solves the issue.

Basically what I am doing is this...

This code block is in mpconfigport.h

#if !(CONFIG_IDF_TARGET_ESP32 && CONFIG_SPIRAM && CONFIG_SPIRAM_CACHE_WORKAROUND)
#define MICROPY_WRAP_MP_BINARY_OP(f) IRAM_ATTR f
#endif
#define MICROPY_WRAP_MP_EXECUTE_BYTECODE(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_LOAD_GLOBAL(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_LOAD_NAME(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_MAP_LOOKUP(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_OBJ_GET_TYPE(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_SCHED_EXCEPTION(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(f) IRAM_ATTR f

I am changing it to read..

#if !(CONFIG_IDF_TARGET_ESP32 && CONFIG_SPIRAM && CONFIG_SPIRAM_CACHE_WORKAROUND)
#define MICROPY_WRAP_MP_SCHED_KEYBOARD_INTERRUPT(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_SCHED_EXCEPTION(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_BINARY_OP(f) IRAM_ATTR f
#endif
#define MICROPY_WRAP_MP_EXECUTE_BYTECODE(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_LOAD_GLOBAL(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_LOAD_NAME(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_MAP_LOOKUP(f) IRAM_ATTR f
#define MICROPY_WRAP_MP_OBJ_GET_TYPE(f) IRAM_ATTR f

The other things I really want to leave in IRAM because if they are taken out of IRAM it could end up causing a pretty large performance hit.

@kdschlosser
Copy link
Collaborator Author

You can also change the following in the same file

#ifndef MICROPY_PY_MACHINE_I2S
#define MICROPY_PY_MACHINE_I2S              (SOC_I2S_SUPPORTED)
#endif

to read

#ifndef MICROPY_PY_MACHINE_I2S
#define MICROPY_PY_MACHINE_I2S              (0)
#endif

If you are not using I2S. There are a couple of function that MicroPython has defined using IRAM when using I2S.

@kdschlosser
Copy link
Collaborator Author

You can also change the following code in the same file

#define MICROPY_PY_MACHINE_BITSTREAM        (1)

to

#define MICROPY_PY_MACHINE_BITSTREAM        (0)

if you are not using the cycle counter in MicroPython (machine.bitstream)

@ste7anste7an
Copy link

There is a typo in /lvgl_micropython/builder/esp32.py. Line 856

with open(mpconfigport, 'rb') as f:

should be

with open(mpconfigport, 'wb') as f:

I toke the branch with the changed mpconfigport.h (lines moved for IRM), disabled I2S, but I need bitstream as it is needed for NeoPixels. Still runs out of IRAM. What would be the reason the the former lvgl_micropython version did not have this problem?

@GC-RnD
Copy link

GC-RnD commented Jun 23, 2024

Hi Kevin...

Using ESP32 SPIRAM..
Built using 6/20/2024 repo
command...

python3 make.py esp32 submodules clean mpy_cross BOARD=ESP32_GENERIC BOARD_VARIANT=SPIRAM DISPLAY=ili9341 INDEV=xpt2046 --flash-size=4 --optimize-size

Build went fine !!!

Flashed to ESP32
Got REPL !

Ran the following code with no issues
Screen turns on (command to driver) but have no text 'HELLO WORLD!'


import lvgl as lv  # NOQA
import ili9341  # NOQA
import lcd_bus
from machine import SPI, Pin  # NOQA
from micropython import const  # NOQA

# Display settings
WIDTH = const(240)
HEIGHT = const(320)

# Display SPI bus settings
LCD_HOST = const(2) # I use vspi which is 
LCD_FREQ = const(400000)
LCD_MISO = const(23)
LCD_MOSI = const(19)
LCD_SCK = const(18)
LCD_CS = const(15)
LCD_DC = const(13)
LCD_BKL = const(27)

# create the SPI bus for the display
spi_bus = SPI(
    LCD_HOST,
    LCD_FREQ,
    mosi=LCD_MOSI,
    miso=LCD_MISO,
    sck=LCD_SCK
)

# create the SPI device on the bus for the display
display_bus = lcd_bus.SPIBus(
    spi_bus=spi_bus,
    dc=LCD_DC,
    freq=LCD_FREQ,
    cs=LCD_CS
)

# create the display driver
display = ili9341.ILI9341(
    data_bus=display_bus,
    display_width=WIDTH,
    display_height=HEIGHT,
    reset_pin=None,
    power_pin=None,
    backlight_pin=LCD_BKL,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True
)

# display.set_power(True)
display.set_backlight(True)
display.init()
# display.set_backlight(100)

# lv.init()
# scr = lv.obj()
# scr = lv.scr_act()
scrn = lv.screen_active()
label = lv.label(scrn)
label.set_text('HELLO WORLD!')
label.align(lv.ALIGN.CENTER, 0, 50)

# lv.scr_load(scr)
lv.refr_now(None)

Doubble checked my pins... I know, I am using VSPI
Also, leading underscores are not working with const .
I must have over looked something very simple.

by the way...
I'm the Galileo thermometer guy.

@kdschlosser
Copy link
Collaborator Author

OH Hey what's going on?

I will get you up and running as fast as I am able to.

Having the underscore prefix to a constant means it is not a variable. IE if you have a module named some_module1 and in that module you have this code

from micropython import const

_TEST = const(1)

In a second module named some_module2 you have the following code

import some_module1

print(some_module1._TEST)

You will get an attribute error.

This is because _TEST is not actually a variable so it is not accessible. Using const works in a similar manner to using the c preprocessor macro define (#define) in C code. except that it's access is limited to only the module it is declared in.

VSPI pins are defined as

MISO: 19
MOSI: 23
CLK: 18
CS: 5
WP: 22
HD: 21

It looks like you have MISO and MOSI flip flopped. Your CS pin is also wrong as well. Pin 5 is the standard pin for the CS line.

This is from the ESP-IDF

#define VSPI_IOMUX_PIN_NUM_MISO 19
#define VSPI_IOMUX_PIN_NUM_MOSI 23
#define VSPI_IOMUX_PIN_NUM_CLK  18
#define VSPI_IOMUX_PIN_NUM_CS   5
#define VSPI_IOMUX_PIN_NUM_WP   22
#define VSPI_IOMUX_PIN_NUM_HD   21

You can use display.set_backlight(100) to turn the backlight on. This is actually a better way and makes your code more portable in the event you are using a display that you can dim the backlight on.

also replace lv.refr_now(None) with the following code.

import task_handler

th = task_handler.TaskHandler()

@kdschlosser
Copy link
Collaborator Author

@ste7anste7an

You are correct. good catch!!!

@GC-RnD
Copy link

GC-RnD commented Jun 23, 2024

That was it... I swapped MO and MI
I have done this before... couldn't they picked easier names.
display.set_backlight(100) works but not with proportions with my screen.
If understand correctly you're working on touch and SD card to all coexist on one SPI bus.
My cs is 15 due to pcb layout.

Thank you very much Kevin !!!

@kdschlosser
Copy link
Collaborator Author

yes they can all exist right now.

give me a few to key out some examples for ya,

@kdschlosser
Copy link
Collaborator Author

Here is an example if the display touch and SDCard are all on the same bus,

import lcd_bus
from machine import SPI  # NOQA
from micropython import const  # NOQA

# Display settings
_WIDTH = const(240)
_HEIGHT = const(320)

# Display SPI bus settings
_SPI_HOST = const(2) # I use vspi which is 
_SPI_MISO = const(19)
_SPI_MOSI = const(23)
_SPI_SCK = const(18)

_LCD_FREQ = const(400000)
_LCD_CS = const(15)
_LCD_DC = const(13)
_LCD_BKL = const(27)

_SD_CS = const(5)  # change this
_SD_FREQ = const(10000)  # change this

_TS_CS = const(10)  # change this
_TS_FREQ = const(1000)  # change this

# create the SPI bus for the display
spi_bus = SPI(
    _SPI_HOST,
    _TS_FREQ,
    mosi=_SPI_MOSI,
    miso=_SPI_MISO,
    sck=_SPI_SCK,
    cs=_TS_CS
)


# create the SPI device on the bus for the display
display_bus = lcd_bus.SPIBus(
    spi_bus=spi_bus,
    dc=_LCD_DC,
    freq=_LCD_FREQ,
    cs=_LCD_CS
)


import lvgl as lv  # NOQA
import ili9341  # NOQA

# create the display driver
display = ili9341.ILI9341(
    data_bus=display_bus,
    display_width=_WIDTH,
    display_height=_HEIGHT,
    reset_pin=None,
    power_pin=None,
    backlight_pin=_LCD_BKL,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True
)


from machine import SDCard  # NOQA
sdcard = SDCard(
    spi_bus=spi_bus,
    cs=_SD_CS,
    freq=_SD_FREQ
)


display.init()
display.set_backlight(100)


import xpt2046  # NOQA
import task_handler  # NOQA

touch = xpt2046.XPT2046(spi_bus)

th = task_handler.TaskHandler()

scrn = lv.screen_active()
label = lv.label(scrn)
label.set_text('HELLO WORLD!')
label.align(lv.ALIGN.CENTER, 0, 50)

@kdschlosser
Copy link
Collaborator Author

I am going to very strongly suggest people use an esp32-s3 with octal flash and octal spiram. I was able to get a crap load of LVGL functions into IRAM which really steps up the performance. and I was also able to overclock both the spiram and the flash to 120Mhz (actually 240Hz because of DDR) which is a HUGE performance gain over the original 80 MHZ. I recommend adding some form of passive cooling to the ESP32 if you are going to do this.

@GC-RnD
Copy link

GC-RnD commented Jun 24, 2024

I take it there "is" a pecking order for the spi bus
whereby you can't load SD card display and touch in any order.

Trying to understand where you're going with this a little bit.
Could we create a generic spi_bus
spi_bus = SPI(2, miso=19, mosi=23, sck=18)
with no baud rate or cs

Then
display = ili9341.ILI9341(
data_bus=(spi_bus=spi_bus, cs=15, freq=400_000, dc=13)
)
touch = xpt2046.XPT2046(
data_bus=(spi_bus=spi_bus, cs=5, freq=1_000)
)
sd = SDCard(spi_bus=spi_bus, cs=14, freq=10_000)

So what is lcd_bus.SPIBus doing ??
And why the touch doesn't need a similar module.

My display background is black my text is white and my buttons are yellow.
In the past the generic setup gave me... white background black text and blue button.
Do I have a color issue?

btn_cb()
needs lv.refr_now(None) or I don't see text changes

and the response seems to be a little bit slow.
any speed tweaks?

import lvgl as lv
import ili9341
import xpt2046
import lcd_bus
import task_handler 
import os, time
from machine import SDCard, SPI  # NOQA

def scr_txt(x):
	print(x)
	try:
		if lv.screen_active() == main:
			msg_txt.set_text(x)
	except Exception as e:
		pass

def btn_cb(event):
	if event.get_code() == lv.EVENT.CLICKED:
		scr_txt('clicked')
		lv.refr_now(None)
		time.sleep(1)
		msg_txt.set_text('I feel you')
		lv.refr_now(None)
		time.sleep(2)
		msg_txt.set_text('Do Again !!')
		lv.refr_now(None)
		th

# Create the SPI bus
spi_bus = SPI(2, 1000, miso=19, mosi=23, sck=18, cs=5)

# SPI device for display
display_bus = lcd_bus.SPIBus(spi_bus=spi_bus, cs=15, freq=400_000, dc=13)
# Start display driver
display = ili9341.ILI9341(
	data_bus=display_bus,
	display_width=240,
	display_height=320,
	reset_pin=None,
	power_pin=None,
	backlight_pin=27,
	color_space=lv.COLOR_FORMAT.RGB565,
	rgb565_byte_swap=True
)

# Start touch driver
touch = xpt2046.XPT2046(spi_bus)

# Mount SD Card
try:
	sd = SDCard(spi_bus=spi_bus, cs=14, freq=10000)
	os.mount(sd, '/sd')
	scr_txt('Flash Mounted')
	scr_txt(os.listdir('/'))
except Exception as er:
	scr_txt(f'Flash Error\n{er}\nrebooting')
	time.sleep(5)
	reset()


main = lv.obj()
msg_txt = lv.label(main)
msg_txt.align(lv.ALIGN.CENTER, 0, 50)
msg_txt.set_text('Hello World !!')

btn = lv.button(main)
btn.set_size(90,40)
btn.align(lv.ALIGN.CENTER, 0, 0)
btn.add_event_cb(btn_cb, lv.EVENT.CLICKED, None)
btn_lbl = lv.label(btn)
btn_lbl.set_text("Click Me !")
btn_lbl.align(lv.ALIGN.LEFT_MID, 0, 0)

lv.screen_load(main)
display.init()
display.set_backlight(100)
th = task_handler.TaskHandler()


print(lv.display_get_screen_active())

lv.display_get_screen_active() does not give me currently loaded screen.
I get... AttributeError: 'module' object has no attribute 'display_get_screen_active'
was hoping to get main.

from lvgl...
lv_obj_t *lv_display_get_screen_active(lv_display_t *disp)
Return a pointer to the active screen on a display
Parameters:
disp -- pointer to display which active screen should be get. (NULL to use the default screen)
Returns:
pointer to the active screen object (loaded by 'lv_screen_load()')

Thanks again Kevin this is great work !!!!

@GC-RnD
Copy link

GC-RnD commented Jun 24, 2024

So went in and changed lv_conf.h
and changed #define LV_THEME_DEFAULT_DARK 0
button is still yellow.

@kdschlosser
Copy link
Collaborator Author

lv_display_get_screen_active is not at the module level. it is apart of the display you created

display = ili9341.ILI9341(
	data_bus=display_bus,
	display_width=240,
	display_height=320,
	reset_pin=None,
	power_pin=None,
	backlight_pin=27,
	color_space=lv.COLOR_FORMAT.RGB565,
	rgb565_byte_swap=True
)

scrn = display.get_screen_active()

But lets say you created the display in the main.py file and in there you imported another python module, lets call it ui.py. well you cannot import main.py form ui.py because of circular imports. So how is that handled? Like this...

this is ui.py that is imported in main.py

import ili9341

display = ili9341.ILI9341.get_default()

scrn = display.get_screen_active()

I thought of damned near everything. 😃

There are some things that I will be addressing when I eventually decide to redo the C code generator. One of those things would solve this issue.

display = ili9341.ILI9341(
	data_bus=display_bus,
	display_width=240,
	display_height=320,
	reset_pin=None,
	power_pin=None,
	backlight_pin=27,
	color_space=lv.COLOR_FORMAT.RGB565,
	rgb565_byte_swap=True
)

scrn = display.get_screen_active()

print(display  == scrn.get_display())

that evaluates to False. This is because the display that is returned by get_display is an instance of the lv_display_t structure where as display is a python wrapper around lv_display_t. Even if I sub classing lv_display_t that doesn't fix that problem either.

The reason why it doesn't work as a sub class is because of how the binding code is written. in order for it to work properly the first field in every structure in LVGL would need to be a void pointer that could be used to hold a micropython base type. That would be the only way it could be done so that things would get returned properly.

@kdschlosser
Copy link
Collaborator Author

I need to change the machine.SPI class so it is a little easier to understand what is happening. I need to make an SPIBus class and an SPIDevice class. I would ideally like to have these in the machine module but because of how it is written it is not possible to do without rewriting the machine module. That's the crappy part.

@kdschlosser
Copy link
Collaborator Author

Also. There is a reason why I have the imports all scattered through the code. This is because of memory use. You want to have as much SRAM free as possible when the frame buffers get allocated. This is even more important if using the ESP32 because there is only a small amount of DMA memory available for the ESP32. With the ESP32-S3 you can allocate SPIRAM as DMA memory, this is not possible with the ESP32. So the more you import the less chance there is of having enough DMA'able memory for running double buffering.

For convenience purposes I have written the code so the display driver will create the frame buffer(s). It will first try to create the buffers as DMA in SRAM. If that fails then it will try and create the buffers in DMA SPIRAM. If that fails then it will try and create a single buffer in SRAM and if that fails then it will try and make it in SPIRAM. The best is going to be 2 buffers that are DMA in SRAM. That is going to give the best performance.

This is a good example that shows some really advanced use. It shows how to ensure that the frame buffers get allocated in SRAM using DMA memory and double buffering. It also shows how to check for the SDCard without having to keep on rebooting the ESP32. AND it even goes into using threads and a mutex/lock to ensure that everything is going to p[lay nice nice with each other. AND it also shows you how to use timers in LVGL instead of having to make a change and then telling LVGL to update the display then pausing the program before making another change.

Click here for example code
import lcd_bus
import machine  # NOQA

# Create the SPI bus
spi_bus = machine.SPI(2, 1000, miso=19, mosi=23, sck=18, cs=5)

# SPI device for display
display_bus = lcd_bus.SPIBus(spi_bus=spi_bus, cs=15, freq=400_000, dc=13)


# doing is this way makes sure that less SRAM 
# is used prior to the frame buffers being made
# as you can see the imports for LVGL and also the display driver happen
# after the buffers get created.
fb1 = display_bus.allocate_framebuffer(240 * 320 * 2, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(240 * 320 * 2, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)


import ili9341
import lvgl as lv


# Start display driver
display = ili9341.ILI9341(
    data_bus=display_bus,
    display_width=240,
    display_height=320,
    frame_buffer1=fb1,
    frame_buffer2=fb2,
    reset_pin=None,
    power_pin=None,
    backlight_pin=27,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True
)

# you need to initilize the display 
# prior to constructing the touch driver
display.init()  


import xpt2046  # NOQA

# Start touch driver
touch = xpt2046.XPT2046(spi_bus)

# Mount SD Card
import os, time, _thread

lock = _thread.allocate_lock()

import task_handler 
th = task_handler.TaskHandler()



# overrides the calling of lv.task_handler so we can call 
# it here with a lock object
def new_handler(event, user_data):
    with lock:
        lv.task_handler()
    
    # tells task_handler.TaskHandler not to call lv.task_handler
    return False  


th.add_event_cb(new_handler, th.TASK_HANDLER_STARTED, None)



def timer2_callback(_):
    with lock:
        msg_txt.set_text('Do Again !!')
        

def timer1_callback(_):
    with lock:
        msg_txt.set_text('I feel you')
        timer2.reset()


timer1 = lv.timer_create(timer1_callback, 1000, None)
timer1.set_repeat_count(1)
timer1.set_auto_delete(False)
timer1.pause()

timer2 = lv.timer_create(timer2_callback, 2000, None)
timer2.set_repeat_count(1)
timer2.set_auto_delete(False)
timer2.pause()


def btn_cb(event):
    if event.get_code() == lv.EVENT.CLICKED:
        if not timer1.get_paused():
            timer1.pause()
            
        if not timer2.get_paused():
            timer2.pause()
        
        scr_txt('clicked')
        timer1.reset()


def scr_txt(x):
    with lock:
        try:
            if lv.screen_active() == main:
                msg_txt.set_text(x)
        except:  # NOQA
            pass
    

sd = None

def try_load_sd():
    global sd
    
    try:
        # I used the 10000 as an example. I do not know if that 
        # is what the frequency is supposed to be. That is something 
        # you will haver to check on.
        # there are also other pins like cd (Card Detect) and 
        # wp "Write Protect". so you need to see if those are available as well.
        # I have not messed about with using the SDCard in MicroPython.
        sd = machine.SDCard(spi_bus=spi_bus, cs=14, freq=10000)
        os.mount(sd, '/sd')
        return True
                
    except:  # NOQA
        return False


exit_thread = False


# this allows for the script to keep on checking 
# to see if the SD Card is available.
def run():
    while not exit_thread:
        if sd is None and try_load_sd():
            scr_txt(f'Flash Mounted\n\n{"\n".join(os.listdir("/"))}')
        
        # you can run whatever other code you want from here.
        # this will allow the repl to still function so if you nmeed to access
        # the esp32 you can do so while the program is running. You just have 
        # to remember that you cannot access anything in LVGL from the repl 
        # because you would be doing so from 2 different threads and LVGL is not
        # thread safe.
        # you can force this thread to exist from the repl by typing in  
        # exit_thread = True            
        time.sleep(1)


main = lv.obj()
msg_txt = lv.label(main)
msg_txt.align(lv.ALIGN.CENTER, 0, 50)
msg_txt.set_text('Hello World !!')

btn = lv.button(main)
btn.set_size(90, 40)
btn.align(lv.ALIGN.CENTER, 0, 0)
btn.add_event_cb(btn_cb, lv.EVENT.CLICKED, None)
btn_lbl = lv.label(btn)
btn_lbl.set_text("Click Me !")
btn_lbl.align(lv.ALIGN.LEFT_MID, 0, 0)

lv.screen_load(main)

display.set_backlight(100)

# you will notice there is a lock object. This is because 
# the task_handler.TaskHander insteance works by using an interrupt timer 
# to schedule a micropython task that gets executed in the main thread. 
# you don't want to call lv.TaskHander from the main thread while possibly
# updating an object from the thread that does the SD Card checking. 

# start the SDCard check thread
_thread.start_new_thread(run, ())

@GC-RnD
Copy link

GC-RnD commented Jun 25, 2024

Kevin... that code is giving me sensory overload !!!
Even though it is well commented, it's going to take a while to digest it.
I wasn't aware that the order of modules imported affected memory use.

it is not possible to do without rewriting the machine module. That's the crappy part.

It makes me question if MicroPython is ready for mainstream use
when a basic interface has had an issue for so dam long.

The example had an error...
Traceback (most recent call last):
File "", line 26, in
MemoryError: Unable to allocate frame buffer

I have 4mb ESP32-WROVER-E-N4R8 ...
MicroPython v1.24.0-preview.39.g411d66586.dirty on 2024-06-17; Generic ESP32 module with SPIRAM with ESP32

lv_display_get_screen_active issue
what i needed was a way to determine what screen is currently active
if lv.screen_active() == main:
is working for me now.

By the way... what is the standard button color?
Because I'm getting yellow sort of orange color.

@kdschlosser
Copy link
Collaborator Author

the N4R8 is 4 MB of flash and 8mb of ram.

The reason why you are getting the memory error is because there is not enough DMA'able memory available. I forgot to divide the buffers sizes by 10.

changes the lines that read

fb1 = display_bus.allocate_framebuffer(240 * 320 * 2, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(240 * 320 * 2, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)

to

fb1 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 10), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 10), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)

@kdschlosser
Copy link
Collaborator Author

some of the ILI series of displays for whatever reason hold the bytes in their internal GRAM reversed. so you need to change this code

display = ili9341.ILI9341(
    data_bus=display_bus,
    display_width=240,
    display_height=320,
    frame_buffer1=fb1,
    frame_buffer2=fb2,
    reset_pin=None,
    power_pin=None,
    backlight_pin=27,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True
)

to read

display = ili9341.ILI9341(
    data_bus=display_bus,
    display_width=240,
    display_height=320,
    frame_buffer1=fb1,
    frame_buffer2=fb2,
    reset_pin=None,
    power_pin=None,
    backlight_pin=27,
    color_space=lv.COLOR_FORMAT.RGB565,
    color_byte_order=ili9341.BYTE_ORDER_BGR,
    # if the colors still are not right then remove the comment marker for the next line
    # rgb565_byte_swap=True  
)

@kdschlosser
Copy link
Collaborator Author

There is no intention of changing the way the SPI works in MicroPython. It's just how they decided to code it. They decided on a design that would make the API work across all boards and use less memory and flash space instead. You can make it work the way they have it but it ends up costing more in the end. The thought process is that most people only have a single device attached to the bus. The big issue is the SDCard CANNOT work properly if there is more than one device on the bus using the ESP32 series of MCU's. It is an impossibility to get it to work properly with the way the MicroPython SPI is done.

It's fine the way it is and it works... It's a shortcoming that I was able to rectify so not a big deal.

@GC-RnD
Copy link

GC-RnD commented Jun 25, 2024

so the color is good to go...

	color_space=lv.COLOR_FORMAT.RGB565,
	color_byte_order=ili9341.BYTE_ORDER_BGR,
	rgb565_byte_swap=True

frame buffer divided... incremented by 10 up to 80

fb1 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 80), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 80), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)

still got...

Traceback (most recent call last):
  File "<stdin>", line 105, in <module>
MemoryError: Unable to allocate frame buffer

@kdschlosser
Copy link
Collaborator Author

I am wondering if the machine.SPI is chewing up all of the available SRAM and that is what is causing the allocation issue.

I am going to have to rework the machine.SPI driver so instead of needing to pass a machine.SPI instance\ it will instead take the host number.

Give me a day to hammer that out. That has got to be what is causing the problem.

Can you do one thing for me. change the buffer allocation code to read this...

try:
    fb1 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 80), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
except MemoryError:
    raise RuntimeError('Failing on first buffer')
try:
    fb2 = display_bus.allocate_framebuffer(int(240 * 320 * 2 / 80), lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_SPIRAM)
except MemoryError:
    raise RuntimeError('Failing on second buffer')

tell me if it is failing on the first or second buffer.

@GC-RnD
Copy link

GC-RnD commented Jun 26, 2024

Traceback (most recent call last):
  File "<stdin>", line 112, in <module>
RuntimeError: Failing on first buffer

I also changed display freq to 40Mhz and is running snappy now.

import lcd_bus
display_bus = lcd_bus.SPIBus(spi_bus=spi_bus, cs=15, freq=40_000_000, dc=13)

So I'm not doing any animations or needing any kind of fast screen refreshes at all.
I have a static screen that updates every 59 seconds and a couple of buttons.
Honesty I never liked having the task handler running interference in the background.
I really liked to refresh the screen when I want to.
That is why I use lv.refr_now(None)

I am having an issue with a widget..

m_box = lv.msgbox(lv.screen_active(), "So", "Hellow World Again", {"Ok", "Cancel", None}, True)

getting error...

Traceback (most recent call last):
  File "<stdin>", line 177, in <module>
TypeError: function takes 1 positional arguments but 5 were given

I posted on LVGL fourm also.

@kdschlosser
Copy link
Collaborator Author

lv.msgbox is like any other widget. You have to create the widget passing a parent or None and set other things

There are these methods to set things

m_box = lv.msgbox(lv.screen_active())
m_box.add_title("some title")
m_box.add_header_button(image_data)
m_box.add_text("Hellow World Again")
m_box.add_footer_button("OK")
m_box.add_footer_button("Cancel")
m_box.add_close_button()

@kdschlosser
Copy link
Collaborator Author

@ikkesia

I need to see the code you are running to make sure that the rotation is being done correctly. If it's not it can cause this kind of an issue. I want to make sure before I go messing about with any of the drivers.

@kdschlosser
Copy link
Collaborator Author

@marksull

what is the display IC you are using?

@ste7anste7an
Copy link

ste7anste7an commented Jul 25, 2024

This morning I tried since a long time to compile the newest firmware and test it on both my 8MB and 4MB flash SPIRAM ESP32-WROVER boards. The compilation went smooth. However, when I execute this little program:

import lvgl as lv  # NOQA
import ili9341  # NOQA
import lcd_bus
from machine import SPI, Pin  # NOQA
from micropython import const  # NOQA

# Display settings
WIDTH = const(240)
HEIGHT = const(320)

# Display SPI bus settings
LCD_HOST = const(2) # I use vspi which is 
LCD_FREQ = const(400000)
LCD_MISO = const(23)
LCD_MOSI = const(19)
LCD_SCK = const(18)
LCD_CS = const(15)
LCD_DC = const(13)
LCD_BKL = const(27)

# create the SPI bus for the display
spi_bus = SPI(
    LCD_HOST,
    LCD_FREQ,
    mosi=LCD_MOSI,
    miso=LCD_MISO,
    sck=LCD_SCK
)

I get this error:

TypeError: can't create 'SPI' instances

Also when changing the LCD_HOST to 0, or 1. On both my boards. Would it have something to do with the SPIRAM that is using a SPI controller internally?

I compiled the firmware with:

 python3 make.py esp32 submodules clean mpy_cross BOARD=ESP32_GENERIC BOARD_VARIANT=SPIRAM DISPLAY=ili9341 INDEV=xpt2046 --flash-size=8 --optimize-size

and without the --flash-size=8 for the 4MB unit.

I also compiled firmware without SPIRAM support and tested that on a normal ESP32. Same result, unfortunately.

@kdschlosser
Copy link
Collaborator Author

You are starting the SPI wrong. Take a look at the README.md file

@ste7anste7an
Copy link

ste7anste7an commented Jul 25, 2024

I must have missed these changes completely. I just copied a quite recent example code from the discussion.
Now, at least I get the display setup without error. Only... nothing shows up on the display. I tested the display with the old lvgl to check that my hardware is working (and that works).
Here is my code:

from micropython import const  # NOQA

_WIDTH = const(240) 
_HEIGHT = const(320)

_TP_FREQ = const(1_000_000)
_TP_CS = const(26)  # CHANGE THIS TO THE CS PIN FOR THE TOUCH PANEL

_SPI_HOST = const(2)
_DC_PIN = const(27)
_MOSI_PIN = const(13)
_MISO_PIN = const(12)
_SCLK_PIN = const(14)
_CS_PIN = const(15)
_RESET_PIN = const(32)
_BACKLIGHT_PIN = const(33)
_FREQ = const(4_000_000)

#import lcd_bus  # NOQA
from machine import SPI
spi_bus = SPI.Bus(host=_SPI_HOST,
                  mosi=_MOSI_PIN,
                  miso=_MISO_PIN,
                  sck=_SCLK_PIN )

spi_device = SPI.Device( spi_bus=spi_bus,
                         freq=10000000,
                         cs=_CS_PIN,
                         polarity=0,
                         phase=0,
                         bits=8 )

import lcd_bus
display_bus=lcd_bus.SPIBus(spi_bus=spi_bus,freq=10000000,cs=_CS_PIN, dc=_DC_PIN )

import lvgl as lv

import ili9341  # NOQA

display = ili9341.ILI9341(
    data_bus=display_bus,
    display_width=_WIDTH,
    display_height=_HEIGHT,
    reset_pin=_RESET_PIN,
    backlight_pin=_BACKLIGHT_PIN,
    backlight_on_state=ili9341.STATE_HIGH,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True
)

display.init()
display.set_backlight(100)


class TouchCal:
   
    def __init__(self):

        left = 0
        right = 240
        top = 0
        bottom = 360

    def save(self):
        pass


cal = TouchCal()




import xpt2046  # NOQA

indev = xpt2046.XPT2046(spi_bus, touch_cal=cal)

scrn = lv.screen_active()
scrn.set_style_bg_color(lv.color_hex(0x000000), 0)

slider = lv.slider(scrn)
slider.set_size(_WIDTH - 10, 10)
slider.center()

arc=lv.arc(lv.screen_active())
arc.set_range(0,360)
arc.set_bg_angles(0,360)
arc.set_size(150,150)
arc.align(lv.ALIGN.CENTER,0,-100)
arc.set_value(100)

scrn = lv.screen_active()
label = lv.label(scrn)
label.set_text('HELLO WORLD!')
label.align(lv.ALIGN.CENTER, 0, 50)


lv.refr_now(None) 

import task_handler  # NOQA

th = task_handler.TaskHandler()

Is there any way how I can debug things? I tried to pass parametes such as phase and polarity to the lcd_bus.SPIBus, but these arguments are not accepted. If you would have a template piece of code that should work with ILI9341 and xpt2046, I would be happy to test that.

@marksull
Copy link
Contributor

Any chance you have had an opportunity to dig into what could be causing the text to display backwards?

Ok, so I dug into this. There were only 4 commits to the ST7796 module from the point on where the text started showing backwards and all your changes were related to the orientation table. Creating a test class and through a process of trial an error, the orientation table that displayed the text direction correctly and the touch layer working in the correct direction for each of the rotations is as follows. I have no idea what I am changing so thought I would drop it here in case this is specific to my dev board WT32-SC01-Plus only.

class SpecialST7796(st7796.ST7796):
    _ORIENTATION_TABLE = (
        _MADCTL_MX,
        _MADCTL_MV | _MADCTL_MY | _MADCTL_MX,
        _MADCTL_MY,
        _MADCTL_MV
    )

@kdschlosser
Copy link
Collaborator Author

change the code and submit a PR for it. This way you get the credit for fixing it.

You were next on my list but you beat me to it,...

For some reason this display differs from the other ST displays with how the orientation table works. I don't know why that is.

@kdschlosser
Copy link
Collaborator Author

I'll take care of putting the code in and mentioning you in the PR...

kdschlosser added a commit that referenced this issue Jul 27, 2024
@kdschlosser
Copy link
Collaborator Author

OK it has been added. so you no longer need to monkey path the code.

@marksull
Copy link
Contributor

OK it has been added. so you no longer need to monkey path the code.

So, I complied with the latest and alas, I see the interface for I2CBus has changed. Previously was using:

i2c_bus = i2c.I2CBus(scl=_SCL, sda=_SDA, freq=_TP_FREQ, use_locks=False)
indev = ft6x36.FT6x36(i2c_bus)

I see that your documentation mentioned that you now split out the Bus and the Device, so I converted the above to:

i2c_bus = i2c.I2C.Bus(host=_HOST, scl=_SCL, sda=_SDA, freq=_TP_FREQ, use_locks=False)
touch_i2c = i2c.I2C.Device(i2c_bus, ft6x36.I2C_ADDR, ft6x36.BITS)
indev = ft6x36.FT6x36(touch_i2c)

I see that the Touch appears to initialize correctly:

Touch Device ID: 0x11
Touch Chip ID: 0x64
Touch Device mode: 0x00
Touch Firmware ID: 0x11
Touch Release code: 0x01

But the touch layer is not responsive. Any ideas why with the new Bus/Device interface the touch is not working?

@ste7anste7an
Copy link

ste7anste7an commented Jul 27, 2024

Got it working! I needed to add the active low reset to the display initialization. it read now:

display = ili9341.ILI9341(
    data_bus=display_bus,
    display_width=_WIDTH,
    display_height=_HEIGHT,
    reset_pin=_RESET_PIN,
    reset_state=ili9341.STATE_LOW,
    backlight_pin=_BACKLIGHT_PIN,
    backlight_on_state=ili9341.STATE_HIGH,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True
)

Now, still the touch does not working. I try to figure out what happens with the xpt2046.XPT2046.

@ste7anste7an
Copy link

ste7anste7an commented Jul 27, 2024

I found the mistake:
I have to initailaze the xpt2026 with the spi_device instead with the spi_bus:

indev = xpt2046.XPT2046(spi_device,debug=True)

Now, the callibration is still mirrored horizontally.

@ste7anste7an
Copy link

ste7anste7an commented Jul 27, 2024

I try to run indev.calibrate(). I do see the first circle and a faint smaller circle a little bit shifted, but it won't react on pressing on the touch screen, and the display does not change. It shows the text Touch Screen Calibration. I tested it on two different boards. One with an XPT2048, the other with a CST816S. The CST816S seems to be properly calibrated already and the touch works flawlesly. On the XPT2048, the touch is mirrored.

I have another weird problem. When displaying an arc with this code:

arc = lv.arc(lv.screen_active())
arc.set_end_angle(200)
arc.set_size(230, 230)
arc.center()
#arc.set_change_rate(3000)
#arc.set_range(0,400)
arc.set_style_arc_width(40,lv.PART.INDICATOR)
arc.set_style_arc_width(40,lv.PART.MAIN)

It shows an arc, but it lets me just one time change the indicator by touching it. Ataching a call back to the arc keeps working, but the indicator 'hangs' after one time chnaging it using touch. I tried the same code on an older LVGL 8.0 micropython instance, and there it works as expected. Does anyone has an explanation for this behaviour?

@kdschlosser
Copy link
Collaborator Author

don't use the calibrate for the time being. It is not yet finished. The whole calibration routine has changed and I just haven't gotten the chance to test it and correct any issue with it.

Both the SPI and the I2C has changed. I changed this because of confusion from the users on how to make things work. But most importantly the ability to share the display bus with a touch controller and also an SDCard reader is now possible where it wasn't before.

There are touch IC's made that can use say an SPI connection or an I2C connection depending on how it is wired. I didn't want to have duplicate code just to handle the type of connection. So I made the touch drivers unaware of what type of connection is being used. Because after all it shouldn't really matter what type of connection is used. In order to do that I had to make the API for I2C and also SPI be the same. Here was the tricky bit. How do I do that while retaining as much of the original MicroPython API as possible?

That is when I came up with the API that is now being used.

here is the API for SPI, I2C and SDCard.

from typing import Optional, Union, ClassVar
from typing import TYPE_CHECKING


if TYPE_CHECKING:
    import array


_BUFFER_TYPE = Union[bytearray, bytes, memoryview, array.array]


# this is in the i2c module

class I2C(object):
    class Bus(object):

        def __init__(
            self,
            host: Union[str, int],
            scl: Union[str, int],
            sda: Union[str, int],
            freq: int = 4000000,
            use_locks: bool = False
        ):
            ...

        def __enter__(self) -> "I2C.Bus":
            ...

        def __exit__(self, exc_type, exc_val, exc_tb) -> None:
            ...

        def scan(self) -> list:
            ...

        def start(self) -> None:
            ...

        def stop(self) -> None:
            ...

        def readinto(self, buf: _BUFFER_TYPE,  nack: bool = True) -> None:
            ...

        def write(self, buf: _BUFFER_TYPE) -> None:
            ...

        def readfrom(self, addr: int, nbytes: int, stop: bool = True) -> bytes:
            ...

        def readfrom_into(self, addr: int, buf: _BUFFER_TYPE, stop: bool = True) -> None:
            ...

        def writeto(self, addr: int, buf: _BUFFER_TYPE, stop: bool = True) -> None:
            ...

        def writevto(self, addr: int, vector: list, stop: bool = True) -> None:
            ...

        def readfrom_mem(self, addr: int, memaddr: int, nbytes: int, addrsize: int = 8) -> bytes:
            ...

        def readfrom_mem_into(self, addr: int, memaddr: int, buf: _BUFFER_TYPE, addrsize: int = 8) -> None:
            ...

        def writeto_mem(self, addr: int, memaddr: int, buf: _BUFFER_TYPE, addrsize: int = 8) -> None:
            ...


    class Device(object):
        _bus: "I2C.Bus" = ...
        dev_id: int = ...
        _reg_bits: int = ...

        def __init__(self, bus: "I2C.Bus", dev_id: int, reg_bits: int = 8):
            ...

        def write_readinto(self, write_buf: _BUFFER_TYPE, read_buf: _BUFFER_TYPE) -> None:
            ...

        def read_mem(self, memaddr: int, num_bytes: Optional[int] = None, buf: Optional[_BUFFER_TYPE] = None) -> Optional[bytes]:
            ...

        def write_mem(self, memaddr: int, buf: _BUFFER_TYPE):
            ...

        def read(self, nbytes: Optional[int] = None, buf: Optional[_BUFFER_TYPE] = None, stop: bool=True) -> Optional[bytes]:
            ...

        def write(self, buf: _BUFFER_TYPE, stop: bool=True) -> None:
            ...


# this is in the machine module

class SPI(object):
    MSB: ClassVar = 0
    LSB: ClassVar = 1

    class Bus(object):

        def __init__(
            self,
            host: Union[str, int],
            mosi: Union[str, int],
            miso: Union[str, int],
            sck: Union[str, int]
        ):
            ...

    class Device(object):

        def __init__(
            self,
            spi_bus: "SPI.Bus",
            freq: int,
            cs: Union[str, int],
            polarity: int = 0,
            phase: int = 0,
            firstbit: int = 0
        ):
            ...

        def write_readinto(
            self,
            write_buf: _BUFFER_TYPE,
            read_buf: _BUFFER_TYPE
        ) -> None:
            ...

        def read(
            self, nbytes: Optional[int] = None,
            buf: Optional[_BUFFER_TYPE] = None
        ) -> Optional[bytes]:
            ...

        def write(self, buf: _BUFFER_TYPE) -> None:
            ...

        
class SDCard(object):
    
    def __init__(
        self,
        slot: int = 1,
        width: int = 1,
        cd: Union[str, int] = -1,
        wp: Union[str, int] = -1,
        # these nexy 3 are if you are using an SPI card reader
        spi_bus: Optional[SPI.Bus] = None,
        cs: Union[str, int] = -1,
        freq: int = 20000000
    ):
        ...
    
    # the rest of the API is the same as the original SDCard API

@kdschlosser
Copy link
Collaborator Author

If the touch is outputting it's information correctly on startup it should be working. I am going to have to take a look see to find out what is happening. Give me a bit to do that...

@TecDroiD
Copy link

hey, long time since i've been here.. Had other business.
Biggest issue to me: documentation is a mess. I can see that there has something changed with the i2cbus.. but there's no hint how this works.
gt911 seems not to know, too...

@kdschlosser
Copy link
Collaborator Author

kdschlosser commented Aug 25, 2024

@TecDroiD

I know, I have to make some proper documentation. I have been actually working on it over the last couple of weeks. I am fixing things upstream in LVGL that will allow an actual full blown documentation to be made that would include the entire API of LVGL in MicroPython. It would also generate a complete stub file for use with VSCode or PyCharm so code completion and intellisense work properly, it would also allow the documentation to be accessible from inside of the IDE as well.

I have already started on writing the ReST files for the mechanics of the binding that includes all of the wrappers and changes made to some of the builtin parts of MicroPython.

I am not sure as to why the GT911 driver is having an issue....

Is the code you are using look like this??

import gt911
import i2c


i2c_bus = i2c.I2C.Bus(
    host=1, 
    sda=10, 
    sdl=11, 
    freq=400000
)

i2c_device = i2c.I2C.Device(
    i2c_bus,
    dev_id=gt911.I2C_ADDR,
    reg_bits=gt911.BITS
)

indev = gt911.GT911(i2c_device)

@TecDroiD
Copy link

Well, almost. except that i2c cannot find Bus or Device since they are inside the I2C class.

working around by using

from i2c import I2C as i2c

haven't tested much with it so far. i saw a slider which didn't work. Well.. going back to the former issue as soon as i have any information on that.

@kdschlosser
Copy link
Collaborator Author

sorry I goofed that code example..

it should be i2c.I2C.Bus and i2c.I2C.Device.. That's my bad...

I went back and corrected it.

@kdschlosser
Copy link
Collaborator Author

once I get around to it I will be modifying the machine.I2C class so the python wrapper will not need to be used.. I have to look at the ESP-IDF to see how it is actually supposed to work since I am willing to bet that it is broken into a bus/device organization like the SPI is.

The reason why I made the change was to keep the API similar to what is seen with the SPI. That has also changed so it is machine.SPI.Bus and machine,SPI.Device.\ This needed to be done in order to share the bus with an SDCard reader or with a display. The way that MicroPython was originally written made it so that there was a single device on a bus and in order to add a second device to the same wires you would have to initialize the bus when changing to a device you wanted to use. This was a lot of extra work that really didn't need to be done. The other thing thing is you would have to manage the CS pin as well. It would end up being a wicked hack to get it working with a display and you would loose some features in the process. It is better to use the SPI like Espressif intended it to be used..

There are people that complain about how "bloated" the ESP-IDF is but if you think about it if a user had to add all of the code to manage the SPI guess what? It is just as bloated as the SPI in the ESP-IDF. If a person only wants to use a single device and they don't need the additional code to handle multiple devices they can copy and paste the relevant parts of the SPI code to their application to allow it to work using only a single device without any of the bloat.

There is also a general misunderstanding on how it works as well. People like to keep reference to the config structures for the SPI which doesn't need to be done. The drivers copy the data out of the structure and place it into a different structure that the driver uses internally. It was done this way so a user is able to initialize a bus or a device on the bus without having to malloc a structure of the config settings. That all gets handled in the driver.

@Phil-LJZ
Copy link

I found a strange problem. This ft6x36 driver seems to only handle LV_EVENT_SHORT_CLICKED events. It doesn't seem to handle long presses, or real-time page refreshes when sliding. Why is this?

This is my device

  • esp32s3n16r8
  • ili9341
  • ft6x36

I tested it with the following code

import lcd_bus
from machine import SPI
from micropython import const

# display settings
_WIDTH = const(240)
_HEIGHT = const(320)
_MOSI = const(7)
_MISO = const(17)
_BL = const(16)
_RST = const(5)
_DC = const(6)
_CS = const(4)
_FREQ = const(60000000)
_BUFFER_SIZE = const(4000000)

#touch settings
_SCL = const(18)
_SDA = const(8)
_TP_FREQ = const(400000)

spi_bus = SPI.Bus(
    host=2,
    mosi=_MOSI,
    miso=_MISO,
    sck=15
)

display_bus = lcd_bus.SPIBus(
    spi_bus=spi_bus,
    dc=_DC,
    cs=_CS,
    freq=_FREQ,
)

fb1 = display_bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_SPIRAM)

import ili9341  # NOQA
import lvgl as lv  # NOQA

lv.init()

display = ili9341.ILI9341(
    data_bus=display_bus,
    frame_buffer1=fb1,
    frame_buffer2=fb2,
    display_width=_WIDTH,
    display_height=_HEIGHT,
    backlight_pin=_BL,
    reset_pin=_RST,
    reset_state=ili9341.STATE_LOW,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True,
)

import i2c  # NOQA
import ft6x36  # NOQA

i2c_bus = i2c.I2C.Bus(host=0, scl=_SCL, sda=_SDA, freq=_TP_FREQ, use_locks=False)
indev_i2c = i2c.I2C.Device(i2c_bus, ft6x36.I2C_ADDR, ft6x36.BITS)
indev = ft6x36.FT6x36(indev_i2c, startup_rotation=lv.DISPLAY_ROTATION._180)

display.init()
display.invert_colors()
display.set_rotation(lv.DISPLAY_ROTATION._90)
display.set_backlight(100)

import task_handler  # NOQA
th = task_handler.TaskHandler()

from machine import SDCard
import os
sdcard = SDCard(
    spi_bus=spi_bus,
    cs=9,
    freq=_FREQ
)
os.mount(sdcard, "/sdcard")

scr = lv.obj()

# Create a slider in the center of the display
slider = lv.slider(scr)
slider.align_to(slider, lv.ALIGN.OUT_BOTTOM_MID, 0, 30)
slider.center()

# Create a label below the slider
slider_label = lv.label(scr)
slider_label.set_text("..................Hello world..................")
slider_label.set_long_mode(lv.label.LONG.SCROLL_CIRCULAR)
slider_label.set_width(100)
slider_label.align_to(slider, lv.ALIGN.OUT_TOP_MID, 0, 0)

lv.screen_load(scr)

Here is a video of my testing process

87c4b1ed9d88cb388318adf6515d3845.mp4

In short, whenever I keep my finger on the display for too long, the display interrupts all animations. Why?

@Phil-LJZ
Copy link

Friends, forgive me for being a bit long-winded. I just found something interesting.

I found the file https://github.com/lvgl/lv_binding_micropython/blob/master/driver/generic/ft6x36.py ,copied its code into the ft6x36_new.py file in esp32s3, imported this module and configured it in the test code, then I tried to run it, and I was surprised to find that it met all my needs! Below is a video recording the test process.

f7a9e48e381cecdf8df8007f1dc218a7.mp4

Following is my test code

import lcd_bus
from machine import SPI
from micropython import const

# display settings
_WIDTH = const(240)
_HEIGHT = const(320)
_MOSI = const(7)
_MISO = const(17)
_BL = const(16)
_RST = const(5)
_DC = const(6)
_CS = const(4)
_FREQ = const(60000000)
_BUFFER_SIZE = const(4000000)

#touch settings
_SCL = const(18)
_SDA = const(8)
_TP_FREQ = const(4000000)

spi_bus = SPI.Bus(
    host=2,
    mosi=_MOSI,
    miso=_MISO,
    sck=15
)

display_bus = lcd_bus.SPIBus(
    spi_bus=spi_bus,
    dc=_DC,
    cs=_CS,
    freq=_FREQ,
)

fb1 = display_bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_SPIRAM)
fb2 = display_bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_SPIRAM)

import ili9341  # NOQA
import lvgl as lv  # NOQA

lv.init()

display = ili9341.ILI9341(
    data_bus=display_bus,
    frame_buffer1=fb1,
    frame_buffer2=fb2,
    display_width=_WIDTH,
    display_height=_HEIGHT,
    backlight_pin=_BL,
    reset_pin=_RST,
    reset_state=ili9341.STATE_LOW,
    color_space=lv.COLOR_FORMAT.RGB565,
    rgb565_byte_swap=True,
)

# import i2c  # NOQA
# import ft6x36  # NOQA
# 
# i2c_bus = i2c.I2C.Bus(host=0, scl=_SCL, sda=_SDA, freq=_TP_FREQ, use_locks=False)
# indev_i2c = i2c.I2C.Device(i2c_bus, ft6x36.I2C_ADDR, ft6x36.BITS)
# indev = ft6x36.FT6x36(indev_i2c, startup_rotation=lv.DISPLAY_ROTATION._180)

from ft6x36_new import ft6x36
touch = ft6x36(sda=8, scl=18, width = 240, height = 320, inv_x=True, inv_y=True, swap_xy=False)

display.init()
#display.invert_colors()
display.set_rotation(lv.DISPLAY_ROTATION._90)
display.set_backlight(100)

import task_handler  # NOQA
th = task_handler.TaskHandler()

from machine import SDCard
import os
sdcard = SDCard(
    spi_bus=spi_bus,
    cs=9,
    freq=_FREQ
)
os.mount(sdcard, "/sdcard")





'''
The following code is taken from https://github.com/Phil-CN/lv_binding_micropython/tree/master/examples/advanced_demo.py.
I just changed the init_gui_esp32 method in the AdvancedDemoApplication class and replaced the content of this method with "pass".
'''






import usys as sys
sys.path.append('') # See: https://github.com/micropython/micropython/issues/6419

# See: https://pymotw.com/2/sys/tracing.html

def mp_trace(frame, event, arg):
    co = frame.f_code
    func_name = co.co_name
    func_line_no = frame.f_lineno
    func_filename = co.co_filename
    print('[%s] [%s] %s:%s' % (event, func_name, func_filename, func_line_no))
    return mp_trace

# sys.settrace(mp_trace)

import lvgl as lv
from lv_utils import event_loop

# lvgl must be initialized before any lvgl function is called or object/struct is constructed!

lv.init()

##############################################################################
# Styles
##############################################################################

class ColorStyle(lv.style_t):
    def __init__(self, color):
        super().__init__()
        self.set_bg_opa(lv.OPA.COVER)
        self.set_bg_color(lv.color_hex3(color))
        self.set_bg_grad_color(lv.color_hex3(0xFFF))
        self.set_bg_grad_dir(lv.GRAD_DIR.VER)
        self.set_bg_main_stop(0)
        self.set_bg_grad_stop(128)

class ShadowStyle(lv.style_t):
    def __init__(self):
        super().__init__()
        self.set_shadow_opa(lv.OPA.COVER)
        self.set_shadow_width(3)
        self.set_shadow_color(lv.color_hex3(0xAAA))
        self.set_shadow_offset_x(5)
        self.set_shadow_offset_y(3)
        self.set_shadow_spread(0)

# A square button with a shadow when not pressed
class ButtonStyle(lv.style_t):
    def __init__(self):
        super().__init__()
        self.set_radius(lv.dpx(8))
        self.set_shadow_opa(lv.OPA.COVER)
        self.set_shadow_width(lv.dpx(10))
        self.set_shadow_color(lv.color_hex3(0xAAA))
        self.set_shadow_offset_x(lv.dpx(10))
        self.set_shadow_offset_y(lv.dpx(10))
        self.set_shadow_spread(0)

class ButtonPressedStyle(lv.style_t):
    def __init__(self):
        super().__init__()
        self.set_shadow_offset_x(lv.dpx(0))
        self.set_shadow_offset_y(lv.dpx(0))


##############################################################################
# Themes
##############################################################################

class AdvancedDemoTheme(lv.theme_t):

    def __init__(self):
        super().__init__()
        self.button_style = ButtonStyle()
        self.button_pressed_style = ButtonPressedStyle()

        # This theme is based on active theme (material)
        base_theme = lv.theme_get_from_obj(lv.screen_active())

        # This theme will be applied only after base theme is applied
        self.set_parent(base_theme)

        # Set the "apply" callback of this theme to our custom callback
        self.set_apply_cb(self.apply)

        # Activate this theme on default display
        lv.display_get_default().set_theme(self)
    
    def apply(self, theme, obj):
        if obj.get_class() == lv.button_class:
            obj.add_style(self.button_style, lv.PART.MAIN)
            obj.add_style(self.button_pressed_style, lv.PART.MAIN | lv.STATE.PRESSED)

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

member_name_cache = {}

def get_member_name(obj, value):
    try:
        return member_name_cache[id(obj)][id(value)]
    except KeyError:
        pass

    for member in dir(obj):
        if getattr(obj, member) == value:
            try:
                member_name_cache[id(obj)][id(value)] = member
            except KeyError:
                member_name_cache[id(obj)] = {id(value): member}
            return member


class SymbolButton(lv.button):
    def __init__(self, parent, symbol, text):
        super().__init__(parent)
        self.symbol = lv.label(self)
        self.symbol.set_text(symbol)
        self.label = lv.label(self)
        self.label.set_text(text)
        self.set_flex_flow(lv.FLEX_FLOW.COLUMN)
        self.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER)


class Page_Buttons:
    def __init__(self, app, page):
        self.app = app
        self.page = page
        self.button_event_count = {'Play': 0, 'Pause': 0}

        self.page.set_flex_flow(lv.FLEX_FLOW.ROW)
        self.page.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START)

        self.button1 = SymbolButton(page, lv.SYMBOL.PLAY, "Play")
        self.button1.set_size(80, 80)

        self.button2 = SymbolButton(page, lv.SYMBOL.PAUSE, "Pause")
        self.button2.set_size(80, 80)

        self.label = lv.label(page)
        self.label.add_flag(lv.obj.FLAG.IGNORE_LAYOUT)
        self.label.align(lv.ALIGN.BOTTOM_LEFT, 0, 0)

        def button_cb(event, name):
            self.button_event_count[name] += 1
            event_name = get_member_name(lv.EVENT, event.code)
            if all((not event_name.startswith(s)) for s in ['DRAW', 'GET', 'STYLE', 'REFR']):
                self.label.set_text('[%d] %s %s' % (self.button_event_count[name], name, event_name))

        for button, name in [(self.button1, 'Play'), (self.button2, 'Pause')]:
            button.add_event_cb(lambda event, button_name=name: button_cb(event, button_name), lv.EVENT.ALL, None)


class Page_Simple:
    def __init__(self, app, page):
        self.app = app
        self.page = page
        self.test_events = []

        self.page.set_flex_flow(lv.FLEX_FLOW.COLUMN)
        self.page.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER)

        # slider
        self.slider = lv.slider(page)
        self.slider.set_width(lv.pct(80))
        self.slider_label = lv.label(page)
        self.slider.add_event_cb(self.on_slider_changed, lv.EVENT.VALUE_CHANGED, None)
        self.on_slider_changed(None)

        # style selector
        self.styles = [('Gray', ColorStyle(0xCCC)),
                       ('Red', ColorStyle(0xF00)), 
                       ('Green',ColorStyle(0x0F0)),
                       ('Blue', ColorStyle(0x00F))] 
    
        self.style_selector = lv.dropdown(page)
        self.style_selector.add_style(ShadowStyle(), lv.PART.MAIN)
        self.style_selector.align(lv.ALIGN.OUT_BOTTOM_LEFT, 0, 40)
        self.style_selector.set_options('\n'.join(x[0] for x in self.styles))
        self.style_selector.add_event_cb(self.on_style_selector_changed, lv.EVENT.VALUE_CHANGED, None)

        # counter button
        self.counter_button = lv.button(page)
        self.counter_button.set_size(80,80)
        self.counter_label = lv.label(self.counter_button)
        self.counter_label.set_text("Count")
        self.counter_label.align(lv.ALIGN.CENTER, 0, 0)
        self.counter_button.add_event_cb(self.on_counter_button, lv.EVENT.CLICKED, None)
        self.counter = 0

    def on_slider_changed(self, event):
        self.slider_label.set_text(str(self.slider.get_value()))

    def on_style_selector_changed(self, event):
        selected = self.style_selector.get_selected()
        tabview = self.app.screen_main.tabview
        if hasattr(self, 'selected_style'): tabview.remove_style(self.selected_style, lv.PART.MAIN)
        self.selected_style = self.styles[selected][1]
        tabview.add_style(self.selected_style, lv.PART.MAIN)

    def on_counter_button(self, event):
        self.counter += 1
        self.counter_label.set_text(str(self.counter))

class Anim(lv.anim_t):
    def __init__(self, obj, val, size, exec_cb, path_cb, time=500, playback=False, completed_cb=None):
        super().__init__()
        self.init()
        self.set_time(time)
        self.set_values(val, val + size)
        if callable(exec_cb):
            self.set_custom_exec_cb(exec_cb)
        else:
            self.set_exec_cb(obj, exec_cb)
        self.set_path_cb(path_cb)
        if playback:
            self.set_playback(0)
        if completed_cb:
            self.set_completed_cb(completed_cb)
        self.start()
        

class AnimatedChart(lv.chart):
    def __init__(self, parent, val, size):
        super().__init__(parent)
        self.val = val
        self.size = size
        self.max = 2000
        self.min = 500
        self.factor = 100
        self.anim_phase1()

    def anim_phase1(self):
        self.phase1 = Anim(
            self,
            self.val,
            self.size,
            lambda a, val: self.set_range(self.AXIS.PRIMARY_Y, 0, val),
            lv.anim_t.path_ease_in,
            completed_cb=lambda a:self.anim_phase2(),
            time=(self.max * self.factor) // 100,
        )

    def anim_phase2(self):
        self.phase2 = Anim(
            self,
            self.val + self.size,
            -self.size,
            lambda a, val: self.set_range(self.AXIS.PRIMARY_Y, 0, val),
            lv.anim_t.path_ease_out,
            completed_cb=lambda a:self.anim_phase1(),
            time=(self.min * self.factor) // 100,
        )
class Page_Text:
    def __init__(self, app, page):
        self.app = app
        self.page = page
        self.page.set_flex_flow(lv.FLEX_FLOW.ROW)
        self.page.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER)
        self.ta = lv.textarea(self.page)
        self.ta.set_height(lv.pct(100))
        self.ta.set_width(lv.pct(100))

class Page_Chart:
    def __init__(self, app, page):
        self.app = app
        self.page = page
        self.page.set_flex_flow(lv.FLEX_FLOW.ROW)
        self.page.set_flex_align(lv.FLEX_ALIGN.SPACE_EVENLY, lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.CENTER)
        self.page.set_style_pad_all(10, lv.PART.MAIN)
        self.page.set_style_pad_gap(10, lv.PART.MAIN)
        self.chart = AnimatedChart(page, 100, 1000)
        self.chart.set_flex_grow(1)
        self.chart.set_height(lv.pct(100))
        self.series1 = self.chart.add_series(lv.color_hex(0xFF0000), self.chart.AXIS.PRIMARY_Y)
        self.chart.set_type(self.chart.TYPE.LINE)
        self.chart.set_style_line_width(3, lv.PART.ITEMS)
        self.chart.add_style(ColorStyle(0x055), lv.PART.ITEMS)
        self.chart.set_range(self.chart.AXIS.PRIMARY_Y, 0, 100)
        self.chart.set_point_count(10)
        self.chart.set_ext_y_array(self.series1, [10, 20, 30, 20, 10, 40, 50, 90, 95, 90])
        # self.chart.set_x_tick_texts("a\nb\nc\nd\ne", 2, lv.chart.AXIS.DRAW_LAST_TICK)
        # self.chart.set_x_tick_length(10, 5)
        # self.chart.set_y_tick_texts("1\n2\n3\n4\n5", 2, lv.chart.AXIS.DRAW_LAST_TICK)
        # self.chart.set_y_tick_length(10, 5)
        self.chart.set_div_line_count(5, 5)

        # Create a slider that controls the chart animation speed

        def on_slider_changed(event):
            self.chart.factor = self.slider.get_value()

        self.slider = lv.slider(page)
        self.slider.set_size(10, lv.pct(100))
        self.slider.set_range(10, 200)
        self.slider.set_value(self.chart.factor, 0)
        self.slider.add_event_cb(on_slider_changed, lv.EVENT.VALUE_CHANGED, None)

class Screen_Main(lv.obj):
    def __init__(self, app, *args, **kwds):
        self.app = app
        super().__init__(*args, **kwds)
        self.theme = AdvancedDemoTheme()
        self.tabview = lv.tabview(self)
        self.page_simple = Page_Simple(self.app, self.tabview.add_tab("Simple"))
        self.page_buttons = Page_Buttons(self.app, self.tabview.add_tab("Buttons"))
        self.page_text = Page_Text(self.app, self.tabview.add_tab("Text"))
        self.page_chart = Page_Chart(self.app, self.tabview.add_tab("Chart"))


class AdvancedDemoApplication:
    def init_gui_SDL(self):
        WIDTH = 480
        HEIGHT = 320
        ZOOM = 1
        FULLSCREEN = False

        self.event_loop = event_loop()
        self.disp_drv = lv.sdl_window_create(WIDTH, HEIGHT)
        self.mouse = lv.sdl_mouse_create()
        self.keyboard = lv.sdl_keyboard_create()
        self.keyboard.set_group(self.group)
        
    def init_gui_esp32(self):
        
        pass

#         # Initialize ILI9341 display
# 
#         from ili9XXX import ili9341
# 
#         # self.disp = ili9341(dc=32, cs=33, power=-1, backlight=-1)
#         self.disp = ili9341(mhz=20, dc=32, cs=33, power=-1, backlight=-1, hybrid=False, factor=8)
# 
# 
#         # Register raw resistive touch driver
# 
#         """
#         import rtch
#         self.touch = rtch.touch(xp = 32, yp = 33, xm = 25, ym = 26, touch_rail = 27, touch_sense = 33)
#         self.touch.init()
#         self.indev_drv = lv.indev_create()
#         self.indev_drv.set_type(lv.INDEV_TYPE.POINTER)
#         self.indev_drv.set_read_cb(self.touch.read)
#         """
# 
#         # Register xpt2046 touch driver
# 
#         from xpt2046 import xpt2046
# 
#         self.touch = xpt2046()

    def init_gui_stm32(self):
        import rk043fn48h as lcd

        hres = 480
        vres = 272
        color_format = lv.COLOR_FORMAT.ARGB8888

        # Register display driver
        self.event_loop = event_loop()
        lcd.init(w=hres, h=vres)
        
        draw_buf1 = lv.draw_buf_create(hres, 50, color_format, 0)
        draw_buf2 = lv.draw_buf_create(hres, 50, color_format, 0)

        self.disp_drv = lv.display_create(hres, vres)
        self.disp_drv.set_flush_cb(lcd.flush)
        self.disp_drv.set_color_format(color_format)
        self.disp_drv.set_draw_buffers(draw_buf1, draw_buf2)
        self.disp_drv.set_render_mode(lv.DISPLAY_RENDER_MODE.PARTIAL)

        # Register touch sensor
        self.indev_drv = lv.indev_create()
        self.indev_drv.set_type(lv.INDEV_TYPE.POINTER)
        self.indev_drv.set_read_cb(lcd.ts_read)

    def init_gui_rp2(self):
        import xpt2046
        import st77xx
        if sys.platform!='rp2': raise ImportError('Only works on the rp2 platform.')
        print('Using RP2 GUI')
        spi=machine.SPI(
            1,
            baudrate=24_000_000,
            polarity=0,
            phase=0,
            sck=machine.Pin(10,machine.Pin.OUT),
            mosi=machine.Pin(11,machine.Pin.OUT),
            miso=machine.Pin(12,machine.Pin.IN)
        )
        self.disp=st77xx.St7789(rot=st77xx.ST77XX_INV_LANDSCAPE,res=(240,320),spi=spi,cs=9,dc=8,bl=13,rst=15,rp2_dma=None)
        self.disp.set_backlight(100)
        self.touch=xpt2046.Xpt2046(spi=spi,cs=16,rot=xpt2046.XPT2046_INV_LANDSCAPE)

    def init_gui(self):

        self.group = lv.group_create()
        self.group.set_default()

        # Identify platform and initialize it

        if not event_loop.is_running():
            if sys.platform == 'rp2':
                self.init_gui_rp2()
            elif sys.platform == 'esp32':
                self.init_gui_esp32()
            elif sys.platform == 'linux':
                self.init_gui_SDL()
            elif sys.platform == 'stm32':
                self.init_gui_stm32()

        # Create the main screen and load it.

        self.screen_main = Screen_Main(self)
        lv.screen_load(self.screen_main)


app = AdvancedDemoApplication()
app.init_gui()

# if __name__ == '__main__':
#    while True:
#        pass

Please forgive me that it is a bit long, because from line 83 onwards I copied and pasted the entire test code from https://github.com/Phil-CN/lv_binding_micropython/tree/master/examples/advanced_demo.py. I just changed the init_gui_esp32 method in the AdvancedDemoApplication class and replaced the content of this method with "pass".

@kdschlosser
Copy link
Collaborator Author

EEE gad man.

As a suggestion, If you are going to use large block of code like that to the following..

<details>
  <summary>Click for code</summary>

code block here wrapped in the triple back ticks

</details>

I recommend doing that for any repo you are posting an issue to where you are posting a large code block. It save from having to scroll so much..

@kdschlosser
Copy link
Collaborator Author

As far as the screen updating with the FT6x36 driver goes. I believe what is happening is in version 9.1 of LVGL something might be broken. In LVGL the indev code is written so the callback could set a flag continue_reading which is supposed to keep looping until the user releases the touch. Now the thing it is also supposed to do while it is doing this is it should be refreshing the display. Apparently the looping code is working correctly but the updating of the display is not taking place. If it is working properly the benefit of it is smoother scrolling.

You can adjust this behavior by overriding the callback function for the indev driver...

import ft6x36
import i2c


class FT6x36(ft6x36.FT6x36):

    def _read(self, drv, data):
        ft6x36.FT6x36._read(self, drv, data)
        
        data.continue_reading = False
        

i2c_bus = i2c.I2C.Bus(
    host=1,
    sda=0,
    scl=0,
    freq=400000
)

i2c_device = i2c.I2C.Device(
    i2c_bus, 
    ft6x36.I2C_ADDR, 
    ft6x36.BITS
)


# and now you use the FT6x36 class that is made above 
# just like you would normally.
indev = FT6x36(i2c_device)

@kdschlosser
Copy link
Collaborator Author

I will open up an issue with regards to it not working properly.

@Phil-LJZ
Copy link

@kdschlosser

Sorry, I am not familiar with markdown syntax, but I will remember this trick, thank you.
Also thank you for your solution, I found that this solution does work in my practice.

@kdschlosser
Copy link
Collaborator Author

Hey there was a time when I didn't know about using the XML tags to do that with Github. Personally I think that code blocks above a set number of line should automatically turn into a collapsible panel. This is an improvement that should be made for Github. It should be automatic.. But it is not something they decided to add tho they did leave holes that someone found and was able to exploit for a good use.

@kdschlosser
Copy link
Collaborator Author

I did open up that issue with LVGL and it not working properly. I am going to correct the issue in LVGL and submit a PR for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants