Questions about define object and effect on memory

Hi Pete,

Your tests were so fast, Thank you so much

In my application (tabvtest) I tested it with hardware IMX or with codeblocks simulator, and it works good the reclangles are not recolored since the i don’t touch in numpad
but if i touch a numpad (any button except OKAY and Backspace) or textarea, i see the cursor moving so many object on screen is changing colors,
I tried this even with the demo widgets,
I find out that is due to the state focused of textarea

lv_obj_add_state(text_pav_num, LV_STATE_FOCUSED);

is this normal that many objects are re-rendered when textarea is focused ?
(note : i’m working with LVGL 8.0.2 in both -simulator and hardware-)

question off topic : May i ask how to use simulator in eclipse (windows) ? I use CodeBlocks and it’s very basic IDE , I followed the tutorial link and i didn’t succed, and i saw that eclipse is more recommended for Linux and Mac, i didn’t continue trying.

In my hardware the CPU Load is about 30% and there’s not much things in my application, do you have some advices to get better performances ?

Thank you so much Pete,

Best Regards

Marouane

Hi Marouane,

Is it possible for you to post a video so I can judge better what you are seeing please?

The eclipse simulator running in windows I set up along time ago so don’t really remember that well. Let me think about it and get back to you if I can remember! :slight_smile:

Kind Regards,

Pete

1 Like

Hi Marouane,

I have now rolled back my simulator and tested your application with LVGL version 8.0 and there does appear to be some level of unexplained CPU utilisation.

Before we do anything else I think it would be prudent to update everything to the latest available NXP versions. I would download the latest MCUXpresso IDE and log in to your NXP account and create a new SDK download and import it a fresh on your development system, this should give you the latest NXP LVGL and FreeRTOS ports, then add your code back to the new installation and re-test to see if it fixes the problems.

What do you think?

Cheers,

Pete

1 Like

No problem you’re welcome Marouane,

Let me know how it goes :slight_smile:

Cheers,

Pete

1 Like

Hi pete,

I did update to the latest available NXP version of MCUXpresso 11.6 and latest SDK available for my board ( containing : LVGL 8.2), and i can’t see differences in performances :frowning: , for my application i get same CPU load around 30%

here I use the widgets_demo : I noticed that in case i disable the Pixel Processing Pipeline (PXP) in lv_conf.h file the CPU load is better .

/* 1: Use PXP for CPU off-load on NXP platforms */
#define LV_USE_GPU_NXP_PXP 0
  • Using PXP : 10FPS & CPU load 43%
  • Without PXP : 14FPS & CPU load : 31%

here videos that shows two case on my screen : demo_widgets_cpu load.zip (1.7 MB)

Normally PXP is used to have better performances and less memory footprint, maybe the configuration is not good,

Do you have any advices ?

for the quote below in LVGL 8.2 still many objects are re-rendered when a textarea is in focused state

Thank you for you help Pete,

Kind regards,

Marouane

Hi Marouane,

Thank you for all the info, looking through everything, this issue appears to be similar to this one.

So I believe the NXP drivers for the IMXRT1060-EVKB board need to be updated to work in a different way for Version 8 onwards… They currently seem to be configured with direct mode disabled, full refresh using full screen double buffers. I would suggest they should run with full refresh disabled, direct mode enabled, full screen double buffered.

I have posted my code which I used to fix this issue on my own hardware platform after migrating from version 7 to version 8 of LVGL on the linked post but there hasn’t been any response from @gianlucacornacchia to mention if it was useful or not.

Here is an example of a potentially modified lvgl_support.c (15.4 KB) from the latest MCUXpresso IDE v11.6.0_8187.(Please bare in mind I can’t test this as I have no hardware so hopefully this will just help you get on the right track to solve the issue!) The required changes are hopefully as follows:

