Event callback function error occurs everytime

What MCU/Processor/Board and compiler are you using?

STM32F103RCT6 (RAM 48 KB, custom board)
display: 128*160, RGB565(16 bit), ST7735/ST7789 lcd driver, SPI comm.
IDE: STM32CubeIDE

What LVGL version are you using?

the latest one (v8.0)

What do you want to achieve?

When I add lvgl examples that have event callback function, it makes hard fault error, I don’t know the reason. All of the examples with events make hardfault errors.
Do I need another custom function in main.c?

And I couldn’t find the information of lv_obj_add_event_cb() function.
When I’ve searched it, it was set_event_cb in previous version.

I added log printf result. How can I solve this problem?

What have you tried so far?

Code to reproduce

main function

void App_Start(void)
{
// lcd init
			lv_init();
			LCD_Init();
			HAL_Delay(5);

// function for setting lcd and touch configuration
			lv_disp_config();
			lv_touch_config();

// put button example function 
            lv_example_btn_1();
}

indev setting

void lv_touch_config(void)
{
	static lv_indev_drv_t indev_drv;
	lv_indev_drv_init(&indev_drv);		// basic initialization
	indev_drv.type = LV_INDEV_TYPE_POINTER;

	static lv_indev_data_t data;
	my_input_read(&indev_drv, &data);
	indev_drv.read_cb = my_input_read;
	lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);
}

my_input_read()

bool my_input_read(lv_indev_drv_t * drv, lv_indev_data_t*data)
{
	static int8_t lastX = 0;
	static int8_t lastY = 0;

	if(XPT2046_TouchPressed())
	{
		XPT2046_TouchGetCoordinates(&lastX, &lastY);
		data->state = LV_INDEV_STATE_PRESSED;
	}
	else
	{
		data->point.x = lastX;
		data->point.y = lastY;
		data->state = LV_INDEV_STATE_RELEASED;
	}
	return false;
}

example that I imported

static void event_handler(lv_obj_t * obj, lv_event_t event)
{
    LV_UNUSED(obj);
    if(event == LV_EVENT_CLICKED) {
        LV_LOG_USER("Clicked");
    }
    else if(event == LV_EVENT_VALUE_CHANGED) {
        LV_LOG_USER("Toggled");
    }
}
void lv_example_btn_1(void)
{
    lv_obj_t * label;

    lv_obj_t * btn1 = lv_btn_create(lv_scr_act(), NULL);
    lv_obj_set_pos(btn1, 10, 10);
    lv_obj_set_size(btn1, 80, 50);
    lv_obj_add_event_cb(btn1, event_handler, NULL);
    lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, -40);

    label = lv_label_create(btn1, NULL);
    lv_label_set_text(label, "Button");

    lv_obj_t * btn2 = lv_btn_create(lv_scr_act(), NULL);
    lv_obj_add_event_cb(btn2, event_handler, NULL);
    lv_obj_align(btn2, NULL, LV_ALIGN_CENTER, 0, 40);
    lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
    lv_obj_set_height(btn2, LV_SIZE_CONTENT);

    label = lv_label_create(btn2, NULL);
    lv_label_set_text(label, "Toggle");
}
#endif

Screenshot and/or video

spi setting
1234
12345
123123

When debugging, I guess the error occurs in lv_obj_add_event_cb(btn1, event_handler, NULL); line.

When I deleted it, there is no error.
Is it caused by callback function? I’ve not changed library function except lv_conf.h

I have no experience yet with v8, but when I look at the function lv_obj_add_event_cb I see four arguments to be passed to it.

/**
 * Add an event handler function for an object.
 * Used by the user to react on event which happens with the object.
 * An object can have multiple event handler. They will be called in the same order as they were added.
 * @param obj       pointer to an object
 * @param filter    and event code (e.g. `LV_EVENT_CLICKED`) on which the event should be called. `LV_EVENT_ALL` can be sued the receive all the events.
 * @param event_cb  the new event function
 * @param           user_data custom data data will be available in `event_cb`
 * @return          a pointer the event descriptor. Can be used in ::lv_obj_remove_event_dsc
 */
struct _lv_event_dsc_t * lv_obj_add_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter, void * user_data);

I guess it is the latest version changed lately.
The number of parameter and contents are quite different from that I’m using, though.
I’m literally annoyed they haven’t organized the update log or documentation with new version or change.
It’s too stupid to have to solve problems this way :exploding_head:

