news 2026/9/3 10:53:58

除湿机彩屏显示方案:LT165A驱动320x240 TFT-LCD开发详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
除湿机彩屏显示方案:LT165A驱动320x240 TFT-LCD开发详解

做除湿机主控开发时,很多团队会遇到一个典型问题:产品功能已经比较完整,但显示界面还停留在数码管或段码屏上。用户能看到的只有“湿度 60%”“连续模式”“水箱满”这类干巴巴的状态,产品卖点很难直观地呈现。于是不少方案开始升级为彩屏显示,常见做法就是使用 LT165A 这类主控芯片搭配 2.8 寸、320×240 分辨率的 TFT-LCD,做出一个信息丰富、操作直观的除湿机彩屏界面。本文会从方案选型、硬件电路、软件架构到 UI 代码实现,完整拆解这套“LT165A + 320×240 彩屏”的除湿机显示方案。无论你是刚开始接触小家电彩屏开发的工程师,还是想把手头段码屏项目升级成彩屏,这篇文章都可以作为一个可以照着落地的参考。

1. 为什么除湿机需要 320X240 彩屏显示

1.1 从段码屏到彩屏的真实痛点

传统除湿机大多使用段码 LCD 或 LED 数码管。这类方案成本低、开发简单,但产品经理和用户都很难满意。段码屏只能显示固定的数字和图标,无法展示湿度趋势、模式说明、倒计时剩余时间等信息;加上丝印和背光颜色固定,整机外观显得比较单调。当除湿机同时具备除湿、干衣、睡眠、定时、负离子等多种功能时,段码屏需要增加大量固定丝印,硬件开模成本反而上升,软件侧也要维护一套复杂的段码映射逻辑。

彩屏方案解决的不仅是“好看”的问题。它能把湿度数字、目标湿度条、水箱状态、运行模式、滤网清洗提醒等功能集中在一屏内,并通过多页面切换来展示不同维度信息。对用户来说,操作逻辑更接近智能手机;对开发团队来说,产品功能迭代只需要修改 UI 资源,不必为了加一个图标重新做一套段码屏掩膜。

在常见的家用除湿机方案中,显示面板尺寸不会太大。2.8 寸的 320×240 分辨率(QVGA)是一个比较平衡的选择:可视面积足够展示温湿度、模式图标和功能按键,点距适中,直接近距离观看不会有明显颗粒感,同时整机结构不用刻意做大。和更大尺寸的 480×272、800×480 屏相比,320×240 在 MCU 资源消耗、图片存储空间和成本上都更可控,非常适合对功耗和成本敏感的小家电产品。

1.2 LT165A 在显示方案中的角色

LT165A 通常作为整套系统的显示控制主控,负责 UI 绘制、按键扫描、蜂鸣器控制,以及与除湿机主控板通信。在项目里,经常采用“主控板 + 显示板”双板结构:主控板负责湿度采集、压缩机控制、风机调速、水位检测等工作;显示板上的 LT165A 负责读取按键、刷新屏幕、显示温湿度数据,并通过串口或 IO 协议与主控板交换状态。

这种方式最大的好处是强弱电分离。压缩机、加热器等大功率器件在主控侧,显示侧只需要处理弱电信号和液晶屏驱动,电磁干扰更容易控制。同时显示功能可以单独调试,UI 改动不影响除湿逻辑,电气验证和软件联调效率都会高不少。

需要注意的是,不同封装的 LT165A 引脚资源、Flash、RAM 大小可能不同。做显示方案选型时,一定要以实际芯片的数据手册和厂商 SDK 为准。UI 图片资源较多的项目,建议提前估算存储空间;如果主控 Flash 不大,可以把大张背景图放在外部 SPI Flash 或者通过图片压缩方式降低占用,这个在后面性能优化部分会详细说明。

1.3 320×240 分辨率下的 UI 设计空间

320×240 虽然不大,但已经可以容纳很多信息。以除湿机首页为例,常见布局是:顶部显示“智能除湿”标题、Wi-Fi 或设置图标;中间用超大数字显示当前环境湿度,旁边显示目标湿度;右侧或底部显示温度、水箱状态和当前运行模式;底部一排图标用于切换自动、连续、干衣、睡眠等模式。

在这个分辨率下,如果要显示汉字,建议使用 16×16 点阵字库作为正文,大标题可以使用 24×24 或 32×32 字库。ASCII 字符一般使用 8×16 或 12×24 点阵即可。如果产品主控资源紧张,甚至可以把界面固定在两张 320×240 背景图上,运行状态变化时只刷新局部数字和图标,这也是一种非常实用的思路。

