OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 115|回复: 2
打印 上一主题 下一主题

ESP32 BOX3 学习例程MQTT增加了一个液晶显示,不定时死机,定位在显示函数,如何解决?

[复制链接]

2

主题

32

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3129
金钱
3129
注册时间
2017-11-28
在线时间
234 小时
跳转到指定楼层
楼主
发表于 前天 11:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
50金钱
前不久购买了正点原子ESP32BOX3小智学习开发板,在测试WiFi_MQTT例程时为了显示动态时间,增加了时间读取及显示功能,谁料到增加时间显示后,随机运行一段时间后会卡死,经调试定位在增加液晶显示时间这一行代码处。
例程包含主函数main.c,及lwip_demo.c和wifi_config.c
其中wifi_config.c主要进行wifi配网初始化等一系列联网操作。
lwip_demo.c main.c实现MQTT功能。
代码内容如下:(粘贴部分相关代码)
//1.wifi_config.c------------------------------------------------------------------------------------------------------
/**
****************************************************************************************************
* @file        wifi_config.c
* @Author      正点原子团队(ALIENTEK)
* @version     V1.0
* @date        2023-08-26
* @brief       网络连接配置
* @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 ESP32-S3 开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址penedv.taobao.com
*
****************************************************************************************************
*/

#include "wifi_config.h"


/* 链接wifi名称 */
#define DEFAULT_SSID        "ALIENTEK-YF"
/* wifi密码 */
#define DEFAULT_PWD         "aa123456789"
/* 事件标志 */
static EventGroupHandle_t   wifi_event;
#define WIFI_CONNECTED_BIT  BIT0
#define WIFI_FAIL_BIT       BIT1

network_connet_info network_connet;
static const char *TAG = "static_ip";
char lcd_buff[100] = {0};

/* WIFI默认配置 */
#define WIFICONFIG()   {                            \
    .sta = {                                        \
        .ssid = DEFAULT_SSID,                       \
        .password = DEFAULT_PWD,                    \
        .threshold.authmode = WIFI_AUTH_WPA2_PSK,   \
    },                                              \
}

/**
* @brief       链接显示
* @param       flag:2->链接;1->链接失败;0->再链接中
* @retval      无
*/
void connet_display(uint8_t flag)
{
    if((flag & 0x80) == 0x80)
    {
        spilcd_fill(0,90,320,240,WHITE);
        sprintf(lcd_buff, "ssid:%s",DEFAULT_SSID);
        spilcd_show_string(0, 90, 240, 16, 16, lcd_buff, BLUE);
        sprintf(lcd_buff, "psw:%s",DEFAULT_PWD);
        spilcd_show_string(0, 110, 240, 16, 16, lcd_buff, BLUE);
    }
    else if ((flag & 0x04) == 0x04)
    {
        spilcd_show_string(0, 90, 240, 16, 16, "wifi connecting......", BLUE);
    }
    else if ((flag & 0x02) == 0x02)
    {
        spilcd_show_string(0, 90, 240, 16, 16, "wifi connecting fail", BLUE);
    }
    else if ((flag & 0x01) == 0x01)
    {
        spilcd_show_string(0, 150, 200, 16, 16, (char*)network_connet.ip_buf, MAGENTA);
    }

    network_connet.connet_state &= 0x00;
}

/**
* @brief       WIFI链接糊掉函数
* @param       arg:传入网卡控制块
* @param       event_base:WIFI事件
* @param       event_id:事件ID
* @param       event_data:事件数据
* @retval      无
*/
static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    static int s_retry_num = 0;

    /* 扫描到要连接的WIFI事件 */
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        network_connet.connet_state |= 0x04;
        network_connet.fun(network_connet.connet_state);
        esp_wifi_connect();
    }
    /* 连接WIFI事件 */
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED)
    {
        network_connet.connet_state |= 0x80;
        network_connet.fun(network_connet.connet_state);
    }
    /* 连接WIFI失败事件 */
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        network_connet.connet_state |= 0x02;
        /* 尝试连接 */
        if (s_retry_num < 20)
        {
            esp_wifi_connect();
            s_retry_num ++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        }
        else
        {
            xEventGroupSetBits(wifi_event, WIFI_FAIL_BIT);
            network_connet.fun(network_connet.connet_state);
        }

        ESP_LOGI(TAG,"connect to the AP fail");
    }
    /* 工作站从连接的AP获得IP */
    else if(event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        network_connet.connet_state |= 0x01;
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "static ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        sprintf(network_connet.ip_buf, "static ip:" IPSTR, IP2STR(&event->ip_info.ip));
        network_connet.fun(network_connet.connet_state);
        xEventGroupSetBits(wifi_event, WIFI_CONNECTED_BIT);
    }
}

