SPI TFT LCD显示的颜色和电脑手机上的颜色不一样,排查原因
这是非常正常的现象不是你代码写错了 一句话结论不同屏幕 ≠ 同一套颜色标准所以显示出来一定有偏差但这里面其实有好几个“坑点”我给你从硬件 → 接口 → 软件 → 人眼一层一层讲清楚。 一、最核心原因屏幕不是一个“级别”的东西 笔记本屏幕高端选手一般特点IPS / OLED支持sRGB / AdobeRGB出厂校色颜色准确偏“真实” 你的 TFT LCD嵌入式特点低成本面板无校色色域小偏色严重常见偏蓝 / 偏绿 所以第一层结论不是你LCD错了是它“本来就不准”⚠️ 二、你这个项目还有一个关键原因RGB565你用的是 SPI ST7789 →RGB565 RGB565 vs 电脑屏幕RGB888类型位数颜色数量RGB88824位1677万RGB56516位65536 直接影响渐变变“断层”banding颜色不细腻某些颜色明显偏差 三、Gamma伽马差异 不同屏幕有不同 gamma 曲线电脑标准 gamma 2.2 TFT厂家随便调的 表现同一个灰色电脑正常灰LCD可能发白 or 发黑 四、驱动初始化参数你这个很可能有影响ST7789 初始化里通常有MADCTL COLMOD GAMMA常见坑❌ 颜色顺序错最常见RGB ↔ BGR 表现红色变蓝色整体偏色 解决st7789_set_color_order(ST7789_BGR);// 或 RGB❌ Gamma参数没调很多驱动直接用默认值 显示效果 偏色 发灰 五、背光 亮度 你的 LCDLED 背光没有亮度曲线校准 笔记本自动亮度 / 色温调节 表现LCD刺眼 / 偏冷 电脑柔和 / 偏暖 六、人眼错觉这个也很真实 同一个颜色小屏更鲜艳大屏更柔和 环境光也影响白天 vs 晚上暖光 vs 冷光 七、总结按影响排序原因影响程度屏幕硬件差异⭐⭐⭐⭐⭐RGB565色深⭐⭐⭐⭐Gamma⭐⭐⭐驱动配置⭐⭐⭐背光⭐⭐ 八、你可以怎么优化✔ 1. 确认颜色顺序必须做测试纯色lv_color_hex(0xFF0000);// 红 如果不红 → RGB/BGR错了✔ 2. 做“颜色补偿”工程常用比如// 偏蓝 → 降蓝// 偏暗 → 提亮✔ 3. 用更高色深如果可能 SPI基本没救就是565 RGB屏并口可以上RGB666RGB888✔ 4. 调整 gamma进阶 改 ST7789 寄存器高级玩法 最后一句人话总结你现在看到的“颜色不一样” 不是 bug 不是代码错 是因为 你在用“玩具屏”和“专业屏”对比如果你下一步想搞 “让这个 LCD 看起来更像手机屏” “UI颜色调优LVGL配色方案”我可以帮你做一套 嵌入式屏幕专用 UI 调色方案非常实战TFT LCD显示纯黑色变成灰色这个现象非常典型而且你这个描述很精准 “黑色看起来像灰色”结论先说清楚在 ST7789 这种 TFT 屏上 —— 这是正常现象物理限制但我们可以把原因拆开讲清楚 给你优化办法 一、核心原因TFT 根本做不到“真黑” TFT LCD 的工作原理 TFT LCD 是这样工作的背后有一个LED背光一直亮前面的液晶只是“挡光”⚠️ 关键点来了液晶 ≠ 完全挡光 所以黑色 “尽量挡住光”但总有光漏出来 看起来就是深灰色 二、对比一下你笔记本差距来源 如果是 OLED 屏黑色 像素直接关闭 真·纯黑 如果是 IPS 屏 也有背光但面板质量高偏振片更好漏光更少 所以更接近黑⚠️ 三、你这个项目的额外“减分项”你用的是 SPI ST7789入门级 LCD❌ 问题叠加1️⃣ 面板质量低漏光严重对比度低2️⃣ 背光太亮很关键 很多模块默认背光 100% 黑色直接被“照灰了”3️⃣ Gamma 没调 黑色区间不够压 四、你看到“灰黑”的真实原因组合背光漏光 面板对比度低 Gamma不准 背光太亮 灰色黑 五、怎么优化实用✔ 方法1调低背光效果最明显如果你背光是PWM控制// 降低占空比 效果黑色更黑对比度提升 这是最有效的一招 ⭐⭐⭐⭐⭐✔ 方法2别用纯黑用“伪黑”lv_color_hex(0x000000);❌ 太死黑 改成lv_color_hex(0x0a0a0a);// 或 0x101010 好处看起来更均匀不会发灰发脏 UI设计常用技巧✔ 方法3调 Gamma进阶ST7789 支持 gamma 曲线 可以让暗部更“压黑”但这个要改寄存器稍复杂✔ 方法4减少大面积纯黑 工程经验大面积纯黑 最容易暴露LCD缺陷 改成深灰背景 卡片式UI 六、UI设计建议很关键你现在做 LVGL建议 别用这种纯黑背景 白字 ❌ 改成深灰背景 柔和白字 ✅ 类似这种风格 七、工程师总结说人话你这个“黑不黑”的问题 不是代码问题 不是LVGL问题 不是SPI问题 是屏幕物理极限 给你一个实战建议最有用如果你这个项目是产品级 直接记住TFT LCD 不适合做“纯黑UI” 最后一句 你现在已经开始注意“显示效果”了这其实说明你已经从能跑 → 好用 → 好看UI/体验阶段在进阶了 如果你想我可以帮你 做一套LVGL暗黑主题专门适配垃圾LCD也好看 包括颜色、对比度、字体、布局让你这个屏幕看起来直接提升一个档次 IPS简介️ IPS 是啥意思IPS In-Plane Switching平面内切换 是一种LCD 液晶屏的显示技术类型就像一种“屏幕做法” 直观理解一下 简单说区别在这IPS液晶分子“平着转”传统TN屏液晶“扭着转” 用大白话解释 你可以这么理解类型类比TN屏门帘角度一变就变形IPS屏百叶窗怎么看都差不多 IPS 的核心特点✔ 1. 可视角度大最重要 从侧面看也不变色、不发白✔ 2. 颜色更准 更接近真实颜色 做 UI / 看图片更舒服✔ 3. 对比度比低端屏好 黑色更“接近黑”但仍不是真黑❌ 缺点也有缺点说明成本高比 TN 贵响应稍慢打游戏极限不如TN仍有漏光黑色不如 OLED 和其他屏对比一下类型特点TN便宜、差VA对比度高IPS综合最均衡 ⭐OLED真黑、最强 回到你这个项目重点你用的ST7789 TFT 有可能是IPS好一点的或“假IPS / 低端IPS”所以你现在看到 黑色发灰 颜色有点偏 即使是 IPS 也会这样因为IPS ≠ 高端屏 IPS 只是“类型”不是“品质保证” 一句话总结IPS 一种让屏幕“角度更好、颜色更准”的LCD技术但你这个小屏 IPS ≈ 入门级 IPS 给你一个工程师判断方法以后看到屏幕参数 重点看这几个而不是只看 IPS色深RGB565 / 666 / 888亮度cd/m²对比度是否校色如果你想我可以帮你 教你怎么判断你这块ST7789到底是不是“真IPS” 或者推荐几款显示效果明显更好的屏适合ESP32LVGL优化显示问题可以优化而且你这段代码其实已经“接近正确”但还有几个典型的LVGL v9 工程级优化点我给你直接改成一个更规范、更稳、更适合后续扩展 UI 的版本。 先说你这段代码的问题重点❌ 1. 没必要再创建一层 father 容器father_lcd_oblv_obj_create(lv_screen_active()); 你现在是screen屏幕 └── father你创建的 └── 子控件 实际上可以直接用 screenscreen └── 子控件❌ 2. 用 width/height 获取屏幕尺寸不优雅scr_act_lcd_width() v9 推荐LV_PCT(100)❌ 3. 样式是“散着写的”后期难维护 工程里应该用style统一管理✅ 优化目标 我给你做的是更干净结构可扩展 UI适合做产品级界面顺便解决你“黑色发灰”的观感问题UI层优化 优化版本推荐直接用#includelvgl.hLV_IMG_DECLARE(wls);staticconstchar*TAGLVGL_DEMO;lv_obj_t*father_lcd_ob;/* 建议统一样式 */staticlv_style_tstyle_screen;/* 初始化样式 */staticvoidstyle_init(void){lv_style_init(style_screen);lv_style_set_bg_color(style_screen,lv_color_hex(0x0f1113));// ❗不是纯黑lv_style_set_bg_opa(style_screen,LV_OPA_COVER);lv_style_set_border_width(style_screen,0);lv_style_set_pad_all(style_screen,0);}/* 创建基础界面 */voidcreat_father_object(void){/* 获取当前屏幕 */lv_obj_t*scrlv_screen_active();/* 应用样式到屏幕推荐 */lv_obj_add_style(scr,style_screen,LV_PART_MAIN);/* ❗ 如果你确实需要父容器比如分层UI */father_lcd_oblv_obj_create(scr);lv_obj_set_size(father_lcd_ob,LV_PCT(100),LV_PCT(100));lv_obj_center(father_lcd_ob);/* 去掉默认样式影响 */lv_obj_set_style_bg_opa(father_lcd_ob,LV_OPA_TRANSP,LV_PART_MAIN);lv_obj_set_style_border_width(father_lcd_ob,0,LV_PART_MAIN);lv_obj_set_style_pad_all(father_lcd_ob,0,LV_PART_MAIN);} 关键优化点解释很重要 1. 不用纯黑解决你“灰黑”问题lv_color_hex(0x0f1113); 比0x000000更好不发灰更均匀更像手机UI 2. 样式集中管理工程必备staticlv_style_tstyle_screen; 好处UI统一风格后期一键换主题不会到处写 style 3. 屏幕直接做背景性能更好lv_obj_add_style(scr,style_screen,LV_PART_MAIN); 少一层对象更省 RAM更少重绘更快 4. father 变“透明层”lv_obj_set_style_bg_opa(...LV_OPA_TRANSP); 这样father 只负责布局背景由 screen 控制⚠️ 你这个项目再给你几个“性能建议”很关键 1. SPI屏 LVGL → 动画要克制lv_bar_set_value(...,LV_ANIM_ON); 会刷屏频繁SPI带宽吃紧卡顿 建议LV_ANIM_OFF能不用就不用 2. 尽量减少全屏刷新 比如少用透明少用阴影少用渐变 3. 字体 / 图片占 RAMLV_IMG_DECLARE(wls); 注意图片尽量压缩用 indexed / alpha 小图 最后给你一个“工程级思路”你现在已经进入这个阶段能显示 → 做界面 → 开始优化下一步应该是UI结构设计 性能优化 视觉优化 如果你要继续往上走强烈建议我可以帮你做一套ESP32 ST7789 LVGL 最优架构实战版包括双缓冲/单缓冲怎么选SPI刷新极限优化UI不卡顿方案类似“手机界面”的UI结构直接说一句 “给我整一套LVGL架构”我给你上点真正工程级的东西 好这个我直接给你一套能落地的工程级 LVGL 架构ESP32 ST7789 SPI。不是讲概念是你可以直接照着改项目的那种。 一句话目标不卡顿 / 易扩展 / 易维护 / 适合做产品️ 一、整体架构核心思路 推荐结构app/ ├── main.c ├── lvgl_port/ ← LVGL移植层驱动、tick、flush ├── ui/ ← UI界面层 │ ├── screen_main.c │ ├── screen_setting.c │ ├── ui_style.c │ └── ui.h ├── widget/ ← 可复用组件 │ ├── ui_bar.c │ ├── ui_card.c │ └── ... └── bsp/ ├── lcd.c ├── touch.c如果有 └── backlight.c 二、LVGL 移植层最关键✔ 1. 双缓冲强烈推荐#defineLVGL_BUF_SIZE(240*40)// 一行一行刷推荐staticlv_color_tbuf1[LVGL_BUF_SIZE];staticlv_color_tbuf2[LVGL_BUF_SIZE];lv_display_set_buffers(disp,buf1,buf2,LVGL_BUF_SIZE,LV_DISPLAY_RENDER_MODE_PARTIAL);✔ 2. flush_cb核心性能点voiddisp_flush(lv_display_t*disp,constlv_area_t*area,uint8_t*px_map){intwarea-x2-area-x11;intharea-y2-area-y11;lcd_set_window(area-x1,area-y1,w,h);lcd_write_data(px_map,w*h*2);// RGB565lv_display_flush_ready(disp);} ⚠️ 重点必须 DMASPI不能阻塞太久✔ 3. tick系统心跳esp_timer_start_periodic(timer,2000);// 2msvoidlv_tick_task(void*arg){lv_tick_inc(2);}✔ 4. LVGL任务单独线程voidlvgl_task(void*arg){while(1){lv_timer_handler();vTaskDelay(pdMS_TO_TICKS(5));}} 三、UI 层设计重点✔ 1. 不要直接在 main 写 UI ❌ 用“屏幕模块化”✔ screen_main.c#includeui.hstaticlv_obj_t*scr;voidscreen_main_create(void){scrlv_obj_create(NULL);lv_obj_set_style_bg_color(scr,lv_color_hex(0x0f1113),0);lv_obj_t*labellv_label_create(scr);lv_label_set_text(label,Hello LVGL);lv_obj_center(label);lv_screen_load(scr);}✔ screen 切换lv_screen_load_anim(new_scr,LV_SCR_LOAD_ANIM_MOVE_LEFT,200,0,false); 四、组件化高级 比如你温度条widget/ui_bar.clv_obj_t*ui_temp_bar_create(lv_obj_t*parent){lv_obj_t*barlv_bar_create(parent);lv_obj_set_size(bar,200,20);lv_bar_set_range(bar,0,100);returnbar;} 用的时候lv_obj_t*barui_temp_bar_create(scr);⚡ 五、性能优化你这个项目重点 1. SPI 带宽限制最大瓶颈 你是 ST7789240x320 × 16bit ≈ 150KB / 帧 SPI 40MHz≈ 20~30 FPS 极限✔ 优化策略✅ 局部刷新必须LV_DISPLAY_RENDER_MODE_PARTIAL✅ 减少动画lv_bar_set_value(bar,val,LV_ANIM_OFF);✅ 禁用阴影 / 渐变lv_obj_set_style_shadow_width(obj,0,0);✅ 图片用 RGB565不要用ARGB8888 ❌炸内存 慢 六、UI设计建议你刚好遇到的坑❌ 别用纯黑0x000000❌ 用0x0f1113✅✔ 卡片式UI推荐背景深灰 卡片稍亮灰✔ 减少大面积刷新 比如不要全屏动画用局部组件更新 七、背光控制强烈建议ledc_set_duty(...,duty); 降低亮度黑更黑UI更舒服 八、最终结构总结LVGL架构核心 驱动层SPI DMA ↓ LVGL移植层flush/tick/task ↓ UI层screen ↓ 组件层widget 九、你现在这个水平我给你一个升级路线你现在已经会用LVGL ✅ 会写界面 ✅下一步建议1️⃣ UI结构化你刚开始做 2️⃣ 性能优化正在踩坑 3️⃣ 产品级UI下一阶段 最后一句很关键LVGL难的不是“会用API” 而是 怎么在SPI带宽限制下做“流畅UI”如果你下一步想更狠一点我可以帮你做一套“类似小米手环/智能屏 UI”的完整模板ESP32可跑包括页面切换动画控制状态栏卡片UI直接可以当产品原型用的那种