2. 整套彩屏显示方案的系统框架

2.1 软硬件数据流向

从系统的角度来看,显示板不只是“画个界面”那么简单。它需要实时读取传感器或主控板状态,然后决定界面元素怎么变化。下面是一个较为通用的数据流。

除湿机主控板 │ │ UART / IIC / 自定义IO协议 ▼ LT165A 显示主控 │ ├── 读取按键输入(GPIO/ADC按键) ├── 接收并解析温湿度、水箱状态 ├── 根据当前页面触发UI刷新 │ ├── SPI/并口数据 → TFT-LCD 320×240 ├── 背光控制信号 → LCD背光升压电路 └── 蜂鸣器引脚 → 按键提示音

在实际项目里,通讯协议可以做得比较简单,比如每隔 100ms 或 200ms 由主控板向显示板发送一帧状态数据,数据格式可以定义为:

帧头(0xAA) + 长度 + 湿度整数 + 温度整数 + 运行模式 + 水箱满标志 + 校验和

显示板收到后,先做校验,再更新界面相关变量。这种数据驱动的 UI 架构,比显示板主动查询传感器要直观很多,而且后续要支持 Wi-Fi 模组或手机 App 控制时,也可以沿用同一套状态消息。

2.2 显示通信接口选择

320×240 的 TFT 屏幕常见接口有 4 线 SPI、8 位并口、16 位并口,以及少部分 RGB 接口。对于 LT165A 这类偏 MCU 的主控来说,最常用的是 4 线 SPI 或 8 位并口。

SPI 接口优点非常明显:占用引脚少,只需要 CS、SCLK、MOSI、DC 四根数据控制线加 RES、BLK,非常适合对引脚资源敏感的小家电产品。缺点是刷新速度相对并口慢。但对于除湿机这类显示内容变化不快、不需要播放视频的产品,SPI 屏的刷新速度完全够用,甚至在切换整页背景图时稍加优化也能接受。

并口屏适合需要频繁刷新局部动画的场景。比如水箱满之后希望出现水滴下落动画,或者模式切换时做滑屏过渡,并口屏刷新速度更快。但并口会占用大量 GPIO,也会带来更复杂的信号完整性问题,通常只有在主控引脚充足、结构空间允许的情况下才推荐。

从方案的角度看,建议优先评估 SPI 接口的 320×240 屏。在 MCU 资源有限的家电控制板场景中,SPI 屏能显著降低硬件设计难度,还能把省下来的引脚留给触摸、外部 Flash、按键和通信。

2.3 彩屏方案中容易忽略的成本项

不少工程师评估彩屏方案时只看屏幕单价,忽略了一套完整显示方案带来的隐性成本。比如中文字库授权、开机 logo 版权、UI 设计资源、图片取模工具、显示屏 IC 初始化代码适配等。除湿机产品还要考虑高湿环境下的屏幕排线防潮、用户界面多语言切换、字体是否需要支持繁体或英文等。

建议项目启动时就把“显示资源包”单独建目录管理。屏幕厂提供的初始化代码往往基于某一款现成主控,需要移植到 LT165A 平台;图片资源要按“UI 源文件 / 转换后 C 数组 / 烧录文件”三层管理,避免后续更换 UI 素材时靠人工反复截图和替换代码。

3. 硬件设计要点与工程环境准备

3.1 主控与屏幕的引脚分配

屏幕驱动板一般放在显示板正面,LCD 通过 FPC 排线连接到 PCB,主控 LT165A 在 PCB 另一面或主控板上。做硬件设计时,第一件事就是给 LCD 模块分配引脚。以 SPI 屏为例,接口信号大致如下:

信号功能方向
LCD_CS片选,低有效主控输出
LCD_SCLKSPI 时钟主控输出
LCD_MOSISPI 数据,主控发往 LCD主控输出
LCD_DC数据/命令选择主控输出
LCD_RES复位,低有效主控输出
LCD_BL背光使能或 PWM 调光主控输出
VCC屏供电 2.8V/3.3V电源输出
GND电源地

在设计时要注意:LCD 的 IO 电平必须与 MCU 匹配。如果 MCU 是 5V 电平,屏幕却是 3.3V 逻辑,需要加电平转换电路,或者确认屏幕模块已经内置电平转换。不要直接让 5V 主控 GPIO 接 3.3V 屏幕,长期使用可能导致屏幕驱动芯片损坏。