/**
* @brief       WIFI初始化
* @param       无
* @retval      无
*/
void wifi_sta_init(void)
{
    static esp_netif_t *sta_netif = NULL;
    network_connet.connet_state = 0x00;
    network_connet.fun = connet_display;

    wifi_event= xEventGroupCreate();    /* 创建一个事件标志组 */
    /* 网卡初始化 */
    ESP_ERROR_CHECK(esp_netif_init());
    /* 创建新的事件循环 */
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    sta_netif= esp_netif_create_default_wifi_sta();
    assert(sta_netif);
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL) );
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));   
    wifi_config_t  wifi_config = WIFICONFIG();
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start());

    /* 等待链接成功后、ip生成 */
    EventBits_t bits = xEventGroupWaitBits( wifi_event,
                                            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                            pdFALSE,
                                            pdFALSE,
                                            portMAX_DELAY);

    /* 判断连接事件 */
    if (bits & WIFI_CONNECTED_BIT)
    {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 DEFAULT_SSID, DEFAULT_PWD);
    }
    else if (bits & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 DEFAULT_SSID, DEFAULT_PWD);
    }
    else
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    vEventGroupDelete(wifi_event);
}


//2.lwip_demo.c------------------------------------------------------------------------------------------------------
/**
****************************************************************************************************
* @file        udp.c
* @author      正点原子团队(ALIENTEK)
* @version     V1.0
* @date        2023-08-26
* @brief       LWIP实验
* @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 ESP32-S3 开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址penedv.taobao.com
*
****************************************************************************************************
*/

#include "lwip_demo.h"

#include "esp_sntp.h"
#include "esp_netif_sntp.h"
#include <time.h>
#include <sys/time.h>



int g_publish_flag = 0;/* 发布成功标志位 */
static const char *TAG = "MQTT_EXAMPLE";
char g_lcd_buff[100] = {0};

/**
* @brief       错误日记
* @param       message     :错误消息
* @param       error_code  :错误码
* @retval      无
*/
static void log_error_if_nonzero(const char *message, int error_code)
{
    if (error_code != 0)
    {
        ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
    }
}

/**
* @brief       注册接收MQTT事件的事件处理程序
* @param       handler_args:注册到事件的用户数据
* @param       base        :处理程序的事件库
* @param       event_id    :接收到的事件的id
* @param       event_data  :事件的数据
* @retval      无
*/
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id);
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;

    switch ((esp_mqtt_event_id_t)event_id)
    {
        case MQTT_EVENT_CONNECTED:      /* 连接事件 */
            ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
            msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
            ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);

            msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

            msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

            msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
            ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
            g_publish_flag = 1;
            /* 订阅主题 */
            msg_id = esp_mqtt_client_subscribe(client, DEVICE_SUBSCRIBE, 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
            break;
        case MQTT_EVENT_DISCONNECTED:   /* 断开连接事件 */

            break;

        case MQTT_EVENT_SUBSCRIBED:     /* 取消事件 */
            ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
            msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
            ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
            break;
        case MQTT_EVENT_UNSUBSCRIBED:   /* 取消订阅事件 */
            ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_PUBLISHED:      /* 发布事件 */
            ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_DATA:           /* 接收数据事件 */
            printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
            printf("DATA=%.*s\r\n", event->data_len, event->data);

            break;
        case MQTT_EVENT_ERROR:

            if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT)
            {
                log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
                log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
                log_error_if_nonzero("captured as transport's socket errno",  event->error_handle->esp_transport_sock_errno);
                ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
            }

            break;
        default:
            ESP_LOGI(TAG, "Other event id:%d", event->event_id);
            break;
    }
}