void lv_port_disp_init(void)
{
    static lv_disp_draw_buf_t disp_buf;

    lv_disp_draw_buf_init(&disp_buf, s_frameBuffer[0], s_frameBuffer[1], LCD_WIDTH * LCD_HEIGHT);

    /*-------------------------
     * Initialize your display
     * -----------------------*/
    DEMO_InitLcd();

    /*-----------------------------------
     * Register the display in LittlevGL
     *----------------------------------*/

    lv_disp_drv_init(&disp_drv); /*Basic initialization*/

    /*Set up the functions to access to your display*/

    /*Set the resolution of the display*/
    disp_drv.hor_res = LCD_WIDTH;
    disp_drv.ver_res = LCD_HEIGHT;

    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = DEMO_FlushDisplay;

    disp_drv.wait_cb = DEMO_WaitFlush;

#if LV_USE_GPU_NXP_PXP
    disp_drv.clean_dcache_cb = DEMO_CleanInvalidateCache;
#endif

    /*Set a display buffer*/
    disp_drv.draw_buf = &disp_buf;

    /* Partial refresh */
    disp_drv.full_refresh = 0;		/* CHANGE 1 */
	disp_drv.direct_mode = 1;		/* CHANGE 2 */

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}

You will need a new function to update the second full screen buffer when the buffers are being flipped by the driver something like this:

/*CHANGE 3*/
static void DEMO_UpdateDualBufffer( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *colour_p ) 
{

	lv_disp_t*	disp = _lv_refr_get_disp_refreshing();
	lv_coord_t	y, hres = lv_disp_get_hor_res(disp);
    uint16_t	a;
    lv_color_t	*buf_cpy;

    if( colour_p == disp_drv->draw_buf->buf1)
        buf_cpy = disp_drv->draw_buf->buf2;
    else
        buf_cpy = disp_drv->draw_buf->buf1;

    for(a = 0; a < disp->inv_p; a++) {
    	if(disp->inv_area_joined[a]) continue;  /* Only copy areas which aren't part of another area */
        lv_coord_t w = lv_area_get_width(&disp->inv_areas[a]);
        for(y = disp->inv_areas[a].y1; y <= disp->inv_areas[a].y2 && y < disp_drv->ver_res; y++) {
            memcpy(buf_cpy+(y * hres + disp->inv_areas[a].x1), colour_p+(y * hres + disp->inv_areas[a].x1), w * sizeof(lv_color_t));
        }
    }
}

The flush function will need to check and decide when to update the second buffer maybe something like this:

static void DEMO_FlushDisplay(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
#if defined(SDK_OS_FREE_RTOS)
    /*
     * Before new frame flushing, clear previous frame flush done status.
     */
    (void)xSemaphoreTake(s_frameSema, 0);
#endif
/*CHANGE 4*/
	if( lv_disp_flush_is_last( disp_drv ) ) {
		DCACHE_CleanInvalidateByRange((uint32_t)color_p, DEMO_FB_SIZE);
		ELCDIF_SetNextBufferAddr(LCDIF, (uint32_t)color_p);
		DEMO_UpdateDualBuffer(disp_drv, area, color_p);
	    s_framePending = true;
	}

}

I have no idea if this code will work but please give it a try hopefully if it doesn’t this is enough of a head start for you to be able to debug it on some real hardware :slight_smile:
A further enhancement once this is working would to restructure the code so LVGL doesn’t ever get blocked by the semaphore, currently I don’t know how often or how long the code blocks on the semaphore in reality as I have no hardware to test, I also don’t know enough about the NXP hardware to speculate at this time the best way to achieve this. An early iteration of my driver also used a semaphore and it proved to introduce a delay in the execution path and ultimately I managed to engineer the use of the semaphore out using a simple flag and not blocking the execution.

Let me know how you get on.

Kind Regards,

Pete

1 Like

Hi @Zebra067 ,

I have been thinking on this further and I believe you might need to do this with flush function to avoid a potential dead-lock in the driver (see the added else clause):