RES 引脚不要直接悬空。虽然很多屏幕模块内部有上电复位电路,但为了初始化时序稳定,建议主控用 GPIO 控制 RES,上电后先拉低一段时间再释放,完成可靠复位。

3.2 电源与背光电路设计

显示方案的电源设计很关键。TFT-LCD 的背光电流是整块显示板的主要功耗来源,如果不做亮度调节,背光可能一直以最大电流工作,导致 PCB 局部发热,也影响屏幕寿命。常见做法是使用一个 PWM 引脚控制背光升压芯片的 EN/调光引脚,实现 0 到 100% 的背光可调。

除湿机在使用场景中有“夜间睡眠”需求,屏幕亮度通常需要降到比较低的水平。这里建议软件上做多级亮度,比如白天 100%,夜间自动降为 40%,用户设置菜单再提供 3 到 5 档手动选择。不要让用户长期在全亮状态下使用,长期高亮不仅费电,还可能加速 RGB 像素老化。

另外需要注意主控、屏幕和按键共地问题。显示板给屏幕供电的路径要足够宽,走线尽量短。屏幕突然切换大画面时电流会短暂波动,如果电源走线过细或滤波电容不足,可能出现屏幕闪一下就恢复的现象。常规做法是在 LCD 的 VCC 引脚附近放置 1uF 和 100nF 并联电容,在背光电源输入端放置 10uF 左右电容。

3.3 示例工程结构

由于 LT165A 实际使用的开发环境和 SDK 与具体厂商有关,本文示例代码采用标准 C 风格,并按通用嵌入式工程结构组织。读者需要把底层接口替换为自己 SDK 中的实现。

dehum_display/ ├── app/ │ ├── app_main.c // 主循环、事件分发 │ ├── app_ui_page.c // 页面调度 │ ├── ui_main_page.c // 除湿机首页 UI │ └── ui_settings_page.c // 设置页 UI ├── driver/ │ ├── lcd_lt165a.c // LCD 驱动(基于 LT165A 平台) │ └── lcd_lt165a.h ├── gui/ │ ├── gui_core.c // 画点、画线、填充、字符 │ ├── gui_core.h │ └── gui_font.c // 字库接口 ├── res/ │ ├── img_bg_main.c // 首页背景图数据 │ ├── img_icon_mode_auto.c // 模式图标 │ └── font_16.c // 16×16 中文字模 └── config/ └── board_config.h // 引脚、IO 定义

这样分层的目的是把“屏幕驱动”“绘图库”“业务页面”解耦。更换屏幕型号时只改 driver 层;更换产品 UI 时只改 app 层和 res 层;需要移植到其他主控平台时,gui 和 app 代码基本可以不改,只重写底层 SPI/GPIO 操作。

4. LCD 底层驱动与初始化实现

4.1 定义引脚和基础数据类型

首先在board_config.h中定义 LCD 引脚。不同 SDK 的 GPIO 封装不同,这里把引脚抽象成宏,方便大家替换到自己的工程。

// board_config.h #ifndef BOARD_CONFIG_H #define BOARD_CONFIG_H #include <stdint.h> // 请根据 LT165A 实际 SDK 的 GPIO 头文件调整 #define LCD_CS_PIN 0x01 #define LCD_SCLK_PIN 0x02 #define LCD_MOSI_PIN 0x03 #define LCD_DC_PIN 0x04 #define LCD_RES_PIN 0x05 #define LCD_BL_PIN 0x06 #define LCD_CS_LOW() gpio_write_pin(LCD_CS_PIN, 0) #define LCD_CS_HIGH() gpio_write_pin(LCD_CS_PIN, 1) #define LCD_DC_LOW() gpio_write_pin(LCD_DC_PIN, 0) #define LCD_DC_HIGH() gpio_write_pin(LCD_DC_PIN, 1) #define LCD_RES_LOW() gpio_write_pin(LCD_RES_PIN, 0) #define LCD_RES_HIGH() gpio_write_pin(LCD_RES_PIN, 1) #define LCD_BL_ON() gpio_write_pin(LCD_BL_PIN, 1) #define LCD_BL_OFF() gpio_write_pin(LCD_BL_PIN, 0) // 屏幕分辨率 #define LCD_WIDTH 320 #define LCD_HEIGHT 240 // 常用颜色,RGB565 格式 #define COLOR_BLACK 0x0000 #define COLOR_WHITE 0xFFFF #define COLOR_RED 0xF800 #define COLOR_GREEN 0x07E0 #define COLOR_BLUE 0x001F #define COLOR_CYAN 0x07FF #define COLOR_ORANGE 0xFD20 #endif

