拓冰建站拓冰建站
首页 / 资讯中心 / 正文

160128液晶模块开发实战:STM32驱动、显存组织与工业现场排障

前阵子帮朋友改造一台用了多年的工业仪表原有的黑白 LCD 屏已经发暗看不清原型号又早停产。思来想去换上了驰宇微 160128 液晶模块160×128 点阵的分辨率足够复刻原机的整页界面宽温特性也比 TFT 方案稳得多。整块屏从接线、驱动到现场调试大概花了一周过程中踩了几个不是屏本身、但比屏本身更能耽误事的坑。这篇就把整个开发过程拆开讲硬件怎么接、初始化代码怎么写、显存怎么组织以及工业现场常见的故障排查思路。如果你是第一次用 160128建议从第 2 节开始看手头已经在调屏的直接看第 5 节和第 6 节应该能帮你少走几趟现场。1. 为什么工业设备还在用 160128 这类点阵屏1.1 在 TFT、串口屏和 OLED 之间绕了一圈先说选型。很多人第一反应是都什么年代了还用单色点阵液晶直接上 TFT 串口屏不是更省事吗我一开始也是这么想的后来在项目里对比了一圈发现实际情况没那么简单。TFT 串口屏开发确实方便但成本和体积摆在那里几十块钱的物料成本在工业产品里可能不明显一旦产品要跑量整机成本差异立刻放大。TFT 在强光下的表现也是硬伤高亮背光在室外阳光直射下反而看不清为了压过环境光背光亮度只能往高了调功耗跟着上去。高温场景下 TFT 液晶的老化速度也比 STN 屏快机箱内部六十到七十度连续开机彩色屏出现偏色、亮度衰减的案例不少见。OLED 在阳光下同样不占优势而且静态画面用久了会担心烧屏工业界面偏偏又大多是静态信息这点很不友好。12864 点阵屏倒是经典但它每屏只能显示 16 列乘 8 行的 8×8 字符稍微复杂一点的菜单页面就要翻好几页。160128 在中间找到了一个平衡点单屏能显示 20 列乘 16 行 ASCII 字符还能画曲线、画表格界面复用价值高逻辑部分功耗又低。方案分辨率单屏信息量强光与高温表现功耗适合场景12864128×648×8 字模下 16 列 8 行不错经典低简单提示类界面160128160×12820 列 16 行可绘图宽温版较稳太阳下可读背光约 60-120mA工业仪表、医疗设备、HMITFT 串口屏320×240 起步信息量大强光需高亮背光高温衰减偏快较高图形密集、用户体验要求高1.2 拿到屏先确认三件事控制器、玻璃类型、视角方向这个建议听起来基础但我见过太多人拿到模块直接上网找“160128 驱动代码”结果怎么调都是花屏。因为 160128 只是一个显示规格不同厂家、不同批次用的控制器可能不一样初始化命令完全不同。常见的控制器有 S1D15705 以及兼容型号也有模块用 ST7529、T6963C 这一类的接口时序和指令集都有差异。拿到屏以后第一件事是看模块背面丝印找控制器型号或者直接向厂家要规格书和初始化例程。驰宇微的模块一般会明确标出控制器型号资料也比较齐全这一步不算难。确认不了控制器型号就盲目写驱动等于蒙着眼睛调屏后面大概率会浪费一天时间在奇怪的显示错位上。玻璃类型同样影响视觉验证。常见的 160128 屏有黄绿底黑字、蓝底白字和 FSTN 黑白显示几种。黄绿屏的对比度在室内看得舒服但视角有方向性安装位置偏低就要选 12 点钟视角的型号否则从操作员站位看过去画面发灰发淡。这个参数在选型时就要定死PCB 结构和安装角度都定了之后想换视角方向往往得重新开模具。还有一个经常被忽略的是工作温度范围。标准型模块和宽温型模块的成本差不了太多但实际环境温度直接决定了液晶是否会“冻住”或“发黑”。户外设备建议直接问厂家要 -20℃ 到 70℃ 的宽温版本并确认控制器内部有没有温度补偿机制。价格贵不了多少省下的却是冬天现场售后的人工成本。2. 硬件接线引脚定义、电源偏压和时序2.1 引脚速查与 STM32 接线实例160128 模块的引脚排列不同厂家会有差异但绝大多数走的是通用 8080 并行接口引脚类型基本一致DB0-DB7、CS、RS(A0)、RD、WR、RESET、VSS、VDD、V0(偏压)、LEDA、LEDK。有几个模块还会把背光负极和逻辑地分开方便做背光开关控制。引脚符号方向作用DB0-DB7输入并行数据总线CS输入片选低电平有效RS/A0输入命令(0)/数据(1)选择RD输入读选通低电平有效WR输入写选通低电平有效RESET输入复位低电平有效VDD/VSS电源逻辑电源V0模拟LCD 偏压调节LEDA/LEDK电源背光正极/负极以 STM32F103 为例我做了一个便于移植的 GPIO 接线模块引脚MCU 引脚DB0-DB7PA0-PA7RS(A0)PB0WRPB1RDPB2CSPB3RESETPB4背光控制PB5 经三极管/MOS 管控制数据线要连续占据同一个端口的低 8 位这样写数据时直接操作 ODR 寄存器一次赋值效率高。如果数据线分散在不同端口说明文档时够用但实际刷新一帧画面要操作多次寄存器CPU 占用差别很大。接线时还有一个容易出问题的地方3.3V 逻辑的模块最好不要直接接 5V 单片机的数据线。有些模块标注的是宽电压5V 电平可以直接兼容但很多 COG 封装的模块逻辑电压只有 3.3V数据线上灌 5V 电平短时间看不出问题长时间运行会加速控制器损坏。稳妥的做法是加电平转换或者把 MCU 的 IO 配置为开漏模式并外接上拉到 3.3V。2.2 偏压 Vop 和背光电源的计算V0 是很多人第一次接线时会忽略的引脚。V0 决定了液晶的对比度电压不合适可能出现“全黑屏”或者“全灰屏”看起来像没驱动实际上是偏压不对。带内部升压电路的控制器一般通过命令设置 Vop外部 V0 引脚只需要接一个上拉电阻或者直接悬空没有内部升压的模块则需要在 V0 和 VSS 之间接一个 10K 电位器通过分压调整对比度。背光则要单独算。白色 LED 背光的典型正向压降在 3.0V 到 3.2V 之间单颗灯珠电流约 20mA一整块 160128 屏通常有 4 到 6 颗灯珠并联总电流 80 到 120mA。如果直接用 5V 电源串联限流电阻限流电阻上的压降是 1.8V 到 2V在 100mA 电流下功耗有 0.2W电阻温度会明显升高。公式很简单R (VCC - VF) / IF假设 VCC5VVF3.2VIF0.1A算出来就是 18 欧姆。但这个电阻只解决了“有电”解决不了电压波动引起的背光亮度变化而且电阻发热会传导到液晶边缘。工业项目里我建议干脆用一颗 100mA 左右的恒流驱动芯片或者用三极管搭一个简易恒流源成本几毛钱亮度稳定寿命也更好。背光电源必须和逻辑电源分开走线。很多“屏幕显示正常但时不时闪一下”的毛病就是背光 PWM 调光的电流尖峰串到了逻辑电源上。2.3 写时序为什么要自己控制160128 这类模块的接口速度并不快写周期典型值在百纳秒到微秒级别。TFT 常用的 FSMC 高速总线直接怼上去不一定能稳定工作因为 FSMC 的时序参数是按外部存储器的典型值设计的对这类老式液晶控制器来说通常太快。我的建议是先用 GPIO 模拟时序点屏等画面正常以后再考虑要不要切到 FSMC。GPIO 模拟写操作的逻辑顺序很简单片选 CS 拉低。根据是命令还是数据设置 RS 电平。把数据写到数据总线上。WR 拉低保持至少几百纳秒。WR 拉高写入完成。片选拉高。读操作会多一步 RD而且读数据时数据线要切换成输入方向。很多实际项目只用写不用读因为控制器内部显示 RAM 可以被主机缓存一份副本没必要频繁回读。3. 驱动代码从一根线点亮到像素坐标系统3.1 底层读写函数先把底层写函数固定下来。下面的代码基于 STM32 标准外设库风格换到 HAL 只需把位操作替换成 HAL_GPIO_WritePin。#define DATA_PORT GPIOA #define LCD_RS_PORT GPIOB #define LCD_RS_PIN GPIO_PIN_0 #define LCD_WR_PORT GPIOB #define LCD_WR_PIN GPIO_PIN_1 #define LCD_RD_PORT GPIOB #define LCD_RD_PIN GPIO_PIN_2 #define LCD_CS_PORT GPIOB #define LCD_CS_PIN GPIO_PIN_3 #define LCD_RST_PORT GPIOB #define LCD_RST_PIN GPIO_PIN_4 static void lcd_pin_set(GPIO_TypeDef *port, uint16_t pin, uint8_t level) { if (level) port-BSRR pin; else port-BRR pin; } static void lcd_data_out(uint8_t dat) { DATA_PORT-ODR (DATA_PORT-ODR 0xFF00) | dat; } static void lcd_write(uint8_t rs, uint8_t dat) { lcd_pin_set(LCD_CS_PORT, LCD_CS_PIN, 0); lcd_pin_set(LCD_RS_PORT, LCD_RS_PIN, rs); lcd_pin_set(LCD_RD_PORT, LCD_RD_PIN, 1); lcd_data_out(dat); lcd_pin_set(LCD_WR_PORT, LCD_WR_PIN, 0); delay_us(1); lcd_pin_set(LCD_WR_PORT, LCD_WR_PIN, 1); lcd_pin_set(LCD_CS_PORT, LCD_CS_PIN, 1); } static void lcd_cmd(uint8_t cmd) { lcd_write(0, cmd); } static void lcd_data(uint8_t dat) { lcd_write(1, dat); }这里 delay 用 1 微秒只是一个保守值。如果你查到手头模块手册里写时间指标比这个小可以酌情缩短但这类屏对写入速度并不敏感保持稍慢一点反而更稳。3.2 初始化序列里的每个命令都在干嘛初始化是每个项目重复折腾最多的地方。以小尺寸 160128 模块常用的 S1D15705 系列控制器为例常见的初始化流程如下。注意不同批次模块的实际命令可能略有差别务必以模块附带的数据手册为准。void lcd_init(void) { // 硬件复位拉低至少 10us lcd_pin_set(LCD_RST_PORT, LCD_RST_PIN, 0); delay_us(10); lcd_pin_set(LCD_RST_PORT, LCD_RST_PIN, 1); delay_us(20); lcd_cmd(0xE2); // 系统复位命令 delay_us(5); lcd_cmd(0x2F); // 打开内部升压/电源电路 lcd_cmd(0x40); // 显示起始行设为第 0 行 lcd_cmd(0xA0); // 列输出方向正常扫描 lcd_cmd(0xC8); // 行输出方向反向扫描 lcd_cmd(0x25); // 选择内部 Vop 调节电阻 lcd_cmd(0x81); // Vop 设置命令入口 lcd_cmd(0x35); // Vop 具体值需要按实际面板微调 lcd_cmd(0xA5); // 进入全屏显示模式用于验证 delay_ms(30); lcd_cmd(0xA4); // 退出全屏显示模式 lcd_cmd(0xAF); // 开启显示 }初始化做完以后屏幕如果显示整屏黑块说明 Vop 值太低没有足够的偏压让液晶翻转如果整屏白亮通常是 Vop 太高或者扫描方向设置反了。遇到图像上下颠倒就交换 0xA0/0xA1左右颠倒就交换 0xC0/0xC8。这组命令没有绝对正确的值只有和你面板匹配的值。调试初始化时有个省时间的办法把 0xA5 全屏测试命令放在开显示之前如果全屏点亮正常说明控制器和电源部分都没问题问题大概率出在后续的扫描方向和 Vop 设定上。3.3 显存缓冲160×128 在代码里怎么组织160128 点阵屏的一页通常按“8 行一页”来组织128 行就是 16 页每一页有 160 列。对 S1D15705 这类的控制器来说显存地址可以理解为“页地址 列地址”页地址从 0 到 15列地址从 0 到 159每写一个字节数据控制器内部列地址会自动加一写到 159 后再加一才会跳页。代码层面最稳妥的做法是在单片机内部维护一份 160×16 字节的显示缓冲区。每个字节代表一列中垂直排列的 8 个像素。像素坐标 (x, y) 到缓冲区的换算关系是#define LCD_COLS 160 #define LCD_PAGES 16 static uint8_t lcd_disp_buf[LCD_COLS * LCD_PAGES]; static void lcd_buf_set_pixel(uint8_t x, uint8_t y, uint8_t on) { uint8_t page y 3; uint8_t bit y 7; uint8_t mask 0x80 bit; // 高位对应上面一行 uint16_t index page * LCD_COLS x; if (on) lcd_disp_buf[index] | mask; else lcd_disp_buf[index] ~mask; }这里把字节的高位对应屏幕上面的行低位对应下面的行是一种常见约定。如果你的模块扫描方向相反把 mask 改成0x01 bit就能纠正。写完缓冲区之后要把变化区域刷到屏幕上。控制器写命令通常是设置起始位置然后连续写数据。static void lcd_set_pos(uint8_t x, uint8_t page) { lcd_cmd(0x00 | (x 0x0F)); // 列地址低 4 位 lcd_cmd(0x10 | ((x 4) 0x0F)); // 列地址高 4 位 lcd_cmd(0xB0 | (page 0x0F)); // 页地址 } void lcd_flush_area(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { uint8_t p0 y0 3; uint8_t p1 y1 3; for (uint8_t page p0; page p1; page) { lcd_set_pos(x0, page); for (uint8_t x x0; x x1; x) { lcd_data(lcd_disp_buf[page * LCD_COLS x]); } } }这套缓冲加局部刷新的架构是后面所有绘图、字符、界面代码的基础。先把它跑通后面的显示功能都只是往缓冲区里画点。4. 显示内容开发字库、曲线刷新与多页菜单4.1 ASCII 字模和汉字字模的处理在缓冲区架构下字符的本质就是“按规则把一批像素点亮”。ASCII 8×16 字体每个字符 16 个字节常规点阵字库文件里一般按“逐行从左到右”的顺序存前 8 个字节对应上半部分的 8 行后 8 个字节对应下半部分。需要把它们转换为缓冲区里的垂直像素到位。void lcd_show_char8x16(uint8_t x, uint8_t y, uint8_t ch, uint8_t bg_clear) { const uint8_t *font font8x16 (ch - 0x20) * 16; for (uint8_t i 0; i 16; i) { uint8_t line font[i]; for (uint8_t b 0; b 8; b) { uint8_t mask 0x80 b; if (line mask) { lcd_buf_set_pixel(x b, y i, 1); } else if (bg_clear) { lcd_buf_set_pixel(x b, y i, 0); } } } lcd_flush_area(x, y, x 7, y 15); }这个函数嵌套了两层循环效率不算高但在 STM32F103 这种主频 72MHz 的 MCU 上显示几个字符完全没压力。如果界面要频繁刷新大量文本可以用查表法把常用的 0 到 9、负号、小数点这些字符单独做成常量数组减少乘法寻址的消耗。汉字 16×16 字模的处理方式类似只是每个字 32 字节常见取模工具默认输出“逐列式”或“逐行式”两种格式。用 PCtoLCD2002 一类的软件取模时建议选择“逐列式、阴码、顺向”这样能直接和上方lcd_buf_set_pixel的像素方向对上。如果取出来的字上下颠倒把取模软件里的“逆向”选项改一下就行。4.2 带缓冲的局部刷新算法很多第一次接触点阵屏的人会写出这样的主循环每 100ms 调用一次全屏刷新把 160×16 个字节全部重新写一遍。这在代码上最简单但在工业现场会有两个问题。第一全屏刷新期间如果 CPU 还要处理采样和控制任务很容易产生抖动第二刷屏时画面闪烁感明显尤其是动态数值跳动的时候。局部刷新的思路是只更新变化了的矩形区域。比如一个温度数值只占了屏幕右上角 40×16 像素的范围改名时只需要 flush 这一小块区域。上面的lcd_flush_area已经实现了区域限制实际调用时就是多传几个边界参数。void lcd_show_temperature(float temp) { char str[8]; snprintf(str, sizeof(str), %6.1f, temp); // 假设温度显示区域左上角在 (100, 16) lcd_clear_area(100, 16, 139, 31); // 先清背景 lcd_show_string8x16(100, 16, str, 0); }这样就避免了整屏刷新看起来界面也清爽。需要注意的是如果控制器写命令不支持“循环连续写一页后自动回卷”区域刷新跨越多个页时每一页都要重新设置列起始地址。4.3 工业界面常用的小技巧实际界面开发中有几个很实用的小技巧属于那种“代码不难但没做过就想不到”的。第一曲线动态滚动。记录最近 100 个采样值每次新数据进来把整条曲线左移一个像素再画最后一个点。这个过程在缓冲区里就是一次内存拷贝再 flush 曲线区域比全部重绘快得多。第二准备两套界面模板。正常运行页面显示实时数值和曲线故障页面显示状态码。两套模板复用同一套绘图函数只是绘制逻辑不同。这样页面切换时不需要重新编译整幅画面不容易出现显示残留。第三多用反色强调关键信息。把缓冲区里某个区域的所有字节全部异或 0xFF再刷新区域就能做出高亮反色效果。这个操作很快在菜单选中项和报警提示上非常好用。第四注意控制写屏频率。显示动态数据时一般 200ms 刷新一次就足够人眼感觉不到卡顿CPU 也有余量。千万不要在定时中断服务函数里直接刷屏复杂操作会拉长中断响应时间。5. 现场最容易踩的五个坑5.1 电源纹波引起的显示斜纹第一个坑是显示画面出现稳定的斜纹或者水平方向有一道道浅色条纹数值刷新时条纹还会跟着缓慢滚动。很多人第一反应是屏坏了换一块屏还是同样问题最后才发现是电源纹波。这类模块的逻辑电源对纹波比较敏感电源上叠加的噪声会导致控制器内部数据读写出错。我用示波器测过故障现场的 VDD 纹波超过 100mV源头是设备里一个功率不大的开关电源。解决办法是在模块电源输入端加一个由磁珠和 100uF 电容组成的 LC 滤波背光电源单独走一路。如果用的是 DC-DC 转换器还建议把开关频率调到模块工作不会误触发的范围外。5.2 宽温环境下的对比度漂移第二个坑发生在冬天。设备在室外冻了一夜早上开机屏幕对比度变得很淡过十几分钟温度上来才慢慢恢复。这是因为液晶材料的阈值电压随温度变化温度越低需要的驱动电压越高。控制器内部如果带温度补偿默认值和你的面板匹配不一定好不带温度补偿的模块就要在软件里做补偿。我的做法是在 EEPROM 里存一组温度补偿系数程序读取板载温度传感器的值按线性插值调整 Vop 寄存器。最基本的形式是static uint8_t lcd_temp_adjust(int8_t temp) { // 不同面板特性不同以下数值为参考必须实际标定 if (temp -10) return 0x3A; if (temp 10) return 0x36; if (temp 40) return 0x32; return 0x2E; }标定时把设备放进高低温箱从 -20℃ 到 70℃ 每 10 度记一组最佳 Vop 值再拟合成表。这个工作一次做完后面所有同类设备都能复用。5.3 复位与上电时序造成的花屏第三个坑和设备上电时序有关。模块正常工作时没问题但 MCU 复位或附近接触器吸合瞬间屏幕偶尔会变成随机花屏要断电重上电才能恢复。这个现象多半是复位脚被干扰拉低或者 VDD 还没稳定时控制器就收到了执行命令。解决分两步。硬件上RESET 引脚加一个 10KΩ 上拉到 VDD并联 1uF 电容到地做一个简单的复位延时电路没用的 RD 引脚不要悬空直接上拉到 VDD。软件上初始化时把系统复位命令放在靠前位置并且在上电延时 50ms 后再进入初始化避免 VDD 爬坡期间执行写操作。5.4 排线和连接器在振动环境下的接触问题第四个坑在振动场合尤其明显。屏幕用一段时间后出现局部行不显示或者用手指按一下屏幕某个角显示就恢复正常过一会儿又不正常。这通常不是控制器坏了而是排线连接器接触不良。工业设备如果长期运行在振动环境排线座加卡扣是最好的选择而且排线要固定住不能悬空晃。维修时也要注意焊接模块引脚时烙铁温度尽量不要超过 320℃单个引脚焊接时间控制在 3 秒以内避免焊盘铜箔和玻璃基板连接处过热导致内部引线断裂。很多人修完排线没多久又坏就是焊接温度没控制住伤了内部连接。5.5 背光功耗和低压差稳压器的发热第五个坑是背光功耗。前面说过白色 LED 背光总电流在 80 到 120mA。如果控制板用的是 LDO 线性稳压输入 24V 经过 LDO 降到 3.3V光是背光这一路就可能产生两瓦多的热这在小机箱里是很严重的发热源。即使输入只有 5VLDO 的耗散也不容小觑。工业项目里最好把背光电源单独用一颗 DC-DC或者直接采用开关型 LED 驱动。如果必须用 LDO也要选输入输出电压差小的型号并且让背光电流不要长期工作在最大额定值。6. 量产前验收与多供应商替换注意点6.1 一份能直接用的出厂检验清单很多项目开发调通了就直接量产结果第一批板子上线就被现场温度打回来细究原因就是没做严格的环境验证。下面这份清单是从几个项目里总结出来的可以直接拿去做投产前测试。测试项目方法通过标准低温启动-20℃ 放置 2 小时通电 1 分钟观察无花屏、无永久性暗线高温老化70℃ 连续运行 72 小时无偏色、无区域性斑块温度冲击-20℃ 到 70℃ 循环 25 次无玻璃裂纹、无水汽渗入振动试验10-500Hz 扫频 1 小时排线无松动显示无闪烁静电放电接触放电 4kV空气放电 8kV画面无永久性异常电源纹波示波器测量 VDD/Vop 引脚纹波低于 100mV视角确认整机装配后在操作员站位观察显示清晰无灰底反白这些测试不是说都要在产线逐台做而是到货抽检和首件确认时至少要做一遍。尤其温度冲击和振动这两项最容易暴露排线和连接器的潜在问题。6.2 同型号不同批次屏的兼容性问题最后说一个在原厂备料时容易掉坑的事。160128 这类老规格产品不同批次换用不同控制器并不罕见。生产计划里如果要对标另一家供应商或者采购时发现驰宇微这批货的控制器型号和上一批不同一定要在程序里做适配。我的做法是在驱动层定义一组初始化配置结构体把列扫描方向、行扫描方向、Vop 初值、命令入口这些参数全部做成可配置项。硬件版本检测靠 PCB 上的拨码电阻或控制器型号读取来实现程序启动时先读配置再执行对应初始化。这样换屏不需要改代码只需要更新配置文件。同型号不同批次之间哪怕控制器相同Vop 的最佳值也会有微小差异。量产时可以在产线校准环节把一块标准屏的显示亮度作为基准用式样调节 Vop 值后写入 EEPROM保证同一批产品画面观感一致。这个流程听起来多了一道工序但能省掉后续大量售后抱怨。从我这几个月的使用来看160128 已经不年轻但把它当一块正常外设而不是黑盒把接口时序、显示缓冲、电源噪声这些问题捋清楚它反而比很多新器件更能扛住现场环境。上面这些坑也都是现场调试调出来的不是看书看出来的。如果你的项目也在用类似的点阵屏照着这篇的思路检查一遍应该能省下不少现场跑路的时间。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门