fix: 修复墨水屏局部刷新参数传反导致的死锁并优化刷新逻辑

This commit is contained in:
seahi committed 2026-09-01 16:17:44 +08:00
1 parent a2a0b9785a
commit 29ad993649
4 files changed
+46 -49

No files matched your search

+7 -4
View File
@@ -86,10 +86,11 @@ void App::run() {
#ifdef CONFIG_DISPLAY_BWR #ifdef CONFIG_DISPLAY_BWR
std::string redContent = ssid; std::string redContent = ssid;
bool needFullRefresh = lastRedContent_.empty() // 修复 Bug: BWR 三色模式受限于硬件物理特性不支持无感快刷
|| (redContent != lastRedContent_) // 因此只要在三色模式下,强制每次更新都走全量三色刷新
|| (fullRefreshCounter_ >= maxFastRefreshes); bool needFullRefresh = true;
#else #else
// 纯黑白模式 (BW) 支持且可以正常享受无感快刷
bool needFullRefresh = (fullRefreshCounter_ == 0) bool needFullRefresh = (fullRefreshCounter_ == 0)
|| (fullRefreshCounter_ >= maxFastRefreshes); || (fullRefreshCounter_ >= maxFastRefreshes);
#endif #endif
@@ -99,7 +100,9 @@ void App::run() {
#ifdef CONFIG_DISPLAY_BWR #ifdef CONFIG_DISPLAY_BWR
lastRedContent_ = redContent; lastRedContent_ = redContent;
#endif #endif
fullRefreshCounter_ = 0; // 修复 Bug:全刷完成后应把计数器重置为 1,不能是 0!
// 如果是 0,下个大循环碰到 `fullRefreshCounter_ == 0` 时又会触发全刷,导致无限连刷!
fullRefreshCounter_ = 1;
} else { } else {
display_.updateFastAndSleep(); display_.updateFastAndSleep();
fullRefreshCounter_++; fullRefreshCounter_++;
+2 -2
View File
@@ -27,12 +27,12 @@ struct DashboardData {
/// 用于检测数据是否有变化,避免无意义的墨水屏全刷 /// 用于检测数据是否有变化,避免无意义的墨水屏全刷
bool operator==(const DashboardData &other) const { bool operator==(const DashboardData &other) const {
// 注意:不比较 lastReport(时间),否则每分钟时间跳动都会打破跳刷机制,导致强制损耗寿命
return temperature == other.temperature && return temperature == other.temperature &&
humidity == other.humidity && humidity == other.humidity &&
co2 == other.co2 && co2 == other.co2 &&
tvoc == other.tvoc && tvoc == other.tvoc &&
ch2o == other.ch2o && ch2o == other.ch2o;
lastReport == other.lastReport;
} }
bool operator!=(const DashboardData &other) const { bool operator!=(const DashboardData &other) const {
return !(*this == other); return !(*this == other);
+35 -41
View File
@@ -1,11 +1,6 @@
/** /**
* @file DisplayManager.cpp * @file DisplayManager.cpp
* @brief 环境监测与网络信息墨水屏 UI 绘制实现。 * @brief 环境监测与网络信息墨水屏 UI 绘制实现。
*
* 屏幕:400×300,通过 CONFIG_DISPLAY_BWR / CONFIG_DISPLAY_BW 编译宏
* 区分三色(黑/白/红)和纯黑白两种模式。
* 英文字体:Font8, Font12, Font16, Font20, Font24
* 中文字体:Font16CN(Unifont 16×16)
*/ */
#include "DisplayManager.h" #include "DisplayManager.h"
#include "ChineseFont.h" #include "ChineseFont.h"
@@ -54,20 +49,29 @@ void DisplayManager::updateFullAndSleep() {
#else #else
ESP_LOGI(TAG, "全量黑白刷新墨水屏..."); ESP_LOGI(TAG, "全量黑白刷新墨水屏...");
EPD_HW_Init_GUI(); EPD_HW_Init_GUI();
static unsigned char emptyRW[EPD_ARRAY] = {0}; static unsigned char emptyRW[EPD_ARRAY];
memset(emptyRW, 0xFF, EPD_ARRAY);
EPD_Display(ImageBW, emptyRW); EPD_Display(ImageBW, emptyRW);
#endif #endif
EPD_DeepSleep(); EPD_DeepSleep();
} }
void DisplayManager::updateFastAndSleep() { void DisplayManager::updateFastAndSleep() {
ESP_LOGI(TAG, "快速刷新墨水屏..."); ESP_LOGI(TAG, "快速无闪烁刷新墨水屏(局部波形)...");
EPD_HW_Init_Fast();
#ifdef CONFIG_DISPLAY_BWR #ifdef CONFIG_DISPLAY_BWR
// 三色模式下理论上不支持无闪烁,退化处理
EPD_HW_Init_Fast();
EPD_WhiteScreen_ALL_Fast(ImageBW, ImageRW); EPD_WhiteScreen_ALL_Fast(ImageBW, ImageRW);
#else #else
static unsigned char emptyRW[EPD_ARRAY] = {0}; // 【终极抗闪烁及死锁修复】
EPD_WhiteScreen_ALL_Fast(ImageBW, emptyRW); EPD_HW_Init_GUI();
// 修复导致硬件死锁在 Epaper_READBUSY() 的传参错误 Bug:
// 原厂 EPD_Dis_Part 函数的签名中:
// PART_COLUMN 代表的是 Y 轴(Height,需传 300)
// PART_LINE 代表的是 X 轴像素宽(Width,需传 400,内部会除以 8)
// 之前长宽传反,越界写寄存器触碰了驱动硬件的红线,导致永远不拉高 Busy 引脚
EPD_Dis_Part(0, 0, ImageBW, EPD_HEIGHT, EPD_WIDTH);
#endif #endif
EPD_DeepSleep(); EPD_DeepSleep();
} }
@@ -81,15 +85,11 @@ void DisplayManager::switchColor(bool toRed) {
#endif #endif
} }
// =========================================================================
// 顶部横幅:标题 + WiFi 状态
// Y: 0 ~ 28
// =========================================================================
void DisplayManager::drawTopBanner(const std::string &networkStatus) { void DisplayManager::drawTopBanner(const std::string &networkStatus) {
#ifdef CONFIG_DISPLAY_BWR #ifdef CONFIG_DISPLAY_BWR
switchColor(true); switchColor(true);
Paint_DrawRectangle(0, 0, EPD_WIDTH, 26, RED, DRAW_FILL_FULL, DOT_PIXEL_1X1); Paint_DrawRectangle(0, 0, EPD_WIDTH, 26, RED, DRAW_FILL_FULL, DOT_PIXEL_1X1);
Paint_DrawString_UTF8(10, 5, "xInfo \xe7\x8e\xaf\xe5\xa2\x83\xe7\x9b\x91\xe6\xb5\x8b\xe7\xbb\x88\xe7\xab\xaf", Font16CN, WHITE, RED); // xInfo 环境监测终端 Paint_DrawString_UTF8(10, 5, "xInfo 环境监测终端", Font16CN, WHITE, RED);
char buf[32]; char buf[32];
snprintf(buf, sizeof(buf), "[%s]", networkStatus.c_str()); snprintf(buf, sizeof(buf), "[%s]", networkStatus.c_str());
@@ -101,7 +101,7 @@ void DisplayManager::drawTopBanner(const std::string &networkStatus) {
switchColor(false); switchColor(false);
#else #else
Paint_DrawRectangle(0, 0, EPD_WIDTH, 26, BLACK, DRAW_FILL_FULL, DOT_PIXEL_1X1); Paint_DrawRectangle(0, 0, EPD_WIDTH, 26, BLACK, DRAW_FILL_FULL, DOT_PIXEL_1X1);
Paint_DrawString_UTF8(10, 5, "xInfo \xe7\x8e\xaf\xe5\xa2\x83\xe7\x9b\x91\xe6\xb5\x8b\xe7\xbb\x88\xe7\xab\xaf", Font16CN, WHITE, BLACK); // xInfo 环境监测终端 Paint_DrawString_UTF8(10, 5, "xInfo 环境监测终端", Font16CN, WHITE, BLACK);
char buf[32]; char buf[32];
snprintf(buf, sizeof(buf), "[%s]", networkStatus.c_str()); snprintf(buf, sizeof(buf), "[%s]", networkStatus.c_str());
@@ -112,22 +112,16 @@ void DisplayManager::drawTopBanner(const std::string &networkStatus) {
#endif #endif
} }
// =========================================================================
// 核心温湿度看板区
// Y: 32 ~ 148
// =========================================================================
void DisplayManager::drawThermoHygrometer(const DashboardData &data) { void DisplayManager::drawThermoHygrometer(const DashboardData &data) {
char buf[32]; char buf[32];
// 更新时间(靠右显示)
if (!data.lastReport.empty()) { if (!data.lastReport.empty()) {
char timeStr[48]; char timeStr[48];
snprintf(timeStr, sizeof(timeStr), "\xe6\x9b\xb4\xe6\x96\xb0: %s", data.lastReport.c_str()); // 更新: snprintf(timeStr, sizeof(timeStr), "更新: %s", data.lastReport.c_str());
Paint_DrawString_UTF8(290, 34, timeStr, Font16CN, BLACK, WHITE); Paint_DrawString_UTF8(290, 34, timeStr, Font16CN, BLACK, WHITE);
} }
// 左侧:室内温度看板 (X: 10 ~ 190) Paint_DrawString_UTF8(15, 36, "室内温度", Font16CN, BLACK, WHITE);
Paint_DrawString_UTF8(15, 36, "\xe5\xae\xa4\xe5\x86\x85\xe6\xb8\xa9\xe5\xba\xa6", Font16CN, BLACK, WHITE); // 室内温度
if (!data.temperature.empty()) { if (!data.temperature.empty()) {
snprintf(buf, sizeof(buf), "%s", data.temperature.c_str()); snprintf(buf, sizeof(buf), "%s", data.temperature.c_str());
Paint_DrawString_EN(15, 68, buf, &Font24, BLACK, WHITE); Paint_DrawString_EN(15, 68, buf, &Font24, BLACK, WHITE);
@@ -138,11 +132,11 @@ void DisplayManager::drawThermoHygrometer(const DashboardData &data) {
// 舒适度判断小标签 // 舒适度判断小标签
float tempVal = strtof(data.temperature.c_str(), nullptr); float tempVal = strtof(data.temperature.c_str(), nullptr);
if (tempVal < 18.0f) { if (tempVal < 18.0f) {
Paint_DrawString_UTF8(15, 115, "[\xe5\x81\x8f\xe5\x87\x89]", Font16CN, BLACK, WHITE); // [偏凉] Paint_DrawString_UTF8(15, 115, "[偏凉]", Font16CN, BLACK, WHITE); // [偏凉]
} else if (tempVal <= 26.0f) { } else if (tempVal <= 26.0f) {
Paint_DrawString_UTF8(15, 115, "[\xe8\x88\x92\xe9\x80\x82]", Font16CN, BLACK, WHITE); // [舒适] Paint_DrawString_UTF8(15, 115, "[舒适]", Font16CN, BLACK, WHITE); // [舒适]
} else { } else {
Paint_DrawString_UTF8(15, 115, "[\xe5\x81\x8f\xe7\x83\xad]", Font16CN, BLACK, WHITE); // [偏热] Paint_DrawString_UTF8(15, 115, "[偏热]", Font16CN, BLACK, WHITE); // [偏热]
} }
} else { } else {
Paint_DrawString_EN(15, 68, "--.-", &Font24, BLACK, WHITE); Paint_DrawString_EN(15, 68, "--.-", &Font24, BLACK, WHITE);
@@ -152,7 +146,7 @@ void DisplayManager::drawThermoHygrometer(const DashboardData &data) {
Paint_DrawLine(200, 36, 200, 142, BLACK, LINE_STYLE_SOLID, DOT_PIXEL_1X1); Paint_DrawLine(200, 36, 200, 142, BLACK, LINE_STYLE_SOLID, DOT_PIXEL_1X1);
// 右侧:相对湿度看板 (X: 210 ~ 390) // 右侧:相对湿度看板 (X: 210 ~ 390)
Paint_DrawString_UTF8(215, 36, "\xe7\x9b\xb8\xe5\xaf\xb9\xe6\xb9\xbf\xe5\xba\xa6", Font16CN, BLACK, WHITE); // 相对湿度 Paint_DrawString_UTF8(215, 36, "相对湿度", Font16CN, BLACK, WHITE); // 相对湿度
if (!data.humidity.empty()) { if (!data.humidity.empty()) {
snprintf(buf, sizeof(buf), "%s%%", data.humidity.c_str()); snprintf(buf, sizeof(buf), "%s%%", data.humidity.c_str());
Paint_DrawString_EN(215, 68, buf, &Font24, BLACK, WHITE); Paint_DrawString_EN(215, 68, buf, &Font24, BLACK, WHITE);
@@ -160,11 +154,11 @@ void DisplayManager::drawThermoHygrometer(const DashboardData &data) {
// 湿度状态小标签 // 湿度状态小标签
float humiVal = strtof(data.humidity.c_str(), nullptr); float humiVal = strtof(data.humidity.c_str(), nullptr);
if (humiVal < 40.0f) { if (humiVal < 40.0f) {
Paint_DrawString_UTF8(215, 115, "[\xe5\xb9\xb2\xe7\x87\xa5]", Font16CN, BLACK, WHITE); // [干燥] Paint_DrawString_UTF8(215, 115, "[干燥]", Font16CN, BLACK, WHITE); // [干燥]
} else if (humiVal <= 70.0f) { } else if (humiVal <= 70.0f) {
Paint_DrawString_UTF8(215, 115, "[\xe9\x80\x82\xe5\xae\x9c]", Font16CN, BLACK, WHITE); // [适宜] Paint_DrawString_UTF8(215, 115, "[适宜]", Font16CN, BLACK, WHITE); // [适宜]
} else { } else {
Paint_DrawString_UTF8(215, 115, "[\xe6\xbd\xae\xe6\xb9\xbf]", Font16CN, BLACK, WHITE); // [潮湿] Paint_DrawString_UTF8(215, 115, "[潮湿]", Font16CN, BLACK, WHITE); // [潮湿]
} }
} else { } else {
Paint_DrawString_EN(215, 68, "--.-%", &Font24, BLACK, WHITE); Paint_DrawString_EN(215, 68, "--.-%", &Font24, BLACK, WHITE);
@@ -180,7 +174,7 @@ void DisplayManager::drawAirQuality(const DashboardData &data) {
Paint_DrawLine(10, 150, EPD_WIDTH - 10, 150, BLACK, LINE_STYLE_SOLID, DOT_PIXEL_1X1); Paint_DrawLine(10, 150, EPD_WIDTH - 10, 150, BLACK, LINE_STYLE_SOLID, DOT_PIXEL_1X1);
// 区域标题 // 区域标题
Paint_DrawString_UTF8(10, 156, "\xe3\x80\x90 \xe7\xa9\xba\xe6\xb0\x94\xe8\xb2\xb4\xe9\x87\x8f\xe6\x8c\x87\xe6\xa0\x87 \xe3\x80\x91", Font16CN, BLACK, WHITE); // 【 空气质量指标 】 Paint_DrawString_UTF8(15, 156, "\xe7\xa9\xba\xe6\xb0\x94\xe8\xb4\xa8\xe9\x87\x8f\xe6\x8c\x87\xe6\xa0\x87", Font16CN, BLACK, WHITE); // 【 空气质量指标 】
// 第 1 列:CO2 (X: 15 ~ 125) // 第 1 列:CO2 (X: 15 ~ 125)
Paint_DrawString_EN(15, 180, "CO", &Font16, BLACK, WHITE); Paint_DrawString_EN(15, 180, "CO", &Font16, BLACK, WHITE);
@@ -191,11 +185,11 @@ void DisplayManager::drawAirQuality(const DashboardData &data) {
Paint_DrawString_EN(15, 204, data.co2.c_str(), &Font20, BLACK, WHITE); Paint_DrawString_EN(15, 204, data.co2.c_str(), &Font20, BLACK, WHITE);
float co2Val = strtof(data.co2.c_str(), nullptr); float co2Val = strtof(data.co2.c_str(), nullptr);
if (co2Val <= 800.0f) { if (co2Val <= 800.0f) {
Paint_DrawString_UTF8(15, 236, "\xe4\xbc\x98", Font16CN, BLACK, WHITE); // 优 Paint_DrawString_UTF8(15, 236, "优", Font16CN, BLACK, WHITE); // 优
} else if (co2Val <= 1200.0f) { } else if (co2Val <= 1200.0f) {
Paint_DrawString_UTF8(15, 236, "\xe8\x89\xaf", Font16CN, BLACK, WHITE); // 良 Paint_DrawString_UTF8(15, 236, "良", Font16CN, BLACK, WHITE); // 良
} else { } else {
Paint_DrawString_UTF8(15, 236, "\xe9\x9c\x80\xe9\x80\x9a\xe9\xa3\x8e", Font16CN, BLACK, WHITE); // 需通风 Paint_DrawString_UTF8(15, 236, "需通风", Font16CN, BLACK, WHITE); // 需通风
} }
} else { } else {
Paint_DrawString_EN(15, 204, "--", &Font20, BLACK, WHITE); Paint_DrawString_EN(15, 204, "--", &Font20, BLACK, WHITE);
@@ -214,9 +208,9 @@ void DisplayManager::drawAirQuality(const DashboardData &data) {
Paint_DrawString_EN(145, 204, data.tvoc.c_str(), &Font20, BLACK, WHITE); Paint_DrawString_EN(145, 204, data.tvoc.c_str(), &Font20, BLACK, WHITE);
float tvocVal = strtof(data.tvoc.c_str(), nullptr); float tvocVal = strtof(data.tvoc.c_str(), nullptr);
if (tvocVal <= 0.6f) { if (tvocVal <= 0.6f) {
Paint_DrawString_UTF8(145, 236, "\xe6\xad\xa3\xe5\xb8\xb8", Font16CN, BLACK, WHITE); // 正常 Paint_DrawString_UTF8(145, 236, "正常", Font16CN, BLACK, WHITE); // 正常
} else { } else {
Paint_DrawString_UTF8(145, 236, "\xe8\xb6\x85\xe6\xa0\x87", Font16CN, BLACK, WHITE); // 超标 Paint_DrawString_UTF8(145, 236, "超标", Font16CN, BLACK, WHITE); // 超标
} }
} else { } else {
Paint_DrawString_EN(145, 204, "--", &Font20, BLACK, WHITE); Paint_DrawString_EN(145, 204, "--", &Font20, BLACK, WHITE);
@@ -237,9 +231,9 @@ void DisplayManager::drawAirQuality(const DashboardData &data) {
Paint_DrawString_EN(275, 204, data.ch2o.c_str(), &Font20, BLACK, WHITE); Paint_DrawString_EN(275, 204, data.ch2o.c_str(), &Font20, BLACK, WHITE);
float ch2oVal = strtof(data.ch2o.c_str(), nullptr); float ch2oVal = strtof(data.ch2o.c_str(), nullptr);
if (ch2oVal <= 0.08f) { if (ch2oVal <= 0.08f) {
Paint_DrawString_UTF8(275, 236, "\xe5\xae\x89\xe5\x85\xa8", Font16CN, BLACK, WHITE); // 安全 Paint_DrawString_UTF8(275, 236, "安全", Font16CN, BLACK, WHITE); // 安全
} else { } else {
Paint_DrawString_UTF8(275, 236, "\xe8\xb6\x85\xe6\xa0\x87", Font16CN, BLACK, WHITE); // 超标 Paint_DrawString_UTF8(275, 236, "超标", Font16CN, BLACK, WHITE); // 超标
} }
} else { } else {
Paint_DrawString_EN(275, 204, "--", &Font20, BLACK, WHITE); Paint_DrawString_EN(275, 204, "--", &Font20, BLACK, WHITE);
@@ -269,7 +263,7 @@ void DisplayManager::drawBottomInfoBar(const std::string &ip,
// 错误页面 // 错误页面
// ========================================================================= // =========================================================================
void DisplayManager::drawError(const std::string &errorMsg) { void DisplayManager::drawError(const std::string &errorMsg) {
Paint_DrawString_UTF8(10, 80, "\xe7\xb3\xbb\xe7\xbb\x9f\xe5\xbc\x82\xe5\xb8\xb8", Font16CN, BLACK, WHITE); // 系统异常 Paint_DrawString_UTF8(10, 80, "系统异常", Font16CN, BLACK, WHITE); // 系统异常
Paint_DrawString_EN(10, 120, errorMsg.c_str(), &Font16, BLACK, WHITE); Paint_DrawString_EN(10, 120, errorMsg.c_str(), &Font16, BLACK, WHITE);
Paint_DrawString_UTF8(10, 150, "\xe5\xb0\x86\xe5\x9c\xa8\xe4\xb8\x8b\xe4\xb8\xaa\xe5\x91\xa8\xe6\x9c\x9f\xe9\x87\x8d\xe8\xaf\x95...", Font16CN, BLACK, WHITE); // 将在下个周期重试... Paint_DrawString_UTF8(10, 150, "将在下个周期重试...", Font16CN, BLACK, WHITE); // 将在下个周期重试...
} }
+2 -2
View File
@@ -828,8 +828,8 @@ CONFIG_NTP_SYNC_TIMEOUT_SEC=15
# #
# 硬件与显示配置 # 硬件与显示配置
# #
CONFIG_DISPLAY_BWR=y # CONFIG_DISPLAY_BWR is not set
# CONFIG_DISPLAY_BW is not set CONFIG_DISPLAY_BW=y
CONFIG_DISPLAY_REFRESH_INTERVAL_MIN=1 CONFIG_DISPLAY_REFRESH_INTERVAL_MIN=1
# end of 硬件与显示配置 # end of 硬件与显示配置