That function in my version is:

/**
 * Add an event handler function for an object.
 * Used by the user to react on event which happens with the object.
 * An object can have multiple event handler. They will be called in the same order as they were added.
 * @param obj       pointer to an object
 * @param event_cb  the new event function
 * @param user_data custom data data will be available in `event_cb`
 */
void lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data);
void lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, void * user_data)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_obj_allocate_spec_attr(obj);

    obj->spec_attr->event_dsc_cnt++;
    obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
    LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);

    obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].cb = event_cb;
    obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].user_data = user_data;
}

Anyway, I’m RE-trying to solve the problem again again again…

It is a hardfault error, It is not possible to determine exactly where the error occurs.
However, it is assumed that a error occurs when lv_event_send continues to pass if the break point is set there.

It should be possible to find the real adress where the hardfault occurs.

In fact that’s not really easy.
Do a search with your favorite search engine. Search for e.g. STM hard fault or HardFault_Handler_C.

The ARM controllers holds the address where the hard fault occurs in a register.

With this address you can take a look with the debugger (set to show assembler code).
Try to break a little before. You should see the C-Code to which the assembler belongs.

When I checked the general purpose register, the program counter (indicates current program address) is 0x80018fc.

Is there any way to know from this?
I don’t know what I can tell except which part of the flash.

1111111 22222

The appropriate information is within the SCB register.
Take search engine and search for ‘stm registers in SCB’

There are PDFs for the related ARM Coretex family where the registers are described:
E.g. CFSR, HFSR, DFSR etc.

You can write your own HardFault_Handler_C where you can read the registers and store in static variables:
I have one in main.c

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"

void HardFault_Handler_C (ExceptionStackFrame* frame, unsigned long lr)
{
  ULONG    ulError = 0;

  volatile unsigned long stacked_r0;
  volatile unsigned long stacked_r1;
  volatile unsigned long stacked_r2;
  volatile unsigned long stacked_r3;
  volatile unsigned long stacked_r12;
  volatile unsigned long stacked_lr;
  volatile unsigned long stacked_pc;
  volatile unsigned long stacked_psr;
  volatile unsigned long back_lr;
  volatile unsigned long _CFSR;
  volatile unsigned long _HFSR;
  volatile unsigned long _DFSR;
  volatile unsigned long _AFSR;
  volatile unsigned long _BFAR;
  volatile unsigned long _MMAR;

  stacked_r0  = frame->r0;
  stacked_r1  = frame->r1;
  stacked_r2  = frame->r2;
  stacked_r3  = frame->r3;
  stacked_r12 = frame->r12;
  stacked_lr  = frame->lr;
  stacked_pc  = frame->pc;
  stacked_psr = frame->psr;
  back_lr     = lr;

#ifdef DEBUG
  ITM_Trace (TR_ERROR, "HardFault occurred:");
  ITM_Trace (TR_ERROR, "Stacked  R0: %8x:", stacked_r0);
  ITM_Trace (TR_ERROR, "Stacked  R1: %8x:", stacked_r1);
  ITM_Trace (TR_ERROR, "Stacked  R2: %8x:", stacked_r2);
  ITM_Trace (TR_ERROR, "Stacked  R3: %8x:", stacked_r3);
  ITM_Trace (TR_ERROR, "Stacked R12: %8x:", stacked_r12);
  ITM_Trace (TR_ERROR, "Stacked  LR: %8x:", stacked_lr);
  ITM_Trace (TR_ERROR, "Stacked  PC: %8x:", stacked_psr);
  ITM_Trace (TR_ERROR, "Stacked BLR: %8x:", back_lr);
#endif

  _CFSR = SCB->CFSR;                                        // Configurable Fault Status Registers
  _HFSR = SCB->HFSR;
  _DFSR = SCB->DFSR;
  _AFSR = SCB->AFSR;
  _MMAR = SCB->MMFAR;                                       // MemManage Fault Address
  _BFAR = SCB->BFAR;                                        // Bus Fault Address


#ifdef DEBUG
  __DEBUG_BKPT();   
#endif

  while (1) {    // Will loop here for ever, so can inspect the stored values
  }

  // or this one, doesn an automatic restart of device

  NVIC_SystemReset ();                                      // Do a reset


}

Please note that v8 is not released yet and thus the documentation is not complete. If you are looking for complete documentation and stability, I recommend continuing to use the release/v7 branch.