这段代码中的gpio_write_pin是假设的函数,实际替换成厂商 SDK 提供的 GPIO 驱动即可。颜色值采用 RGB565,这也是绝大多数 320×240 SPI 屏默认的数据格式。

4.2 SPI 写命令与写数据

LCD 屏幕驱动通常分“写命令”和“写数据”两种操作。以常见的 ILI9341 类驱动 IC 为例,当 DC 引脚为低电平时,写入的字节被解释为命令;当 DC 引脚为高电平时,写入的字节被解释为数据。

// lcd_lt165a.c #include "lcd_lt165a.h" #include "board_config.h" #include "string.h" static void spi_write_u8(uint8_t dat) { // 在这里调用 LT165A 的 SPI 发送函数 // 示例:spi_master_transmit(SPI_DEV_LCD, &dat, 1); } static void spi_write_u16(uint16_t dat) { uint8_t hi = (uint8_t)(dat >> 8); uint8_t lo = (uint8_t)(dat & 0xFF); spi_write_u8(hi); spi_write_u8(lo); } static void lcd_write_cmd(uint8_t cmd) { LCD_CS_LOW(); LCD_DC_LOW(); spi_write_u8(cmd); LCD_CS_HIGH(); } static void lcd_write_data_u8(uint8_t dat) { LCD_CS_LOW(); LCD_DC_HIGH(); spi_write_u8(dat); LCD_CS_HIGH(); } static void lcd_write_data_u16(uint16_t dat) { LCD_CS_LOW(); LCD_DC_HIGH(); spi_write_u16(dat); LCD_CS_HIGH(); }

实际写入时,为了提升性能,很多 SPI 外设支持连续发送一个缓冲区。如果数据量大,比如填充一整行 320 像素,一次发送 640 字节比逐字节发送效率高得多。LCD 驱动 IC 内部的显存地址会自动累加,所以连续发送像素数据时不需要反复发送地址设置命令。

在此基础上可以封装设置窗口函数:

static void lcd_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { if (x0 < 0) x0 = 0; if (y0 < 0) y0 = 0; if (x1 >= LCD_WIDTH) x1 = LCD_WIDTH - 1; if (y1 >= LCD_HEIGHT) y1 = LCD_HEIGHT - 1; lcd_write_cmd(0x2A); // 列地址设置 lcd_write_data_u16(x0); lcd_write_data_u16(x1); lcd_write_cmd(0x2B); // 行地址设置 lcd_write_data_u16(y0); lcd_write_data_u16(y1); lcd_write_cmd(0x2C); // 开始写显存 }

很多 320×240 屏幕的驱动 IC 初始化代码都可以从厂家资料中直接复制。因为屏幕内部 IC 型号不同,初始化寄存器序列也不同,这里不贴完整初始化数组,避免照搬到不同型号屏幕上无法显示。你只需要保证初始化流程是:复位、延时、读取屏幕 ID 或直接发送厂商初始化数组、开启显示。

4.3 画点与区域填充

有了写窗口的能力,画点就非常简单了。先发送命令进入写内存状态,再写一个像素点,注意坐标方向要与屏幕安装方向一致。

void lcd_draw_pixel(uint16_t x, uint16_t y, uint16_t color) { if (x >= LCD_WIDTH || y >= LCD_HEIGHT) { return; } lcd_set_window(x, y, x, y); lcd_write_cmd(0x2C); lcd_write_data_u16(color); } void lcd_fill_rect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) { uint32_t area; uint32_t i; uint8_t hi; uint8_t lo; if (x0 > x1) { uint16_t tmp = x0; x0 = x1; x1 = tmp; } if (y0 > y1) { uint16_t tmp = y0; y0 = y1; y1 = tmp; } area = (uint32_t)(x1 - x0 + 1) * (uint32_t)(y1 - y0 + 1); lcd_set_window(x0, y0, x1, y1); lcd_write_cmd(0x2C); hi = (uint8_t)(color >> 8); lo = (uint8_t)(color & 0xFF); LCD_CS_LOW(); LCD_DC_HIGH(); for (i = 0; i < area; i++) { spi_write_u8(hi); spi_write_u8(lo); } LCD_CS_HIGH(); }

填充整屏 320×240 共 76800 个像素,RGB565 就是 153600 字节。如果 SPI 时钟不够快,全屏填充会感觉到明显的刷新时间。所以在应用层要尽量少做全屏填充,改为局部刷新小区域。这也是后面 UI 设计的重要原则。

5. 除湿机彩屏 UI 实战:从页面划分到代码

5.1 页面布局建议