/**
* @brief       lwip_demo进程
* @param       无
* @retval      无
*/
void lwip_demo(void)
{
        time_t now;
    struct tm timeinfo;
    struct timeval tv_now;
    char strftime_buf[64]={0};

    char mqtt_publish_data[] = "alientek dnesp32s3 mqtt demo";
    /* 设置客户端的信息量 */
    esp_mqtt_client_config_t mqtt_cfg = {
    .broker.address.hostname = HOST_NAME,                   /* MQTT地址 */
    .broker.address.port = HOST_PORT,                       /* MQTT端口号 */
    .broker.address.transport = MQTT_TRANSPORT_OVER_TCP,    /* TCP模式 */
    .credentials.client_id = CLIENT_ID,                     /* 设备名称 */
    .credentials.username = (char*)USER_NAME,               /* 产品ID */
    .credentials.authentication.password = PASSWORD,        /* 计算出来的密码 */
    };

    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);

    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
    esp_mqtt_client_start(client);
       
//增加时间获取代码部分---------------------->>>-----------------------------------------       
        time(&now);//获取时间戳
    setenv("TZ","CST-8",1); //设置时区(例如中国标准时间CST-8)
    tzset();

    // 配置 SNTP,使用默认 NTP 服务器
    esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
    config.sync_cb = sntp_sync_time; // 注册同步回调函数
       
        esp_netif_sntp_init(&config);
    esp_netif_sntp_start();

    if(esp_sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED)    //检查是否同步成功
         ESP_LOGI("SNTP-OK", "Check Time synchronized");
    LEDB_OFF;
//-------------------------------------------<<<-----------------------------------------       

    while(1)
    {
        if (g_publish_flag == 1)
        {
            esp_mqtt_client_publish(client,DEVICE_PUBLISH,(char *)mqtt_publish_data,strlen(mqtt_publish_data),1,0);
                       
//增加代码用于屏幕显示---------------------->>>-------------------------------------------               
                        time(&now);//获取时间戳
                        localtime_r(&now,&timeinfo);    //将时间戳转换为本地时间结构体
            strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
            ESP_LOGI("TIME","当前日期/时间:%s",strftime_buf);
                        lcd_show_string(0, 200, 200, 16, 16, (char*)strftime_buf, MAGENTA);  //--->随机运行一段时间,此处容易死机
//-------------------------------------------<<<------------------------------------------                       
        }

        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}


//3.main.c-------------------------------------------------------------------------------------------------------------

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_mac.h"
#include "my_spi.h"
#include "lcd.h"
#include "aw9523b.h"
#include "lwip_demo.h"
#include "wifi_config.h"
#include <stdio.h>

void app_main(void)
{
    esp_err_t ret;

        ret = nvs_flash_init();     /* 初始化NVS */
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ESP_ERROR_CHECK(nvs_flash_init());
    }
       
        my_spi_init();              /* SPI初始化 */
    myiic_init();               /* IIC初始化 */  
        aw9523b_init();              /* 初始化按键 */
    lcd_init();              /* LCD屏初始化 */
       
        lcd_show_string(0, 0, 240, 32, 32, "ESP32-S3", RED);
    lcd_show_string(0, 40, 240, 24, 24, "WiFi Aliyun Test", RED);
    lcd_show_string(0, 70, 240, 16, 16, "ATOM@ALIENTEK", RED);

    wifi_sta_init();            /* WiFi STA模式初始化 */
    lwip_demo();                /* lwip测试代码 */
}
       


问题出现在lwip_demo.c代码中增加的实时时间显示。请见如上注释
多次调试定位,确认是液晶显示代码卡死,网上查询说是液晶显示通讯不能中断,可能与free_rtos有冲突。
也尝试通过新建显示任务将刷新时间的功能,通过消息队列将该功能作为单独任务,结果也是同样卡死在
lcd_show_string(0, 200, 200, 16, 16, (char*)strftime_buf, MAGENTA);  //--->随机运行一段时间,此处容易死机
想请教一下,正点原子提供的液晶显示函数,难道就不能正常使用吗

回复

使用道具 举报

0

主题

130

帖子

0

精华

高级会员

Rank: 4

积分
667
金钱
667
注册时间
2026-1-29
在线时间
49 小时
2#
发表于 前天 13:34 | 只看该作者
针对ESP32 BOX3开发板在MQTT例程中增加液晶显示后出现不定时死机的问题,结合代码结构分析,以下是可能的原因及解决方案:

一、SPI总线配置与硬件层优化
SPI传输模式与时钟分频:

