简介:HT1621B 驱动笔段式液晶显示屏的完整测试工程包,面向嵌入式开发中需要快速验证显示驱动逻辑的工程师,也适合正在学习笔段式液晶显示原理的初学者。资源共 31 个文件,以 IAR EWARM 完整工程为主体,包含 C 源码文件、IAR 工程配置(ewp/eww)、hex 烧录文件、xcl 链接配置文件,以及调试批处理、备份工程和输出目录,各部分用途清晰,便于对照学习。通过阅读这些文件,可掌握串行接口初始化、寄存器配置、显示数据写入和自动消影等关键代码路径,理解 HT1621B 驱动最多 48 个笔段、支持多种显示模式的实际用法。工程还体现了低功耗设计思路,适合电池供电的电子表、智能家居控制面板等场景,从驱动初始化到显示刷新均有完整示例可循。压缩包仅 144KB,结构紧凑,可直接参考其驱动框架完成移植。已有 1005 人学习,配套测试说明覆盖静态显示、动态显示、亮度调节等场景,结合示例工程能快速定位硬件连线或时序问题,有效缩短液晶驱动开发与验证周期。 前几天整理硬盘里的老物料,翻出一个有年份的压缩包:HT1621B驱动LCD测试过完整.rar。这个名字很朴素,但当年为它熬过好几个晚上。HT1621B这颗芯片在段码LCD驱动领域属于常青树,电子秤、温湿度计、血糖仪、万用表、智能电表上到处都有它的身影,体积小、外围省、价格低,一颗芯片加几颗电阻电容,就能把一块液晶玻璃变成“写显存就能显示”的外设。这篇东西不打算复述数据手册,而是把这个完整驱动从硬件接线、通信协议到代码实现和实测坑点重新梳理一遍,尤其在极化、feedthrough、对比度这几个容易翻车的地方多说几句。正在用51、STM32或ESP32折腾段码屏的朋友,可以直接当作业抄。
1. 为什么是HT1621B:一颗把段码LCD变成“简单外设”的芯片
1.1 段码LCD和点阵屏完全不是一回事
很多从OLED、TFT入门的开发者第一次拿到段码LCD会懵:屏幕怎么没有控制器?实际上段码LCD本质是一块电容性玻璃,亮度由COM线和SEG线之间的交流电压差决定,任何一条线上出现长时间直流分量都会导致液晶材料电解、极化,时间长了屏幕会出现永久黑斑。所以驱动段码屏不是简单给引脚加高电平就行,需要用固定的波形定时翻转COM和SEG电平,专业说法叫交流驱动。
这件事听起来不复杂,但用单片机纯IO口去做非常难受:先是引脚占用——显示8位数字加一堆图标,几十个段口一个都不能少;再是波形频率——刷新太快MCU忙不过来,刷新太慢屏幕会闪;还要保证波形严格对称,否则直流分量迟早搞坏玻璃。这就是HT1621B这类专用驱动芯片存在的意义。
1.2 HT1621B的核心优势
HT1621B是Holtek出的段码LCD驱动芯片,内部集成128 bit显示RAM、RC振荡器、偏压电阻网络和COM/SEG扫描逻辑。MCU只需要通过3线或4线串行接口把显示数据写进去,之后芯片自己生成交流驱动波形,MCU完全不用管刷新这件事。
它支持的配置也很灵活:最多32个SEG段口、4个COM口,可选1/2或1/3偏压、1/4到1/1占空比,工作电压大概2.4V到5.2V,一颗芯片就能带动一整块常用段码玻璃。和同系列对比,HT1621B是改进版本,HT1621A/B几乎兼容,而HT1622能支持更多COM、驱动更大的玻璃屏。对于绝大多数仪表类项目,HT1621B的128点已经足够。
| 型号 | 最大点阵 | 典型场景 |
|---|---|---|
| HT1621B | 128点(32 SEG x 4 COM) | 电子秤、温湿度计、血糖仪 |
| HT1622 | 256点(支持更多COM) | 多图标、多路数字显示面板 |
| MCU直接IO驱动 | 看引脚数 | 只适合极少量段且不计成本的情况 |
一句话总结:HT1621B把最麻烦的交流扫描和偏压管理全包了,MCU负责的只是“告诉它哪个段亮、哪个段灭”。这也是那个rar里驱动能“测试过完整”的根本原因——芯片分担了绝大部分时序压力。
2. 硬件连接与上电细节:显示正常的第一步
2.1 典型接线一览
段码LCD模组和HT1621B之间,通常是玻璃的COM/SEG引脚直接焊到芯片的COM0~COM3和SEG0~SEG31上,这部分由液晶玻璃厂商的图纸决定,不需要软件干预。开发者真正要关心的是MCU和HT1621B之间的接口。
| HT1621B引脚 | 接到哪里 | 说明 |
|---|---|---|
| VDD | 系统电源(3.3V或5V) | 就近放0.1uF去耦电容 |
| VSS | GND | 可靠接地 |
| VLCD | 偏压输入 | 决定对比度,具体见2.2 |
| CS | MCU GPIO | 片选,低有效 |
| WR | MCU GPIO | 写时钟,数据在上升沿锁存 |
| DATA | MCU GPIO | 双向数据线 |
| RD | VDD | 不需要读显存时直接接高 |
| COM0~COM3 | LCD玻璃COM引脚 | 直接连屏 |
| SEG0~SEG31 | LCD玻璃SEG引脚 | 直接连屏 |
我见过不少人把RD悬空,正常工作时问题不大,但悬空脚容易引入干扰,最好接VDD。如果只往HT1621B里写数据不读显存,RD接高就够了,读显存功能基本用不到。
2.2 VLCD偏压和对比度:别看只是颗电阻
VLCD是调节对比度的关键。HT1621B内部有偏压发生器,外部通常需要配合电阻分压网络或可调电阻,把偏压设置在合适电平。偏压太低,段码显示淡、像没睡醒;偏压太高,不该亮的段也会“鬼影”微亮,整个屏灰蒙蒙。
大多数厂商的段码玻璃模组会直接配套好偏压电阻甚至把电阻做在玻璃柔性板上,这种情况直接用默认配置即可。如果自己画板,建议VLCD到VDD之间留一个可调电阻焊盘,调试时先调到中间值,再根据显示效果微调。实测中常见问题是:为了追求显示浓,把VLCD调过头,结果出现全屏微亮,这时候不是程序问题,而是偏压过冲。
偏压和COM数量还要通过命令配置匹配,比如玻璃设计是1/3偏压、4 COM,软件里就写对应的BIAS/COM配置命令。这部分后面代码里会提到。
2.3 上电时序:别在电源爬坡时发命令
硬件上电时,建议等VDD稳定后再拉高CS和WR,然后延时几十毫秒再初始化HT1621B。曾经遇到过一个很怪的现象:板子上电瞬间屏幕显示一坨乱码,过一会儿自己好了。查到最后是MCU比HT1621B先跑起来,在电源还没稳定的时候就发出了初始化命令,芯片没能正确接收。解决方法是MCU启动后先延时100ms再初始化HT1621B,或者让HT1621B的VDD由MCU的IO控制先上电。
另外去耦电容要尽量靠近芯片VDD引脚,不要省。段码LCD是电容性负载,COM/SEG翻转瞬间电流不小,电源上有毛刺会导致显示抖动。
3. 通信协议和显存映射:驱动代码的地基
3.1 3线还是4线:多数情况用3线
HT1621B支持3线和4线两种接法。3线用CS、WR、DATA,不接RD,适合纯写应用;4线多一个RD口,可以从芯片读回显存内容。电子秤、仪表这类应用大多数只需要写显示数据,3线就够了,还能省一个GPIO。
时序本身不复杂,核心是“数据准备好之后,给WR一个上升沿”。CS拉低表示一次通信开始,WR上升沿把DATA上的电平锁存进芯片。每发一个bit,WR都要从低到高跳变一次。整个过程就是按位把数据“抖”进去,跟模拟SPI很像。
3.2 命令和数据:两种帧结构必须分清
HT1621B的通信帧分两种:写命令和写数据。格式差别在开头3位标志位。
| 帧类型 | 起始标志 | 后续内容 |
|---|---|---|
| 写命令 | 101 | 9位命令码 |
| 写数据 | 110 | 6位RAM地址 + 8位数据 |
这就是网上很多代码看着不一样的原因:有的函数传参传9位命令码,有的函数把“101”也算进去了。我看到过一堆驱动里写WriteCmd(0x28)、WriteCmd(0x52),其实说的都是BIAS/COM配置,只是代码里有没有把开头3位标志组装进去的区别。自己写驱动时,建议统一按“标志位 + 内容”的原始格式实现,底层函数封装好,后面改起来不迷糊。
常用命令有这么几条:
| 命令含义 | 9位命令码(不含101标志) | 说明 |
|---|---|---|
| System Enable | 0x001 | 打开系统振荡器,必发 |
| System Disable | 0x000 | 关闭系统,省电 |
| LCD On | 0x003 | 打开LCD输出 |
| LCD Off | 0x002 | 关闭LCD输出,显示全灭 |
| BIAS/COM配置 | 0x052(常见值) | 1/3偏压、4 COM,务必按玻璃手册调整 |
注意:BIAS/COM配置的值取决于具体液晶玻璃,0x052是很多4 COM小玻璃常用的写法,但不是所有屏通用。拿到新屏第一件事是查玻璃规格书,确认偏压是1/2还是1/3,COM是几位,再填这个命令。
3.3 RAM地址和COM/SEG的关系
HT1621B内部显示RAM共128 bit,组织方式可以理解为一张表:横向SEG地址,纵向COM位。写入数据时,6位地址指向某个SEG地址,8位数据中的每一位对应COM0~COM3(4 COM模式下只用到低4位)。
这里有个新手最容易犯的错:想点亮某个笔段,以为是按COM或SEG直接编址,结果写进去发现错位。实际上要拿到液晶玻璃的“段位映射图”,看这个笔段接在SEG几、COM几,然后到对应的RAM地址和位上去写1。
因为一次WriteData会覆盖某个地址的8位数据,直接操作某个笔段会破坏同地址其他COM位的状态。稳妥做法是MCU里维护一个32字节的显示缓冲Buffer,先改Buffer再整段刷新,或者用Read-Modify-Write流程从芯片读回原值再改位。绝大多数驱动都用Buffer方式,代码简单还不依赖RD引脚。
4. 完整驱动实现:可抄作业的代码
4.1 底层IO时序:WR上升沿锁存
下面以8051为例给一套完整实现。IO定义根据自己板子改,核心逻辑通用:
// ht1621b.c 部分 #include <reg52.h> #include <intrins.h> sbit HT1621_CS = P2^0; sbit HT1621_WR = P2^1; sbit HT1621_DAT = P2^2; // 简单延时,按MCU主频调整 static void HT1621_Delay(void) { _nop_(); _nop_(); _nop_(); } // 发送1bit:先WR拉低,放数据,再WR拉高,上升沿锁存 static void HT1621_SendBit(unsigned char dat) { HT1621_WR = 0; HT1621_DAT = dat ? 1 : 0; HT1621_Delay(); HT1621_WR = 1; HT1621_Delay(); } // 发送cnt个bit,从高位开始 static void HT1621_SendBits(unsigned long dat, unsigned char cnt) { unsigned char i; for (i = 0; i < cnt; i++) { unsigned char bitv = (unsigned char)((dat >> (cnt - 1 - i)) & 0x01); HT1621_SendBit(bitv); } }顺序不能反:必须先WR=0,再送DATA,最后WR=1。如果先送DATA后拉WR,虽然多数情况下也能工作,但WR上升沿可能正好落在DATA转换的毛刺上,高温或线长时容易随机出错。
4.2 命令封装与初始化
// 写命令:101标志 + 9位命令码 void HT1621_WriteCmd(unsigned short cmd) { HT1621_CS = 0; HT1621_SendBits(0x05, 3); // 0x05 二进制就是 101 HT1621_SendBits(cmd, 9); HT1621_CS = 1; } // 写数据:110标志 + 6位地址 + 8位数据 void HT1621_WriteData(unsigned char addr, unsigned char dat) { HT1621_CS = 0; HT1621_SendBits(0x06, 3); // 0x06 二进制就是 110 HT1621_SendBits(addr, 6); HT1621_SendBits(dat, 8); HT1621_CS = 1; } // 初始化 void HT1621_Init(void) { HT1621_CS = 1; HT1621_WR = 1; HT1621_DAT = 1; HT1621_Delay(); HT1621_WriteCmd(0x001); // System Enable HT1621_WriteCmd(0x052); // 1/3 bias, 4 COM,按你的玻璃手册调整 HT1621_WriteCmd(0x003); // LCD On HT1621_Clear(); }初始化顺序值得注意:先System Enable,再配置BIAS/COM,最后LCD On。反过来先打开LCD输出再配置偏压,上电瞬间可能出现短暂乱码。这里把Clear函数放在LCD On之后是有意的——先把显示内容清空再开输出,避免旧显存内容在屏幕上闪一下。
4.3 显示Buffer和段码表
unsigned char HT1621_Buf[32]; // 32个SEG地址,4 COM模式低4位有效 // 把某个SEG地址、某个COM位的状态写进Buffer void HT1621_SetSegBit(unsigned char segAddr, unsigned char com, unsigned char on) { if (on) HT1621_Buf[segAddr] |= (0x01 << com); else HT1621_Buf[segAddr] &= ~(0x01 << com); } // 整屏刷新 void HT1621_Refresh(void) { unsigned char i; for (i = 0; i < 32; i++) HT1621_WriteData(i, HT1621_Buf[i]); } void HT1621_Clear(void) { unsigned char i; for (i = 0; i < 32; i++) HT1621_Buf[i] = 0; HT1621_Refresh(); }这里必须强调:代码里的HT1621_SetSegBit(segAddr, com, on)只是底层设置函数,具体显示“数字0”、“数字1”需要一张段码表,把每个笔段对应到具体的SEG地址和COM位。每个厂家玻璃的走线都不一样,网上抄来的表基本不能直接用。我拿到新屏的第一件事,就是找屏幕厂商要段位走线图,然后在Excel里画一张“SEG/COM对应笔段”的表,再写成C数组。这一步偷懒,后面全是坑。
4.4 亮度调节和闪烁:两个实用小技巧
HT1621B没有直接的“软件亮度寄存器”,但可以通过周期性切换LCD On/LCD Off来实现视觉上的亮度调节。思路是在定时器中断里做一个PWM:每10ms一个周期,LCD On持续时间和LCD Off持续时间按目标亮度分配。
unsigned char brightness = 8; // 0~10,越大越亮 void Timer_ISR(void) // 假设定时器每1ms中断一次 { static unsigned char cnt = 0; cnt++; if (cnt >= 10) cnt = 0; if (cnt < brightness) HT1621_WriteCmd(0x003); // LCD On else HT1621_WriteCmd(0x002); // LCD Off }注意这个方案有代价:频繁发命令会占用MCU时间,也会增加芯片功耗和EMI。量产品上我更推荐用硬件调VLCD电压或换偏压档位来调节对比度,软件PWM只适合调试阶段或临时演示。
闪烁功能就简单了,定时器里每一定周期让整屏显隐切换即可。只闪某个数字的话,在Buffer里把那一位的段全部清零或点亮,再调用HT1621_Refresh()。
5. 实测踩坑:极化、feedthrough和“鬼影”问题
5.1 LCD极化不是玄学:长时间直流会毁屏
段码LCD的液晶材料一旦长时间承受直流电压,会发生电化学反应,产生不可逆的“极化”损坏,表现是屏幕某个区域变黑、出现残影,或者某段永远显示。这就是“LCD极化避免”要解决的问题。
HT1621B在正常工作时,内部会交替改变COM和SEG的电压极性,保证每个像素上平均直流分量为零,所以芯片本身已经把极化风险处理掉了。但调试时经常有人图省事,用杜邦线直接给玻璃的COM和SEG接VDD和GND测试,看某个段亮不亮。这种做法很危险,几秒钟可能没事,时间稍长液晶就废了。正确做法永远是先写好HT1621B的初始化,通过WriteData点亮笔段。
另外,屏幕长时间显示同一内容,虽然波形是交流的,但部分段长期处于同一偏置状态,可能出现类似烧屏的记忆效应,软性段码屏尤其明显。我的习惯是产品里做一个“屏幕保护”逻辑:如果画面长时间不变,每隔一两秒把所有段全亮一下再恢复,把肉眼不可见的电荷积累打散。这个在医疗和工控仪表里很常见。
5.2 feedthrough是怎么来的:寄生电容在捣乱
feedthrough(穿透效应)是段码LCD驱动里一个很经典的现象。LCD的COM线和SEG线在玻璃上并行布得很近,之间存在寄生电容。当COM线电压翻转时,电荷会通过寄生电容耦合到SEG线上,导致SEG电压瞬间被抬高或压低。
如果这个瞬态幅度恰好超过液晶的阈值电压,本不该亮的段就会“偷亮”一下。屏幕内容一直在变时,人眼看到的是某些段有轻微闪烁或重影;固定画面时表现为个别笔段微微发灰,尤其偏压调高后更明显。
解决feedthrough有固定的三板斧:
- 降低VLCD电压,让偏压刚好满足显示对比度即可,别追求越浓越好
- 检查偏压配置的档位,1/3偏压和1/2偏压对阈值裕量影响很大
- 在PCB布局上注意COM/SEG走线不要过长平行,减少寄生电容耦合
软件上能做的有限,但有个细节:WR翻转不要用超高速,给LCD驱动波形留一点爬坡时间,有时候能明显减少“偷亮”。我在某个项目里把IO翻转速度从最高档降到中等档,鬼影就消失了。
5.3 一次花屏排查链路:从电源到波形的完整排查
有个典型故障值得复盘:板子通电后全屏正常,但刷新数字时随机出现几条“毛刺段”闪一下,有时还出现显示变淡。
排查链路是这样的:
- 先看电源。示波器表笔贴近HT1621B的VDD引脚,发现刷新瞬间电源上有明显毛刺,幅度接近200mV。原因:屏幕刷新瞬时电流大,去耦电容放得太远。
- 在VDD和VSS之间补了0.1uF陶瓷电容,毛刺降到50mV以内,但问题没消失。
- 再看VLCD电压,发现调得偏高,屏幕整体有点“蒙”。适当降低偏压后,闪段明显减少。
- 最后检查WR线,发现MCU的IO翻转速度过快,加上杜邦线太长,信号反射严重。降低翻转速度、拉长每bit延时后,问题彻底解决。
这个案例说明:段码屏显示异常,很多时候不是代码逻辑错,而是电源、偏压、信号完整性三个环节的连锁反应。排查顺序建议固定为:电源纹波 -> VLCD/偏压 -> 通信时序 -> 布线。
5.4 对比度调整的正确顺序
对比度太低、太强、有鬼影,处理顺序应该是:先确认偏压档位命令和玻璃一致,再调VLCD电阻,最后才考虑是不是feedthrough。很多人一上来就改代码,折腾半天发现是VLCD电阻焊错了。
另外调试时手上最好有一个精密可调电位器临时代替固定电阻,找到合适阻值后再换成固定电阻。我曾经图省事直接焊固定电阻,结果换了批次玻璃后对比度偏淡,不得不重新打样,教训深刻。
6. 从51到ESP32:快速移植与扩展思路
6.1 把IO模拟时序搬走的三个改动点
这个驱动最大的优点就是纯IO模拟,几乎不依赖单片机外设,移植到ESP32、STM32只改三处:
- IO宏定义:51的
sbit换成gpio_set_level或HAL库的HAL_GPIO_WritePin - 延时函数:51的
_nop_()换成几个微秒的延时,ESP32上用esp_rom_delay_us - 底层IO初始化:把CS、WR、DAT三个GPIO配成推挽输出
SPI硬件外设也能驱动HT1621B,但要注意HT1621B不是标准SPI从机,没有寄存器地址自动递增这类特性,硬件SPI反而容易在时序细节上吃亏。我自己试过用ESP32的SPI主机方式驱动,时钟极性、相位都要调,而且CS控制不直接,最后又换回IO模拟。除非对性能有极致要求,否则IO模拟是更稳的选择。
6.2 没有段码表怎么下手
从网上下载的“完整驱动”最缺的东西往往是段码表。真没有图纸时,可以自己“扫”出来:写一个测试程序,循环对每个SEG地址、每个COM位写1,刷新后拍照记录哪个笔段亮了,人工整理出一张映射表。这个过程枯燥,但很可靠。
注意:有些玻璃的SEG/COM在PCB上经过了跳线或转接,不一定是芯片引脚顺序直接对应玻璃引脚顺序。扫描时要以芯片的SEG0~SEG31、COM0~COM3为基准,反复对照照片标注。
6.3 这个驱动还能用在哪些项目里
HT1621B适合所有段码LCD显示场景,典型的有:
- 电子秤:称重模块+HT1621B+4位数码段码屏,成本极低
- 温湿度计:显示温度、湿度、时间、闹钟图标
- 电池电量表:显示电量百分比和电池图标
- 小家电面板:微波炉、咖啡机的状态显示
我后来把这段驱动封装成简洁的接口,更换不同液晶玻璃时只需要替换一张段码表和BIAS/COM配置命令,其余代码原封不动。那次踩完“换批次玻璃对比度变淡”的坑之后,我在每个项目设计阶段都会提前找屏幕厂要段位图,并且把偏压电阻位做成可调电阻焊盘。最近还在做的一件事是把HT1621B驱动和低功耗唤醒结合,芯片进去休眠前清空显示并关掉LCD On,唤醒后重新初始化,功耗能压低不少。这个方向如果你也在做电池供电的段码屏产品,值得顺带研究一下。
本文还有配套的精品资源,点击获取