在实际做除湿机 UI 时,建议先和产品经理把页面结构梳理清楚。不要一上来就做十个页面,先把用户每天使用频率最高的一两个页面做好。常见页面划分如下:

页面显示内容操作
首页当前湿度、目标湿度、温度、模式、水箱状态切换模式、进入设置
模式页自动、连续、干衣、睡眠选择模式
定时页定时开机/关机倒计时调整时间
设置页亮度、蜂鸣器、滤网复位、语言保存设置

首页布局参考:

------------------------------------------- | 智能除湿机 [设置] [亮度] | ------------------------------------------- | | | | 65 %RH | 室内 | | [目标 50%] | 25℃ | | | 水箱 | | | [ OK ]| ------------------------------------------- | [自动] [连续] [干衣] [睡眠] | -------------------------------------------

当用户按下底部“连续”图标时,图标高亮,顶部或中间的状态文字变为“连续除湿中”,当前湿度数字刷新逻辑不变。页面切换的动画应当克制,除湿机属于功能型家电,界面反馈要快,不要做过长转场效果。

5.2 首页状态数据结构

先定义一个结构体,用来保存除湿机实时状态。

// app_ui_page.h #ifndef APP_UI_PAGE_H #define APP_UI_PAGE_H #include <stdint.h> typedef enum { DEHUM_MODE_AUTO = 0, DEHUM_MODE_CONTINUE, DEHUM_MODE_DRY, DEHUM_MODE_SLEEP, DEHUM_MODE_MAX } dehum_mode_t; typedef struct { uint8_t power_on; uint8_t target_humidity; // 目标湿度百分比,例如 50 uint8_t current_humidity; // 当前湿度百分比 int8_t temperature; // 当前温度,单位 ℃ dehum_mode_t mode; uint8_t tank_full; uint8_t fan_level; uint8_t backlight_level; } dehum_status_t; void app_ui_init(void); void app_ui_update(const dehum_status_t *status); void app_ui_set_backlight(uint8_t level); #endif

这个结构体里的字段,几乎就是显示板上需要关注的全部业务状态。主控板通过串口把数据填写到这个结构体,UI 模块只根据状态变化来刷新屏幕,这种设计在后续维护时非常方便。

5.3 大数字湿度刷新

除湿机最核心的指标是“当前湿度”。为了让用户一眼看到,通常会使用很大的字体,比如 48×64 像素的数字图片或点阵字模。由于大数字取模后占用空间较大,很多项目会直接在背景图中把数字区域留空,然后只对数字区域局部刷新。

这里的关键是:数字刷新要处理“旧数字擦除”和“新数字绘制”两个动作。如果直接在新位置画新数字,而旧数字没有擦掉,屏幕上就会出现残影。

// ui_main_page.c 核心片段 #include "ui_main_page.h" #include "gui_core.h" #include "lcd_lt165a.h" static uint8_t last_humidity = 0xFF; static dehum_mode_t last_mode = DEHUM_MODE_MAX; static uint8_t last_tank_full = 0xFF; // 大数字区域背景色 #define NUM_BG_COLOR COLOR_BLACK #define NUM_AREA_X0 40 #define NUM_AREA_Y0 60 #define NUM_AREA_X1 200 #define NUM_AREA_Y1 130 extern const unsigned char gImage_bg_main[]; // 首页背景图 extern const unsigned char gImage_humidity_unit[]; // %RH 图标 // 根据湿度值更新中间大数字,实际绘制函数需要对接字库索引 static void draw_humidity_number(uint8_t humidity) { char buf[8]; uint16_t color; if (humidity > 99) { humidity = 99; } // 1. 先擦除旧数字区域 lcd_fill_rect(NUM_AREA_X0, NUM_AREA_Y0, NUM_AREA_X1, NUM_AREA_Y1, COLOR_BLACK); // 2. 把湿度整数转成字符串 buf[0] = '0' + (uint8_t)(humidity / 10); buf[1] = '0' + (uint8_t)(humidity % 10); buf[2] = '\0'; // 3. 根据湿度值决定大数字颜色 if (humidity >= 70) { color = COLOR_ORANGE; // 高湿告警 } else { color = COLOR_WHITE; } // 4. 调用大字库绘制字符串 gui_draw_huge_string(NUM_AREA_X0 + 20, NUM_AREA_Y0 + 10, buf, color); }

gui_draw_huge_string需要对接你自己的大号数字字库,不同方案使用的大号字体不一样。有的工程师直接用图片素材代替数字,每个数字一张小图,效果会更好,但图片存储占用略高。用字库方案则更节省 Flash,只是 0~9 的字模本身不能做花哨设计。