As I noted in your previous post, at least part of the issue is that your event handler signature is incorrect. Event handlers now take one parameter (lv_event_t *). Here is an example:

static void event_handler(lv_event_t *e)
{
    LV_UNUSED(obj);
    if(e->code == LV_EVENT_CLICKED) {
        LV_LOG_USER("Clicked");
    }
    else if(e->code == LV_EVENT_VALUE_CHANGED) {
        LV_LOG_USER("Toggled");
    }
}

I’ve actually checked SCB register after I replied.

The result is this:

  1. check the ccr
    -> DIV_0_TRP = 1 means it is trapped cause divided by 0.
    When I checked it, it always 0x1.
    0001

  2. check ICSR
    -> VECTACTIVE = 3 means it makes hardfault.
    0002

  3. check the HFSR
    -> forced = 1 means it makes hard fault error.
    0003

  4. check the cfsr
    -> invstate = 1 means it attempts to enter an invalid instruction set state.
    0004

I check the ICSR (interrupt control register), ISRPENDIND (interrupt pending) is 1. I don’t know why the error happened when I call interrupting.

I’m not sure, but when I searched the internet, something came to my eyes.
I set the pin as an interrupt for the touch input.
By the way, I haven’t defined any callback functions for this. (On the internet, someone says it is necessary)
In cubemx, IRQHandler was set with EXTI pin, touch input pen irq, but callback function was not defined separately.
Could this be the cause of the problem?
However, there is no problem when I just get touch input. The problem occurs with event func.
If you know anyone, please reply.

2121212121

I’ve changed it and no difference.
Why should I change it anyway? I’m using this version and it should work without any change if it is right example code.
It is soooooo weird.

Is this because of it, I didn’t disable the interrupt when LVGL is running? I’ve read v7 documentation.
I’m so confused :exploding_head:

https://docs.lvgl.io/dev/en/html/porting/os.html

Interrupts

Try to avoid calling LVGL functions from the interrupts (except lv_tick_inc() and lv_disp_flush_ready() ). But, if you need to do this you have to disable the interrupt which uses LVGL functions while lv_task_handler is running. It’s a better approach to set a flag or some value and periodically check it in an lv_task .

In your original post, you stated that you are using v8, not v7. The event API is different in v8.

Yes, I’m using v8, I should read previous documentation because there is no reference for v8 :sob:

I’ve changed the event callback function, it makes hard fault error, though.
Is it because of the memory? Quite confused.

static void event_handler(lv_event_t* event)
{
    printf("Clicked\n");
    //LV_UNUSED(obj);
}

void lv_example_btn_1(void)
{
    lv_obj_t * label;

    lv_obj_t * btn1 = lv_btn_create(lv_scr_act(), NULL);
    lv_obj_set_pos(btn1, 10, 10);
    lv_obj_set_size(btn1, 80, 50);
    lv_obj_add_event_cb(btn1, &event_handler, LV_EVENT_PRESSED, NULL);
    lv_obj_add_flag(btn1, LV_OBJ_FLAG_EVENT_BUBBLE);
    lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, -40);
    label = lv_label_create(btn1, NULL);
    lv_label_set_text(label, "Button");
}

#endif
struct _lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_t filter, void * user_data)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_obj_allocate_spec_attr(obj);

    obj->spec_attr->event_dsc_cnt++;
    obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc, obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
    LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);

    obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].cb = event_cb;
    obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].filter = filter;
    obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].user_data = user_data;
}

There are a few syntactical errors in your code. I’m not sure if you have warnings enabled, but all of these were flagged by GCC/clang warnings, so you might want to consider enabling those.

Here is the corrected example. I also included comments where I adjusted the code.

void lv_example_test(void)
{
    lv_obj_t * label;

    /* too many arguments */
    lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
    lv_obj_set_pos(btn1, 10, 10);
    lv_obj_set_size(btn1, 80, 50);
    lv_obj_add_event_cb(btn1, &event_handler, LV_EVENT_PRESSED, NULL);
    lv_obj_add_flag(btn1, LV_OBJ_FLAG_EVENT_BUBBLE);
    /* too many arguments */
    lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -40);
    /* too many arguments */
    label = lv_label_create(btn1);
    lv_label_set_text(label, "Button");
}

The v8 documentation is still a WIP, but you can find the preliminary version here. There are quite a few pages which haven’t been updated yet, so I suggest you also use the examples in the lvgl folder as a reference for the new APIs.