static void DEMO_FlushDisplay(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
#if defined(SDK_OS_FREE_RTOS)
    /*
     * Before new frame flushing, clear previous frame flush done status.
     */
    (void)xSemaphoreTake(s_frameSema, 0);
#endif
/*CHANGE 4*/
	if( lv_disp_flush_is_last( disp_drv ) ) {
		DCACHE_CleanInvalidateByRange((uint32_t)color_p, DEMO_FB_SIZE);
		ELCDIF_SetNextBufferAddr(LCDIF, (uint32_t)color_p);
		DEMO_UpdateDualBuffer(disp_drv, area, color_p);
	    s_framePending = true;
	} else lv_disp_flush_ready( disp_drv );

} 

Cheers,

Pete

1 Like

Hi Pete,

Thank you so much for your support, I have much better performances (33 FPS, 6% CPU load) than the last configuration, when it’s display something stable without animation or changing parts in screen,

in the version of LVGL (8.0) i used in last MCUXpresso SDK there were no option to direct_mode ( it wasn’t defined in structure _lv_disp_drv_t ),

Thank you, The file is not working but it’s very close helps me so much, that file lvgl_support.c is configured with the old version,
here is the original new version of lvgl_support.c (14.1 KB) from the latest MCUXpresso IDE v11.6.0_8187. :

  • there is no function DEMO_WaitFLush.
  • the implementation of function DEMO_FlushDisplay

this function i made it like this:

static void DEMO_WaitFlush(lv_disp_drv_t *disp_drv)
{
	#if defined(SDK_OS_FREE_RTOS)


	    if (xSemaphoreTake(s_frameSema, portMAX_DELAY) == pdTRUE)
	    {
	        /* IMPORTANT!!!
	         * Inform the graphics library that you are ready with the flushing*/
	        lv_disp_flush_ready(disp_drv);
	    }
	    else
	    {
	        PRINTF("Display flush failed\r\n");
	        assert(0);
	    }
	#else
	    while (s_framePending)
	    {
	    }

	    /* IMPORTANT!!!
	     * Inform the graphics library that you are ready with the flushing*/
	    lv_disp_flush_ready(disp_drv);
	#endif
}

here is the file modified with what you suggested : lvgl_support.c (16.4 KB), it’s working and have a good performances, but after a while (around 10min) of displaying the display is blocked and touchpad doesn’t work, (mcu block somewhere, and need to reset)
can you check if it’s okay in the configuration ?

I’m very grateful for your help Pete,

Thank you so much,

Best regards,

Marouane

Hi Marouane ( @Zebra067 ),

I am glad you have seen a good improvement in performance :grinning: not good you are experiencing a crash :thinking: It might be worth enabling asserts and logging for LVGL to help track down that issue?
Also I don’t totally understand your response with regard to versions of files etc. and maybe this could contribute to the crashing also. Here is the SDK I generated on the NXP website a couple of days ago for the newly installed MCUXpresso latest IDE I downloaded. Can you confirm this is the correct version for your hardware. I have imported the projects from this SDK and I have used the project named evkbmimxrt1060_lvgl_demo_widgets

If you can post the link from NXP website to share your downloaded SDK I can also check this also if that is helpful?

Kind Regards,

Pete

Hi Pete,

excuse my bad english :face_with_hand_over_mouth:
it’s going well, it’s been 3hours running without a crush, maybe a crush was caused by my debbuger (sometimes causes weird things lol)

the SDK you sent me is the same one i use (2.12.0) see the screen bellow:

maybe when you did modification in file lvgl_support.c it was the one i uploaded in comments and i worked with it before (it was related with SDK version 2.11.1)
in the new version of configuration, they don’t use disp_drv.wait_cb = DEMO_WaitFlush; it’s not defined, and the procedure DEMO_FlushDisplay was declared differently,
that’s why i wanted you to look at the code and tell me what do you think,

Another question : for the touchpanel,(the touchpanel needs to be interrogated for it gives us positions X,Y,State) is it preferable to make it work with :

  • interruption : just in case of press: we interrogate touchpanel to fill a data static variables stored by structure lv_indev_data_t that will get used by indev_drv.read_cb
  • polling : interrogate touchpanel every 30ms (communication by I2C)