5.4 模式图标高亮切换

模式按钮在 UI 上表现为一排图标按钮。用户选择不同模式时,对应图标从“灰色普通状态”切换为“彩色高亮状态”。这种状态切换如果每次都整幅刷新图片,SPI 屏可能会显得卡顿。更合理的做法是只更新图标区域。

图标在 UI 工程里通常是 48×48 或 64×64 像素的图片,取模后存成 C 数组。不同状态的图标分别取模,比如gImage_icon_mode_auto_normal[]gImage_icon_mode_auto_active[]

// UI 层通过状态机判断模式切换并刷新图标 void ui_main_page_handler(const dehum_status_t *status) { // 模式变化时,只更新模式图标区域 if (status->mode != last_mode) { switch (status->mode) { case DEHUM_MODE_AUTO: gui_draw_image(10, 190, 48, 48, gImage_icon_mode_auto_active, LCD_IMAGE_TYPE_RGB565); gui_draw_image(90, 190, 48, 48, gImage_icon_mode_continue_normal, LCD_IMAGE_TYPE_RGB565); break; case DEHUM_MODE_CONTINUE: gui_draw_image(10, 190, 48, 48, gImage_icon_mode_auto_normal, LCD_IMAGE_TYPE_RGB565); gui_draw_image(90, 190, 48, 48, gImage_icon_mode_continue_active, LCD_IMAGE_TYPE_RGB565); break; default: break; } last_mode = status->mode; } // 水箱满状态变化时,显示“请清空水箱”提醒 if (status->tank_full != last_tank_full) { if (status->tank_full) { gui_draw_string(150, 220, "Tank Full!", COLOR_RED); } else { lcd_fill_rect(150, 220, 300, 236, COLOR_BLACK); } last_tank_full = status->tank_full; } }

关于图片数组的声明,实际项目中可能是:

extern const unsigned char gImage_icon_mode_auto_normal[];

然后在对应的.c文件里用大数组保存像素数据。由于 320×240 RGB565 全屏图数据约为 153.6KB,如果 MCU 的 Flash 够大,可以把整张背景图作为数组存放;如果不够,可以考虑把背景压缩成 RLE 格式,或者改为纯色填充加小图组合。

5.5 主循环与数据更新策略

UI 刷新的关键不是越快越好,而是“变化时才刷、变化时快速刷”。除湿机的湿度通常变化不快,温度更慢,所以完全没必要让 UI 以 30 帧甚至 60 帧的速度刷新。高帧率只会白白消耗 CPU 和背光功耗。

主循环可以设计成固定时间片轮询:

// app_main.c 伪代码 #include "app_ui_page.h" #include "dehum_protocol.h" int main(void) { dehum_status_t status; app_ui_init(); while (1) { // 1. 接收主控板状态 if (dehum_protocol_parse(&status) == 0) { // 2. 更新 UI,内部判断哪些元素需要刷新 app_ui_update(&status); } // 3. 扫描按键 scan_key_process(); // 4. 处理背光延时熄灭等系统任务 system_task_process(); // 5. 简单延时,一般 10~50ms 一次 delay_ms(20); } }

这种方式的好处是逻辑清晰、便于调试。如果你在产品里加入了蜂鸣器提示音,也可以在按键扫描中同步处理。按键消抖、短按和长按区分建议放在一个独立模块中,不要和 UI 绘制代码混在一起。

6. 常见显示问题与排查方案

在 LT165A 彩屏显示方案调试过程中,最容易遇到下面几个问题。这些问题大部分不是 MCU 坏了,而是时序、供电或软件刷新逻辑导致的。

问题现象常见原因解决思路
白屏无显示LCD 初始化时序不对、RES 复位无效、SPI 极性不匹配检查 RES 拉低时间,核对初始化数组,检查 SPI CPOL/CPHA
黑屏但背光亮背光正常但没有发送初始化命令或显示写入未开启确认初始化最后执行了打开显示命令,确认写窗口和像素数据正常
屏幕颜色偏色RGB565 高低字节顺序反了交换高字节和低字节,或在数据发送函数里调整字节序
切换页面时花屏刷新时没有设置完整窗口,或数据发送被中断打断检查 x0/y0/x1/y1,在连续写像素时尽量关中断或用 DMA
图标花屏 / 颜色错乱图片取模格式与实际屏幕不一致确认取模工具选择 RGB565,确认图片宽高、像素扫描方向
界面偶尔卡死SPI 通信异常导致 LCD 状态错乱增加错误恢复机制,定期或按键触发时重新初始化 LCD
湿度数字边缘有残影旧数字未真正清干净把清屏矩形区域扩大 1~2 像素,避免取模字体边缘漏擦
上电偶发显示异常电源建立慢于 MCU 复位时序LCD 的 RES 由 MCU 控制,启动时延时 30ms 以上再初始化
睡眠模式屏幕闪烁背光 PWM 频率过低把背光 PWM 频率提高到 1kHz 以上,避免人眼可感知闪烁

