Description
Hello,
I have added a canvas on an object. I can drag the object around the screen and I also change some pixels of the canvas periodically with lv_canvas_set_px.
The problem arises when the lv_canvas_set_px is called while the object is dragged because the code hangs. I believe it is because a LVGL function is called during another LVGL function is in progress.
Is it possible that these 2 operations are done simultaneously ?
If not, what is the best way to create a lock mechanism ?
I have gone through below link but I have only one thread and I think the link applies for multiple threads: Threading Considerations — LVGL documentation
What MCU/Processor/Board and compiler are you using?
EK-RA8D1
What LVGL version are you using?
latest
It would be beneficial if you provided a code example of what you are attempting to do.
From your description I am guessing you want to do something along the lines of what is seen in the code below.
In this example the touches get processed when calling lv_task_handler and based on the kind of touch that occurred is what is going to dictate what callback function gets called. If you want to render a pixel to the canvas then touch and release. If you want to drag it then touch, hold and drag.
static void drag_event_handler(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_indev_t * indev = lv_indev_active();
if(indev == NULL) return;
lv_point_t vect;
lv_indev_get_vect(indev, &vect);
int32_t x = lv_obj_get_x_aligned(obj) + vect.x;
int32_t y = lv_obj_get_y_aligned(obj) + vect.y;
lv_obj_set_pos(obj, x, y);
}
static void click_event_handler(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_indev_t * indev = lv_indev_active();
if(indev == NULL) return;
lv_display_t * disp = lv_obj_get_display(obj);
if(disp == NULL) return;
int32_t x = lv_obj_get_x(obj);
int32_t y = lv_obj_get_y(obj);
lv_point_t point;
lv_indev_get_point(indev, &point);
x = point.x - x;
y = point.y - y;
lv_canvas_set_px(obj, x, y, lv_color_hex(0x00FF00), 255);
}
lv_obj_t * canvas;
canvas = lv_canvas_create(lv_screen_active());
lv_obj_set_size(canvas, 250, 250);
lv_obj_set_style_border_width(canvas, 3, 0)
lv_obj_set_style_border_color(lv_color_hex(0xFF0000), 0)
lv_obj_set_style_border_opa(255, 0)
lv_obj_add_event_cb(canvas, drag_event_handler, LV_EVENT_PRESSING, NULL);
lv_obj_add_event_cb(canvas, click_event_handler, LV_EVENT_CLICKED, NULL);
lv_obj_t * label = lv_label_create(canvas);
lv_label_set_text(label, "Click/Drag me");
lv_obj_center(label);
Hello,
lv_canvas_set_px is called periodically every 1 second from a timer and changes the color of some pixels.
The problem is that is I also drag the canvas, the program crashes because it cannot handle both operations at the same time (changing the pixel color and dragging the canvas).
I am trying to find a way to ‘disable’ dragging while lv_canvas_set_px is executed. I have tried with the _remove_flag function but still not working.
before you add/remove/change a pixel make sure to call lv_obj_update_layout(canvas)right before setting the pixel. This will force any position changes to the canvas to be realized.
also when you say you are setting a pixel in a timer… what kind of a timer are you using? If you are using a hardware timer this is a no no because you are doing something from inside of an ISR. You should be using an lv_timer for this and not from a hardware timer.
1 Like
Thank you, lv_timer seems to work fine.
So calling LVGL functions from hardware ISRs is not recommended, right ? Can you give a brief explanation for that ? Is it because the LVGL operations need to synchronize with internal LVGL timers ?
you cannot allocate memory from an ISR that’s why. There are lots of things in LVGL that allocate memory and that is going to cause a crash or lockup.
The ONLY thing that is able to be called from an ISR safely is lv_tick_inc
. and it is encouraged to do so this way LVGL is able to keep time properly.
If using the timers that are build into LVGL the timers will get run when lv_task_handler
gets called in the main loop; Each timer’s callback function gets called one after the other and not in parallel. so you would not have any issues with corrupted memory as the result of them being run, unlike an ISR. You will not have any issues if memory allocation occurs because it is being run from the main loop and not from inside of an ISR.
I do want to clear something up. You can allocate memory from inside of an ISR but there is special care that is needed and what needs to be done is going to be very specific to the MCU that the software is running on. LVGL is blind to the hardware that it is being run on and it needs to be that way in order to be able to be compiled to run on just about any kind of an MCU made so long as there is enough program storage and memory available. There is no code in LVGL that is specific to an MCU which is the reason why you cannot call any LVGL function from inside of an ISR except for lv_tick_inc
2 Likes
Ok, thanks a lot for the explanation.
Regards
One issue with code inside an ISR callback is that the ISR will interrupt program execution immediately, execute the callback code, and resume program execution where it left off. So one risk with ISRs (analogous to multi-threading) is that the ISR callback code could alter the internal state used by the code that was executing and paused at the time of the interrupt. This is likely to happen if your ISR calls an LVGL function to update an object at the point in time a different LVGL function is processing the previous object updates.
It’s not about code in LVGL being specific to an MCU or not, it is because LVGL isn’t thread-safe (as stated in the documentation linked above) and thus in general one LVGL function needs to complete before another one starts. lv_tick_inc
is an exception to this rule as it uses atomic data, ie thread-safe.
This isn’t a problem specific to LVGL, it’s a general issue regarding ISRs, and care must be taken with ISR callback implementations. Allocating memory on the stack inside an ISR can cause an issue depending on the hardware and platform, heap allocations would likely be fine depending on the memory manager, but a general rule of thumb is to do as little as possible inside the ISR and set a semaphore/flag/add to a queue and have your main loop detect that and actually do the work. Avoid using functions like Serial.print and delay() inside an ISR as those functions generally rely on interrupts too, and interrupts inside interrupts generally won’t work, or will cause other issues, depending on the platform.
2 Likes