i assume that polling is not a good solution we will use i2C bus and a maybe it will causes a little decrease in performances,
and in case of interrupt i don’t know how it works in the case of i2c is writing data in structure and lvgl is reading at the same time, should I use semaphore in there to protect data in case of conflict ?

which one do you think is better ?

Thank you very much Pete,

kind regards,

Zebra :slight_smile:

Hi @Zebra067 ,

Don’t apologise for your English, I can only speak English lol so I have much respect for people who can speak in multiple languages! :grinning:

I am still not convinced about your file versions, the file I sent was from the SDK installed on my machine. It doesn’t matter though if your up a running and happy with your performance that is all that matters. :slight_smile:

With regard to your touch panel, I have written and used i2c, rs232 and most recently USB touch drivers with LVGL. My usual approach is to use a FreeRTOS queue to collect touch events from with in an interrupt handler for the device. Then have LVGL call a function to read the FreeRTOS queue.

Can you tell me which drivers you are using currently, is it from the NXP SDK?
Can you say which touch screen chip you are using?

I am working on some template code for you if you can answer my questions above I will customise it as much as I can for your application.

Kind Regards,

Pete

1 Like

he @pete-pjb,

Thank you , english is the universal language, it’s important :smiley:

I’am sure that you have modified the file i sent you :stuck_out_tongue: because there’s a macro for my panel that not supported by the demo demo_panel_ampire, yes it doesn’t matter the version, i just wanted to let you know that the file you sent was not the last update

I’am using ILITEK2511, and i didn’t find a driver for this , so i use for now the driver fsl_gt911.c(included in SDK) that i adapted to make it work with my touchpanel for now, (i should write it properly later)

i use a global volatile bool variable that in my IRQ_handler i put it to true, and i wrote a condition in the part of reading data from i2c )driver if this variable is true the MCU send a command to touchpanel for reading data),
i don’t know how to explain this in a good way, but i use a global variable change value by IRQ_handler and i use this variable in my driver, i assume it’s not a good use for organization of code, and i believe that a driver must not be related to global variable of any project to make it easy for porting.

kind regards,

Zebra,

1 Like

Hi @Zebra067 ,

Here is a template which I hope will give you a good performance:

#define THREAD_STACKSIZE	1024					// This is more than enough! 
#define TOUCH_PRIORITY		tskIDLE_PRIORITY + 2	// You probably want to set this the same as LVGL

// Global or Static variables ( I have all these defined in structure to keep them together personally )
lv_indev_drv_t			indev_drv;
lv_indev_data_t			last_state;
QueueHandle_t			touch_state_q;
SemaphoreHandle_t 		TouchTaskSemaphore = NULL;


// Replacement for current lv_port_indev_init() function.
void lv_port_indev_init(void)
{
    static lv_indev_drv_t indev_drv;

    /*------------------
     * Touchpad
     * -----------------*/

    /*Initialize your touchpad */
    DEMO_InitTouch();

    /*Register a touchpad input device*/
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = &touch_read;         /*This function will be called periodically (by the LVGL) to get the touch position and state*/
    indev_drv.user_data = NULL;
    indev = lv_indev_drv_register(&indev_drv);
	pointer_state_q = xQueueCreate( 256, sizeof( lv_indev_data_t ) );		// We can queue up to 256 touch events!
	TouchTaskSemaphore = xSemaphoreCreateBinary();
    /* Add a line here to attach the receive_touch_isr( ) function to the hardware interrupt */
	xTaskCreate( (TaskFunction_t)sysmsg_task, "Touch Handler", THREAD_STACKSIZE, NULL, TOUCH_PRIORITY, NULL );

}

// Replacemnt for current DEMO_ReadTouch() function
void touch_read(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data) {

	static lv_indev_data_t		event_d_q;

	if( xQueueReceive( touch_state_q, &event_d_q, 0 ) ) {
		memcpy( data, &event_d_q, sizeof(lv_indev_data_t) );
		data->continue_reading = (uxQueueMessagesWaiting( touch_state_q ) ? pdTRUE : pdFALSE);	// If more events are available let LVGL know
#ifdef _DEBUG_INDEV_TOUCH_MOUSE_
		printf( "INDEV: %ld, %ld, %s\r\n", data->point.x, data->point.y, data->state ? "PR" : "RL" ) ;
#endif
	}
    return;
}