这几个问题中,最容易被忽视的是 SPI 字节序和图片取模格式。很多 SPI 屏默认先接收高字节,但取模工具生成的数据可能是低字节在前,结果导致颜色错乱。遇到偏色问题时,不要先怀疑屏幕型号,可以先用纯色填充整屏做验证;如果纯色显示正确,再定位图片数据格式问题。

7. 性能优化与资源管理建议

7.1 局部刷新代替整屏刷新

除湿机UI界面上真正频繁变化的元素不多,通常只有湿度数字、当前模式图标、水箱状态。静态背景、标题文字、按键底图完全可以在页面切换时一次性绘制,平时不做任何刷新。

因此建议在 UI 设计阶段,就明确哪些区域是静态区域,哪些区域是动态区域。动态区域越小,SPI 刷新的负担越轻。比如只刷新湿度数字的 120×50 像素区域,而不是刷新整个 320×240 屏幕,理论上传数据量能减少十几倍。

7.2 减少全屏 Buffer 占用

很多从单片机屏幕开发入门的朋友会习惯性地想在内存里开一个全屏显存数组,方便先画好整个页面内容再一次性上传到 LCD。但对于 320×240 RGB565 来说,全屏 Buffer 约需要 153KB,普通 MCU 的 RAM 通常难以承受,即使能放下也会严重影响其他业务代码运行。

推荐的做法是“直接写屏 + 局部状态缓存”。虽然底层 LCD 芯片内部有自己的显存 GRAM,应用层不维护完整备份,但只要把每个 UI 控件的当前值保存下来,就可以通过前后比较判断是否更新,从而实现低内存的局部刷新。

7.3 图片资源压缩与外部存储

除湿机 UI 素材中,如果包含多套背景图、多国语言文字、开机动画,Flash 空间可能不够。这时有几种处理方式:

  • 使用 16 位色转 8 位色或 RLE 压缩,常用于背景简单、渐变色较少的页面。
  • 把图片分成多个小块,按需加载到内存,不在内存里放大图。
  • 接 SPI Flash 或 SD 卡保存 UI 资源,主控在上电时按需读取。
  • 中文字库只保留产品需要的几百个汉字,不要全量放 6716 字的 GB2312 二级字库。

需要注意:越简单的压缩算法,解码消耗越少;复杂压缩虽然节省空间,但 CPU 解压时间可能让页面切换变慢。建议先统计资源占用,再决定是否需要压缩。

7.4 Flash 保存用户设置的磨损策略

除湿机通常需要记忆用户上次设置的亮度、目标湿度、模式。这些设置如果每次变化都写入 Flash,会显著缩短 Flash 寿命。一般做法是把设置缓存到 RAM,只有退出设置页或关机前才统一保存;更复杂的场景可以使用多备份区循环写入,避免反复擦写同一个扇区。

显示板还要注意“频繁更新显示内容”和“频繁保存设置”之间的区别。UI 数字每秒刷新一次没问题,但 Flash 写入不能每秒一次。两者一定要分开处理。

8. 生产与工程化注意事项

8.1 静电防护与软件自恢复

除湿机使用环境湿度较高,外壳容易积累静电。显示面板作为用户频繁接触的部件,非常容易受到静电放电影响。硬件上需要有 ESD 防护器件,尽量在 FPC 座、按键、USB 调试口等位置加 ESD 抑制二极管。

软件上也可以增加自恢复机制。比如主循环里维护一个“LCD 通讯异常计数”,如果写入 OLED/LCD 时连续返回异常,或者用户按键发现显示无响应,可以自动重新初始化 LCD 并刷新当前页面。不要小看这个功能,量产产品在复杂电磁环境中难免出现偶发异常,能自恢复比让用户断电重启体验好很多。

8.2 UI 设计与版本管理

当产品经理修改 UI 文案、图标颜色或界面布局时,如果素材和代码没有分离开,嵌入式工程师会非常痛苦。常见的做法是在工程中建立res目录,把图片的 C 数组单独编译为一个模块。UI 设计师改图后,只需要重新运行转换工具生成对应.c文件,再替换到工程中,业务代码不需要变化。

