行业资讯
📅 2026/9/1 3:53:20
STM32驱动HT1625段式液晶:从硬件接法到段码映射的完整实战
简介本资源是一套面向嵌入式开发工程师与STM32初学者的HT1625段式液晶驱动实战代码包聚焦解决STM32微控制器Cortex-M内核与HT1625专用液晶驱动芯片之间的通信与显示控制难题适用于智能仪表、家电面板、工业人机界面等低功耗段码屏应用场景。压缩包共97个文件含46个头文件.h定义寄存器映射、段码表及API接口、44个源文件.c涵盖GPIO模拟串行协议、初始化、段码刷新、亮度调节等核心驱动逻辑、2个启动文件.s以及Keil工程配置.uvproj/.uvopt、批量清理脚本.bat、可执行固件.hex等结构完整便于直接移植到STM32F10x平台。已有1278人学习下载资源基于实际硬件验证提供模块化函数封装如LCD_Init、LCD_DisplayDigit、LCD_SetSegment附带清晰的硬件连接说明与时序控制注释显著降低段式液晶驱动开发门槛节省从零调试SPI/I²C模拟通信及段码映射的时间成本。 如果你手头正好有一份“HT1625 STM32”的驱动包或者正准备在STM32上点一块段式液晶这篇文章就是给你准备的。博主在做一个温控器面板时显示内容其实很固定温度、湿度、几个状态图标不需要彩色动画也不需要大屏交互。一开始想过用1602觉得字符屏能显示的内容太死板也想过用OLED又觉得成本和功耗对电池供电的产品不友好。最后把目光放在段式液晶上而段式液晶的核心驱动芯片就是HT1625。这篇文章会从芯片原理、硬件接线、驱动代码、调试踩坑几个方面完整讲一遍适合正在做家电面板、仪器仪表、带电池低功耗产品的开发者参考也适合第一次接触段式液晶驱动的朋友照着抄作业。1. HT1625是什么为什么我在项目里选了它1.1 从段式液晶的基础物理说起段式液晶和我们熟悉的点阵屏思路不一样。点阵屏是排列一个个像素点用扫描方式逐个点亮而段式液晶在出厂时就把显示形状做死在玻璃上比如一个8字形的数字位、一个“℃”符号、一个电池图标。它不能显示任意图形但对固定内容的显示来说这种方案又便宜又稳定。段式液晶不管形状多复杂内部电极始终是行列扫描的公共电极COM和段电极SEG交叉驱动芯片在某个COM上施加驱动电压再通过SEG决定该交叉位置的段亮不亮。一个32SEG×4COM的屏最多可以有128个显示段已经可以覆盖大多数仪表面板的显示需求。HT1625就是专门干这件事的芯片。它把CPU和玻璃面板隔离开CPU只需要通过串行接口把“地址数据”告诉HT1625芯片内部有对应每个显示段的RAM位写1对应段亮写0对应段灭。换句话说HT1625替我们完成了几十个段电极的刷新控制CPU只要负责决定“哪个段亮”不用管“怎么加电压”。1.2 为什么不直接上1602或OLED有朋友会问做显示直接用OLED它不香吗我也用过OLED确实漂亮但要在项目里做取舍1602字符屏虽然便宜但只能显示ASCII字符想显示“℃”“kWh”这种符号得自己造字模而且标准1602要8根数据线加RS、E等控制线占用的GPIO很多还只能显示纯字符。OLED和TFT可以显示任意内容但一块小尺寸OLED屏的价格是段式液晶的几倍功耗也明显偏高。对于长期显示同一界面的温控器、表计类产品OLED并不是最划算的选择。段式液晶最大的两个优势是低功耗和低成本尤其在阳光下显示效果还比OLED更好不依赖背光户外仪表特别喜欢用。我那个温控器面板显示内容固定为两位温度、两位湿度、一个风扇图标、一个加热图标和几个单位符号完全就是段式液晶的舒适区。驱动一颗HT1625加上一片段式玻璃的成本比同尺寸OLED低一大截在代工厂那边加工周期也不长。1.3 网上那份“HT1625 STM32.rar”里到底装了啥这类压缩包在很多电子论坛、下载站都能搜到标题大多是“HT1625 STM32.rar_HT1625液晶驱动_HT1625驱动程序_STM32 段式液晶_stm32 产品”。下载下来打开看一般不会是一个完整工程而是某个产品开发过程中抽出来的源码和资料。常见的内容包括底层驱动源文件ht1625.c、ht1625.h这是整个包的核心引脚定义头文件通常用宏定义好DATA、WR、CS接在STM32的哪个IO一个Keil工程或工程片段有些包直接从正式产品里扒出来能编过芯片手册PDF或液晶玻璃规格书这两个文档最重要没有它们你很难调对比度和段码。我建议拿到这类压缩包后第一步不要急着打开代码先看有没有原理图或者液晶玻璃的规格文档。因为不同厂家做的段式玻璃段码走线不一样代码里的段码表基本都要重改而玻璃规格书就是改段码的依据。如果包里只有代码没有文档那只能把代码当作框架参考显示部分还是要自己重新映射。2. 硬件接线与工程准备2.1 HT1625常用引脚与STM32的连接HT1625的串行接口并不复杂常用引脚除了电源和地之外主要就是下面这几个引脚功能说明CS片选低电平有效一次通信开始前拉低结束后拉高WR写时钟数据在WR上升沿被锁存DATA串行数据高位先出先发标志位后发地址/数据RD读时钟实际项目基本用不到悬空或接高电平VLCDLCD偏压输入决定液晶对比度通常由VDD分压得到BZ蜂鸣器输出部分芯片带蜂鸣器驱动按需使用IRQ闹钟/定时中断输出大多悬空STM32端接法很简单例如F103开发板上我习惯用PB0、PB1、PB2分别接DATA、WR、CS。三根线都是输出直接推挽输出就行。RD如果不读数据就接高电平或者直接悬空。注意有些资料会把HT1625叫做“三线接口”意思是CS、WR、DATA三根线就能写数据不需要RD参与。这和SPI的四线制有区别但也因此有不少人直接用GPIO模拟而不是开硬件SPI。2.2 电源、VLCD和液晶对比度的关系HT1625的工作电压范围比较宽常见支持2.4V到5.2V所以直接接在STM32的3.3V供电上没有问题。真正容易出问题的是VLCD引脚它决定了段式液晶的偏压高低也直接决定显示对比度。VLCD如果直接接VDD很多屏会显得全黑或者对比度太深如果电压太低显示会非常淡。稳妥的做法是在VDD和VLCD之间接一个分压电阻通常是10K电位器或者固定电阻分压。我实测下来3.3V供电的HT1625VLCD在3.0V到3.3V之间对大部分玻璃都能有不错的效果建议先用一个10K电位器调出合适电压再换成固定电阻。还需要考虑电池供电的情况。如果系统电压会从4.2V掉到3.0V那么纯电阻分压的VLCD也会跟着电压波动液晶对比度就会忽深忽浅。这种情况要么加一颗低成本的电荷泵稳压要么用带内置稳压的HT1625衍生型号或者软件上做电压补偿。温度对液晶也有影响温度越低液晶响应越慢、对比度也会变化这也是很多仪表类产品会做温度补偿的原因。2.3 新建STM32工程时驱动层怎么封装不管用的是标准库还是HAL库驱动HT1625的代码都不应该和具体库绑死。最好的做法是把IO操作封装成宏底层换平台时只改一个头文件。如果工程用的是HAL库可以这么封装#define HT1625_DATA_HIGH() HAL_GPIO_WritePin(HT1625_DATA_GPIO_Port, HT1625_DATA_Pin, GPIO_PIN_SET) #define HT1625_DATA_LOW() HAL_GPIO_WritePin(HT1625_DATA_GPIO_Port, HT1625_DATA_Pin, GPIO_PIN_RESET) #define HT1625_WR_HIGH() HAL_GPIO_WritePin(HT1625_WR_GPIO_Port, HT1625_WR_Pin, GPIO_PIN_SET) #define HT1625_WR_LOW() HAL_GPIO_WritePin(HT1625_WR_GPIO_Port, HT1625_WR_Pin, GPIO_PIN_RESET) #define HT1625_CS_HIGH() HAL_GPIO_WritePin(HT1625_CS_GPIO_Port, HT1625_CS_Pin, GPIO_PIN_SET) #define HT1625_CS_LOW() HAL_GPIO_WritePin(HT1625_CS_GPIO_Port, HT1625_CS_Pin, GPIO_PIN_RESET)再结合Keil或STM32CubeMX初始化GPIO就能做到驱动逻辑和底层无关。如果你手上的代码包是标准库写的迁移到HAL工程时理论上只需要把底层IO读写换成上面这组宏其余函数不用改。3. HT1625的驱动核心命令、时序与RAM映射3.1 芯片怎么知道来的是命令还是数据HT1625的串行接口把一次通信称为一个“帧”帧的开头是ID标识位。写命令和写数据的ID不同芯片根据ID区分接下来收到的内容。常用的ID规则是写命令以二进制的101开头写RAM数据同样以101开头但后面的内容长度和格式不一样。也就是说HT1625没有单独的地址总线所有信息都是通过DATA一根线串行送进去的。这也是段式驱动芯片功耗低的原因之一引脚少、状态简单内部不需要复杂协议解析。3.2 写命令帧的格式写命令帧由3位ID加8位命令组成常见格式如下101 CMD7 CMD6 CMD5 CMD4 CMD3 CMD2 CMD1 CMD0其中8位命令涵盖了系统振荡器开关、LCD偏压开关、偏压比例选择、时钟源选择等内容。以常见的HT1621系列命令习惯类推HT1625的初始化命令通常是下面这种组合系统使能命令0x01打开芯片内部系统振荡器LCD偏压使能命令0x03打开LCD驱动输出偏压比例选择命令例如0x29选择1/2偏压或1/3偏压时钟源命令选择内部RC振荡器、外部晶振或外部时钟输入。具体每一位的命令值请务必以你手里的HT1625芯片手册为准。不同封装、不同批次命令定义可能和HT1621有些许差异我在这里列出的是通用逻辑不是让手头板子直接照抄的唯一标准。3.3 写RAM数据帧的格式要显示内容核心操作是往HT1625的显示RAM里写数据。写RAM的帧结构一般是101 RAM_ADDR(6位) RAM_DATA(4位)地址的6位通常对应SEG序号数据的4位对应COM0到COM3。比如想让第3个COM、第15条SEG交叉处的段点亮就向RAM地址15写入数据0x04因为3号位对应COM2。这里要理解一个重要概念段式液晶同一个字符的七段笔画往往是分布在不同COM、不同SEG上的。例如数字“8”的七段加上小数点一共8个段这8个段可能分别由4个COM和多个SEG控制。所以写段码不是简单发一个“8”的ASCII而是要把每一个笔画的亮灭状态按硬件排布逐一填到显存相应位置。3.4 时序波形与GPIO模拟的要点HT1625的时序很简单每次写一位时先把DATA引脚设为目标电平然后拉低WR再拉高WR这一位就锁存进去了。整个过程可以看作在WR上升沿采样DATA。用GPIO模拟时的参考代码static void HT1625_WriteBits(uint8_t data, uint8_t cnt) { uint8_t i; for (i 0; i cnt; i) { if (data 0x80) { HT1625_DATA_HIGH(); } else { HT1625_DATA_LOW(); } HT1625_WR_LOW(); HT1625_DelayUS(2); HT1625_WR_HIGH(); HT1625_DelayUS(2); data 1; } }其中cnt表示要发送的位数。每次通信前先拉低CS发送完一帧后拉高CS保证芯片正确识别帧边界。为什么很多人建议用GPIO模拟而不是硬件SPI因为HT1625的帧规则不是标准SPI命令和数据长度不同地址长度也不同用硬件SPI反而要处理不同位长和片选时序灵活性差。GPIO模拟的代码逻辑简单一眼能看懂加几个延时也不影响性能。STM32主频通常在72MHz以上模拟出来的时序完全有余量。4. 完整驱动代码实现以STM32 F103为例4.1 引脚定义与底层IO初始化我用的是STM32F103C8T6数据口、时钟口、片选口分别选PB0、PB1、PB2。GPIO初始化代码如下#define HT1625_DATA_GPIO_Port GPIOB #define HT1625_DATA_Pin GPIO_PIN_0 #define HT1625_WR_GPIO_Port GPIOB #define HT1625_WR_Pin GPIO_PIN_1 #define HT1625_CS_GPIO_Port GPIOB #define HT1625_CS_Pin GPIO_PIN_2 static void HT1625_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin HT1625_DATA_Pin | HT1625_WR_Pin | HT1625_CS_Pin; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); HT1625_CS_HIGH(); HT1625_WR_HIGH(); HT1625_DATA_HIGH(); }用标准库的工程把HAL_GPIO_Init换成GPIO_InitStruct和GPIO_Init逻辑一模一样。引脚速度和模式都选推挽输出不要选开漏因为这个电路没有外接上拉电阻。4.2 写命令和写RAM的函数封装有了最底层的位发送函数写命令和写RAM就很好实现了static void HT1625_WriteCmd(uint8_t cmd) { HT1625_CS_LOW(); HT1625_WriteBits(0x80, 3); // ID 1010x80高位是101 HT1625_WriteBits(cmd, 8); HT1625_CS_HIGH(); HT1625_DelayUS(5); } static void HT1625_WriteRAM(uint8_t addr, uint8_t data) { HT1625_CS_LOW(); HT1625_WriteBits(0xA0, 3); // ID 101 HT1625_WriteBits(addr, 6); // RAM地址6位 HT1625_WriteBits(data, 4); // COM数据4位 HT1625_CS_HIGH(); HT1625_DelayUS(5); }这里的0x80二进制是10000000取高3位是100但更常见的ID是101。所以严谨一点应该直接发送高3位101写成HT1625_WriteBits(0xA0, 3); // 0xA0 10100000高位3位是101写命令时我用0xA0写3位也是对的只是语义上容易让新人困惑。实际项目里可以直接把ID定义为常量#define HT1625_ID_CMD 0xA0 #define HT1625_ID_WRAM 0xA0重点是明白三种模式下发送的第一字节高3位都是101后面的位数不同而已。4.3 显存缓冲与整体刷新显示多个字符时如果每显示一个数字就往HT1625里写一次RAM程序会显得很零散而且容易出现闪烁。我习惯在MCU内部维护一个32字节的显存数组static uint8_t ht1625_ram[32];每个元素对应HT1625的一个RAM地址。显示逻辑只改这个数组然后一次性把所有数组内容刷到芯片里void HT1625_Refresh(void) { uint8_t i; for (i 0; i 32; i) { HT1625_WriteRAM(i, ht1625_ram[i]); } }这种显存机制是一个很好的架构因为写芯片是异步的随时可以调用需要显示什么内容只要提前把RAM数组安排好。后续加动画、加状态切换都方便。4.4 单段控制接口为了方便上层使用我把显存操作封装成“按段操作”的接口调用方不用关心具体哪个RAM地址的哪一位void HT1625_SetSegment(uint8_t seg, uint8_t com, uint8_t on) { if ((seg 32) || (com 4)) return; if (on) { ht1625_ram[seg] | (1 com); } else { ht1625_ram[seg] ~(1 com); } }这里的seg对应SEG序号范围0到31com对应COM0到COM3。只要把玻璃规格书里的段笔画和这个接口对应上就能逐段控制。如果项目里只有几个固定图标这个接口就够了。4.5 显示数字的段码表制作数字显示是段式液晶最常见的需求。一个7段数字加小数点的标准布局是a、b、c、d、e、f、g、dp八段。如果是普通LED数码管段码很好写但段式液晶的段码跟玻璃走线强相关没法只给一个通用数组直接抄。通常做法是先把玻璃规格书里的一个数字位展开看它的a段接到哪个SEG、哪个COMb段接到哪里。然后把一个数字对应到8个段的开关状态在初始化时写一个映射表static const uint8_t seg_digit_map[10][8] { /* a, b, c, d, e, f, g, dp */ {1, 1, 1, 1, 1, 1, 0, 0}, /* 0 */ {0, 1, 1, 0, 0, 0, 0, 0}, /* 1 */ {1, 1, 0, 1, 1, 0, 1, 0}, /* 2 */ {1, 1, 1, 1, 0, 0, 1, 0}, /* 3 */ {0, 1, 1, 0, 0, 1, 1, 0}, /* 4 */ {1, 0, 1, 1, 0, 1, 1, 0}, /* 5 */ {1, 0, 1, 1, 1, 1, 1, 0}, /* 6 */ {1, 1, 1, 0, 0, 0, 0, 0}, /* 7 */ {1, 1, 1, 1, 1, 1, 1, 0}, /* 8 */ {1, 1, 1, 1, 0, 1, 1, 0}, /* 9 */ };然后在上层通过查表把每一段的亮灭设置到HT1625_SetSegment函数里。这个表比直接算段码直观出错也好排查。真正的段码表一定要对着玻璃规格书做同一个“8”在不同厂家玻璃上的SEG/COM接法可能完全不同。4.6 初始化流程初始化HT1625的顺序很关键乱序可能会导致上电后屏幕有残影或者亮度异常。我的参考顺序如下void HT1625_Init(void) { HT1625_GPIO_Init(); HT1625_DelayMS(50); HT1625_WriteCmd(HT1625_CMD_SYSEN); // 开系统振荡器 HT1625_WriteCmd(HT1625_CMD_LCDON); // 打开LCD偏压 HT1625_WriteCmd(HT1625_CMD_BIAS_CFG); // 设置偏压比例 HT1625_WriteCmd(HT1625_CMD_RC_CFG); // 选择时钟源 HT1625_Clear(); HT1625_Refresh(); }命令宏的具体值以芯片手册为准但流程顺序基本就是这样。先打开系统振荡器让芯片内部跑起来再开LCD输出然后配置偏压和时钟最后清屏刷新一次避免上电显示乱码。5. 调试记录与常见问题排查5.1 现象屏幕全亮所有段都显示全亮这个现象在段式液晶调试里非常常见基本可以锁定是偏压或初始化顺序的问题。先看VLCD电压是不是太高试着用一个10K电位器把VLCD调低一点看有没有改善。再看初始化代码里有没有把LCDOFF和LCDON顺序搞反如果上电默认是关LCD偏压的漏发LCDON命令有些屏会出现略微发灰但不显示内容的异常全亮则多半是VLCD过高或偏压比例配置不正确。另一个容易忽略的坑是CS时序。如果CS一直处于低电平HT1625不知道一帧在哪里结束可能把后面所有数据都当作地址和数据进行解码结果就是RAM里被写满随机数据显示全亮。调试时先用逻辑分析仪抓CS波形确认每一帧结束后CS确实拉高了。5.2 现象屏幕全不亮全不亮时优先查硬件有没有正常工作。测量VDD和GND电压确认VLCD有足够电压。然后用万用表检查DATA和WR有没有虚焊这种引脚间距小的液晶排线焊接时很容易桥接。软件方面可以先用最简单的测试程序只点亮某一个固定段比如调用HT1625_SetSegment(0,0,1)再刷新如果固定段能亮说明底层时序和初始化没问题问题只出在段码映射上。如果连固定段都不亮就要回头检查写命令函数和写数据函数里的位发送顺序尤其注意最高位是不是真的先发送。5.3 现象显示乱码、数字错位乱码的问题十有八九是段码表映射错误。段式液晶玻璃的走线并不按人的直觉排列经常出现第一个数字的a段在SEG1第二个数字的a段却跑到SEG12的情况。如果看规格书不够仔细段码表写错很正常。排查方法很笨但有效把所有段先全部点亮拍一张照片然后只点亮一个SEG、一个COM比如HT1625_SetSegment(0,0,1)再刷新拍一张照片。每张照片对应一个RAM地址和位。拿这些照片和玻璃规格书对照重新整理映射表。这个过程第一次做比较花时间但做完就能彻底理解这块屏的走线。5.4 现象刷新时闪烁闪烁通常有两个原因。一是刷新频率太低如果Refresh函数和业务代码放在一个大循环里中间夹了很多延时实际刷新率可能不到30Hz肉眼能看出闪。解决办法是提高刷新频率或者在主循环里用定时器驱动固定20ms刷新一次。二是两次写RAM之间间隔太长导致一边刷一边显示液晶处于“半刷新”状态。可以在刷新前先把所有数据准备好再做连续写操作期间不要夹杂长时间延时的其它代码。还有一个容易忽略的地方当使用在线调试器时比如ST-LINK如果触发了断点或者单步运行HT1625的刷新过程被暂停液晶就会停在某个奇怪状态。这不一定说明代码有问题继续全速运行可能就正常了。5.5 我踩过的一个低级坑LCD偏压命令漏发有一次我调试新做的一版PCB屏幕怎么都不亮折腾了一个晚上最后发现是初始化代码里少了LCDON命令。当时参考的是HT1621的代码HT1621的初始化里有些人只发SYSEN有人认为SYSEN就包含了LCD输出但实际上LCD偏压和系统振荡器使能是两条独立的命令。HT1625更是如此少了LCDON命令系统振荡器虽然在工作但LCD驱动输出没打开屏幕自然什么都没有。这个经历让我养成了一个习惯拿到任何一颗驱动芯片先通读一遍初始化命令列表把影响到LDO、偏压、输出使能这些开关方向的命令单独标出来再对照代码确认每条命令都存在。芯片手册用半小时通读比在调试器里耗一晚上值多了。6. 这套驱动怎么扩展和复用6.1 从HT1625到同系列芯片HT1621、HT1622、HT1625这几兄弟驱动逻辑非常接近尤其是命令帧结构和RAM映射方式基本思路一致。区别主要在SEG和COM的数量上限、RAM地址位数、命令细节上。如果换一颗HT1621只需要把地址位从6位改成更适合HT1621的位数减少显存数组大小改一下初始化命令宏刷新函数和显存架构可以不怎么动。如果你在几个不同项目里反复用到段式液晶建议一开始就把“底层时序”和“上层段码映射”分成两个模块换屏只换底层配置。6.2 配合FreeRTOS或裸机调度器在带FreeRTOS的项目里HT1625刷新任务不应该是最高优先级它属于周期性显示任务建议作为低优先级任务来跑。显存数组可以被多个任务写但刷新任务读取时需要加互斥量保护防止刷新过程中数据被修改导致某帧显示内容错乱。低功耗场景下进入Stop模式前可以把CS、WR、DATA引脚都拉低避免悬空引脚通过内部上下拉电阻漏电。需要显示时先唤醒芯片重新初始化一遍HT1625再刷新这个重新初始化流程和上电初始化是同一个函数。6.3 后续如果换成OLED屏就算后续产品改成了OLED上层显示接口也可以保留成HT1625_SetSegment、HT1625_ShowNumber这种抽象接口。只要上层逻辑不直接操作显存数组换屏时重写底层实现就行了。我在实际项目中就是这么做的显示模块被抽象成display.h后来从段式液晶切到OLED只花了一天时间就完成了适配。还有一个小技巧HT1625的驱动代码可以和调试串口配合使用。在驱动里临时加一个调试函数把每次写入的RAM地址和数据通过串口打印出来。这样在段码调试时不用一直盯着屏幕看日志就能知道程序想把哪个段点亮对比屏幕实际效果排错效率高很多。最后再分享一点个人经验段式液晶这东西第一次接触会觉得段码很绕但只要理解了COM/SEG交叉寻址和显存映射后面所有屏都大同小异。我自己总结的经验是玩段式液晶先研究玻璃规格书再写驱动先点亮一个段再点亮一个字符最后再追求代码架构。前期多花半小时把段码表整理清楚后期少熬夜排查乱码。手头那份“HT1625 STM32.rar”驱动包不妨把它当做一个参考骨架真正落地时还是要结合自己的硬件去改段码、改命令、改初始化顺序。希望这篇文章能帮你在STM32上少踩几个坑顺利点亮自己的段式液晶。本文还有配套的精品资源点击获取