// Interrupt Service Handler for Ilitek Chip
void receive_touch_isr( ) {			// Attach this  interrupt handler to the hardware interrupt associated with the Ilitek chip

	static BaseType_t 	xHigherPriorityTaskWoken;

    xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR( TouchTaskSemaphore, &xHigherPriorityTaskWoken );		// This wakes the touch_task() so it can collect data.

    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
 
/* This type of task driven approach allows for defered processing of the data so you don't have to do lots of lengthy 
 * i2c processing from an interrupt handler which is obviously not desired and utilises the benefits of having an RTOS */ 
void touch_task( void *p ) {
	
	lv_coord_t		touch_x, touch_y;
	uint8_t			Touched;
	
	while( 1 ) {	// It's a task so it runs for ever...
		if( xSemaphoreTake( TouchTaskSemaphore, portMAX_DELAY ) == pdTRUE ) {
			 
			if (kStatus_Success == GT911_GetSingleTouch(&s_touchHandle, &touch_x, &touch_y)) {
				Touched = LV_INDEV_STATE_PR;
			} else {
				Touched = LV_INDEV_STATE_REL;
			}

			/*Set the last pressed coordinates*/
			last_state.point.x = touch_x * LCD_WIDTH / s_touchResolutionX;
			last_state.point.y = touch_y * LCD_HEIGHT / s_touchResolutionY;
			last_state.state = Touched;
			xQueueSend( touch_state_q, &last_state, 0 );
		 }
	}
}

I have looked at the i2c drivers and I think this will be okay with the NXP code.

They only thing I can’t spot is where the NXP driver sets the interrupt service handler for the touch chip so hopefully you can see that!

Again this code is completely uncompiled and untested as I don’t have the hardware so my apologies in advance if there are any errors!

Let me know if you have any success. Good Luck! :smiley:

Kind Regards,

Pete

1 Like

Hi @pete-pjb,

Thank you for your suggestion, I’ll try to make it work, and see if there’s better performances,
in the DEMO of NXP there’s no ISR for the touch, and it’s working in polling and communication with touchpanel is called periodically (by the LVGL) to get the touch position and state by processing the i2C,
For me i Added an ISR_Handler and all what does is put the g_inputSignal to true, and it’s not processing i2C in isr_handler :

extern volatile bool g_InputSignal;

void GPIO_IRQHandler(void)
{
    /* clear the interrupt status */
    GPIO_PortClearInterruptFlags(BOARD_TOUCH_INT_GPIO, 1U << BOARD_TOUCH_INT_PIN);
    /* Change state of switch. */
    g_InputSignal = true;
    SDK_ISR_EXIT_BARRIER;
}

and in the function called periodically by LVGL GT911_ReadRawTouchData i made a condtition if the g_InputSignal is true we need to read data from touchpanel and put a g_inputSignal to false

static status_t GT911_ReadRawTouchData(gt911_handle_t *handle, uint8_t *touchPointNum)
{
    status_t status;


    if (g_InputSignal)
    {
    status = handle->I2C_ReceiveFunc(handle->i2cAddr, GT911_REG_STAT, GT911_REG_ADDR_SIZE, &gt911Stat, 1);
    if (kStatus_Success != status)
    {
        *touchPointNum = 0;
        return status;
    }

    *touchPointNum = gt911Stat & GT911_STAT_POINT_NUMBER_MASK;

    if (0U != (gt911Stat & GT911_STAT_BUF_MASK))
    {
        if (*touchPointNum > 0U)
        {
            status = handle->I2C_ReceiveFunc(handle->i2cAddr, GT911_REG_FIRST_POINT, GT911_REG_ADDR_SIZE,
                                             (void *)handle->pointReg, (*touchPointNum) * sizeof(gt911_point_reg_t));
        }

        /* Must set the status register to 0 after read. */
        gt911Stat = 0;
        status    = handle->I2C_SendFunc(handle->i2cAddr, GT911_REG_STAT, GT911_REG_ADDR_SIZE, &gt911Stat, 1);
    }


	return status;
}

What do you think about this ?

i would have many tasks to manage later, so i don’t know if it’s good for me to add another task just to handle touchpanel, while i can handle in the same task with LVGL.

I’ll try for sure to use what you suggested,

Thank you so much for your suggestion

Kind regards,

Zerba,

1 Like

Hi @Zebra067 ,

I think your solution should work well also, and if you find it works well right now and you are not missing any touch events, there is probably no need to change it.
The advantage my approach brings is that the events are queued and providing the Ilitek chips hardware FIFO doesn’t overrun you shouldn’t miss any events but if your solution is providing an acceptable performance that’s all good.

The extra task shouldn’t really cause any major issues as it will be sleeping/blocked until such time as there is anything to do so that shouldn’t be a problem.
You can also reduce the stack size for the task if you want to keep resource utilisation down, it should function happily with the same size stack as the idle task as there are few variables declared on the stack. I just popped in some arbitrary value when typing up the code.

Personally I don’t like any code executing from with in LVGLs framework to introduce any delay or blocking if I can help it. So by having the “potentially relatively lengthy” i2c accesses in a separate task it stops them from blocking the rendering of the GUI if that makes sense? You can then use different priorities in the tasks to balance the system to provide the CPU time where it is most needed at any point in time and keep the GUI responsive at all times.

Another way of improving GUI performance I use, is to implement a FreeRTOS task which runs at a lower priority than the GUI with a queue(similar to the touch task above) which contains a case statement for many different background tasks then LVGL can post messages to this queue, so for example if I want to load a file into a text box LVGL will queue a request to the FreeRTOS task which has been triggered by an LVGL event and the task will carry out the slow process of reading from an SD card etc. at a lower priority in the background but throughout the period of the file loading the GUI remains absolutely responsive. If this file handling was called directly from within the event handler which queues the request in LVGL, the GUI would halt completely while the file is loading which personally I don’t like at all. I hope that makes sense.

Kind Regards,

Pete

1 Like

Hi @Zebra067 ,

I need to send you an email with regard to helping you with Eclipse, can you send an email to petebone00@gmail.com from the address you would like me to send a response to.

Kind Regards,

Pete

1 Like

Hi @pete-pjb ,

Yes, it works for now but I see that your approach is better and it would the two tasks work in same time and datas are protected,

Of course it’s preferable to not having any process making delays that would affect LVGL performances so your approach of using a second task makes sense, Thank you for suggesting me this,

i’m grateful for your support

Kind regards,

Zebra

1 Like

Hi pete-pjb, If use “else lv_disp_flush_ready( disp_drv );”, my programe will block in wait lock, blow “11112” will not print:

task
lock()
lv_task_handler();
unlock()

gui:
printf(“11111\r\n”)
lock()
printf(“11112\r\n”)
gui code
unlock()

If use like this, the area of PERF_MONITOR will twinkle:
if( lv_disp_flush_is_last( disp_drv ) ) {
DCACHE_CleanInvalidateByRange((uint32_t)color_p, DEMO_FB_SIZE);
ELCDIF_SetNextBufferAddr(LCDIF, (uint32_t)color_p);
DEMO_UpdateDualBuffer(disp_drv, area, color_p);
s_framePending = true;
}
lv_disp_flush_ready( disp_drv );

Hi @humphrey ,

I am sorry but I have been away on business for a couple of weeks.

If this is still an issue for you perhaps you can let me know a bit more information?

Sorry but I am not totally certain about what is happening based on what you have posted so far. :slight_smile:

Kind Regards,

Pete

Hi @pete-pjb , thank you very much, I have resolved it by your this code:

1 Like