图片命名建议遵循统一规则,比如:

资源名说明
img_bg_main首页背景图
img_icon_mode_auto_normal自动模式普通图标
img_icon_mode_auto_active自动模式高亮图标
img_icon_tank_full水箱已满图标
font_huge_digit大数字字库

同时维护一个“资源对照表”,记录每张图片的尺寸、色彩格式、取模工具版本、UI 源文件版本。这样当屏幕颜色不对或者图片花屏时,能快速判断是取模参数错了还是源文件改了。

8.3 调试顺序与量产验证建议

彩屏显示项目的调试顺序,个人建议是:先点亮背光,再点亮屏幕并显示纯色,然后显示一张静态背景图,接着实现文本和数字刷新,最后接入串口协议进行整机联调。

为什么先显示纯色?因为纯色能最快验证硬件接线、SPI 通信和 RGB565 字节序是否正常。如果纯色显示成功,说明主控到 LCD 的链路没问题,剩下的问题基本都是应用层或取模格式问题。如果纯色显示都不正常,就应该优先排查硬件接线和 LCD 初始化序列。

量产前还需要做高低温与长时间老化测试。重点观察屏幕刷新是否变慢、颜色是否偏移、白屏/花屏概率是否升高。除湿机产品在冷凝环境附近使用,屏幕排线可能处于较高湿度中,建议在结构设计中让 FPC 走线避开冷凝水路径,必要时在排线座周围点胶防护。

9. 最后的开发经验总结

把 LT165A 与 2.8 寸 320×240 彩屏结合来做除湿机,整体难度并不高,但要做好需要把显示驱动、UI 设计、资源存储和业务协议一起规划清楚。先把底层画点、填充、图片显示调通,再逐步增加文本、图标和页面逻辑,同时坚持“界面元素只在变化时刷新”的原则,这样即使 MCU 资源有限,也能做出流畅的彩屏效果。

如果你手头也有一块 320×240 的 SPI 屏和类似的 MCU 开发板,最直接的动手方式就是先画一条纯色线或一个渐变矩形,然后用串口把温度湿度数据喂进去,观察数字刷新是否自然流畅。等你把这种“局部刷新 + 状态驱动”的显示思路跑通,后面无论是做除湿机、空气净化器还是加湿器彩屏方案,都会顺手很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 10:52:53

GLM-5.3实测:代码生成、安全分析与基准测试的真实价值

大家在挑选大模型的时候&#xff0c;往往最关心三个问题&#xff1a;代码写得好不好、安不安全、跑分是不是真的有用。最近 GLM-5.3 成了圈子里讨论度很高的一个名字&#xff0c;很多开发者都在观望它到底值不值得接入自己的工具链。本文不打算只念参数表&#xff0c;而是按实际…

作者头像 李华
网站建设 2026/9/3 10:52:35

GESP C++ 二级(2026.09)

【GESP C二级考试考点详细解读】 【GESP C二级考试考点详细解读】_gesp二级考试知识点-CSDN博客 GESP 二级真题考点分析和解题思路 GESP 二级真题考点分析和解题思路_gesp二级相关的数学内容-CSDN博客 GESP二级知识点复习参考 GESP二级知识点复习参考_gesp二级基础知识-CSDN博客…

作者头像 李华
网站建设 2026/9/3 10:52:32

10分钟快速搭建 SkyWalking 服务

从 0 开始入门 SkyWalking,搭建 SkyWalking 服务,并接入 Java 项目中实现分布式链路追踪。 Tags 目录: 1. 概述 2. 搭建 SkyWalking 单机环境 3. 搭建 SkyWalking 集群环境 4. 告警 5. 注意事项 6. Spring Boot 使用示例 1. 概述 1.1 概念 SkyWalking 是什么? FROM Ap…

作者头像 李华
网站建设 2026/9/3 10:47:52

QtScrcpy:不装 App 就能投屏控手机的跨平台工具,5 分钟跑通

QtScrcpy&#xff1a;不装 App 就能投屏控手机的跨平台工具&#xff0c;5 分钟跑通 【免费下载链接】QtScrcpy Android real-time display control software 项目地址: https://gitcode.com/GitHub_Trending/qt/QtScrcpy QtScrcpy 是一款基于 scrcpy 技术的跨平台工具&a…

作者头像 李华
网站建设 2026/9/3 10:46:44

1.7.10最强钻石剑毕业攻略:附魔选择与铁砧合成全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华