周老师,请问一下创建多个线程会为什么屏幕在接收数据时就卡死了?

#include <pthread.h>
#include <string.h>
#include <unistd.h>  // for usleep
#include "serial_main.h"
#include "lvgl/lvgl.h"
#include "serial_thread.h"

#if 1
pthread_t thread_id;  // 线程ID
pthread_t update_thread_id;  // 线程ID
pthread_mutex_t mutex;  // 互斥锁,用于保护全局数据访问

//char received_data[MAX_DATA_LENGTH];  // 全局变量,存储接收到的数据
extern lv_obj_t *textarea;  // 声明一个外部变量, LVGL文本框对象指针
extern lv_obj_t * dropdown;
char * new_data = NULL;  // 用于存储新选项的指针


#if 1
// 线程函数,用于接收串口数据并更新LVGL文本框
void *thread_function(void *arg) {
    while (1) {
        serial_recv();  // 调用串口接收函数
        char *data = get_received_data();

        pthread_mutex_lock(&mutex);  // 加锁,保护全局数据访问
        if (data != NULL && strlen(data) > 0) {
            lv_textarea_add_text(textarea, data);  // 更新LVGL文本框内容
            memset(data, 0, sizeof(data)); //用于将一块内存区域的内容设置为指定的值
            //lv_dropdown_set_options(dropdown, data);  // 将接收到的数据设置为选项
            //new_data = data;
        }
        pthread_mutex_unlock(&mutex);  // 解锁
        usleep(1000);  // 添加适当的延迟以防止CPU占用过高
    }
    return NULL;
}
#endif

#if 0
// 线程函数,用于将接收到的数据设置为下拉列表的选项
void * update_dropdown_thread(void *arg) {
    while (1) {
        pthread_mutex_lock(&mutex);  // 加锁,保护全局数据访问
        if (new_data != NULL) {
            lv_dropdown_clear_options(dropdown);// 在更新下拉列表之前先清除旧选项
            lv_dropdown_set_options(dropdown, new_data);  // 将接收到的数据设置为选项
            new_data = NULL;  // 清空新选项指针
        }
        pthread_mutex_unlock(&mutex);  // 解锁
        usleep(1000);  // 添加适当的延迟以防止CPU占用过高
    }
    return NULL;
}
#endif

// 初始化函数,用于创建线程和初始化其他必要资源
void initialize_serial_thread() {
    pthread_mutex_init(&mutex, NULL);  // 初始化互斥锁

    #if 1
    // 创建线程
    if (pthread_create(&thread_id, NULL, thread_function, NULL) != 0)  /*pthread_create用于创建一个新的线程, 函数返回一个整数值,如果成功创建线程,则返回0;如果失败,则返回一个非零值*/
    {
        perror("Failed to create thread");
        // 处理线程创建失败的情况
        // 可以选择退出程序或者采取其他错误处理措施
    }
    #endif

    #if 0
    if (pthread_create(&update_thread_id, NULL, update_dropdown_thread, NULL) != 0)  /*pthread_create用于创建一个新的线程, 函数返回一个整数值,如果成功创建线程,则返回0;如果失败,则返回一个非零值*/
    {
        perror("Failed to create thread");
        // 处理线程创建失败的情况
        // 可以选择退出程序或者采取其他错误处理措施
    }
    #endif
}
#endif

这是两个线程,在注释掉把接收的数据设置为下拉列表选项的线程和函数,是可以正常接收数据,打开这个线程,串口接收数据就直接卡死了

// 下拉列表事件处理函数
static void dropdown_event_cb(lv_event_t * e) {
    /*获取事件类型*/
    lv_event_code_t code = lv_event_get_code(e);
    /*获取触发事件的目标对象*/
    lv_obj_t * dropdown = lv_event_get_target(e);
    //获取事件传递的用户数据
    lv_obj_t * label = lv_event_get_user_data(e);
    if (code == LV_EVENT_VALUE_CHANGED) {
        char buf[64];
        lv_dropdown_get_selected_str(dropdown, buf, sizeof(buf));  // 获取选中的项
        lv_label_set_text(label, buf);  // 更新按钮上的文本为选中的项
        lv_obj_del(dropdown);  // 删除下拉列表
    }
}

/*按钮文本事件函数*/
void serial_btn1_event_cb(lv_event_t * e){
    /*获取事件类型*/
    lv_event_code_t code = lv_event_get_code(e);
    /*获取触发事件的目标对象*/
    lv_obj_t * btn1 = lv_event_get_target(e);
    //获取btn的后代label
    lv_obj_t * label = lv_obj_get_child(btn1, 0);
    if (code == LV_EVENT_LONG_PRESSED){
        // 创建一个下拉列表
        dropdown = lv_dropdown_create(lv_scr_act());
        lv_obj_set_pos(dropdown, 700, 85);  // 设置下拉列表位置

        lv_obj_add_event_cb(dropdown, dropdown_event_cb, LV_EVENT_ALL, label);  // 绑定下拉列表事件
    }
}

这是给按钮绑定创建的下拉列表部件


LVGL的lv_timer_handler函数也需要加锁,一般LVGL的lv_timer_handler是一个独立线程,其他线程的操作与LVGL有任何关系时,需要与 lv_timer_handler 所在的线程互斥操作


我后面给主函数循环里也加锁了,结果是一样的接收串口数据会卡顿

问题应该出在这里:

  1. 给出的代码中没看到 new_data 的生成过程
  2. 生成的 new_data 数据要符合lvgl api的要求,阅读:https://lvgl.100ask.net/master/widgets/dropdown.html#set-options

也有可能是 dropdown 是空指针导致的问题,操作之前应该检查 dropdown 指针

image
周老师,我这里暂时是关闭了,不能直接将串口接收的数据给new_data指针吗,是不是因为指针指向的不是串口数据地址导致的

这是目前我想到的解决思路

好的,谢谢周老师解惑