确保LCD的SPI总线配置为Mode 3(CPOL=1, CPHA=1),这是大多数TFT屏幕的最佳模式。
调整SPI时钟分频,例如从默认的8分频改为2分频,可显著提升传输速率。
示例代码修改(需根据实际LCD驱动库调整):

spi_bus_config_t buscfg = {
    .mosi_io_num = GPIO_NUM_23,
    .sclk_io_num = GPIO_NUM_18,
    .max_transfer_sz = 4096,  // 增大DMA缓冲区
    .flags = SPICOMMON_BUSFLAG_MASTER
};
spi_bus_initialize(HSPI_HOST, &buscfg, 2);  // 启用DMA通道

      
电源稳定性检查:

LCD和ESP32需独立供电,总电流需求≥500mA,电源轨建议增加10μF+0.1μF去耦电容。
禁用不必要的省电模式,防止DVFS动态调频引发突发性降频:
     
esp_pm_configure(&(esp_pm_config_t){
    .max_freq_mhz = 160,
    .min_freq_mhz = 160,
    .light_sleep_enable = false
});

      
二、任务调度与FreeRTOS冲突解决
任务优先级与核心分配:

将LCD渲染任务固定到Core 1,并赋予较高优先级(≥3),避免与Wi-Fi/MQTT任务(Core 0)竞争CPU资源:

void lcd_render_task(void *pvParameters) {
    while(1) {
        xSemaphoreTake(lcd_mutex, portMAX_DELAY);
        // 调用lcd_show_string等显示函数
        xSemaphoreGive(lcd_mutex);
        vTaskDelay(pdMS_TO_TICKS(10));  // 降低刷新频率
    }
}
// 在app_main中创建任务:
xTaskCreatePinnedToCore(lcd_render_task, "LCD", 4096, NULL, 3, NULL, 1);

      
避免在中断中调用LCD函数:

lcd_show_string等函数可能包含阻塞操作(如SPI传输),需确保其仅在任务上下文中调用。例如,在MQTT事件回调中通过消息队列通知渲染任务更新界面,而非直接操作LCD。
三、代码逻辑与内存管理优化
字符串格式化与缓冲区溢出:

检查strftime_buf和lcd_buff的大小是否足够,避免因格式化时间字符串过长导致内存越界。例如,将strftime_buf扩展至64字节以上:

char strftime_buf[64];
strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S", &timeinfo);

      
SNTP时间同步的异步处理:

当前代码在主循环中频繁调用time()和localtime_r(),可能因时间同步未完成导致空指针异常。建议在SNTP同步完成后才启动时间显示,并通过标志位触发更新。
四、调试与验证方法
添加日志追踪:

在lcd_show_string前后插入日志输出,确认死机前最后执行的代码位置:

ESP_LOGI("DEBUG", "Before lcd_show_string");
lcd_show_string(...);
ESP_LOGI("DEBUG", "After lcd_show_string");

      
压力测试与温度监控:

长时间运行测试,监测ESP32温度(建议≤65℃),排除过热降频导致的死机。可通过esp_temperature_get()获取芯片温度。
总之,优先尝试方案一和方案二,尤其是SPI配置和任务调度优化。若仍无法解决,则需深入排查内存泄漏和中断冲突。
回复

使用道具 举报

2

主题

32

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3129
金钱
3129
注册时间
2017-11-28
在线时间
234 小时
3#
 楼主| 发表于 昨天 09:37 | 只看该作者
非常感谢回帖,虽然一眼看起来就是AI查询结果,但是我还是认真拜读了一下,准备按步骤测试一下。当然在出现这个问题的时候,我自己也会用这种方式,先力求自己解决。首先第一,SPI总线配置与硬件层优化,我没有怀疑有什么问题,因为是正点原子开发板自带的测试程序,绝对应该没什么问题,原来没看,但是既然提出来了,也不放过研究一下是否是硬件设置问题;第二个任务调度与FreeRTOS冲突解决,这个之前我也查询到了,单独建立一个任务专门做LCD时间刷新,也将核心转到了core1,测试结果没有解决问题;第三点,代码逻辑与内存管理优化,字符串格式化与缓冲区溢出,目前没有发现什么问题,也尝试加大数组,没有改善。至于第四点,问题排查分析定位,不在这里,明确在lcd_show_string(...);
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-9-9 20:45

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表