ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32实战:TM1622与HT1622段码LCD驱动移植全解析

STM32实战:TM1622与HT1622段码LCD驱动移植全解析 做嵌入式这几年段码LCD屏在我手里算是“老朋友”了。热水器面板上那个橙红色的大数码管背后空调遥控器那个小小的液晶屏背后电磁炉显示火力档位的那块玻璃面板背后——十个里有八个用的都是HT1622这类段码驱动芯片而这两年国产替代方案里最常见的就数TM1622。最近一个项目要在STM32平台上把老产品里的TM1622驱动完整“搬”过来顺便重新梳理了整块板的硬件设计。过程中踩了不少坑也把两芯片之间的兼容性差异、偏压电路设计、指令时序这些细节彻底搞明白了。这篇就当是实战笔记给做STM32、做家电控制面板、做仪表显示的同行一个可以直接复用的参考。1. 先搞明白TM1622和HT1622到底是什么关系1.1 从HT1622到TM1622国产替代的兼容性现实做硬件选型的时候问供应商“HT1622和TM1622能不能直接换”十有八九会得到一句“完全兼容”。严格讲这个说法得打个折。TM1622是深圳天微在HT1622基础上做的国产替代型号两者在功能、引脚排列上基本一致大部分情况下可以直接替换。但“基本一致”不等于“完全一致”。我在实际项目中确实遇到过这种情况同样的初始化代码在HT1622上正常在TM1622上会出现首帧显示有杂点的现象需要增加复位后的软件延时才能稳定。另外两家在偏压选项、振荡器配置这些参数的出厂校验标准也不一定完全相同。所以做驱动移植的时候最忌讳直接拿旧代码改个引脚就上板一定要把两个芯片最新版数据手册的指令表、时序参数表从头到尾对照一遍尤其是命令字、RAM地址映射、建立保持时间这三处。1.2 引脚与内部结构速览TM1622和HT1622的引脚定义高度相似核心引脚大概有这么几类。引脚名方向功能说明CS输入片选低电平有效一次完整通信期间必须保持低WR输入写时钟上升沿锁存DATA上的数据RD输入读使能只用写功能时可以悬空或固定高DATA双向串行数据线写模式输入读模式输出OSC输入/输出振荡器引脚典型应用接RC网络或外部时钟VLCD输入LCD驱动电压必须正确去耦不能悬空COM0~COM7输出公共端扫描输出SEG0~SEG31输出段输出数目和封装有关内部结构可以理解为三个部分控制逻辑负责解析串行指令、维护一个SRAM显示缓冲区、偏压电阻网络和输出驱动电路负责把RAM里的“0/1”变成COM/SEG上实际的高低电平波形。这个芯片本身不存字模它只管理“哪一个点阵位置是否点亮”具体哪段亮成什么形状是MCU这边通过字模表换算好再填进RAM区域的。1.3 工作模式与显示能力HT1622这类芯片通常支持多种COM和SEG的组合配置比如最大8个COM、最多32个SEG的组合或者用更少的SEG换取更多COM具体组合方式以手册的RAM映射表为准。不同组合直接决定你能驱动多大的屏以及扫描占空比。这里要引入两个概念占空比Duty和偏压Bias。占空比1/4 Duty表示4个COM循环扫描每行只有1/4时间被驱动偏压1/3 Bias表示非选通点的电压是选通电压的1/3。这两个参数是配套的。4COM屏幕常用1/3 Bias8COM屏幕往往需要1/4 Bias电压关系不匹配屏幕要不就是鬼影严重要不就是对比度淡得看不清。TM1622和HT1622的数据手册里命令字会同时带上Bias和Duty设置写初始化代码时一定要和屏幕规格书对应上不要凭感觉选。2. 硬件设计比驱动代码更容易翻车的地方2.1 VLCD偏压电路与对比度设计很多第一次做段码屏项目的人栽的第一个跟头就是VLCD。数据手册上VLCD引脚边上只画了一个去耦电容于是有人干脆不接或者只放了一个0.1μF电容到地结果屏幕显示极淡怎么调代码都没用。先说结论VLCD不能浮空。LCD的驱动电压等级是通过VLCD引脚接入芯片内部的偏压电阻网络产生的正常工作状态下VLCD上必须有稳定的电压。常见做法是把VLCD直接接到VDD然后靠近芯片放置0.1μF电容有些电路再并一个1μF到10μF的电解电容用于低频滤波。如果VDD是3.3V而屏幕本身需要更高驱动电压才能达到理想对比度那就得考虑把VLCD抬高。常见的做法是用一个小型电荷泵或DC-DC把电压升到5V左右再接到VLCD。这里务必注意两点VLCD不允许超过芯片绝对最大额定值通常不超过6V左右具体查手册VLCD走线尽量短粗远离高频开关节点否则屏幕上会出现肉眼可见的条纹。如果你想做对比度可调可以在VLCD和VDD之间串一个电位器通过调整分压来微调偏压等级。不过这种模拟调节方式批量生产时一致性差现在更推荐在代码里统一设置Bias配合固定硬件电路效率更高也更可控。2.2 接口电平匹配3.3V MCU接5V驱动芯片STM32的GPIO输出是3.3V而TM1622如果工作在5V供电输入端高电平阈值Vih有可能是0.7×VDD也就是3.5V。这时候3.3V的高电平输出在理论上是“不够高”的存在不确定性。我这边的经验是如果TM1622也工作在3.3V那完全没有问题但要注意屏幕本身的规格是否支持3.3V驱动如果TM1622必须工作在5V不要赌阈值加一级电平转换最稳妥最省事的方案是使用74LVC1T45这类单路双向电平转换芯片TSSOP-6封装三路信号分别接三片占板面积很小也可以用一个NMOS做开漏转换DATA时钟线上拉电阻取10kΩ左右速率完全够。另外提醒一句STM32F103系列GPIO虽然标称5V容忍但“容忍”不表示可以直接当5V接口用尤其是输入模式时上拉状态下的电流路径容易踩暗坑。2.3 PCB布局与走线细节段码屏驱动电路对PCB布局的要求不算苛刻但有几条经验值得注意芯片尽量靠近LCD排线座COM/SEG走线越短越好COM/SEG线之间不要平行走太长距离实在避不开就拉开间距、中间铺地隔离否则排线串扰会导致相邻笔画鬼影VLCD的去耦电容和OSC的RC网络都要放在芯片引脚附近回流路径越短越好如果产品有EMC认证需求在LCD排线接口处预留TVS或ESD保护器件位置SEG/COM线上串联22Ω~33Ω电阻能明显抑制振铃代价是显示波形边沿变缓但通常不影响正常扫描。2.4 背光电路设计段码屏带的LED背光驱动方式很简单一个限流电阻串进去功率和电流算好就行。白色背光LED压降一般在3V左右5V供电下限流电阻压降约2V按20mA算就是100Ω左右。实际亮度需求可以调整一般我控制在5mA~15mA兼顾亮度和寿命。如果需要PWM调光建议用PNP三极管或者PMOS做高端开关MCU的PWM引脚通过一个1kΩ~10kΩ电阻驱动三极管基极。为什么要高端开关而不是低端因为多数段码屏的背光LED公共端是共阳结构高端开关更贴近实际走线而且可以避免LED地和LCD驱动地之间产生额外压降干扰。3. 驱动原理显存、时序与扫描刷新3.1 段码屏的寻址逻辑段码屏的本质是一个点阵COM是行SEG是列但和点阵屏不同的是每个SEG往往对应屏幕上的一整段笔画比如数字“8”的七段加小数点就需要8个SEG。驱动芯片内部维护一块RAM每一个bit对应一个“COMSEG”交叉点bit为1表示这点显示。那么MCU写入一个字节怎么对应到屏幕上的段举一个简化例子如果芯片RAM映射是“每个字节对应一个SEG字节的bit0~bit7对应COM0~COM7”那么在4COM模式下其实只有低4位有效高4位可以填0。不同芯片、不同工作模式的映射规则不一样这恰好是移植时最容易出错的点。我的做法是先画出屏幕的COM/SEG分配表再动手写驱动。一个数字“8”的七段分别叫a、b、c、d、e、f、g加上小数点dp这8个段如果恰好落在同一SEG的不同COM上那么一个字节就能控制一个完整数字如果落在不同SEG上就需要分别改两个字节。这块表画清楚了驱动代码就是查表填空。3.2 写命令与写数据的时序TM1622/HT1622是串行接口本质上是一根时钟线加一根数据线CS做片选。一次通信的过程大概是CS拉低使能芯片在DATA上放好一位数据给WR一个上升沿芯片在这一刻锁存DATA重复步骤2、3直到整条命令或数据发完CS拉高结束本次通信。这里有几个关键点数据位顺序多数芯片手册写的是最高位先发但网上流传的驱动代码两种都有移植时一定要和手册核对命令和数据的区分通常靠发送内容的前缀或者专门的模式命令来区分而不是像SPI那样靠引脚建立时间和保持时间手册会给WR上升沿前后DATA需要稳定的最小时间比如建立时间几十ns、保持时间几十ns这种量级对STM32的GPIO翻转来说没啥压力但用超长延时反而可能引发其他问题。下面这个表格整理了常见时序参数类型具体数值请以你手上那颗芯片的官方手册为准。参数含义典型量级tSU数据建立时间数十nstH数据保持时间数十nstCKWWR高电平/低电平宽度数百nstCSCS建立与释放时间数百ns很多芯片实际工作速度比手册标称快很多所以我见过有驱动代码在STM32上不加任何延时也能正常工作。但我不建议这么做量产时会积累风险。稳妥的做法是加几个空指令或者1μs级延时保证每个 WR 沿之间的间隔稳定。3.3 刷新率与功耗段码LCD是动态扫描的内部振荡器产生扫描时序依次驱动每一条COM线。刷新率的决定因素包括内部RC振荡频率和占空比设置。振荡频率越高刷新越流畅但功耗也越高反过来蹭着人眼不闪烁的底线可以去压低功耗。软件层面有个细节很多应用定时把整块显示RAM刷下去几十个字节每次刷屏还带CS片选和命令前缀传输耗时虽然不长但在低功耗产品里就是浪费。更好的做法是维护一份软件缓存只在内容变化时把变化的字节刷下去。对于绝大多数段码屏应用显示内容变化频率极低这种差异刷新能明显降低MCU负载和总线占用。4. STM32平台移植实战从标准库到HAL的抽象4.1 移植思路老项目里的TM1622驱动多半是51单片机标准库风格或者STM32标准外设库风格一堆GPIO_SetBits、GPIO_ResetBits。现在换到HAL库最省事的办法不是逐行改API而是先封一层宏把底层的引脚操作隔离出来。驱动主体函数保持逻辑不变只替换底层擦写宏和延时函数。我当时用STM32CubeMX初始化工程时钟72MHzGPIO输出速度设为High三个信号线CS、WR、DATA分配在同一组GPIO上方便管理和快速翻转。下面这是头文件的示例结构重点是引脚宏定义和命令宏的占位。#ifndef __LCD_TM1622_H #define __LCD_TM1622_H #include main.h #define LCD_CS_PORT GPIOB #define LCD_CS_PIN GPIO_PIN_12 #define LCD_WR_PORT GPIOB #define LCD_WR_PIN GPIO_PIN_13 #define LCD_DATA_PORT GPIOB #define LCD_DATA_PIN GPIO_PIN_14 #define LCD_CS_L() HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET) #define LCD_CS_H() HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET) #define LCD_WR_L() HAL_GPIO_WritePin(LCD_WR_PORT, LCD_WR_PIN, GPIO_PIN_RESET) #define LCD_WR_H() HAL_GPIO_WritePin(LCD_WR_PORT, LCD_WR_PIN, GPIO_PIN_SET) #define LCD_DATA_L() HAL_GPIO_WritePin(LCD_DATA_PORT, LCD_DATA_PIN, GPIO_PIN_RESET) #define LCD_DATA_H() HAL_GPIO_WritePin(LCD_DATA_PORT, LCD_DATA_PIN, GPIO_PIN_SET) /* 命令字定义按TM1622数据手册填入 */ #define LCD_CMD_SYS_EN 0x01 #define LCD_CMD_LCD_ON 0x03 #define LCD_CMD_BIAS_DUTY 0x28 void LCD_Init(void); void LCD_WriteCmd(uint8_t cmd); void LCD_Clear(void); void LCD_SetPixel(uint8_t seg, uint8_t com, uint8_t on); void LCD_SetNumber(uint8_t position, uint8_t value, uint8_t dot); #endif4.2 核心驱动实现底层写函数是整个驱动的心脏。先看代码void LCD_DelayUS(volatile uint32_t us) { us us * 72; // 72MHz 下粗略延时 while (us--) { __NOP(); } } void LCD_WriteBits(uint8_t data, uint8_t count) { for (uint8_t i 0; i count; i) { if (data (0x80 i)) LCD_DATA_H(); else LCD_DATA_L(); LCD_WR_L(); LCD_WR_H(); } } void LCD_WriteCmd(uint8_t cmd) { // 命令模式前缀和位长以手册为准这里示意 LCD_CS_L(); LCD_WriteBits(0x80 | cmd, 9); LCD_CS_H(); } void LCD_Init(void) { LCD_CS_H(); LCD_WR_H(); LCD_DATA_H(); HAL_Delay(20); LCD_WriteCmd(LCD_CMD_SYS_EN); LCD_WriteCmd(LCD_CMD_BIAS_DUTY); LCD_WriteCmd(LCD_CMD_LCD_ON); LCD_Clear(); }这里有个细节LCD_WriteBits里先拉低WR再拉高等效产生一个上升沿。有的驱动是先拉高再拉低或者反过来那就是用下降沿锁存了。这个顺着芯片手册来别想当然。至于延时我写了一段基于空指令的微秒延时但更规范的做法是用定时器做时间基准或者直接用DWT循环计数精度更高。之所以建议发送期间加一点延时是为了避免在高速GPIO翻转时DATA电平还没稳定WR就上跳了尤其在排线比较长、寄生电容比较大时这个问题会被放大。4.3 显示数字和字符的API段码屏要显示数字本质上就是查字模表把数字的笔画位置写入屏幕显存。假设屏幕上每个数字占用同一SEG组那么字模表长这样const uint8_t seg_code[10] { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x07, // 7 0x7F, // 8 0x6F // 9 };这串值和实际屏幕段序是对应的。我在项目中就吃过亏屏幕规格书上“a”段定义在COM1而芯片RAM低字节对应COM0结果所有数字整体旋转看起来就像乱码。所以字模表必须和实际段序对齐。4.4 RTOS与低功耗场景的处理如果你用了FreeRTOS多个任务都可能操作屏幕那就必须给驱动加保护。最简单的方法是定义一个互斥量任何一次完整的“读-算-写”过程都要持有互斥量。注意不是每个字节持有一下而是一次显示刷新流程持有否则两个任务交错写入屏幕照样花。低功耗场景下进入STOP或待机模式前建议先把LCD关掉或者清屏原因是液晶长时间处于直流偏置状态会老化甚至极化。合理的顺序是先发命令关闭显示再清RAM延时几十毫秒等LCD放电稳定后再断电等待。不要一进睡眠就把主电源拉掉当时看着没事长期可靠性会打折扣。5. 实测过程中的踩坑记录5.1 上电乱码芯片复位要等第一个坑是上电瞬间屏幕乱码。现象是系统刚上电那几百毫秒所有段乱跳然后才恢复正常。排查链路先以为是显示RAM内容随机所以在初始化里加了清屏清屏后乱码依旧说明问题发生在清屏之前用示波器抓CS、WR、DATA发现MCU复位完成后很快就发了第一条命令而TM1622内部振荡器还没稳定命令根本没被正确解析解决在初始化开始先HAL_Delay(20)等芯片内部稳定再关显示、清屏、开显示。原因不复杂TM1622的RC振荡器起振需要时间如果上电后立刻去写寄存器芯片还在“混沌状态”什么命令都白搭。类似的如果你用外部时钟源驱动OSC也要等待时钟稳定再操作。5.2 屏幕很淡VLCD偏压问题这个项目初期试产了二十块板子有块板屏幕明显偏淡。排查链路代码完全一样排除固件问题对比测量两块板的VLCD电压正常的板子3.28V异常的板子2.9V最终发现是VLCD去耦电容焊错位导致偏压不稳。段码LCD的对比度对VLCD电压非常敏感差了0.3V就能看出差异。硬件调试时先用万用表确认VLCD电压在屏规格要求范围内再动代码。5.3 高速翻转时花屏时序余量不足还有一次情况比较隐蔽逻辑分析仪看数据完全正确但屏幕偶尔花一下尤其是温度升高以后更频繁。这其实是典型的时序余量不足——逻辑分析仪采样率低看不出ns级的问题而GPIO翻转太快WR上升沿附近DATA的建立时间没满足芯片要求。解决方式有两个发送期间临时关中断防止任务切换打断半个字节的发送在每一位翻转后加几个空操作把时钟拉宽到安全范围。我最终两个都做了。代码里在LCD_WriteBits循环中加了两次__NOP()同时在调用驱动时用临界区保护效果很稳定。要注意临界区时间不能太长否则影响RTOS实时性这也是为什么底层驱动要尽量精简、别在临界区里做无谓延时。5.4 用示波器快速判断问题方向调段码屏驱动示波器比逻辑分析仪更有用。我常用的检查顺序测量CS片选时序确认MCU确实在和芯片通信测量WR上升沿放大看DATA是否在这个沿之前已经稳定测量VLCD纹波如果显示有横纹多半是VLCD电容不足或者地回路不好测量COM输出波形确认扫描正常。COM波形应该是阶梯状交替电平如果波形异常说明芯片根本没起来。这套流程基本能覆盖90%的显示问题。5.5 移植到其他型号芯片的注意事项如果你的项目用的是GD32、APM32这类国产替代MCUHAL驱动基本可以直接编译但延时函数要重新校准。GD32的内核和STM32F103基本一致但主频配置不同72MHz延时代码不用改APM32F103也类似。如果是GD32F303或者GD32F4系列主频更高空指令延时的时间就完全变了这时候最好用systick或定时器做延时基准。另外不同厂商的HAL库对GPIO_PIN_SET/GPIO_PIN_RESET这类枚举定义可能略有差异但基本都是兼容的重点检查引脚时钟使能函数即可。6. 驱动移植的后续扩展动态显示与低功耗优化驱动跑通只是第一步实际上产品化过程中还有不少可以优化的地方。6.1 动态扫描显示段码屏本身由驱动芯片动态扫描但MCU侧也可以主动控制刷新时机。比如数码管闪烁效果不需要MCU频繁开关段只要周期性地改显存里对应位的值再比如倒计时显示可以先改低一位、再改高一位避免一次改多位时在屏上看到“瞬间乱码”。具体实现上我会在软件里维护一个display_buf[SEG_CNT]数组所有修改都只改这个数组然后由定时器周期性调用LCD_Update()把整个数组刷到芯片。如果改动不大可以只在有变更时刷用dirty标志控制。6.2 软件设计上的分层我通常把驱动拆成三层底层硬件抽象层引脚宏、延时、片选控制芯片驱动层写命令、写显存、清屏、初始化应用展示层显示数字、冒号闪烁、菜单切换。这样做的好处很明显以后如果换了驱动芯片比如从TM1622换成TM1621或者别的兼容型号只需要改第二层应用层完全不用动。这次移植到STM32我也保留了这套分层结构后续再换平台会轻松很多。6.3 长期可靠性验证段码屏产品不像手机那样天天刷屏但一年365天不间断运行还是要做几项验证高温高湿环境下放置48小时观察显示是否出现残影快速上下电500次确认上电复位时序不会偶发失败用稳压源缓慢拉低VDD到欠压值确认掉电瞬间不会产生异常显示。我这次实测时发现快速上下电时偶尔有首帧异常最后定位到是MCU复位输出高电平的瞬间TM1622提前被选中并收到一段垃圾数据。解决方法是给TM1622的CS加一个上拉电阻同时在软件初始化时尽早执行一次完整清屏。7. 关于TM1622和HT1622的一点补充体会回到文章开头那个问题TM1622能不能完全替代HT1622我的答案是可以但前提是你要把“完全兼容”这句话当成一个起点而不是终点。两个芯片的引脚定义、基本命令、RAM映射在绝大多数情况下是一致的直接换上去大多数项目都能跑。但如果你做的产品对可靠性、低功耗、EMC有要求那就必须把替换型号重新做一轮完整的验证特别是上电时序、振荡器频率偏差、VLCD驱动能力这几个指标不同批次、不同foundry的产品可能都有细微差别。我在实际项目中最终沿用TM1622主要原因是供货和价格确实有优势导线货源充足。但我在硬件上留了一个后手PCB上把HT1622和TM1622两种封装的焊盘都画上了万一后续供应商出问题可以直接在产线上换用另一款芯片固件只改一个宏定义就行。这种“双源设计”对消费电子产品来说是实实在在的抗风险手段。段码屏驱动不算高深技术但细节极多。一个VLCD电容、一个命令字前缀、一位数据顺序都能让屏幕表现天差地别。希望这篇笔记能帮后来者少走几步弯路尤其是那些刚上手STM32、第一次接触TM1622/HT1622的朋友硬件设计和软件移植两头都能心里有底。
返回列表