ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

段码LCD驱动芯片FZH1643实战:I2C通信、键盘扫描与低功耗设计

2026/9/8 15:25:42 拓冰建站 浏览量
段码LCD驱动芯片FZH1643实战:I2C通信、键盘扫描与低功耗设计 做温控面板那会儿我吃过一次亏。整块板子8个按键、一个段码LCD主控用STM32F030一开始图省事直接拿IO口推LCD结果显示刷新和按键扫描抢时序按一下键抖得跟拨浪鼓一样最后没辙把显示和输入拆成了两颗芯片分开跑才勉强稳定。后来接触到FZH1643这种带键盘扫描的LCD驱动芯片一根I2C总线把显示和按键全接管才发现当初绕了多大一圈。这篇文章就围绕FZH1643的I2C接口、双模式切换和键盘扫描机制讲讲这类显示输入合封方案在实际项目里怎么用以及哪些地方特别容易踩坑。适合正在做小家电、工控面板、仪器仪表显示交互的朋友参考不管是换型选型还是调试排错应该都能用得上。1. 段码屏驱动的老问题FZH1643是怎么一次解决的1.1 段码LCD的驱动原理为什么要专门的驱动芯片先理清一个基本问题段码LCD就是那种显示数字和符号的液晶屏不是TFT彩屏本质上是一个电容性负载每个显示段相当于一个微小电容。给某一段加上直流电压时间一长液晶材料会极化轻则显示重影、黑斑重则直接把屏搞坏。所以驱动段码LCD必须用交流波形通过COM口和SEG口之间的电压差来控制每个段是否点亮。很多人第一次接触段码屏会问为什么不能像驱动LED那样直接拉高拉低因为LCD的点亮取决于COM和SEG之间的有效电压差而且要考虑方向和时序。以常见的1/4占空比为例4个COM轮流被选通剩下的SEG引脚在对应时段输出段码信号通过偏压配置比如1/3偏压来保证未选通段的电压低于阈值不产生半亮或影子效果。这就解释了为什么需要专门的LCD驱动芯片它要在后台持续不断地生成多路COM扫描波形同时把显存里的数据实时映射到SEG输出口。让主控直接干这个活不仅IO口数量不够CPU时间也被吃掉一大块。FZH1643这类芯片内部集成了振荡器、分频器、扫描时序发生器和显存主控只需要告诉它哪个段亮、哪个段灭剩下的刷新工作它自己完成。1.2 键盘扫描功能的加入主控从双任务变成单任务单独一颗LCD驱动芯片不难找HT1621、HT1622这类都是老牌产品。但它们不管按键面板上的矩阵键盘还得主控自己扫描。于是主控面临一个尴尬局面一边是LCD驱动芯片通过串口接收显存数据另一边是主控要周期性地给矩阵键盘的行线输出扫描电平、读回列线状态、做防抖处理。矩阵键盘的扫描看着简单写起来全是细节。行线什么时候输出高、什么时候输出低列线要不要开内部上拉按键按下的瞬间怎么去抖长按、连击怎么判断这些逻辑每写一次就要调试一次。特别是在主频不高的MCU上还要兼顾显示刷新的实时性、通信协议的解析代码稍微乱一点按键就出现误触发或者丢键。FZH1643把键盘扫描逻辑做进了芯片内部。主控设置好扫描使能和相关参数后芯片会自动周期性地扫描矩阵键盘把按键状态存到内部寄存器里。主控想查看有没有按键按下直接通过I2C读几个字节就行。从软件层面看原来是负责显示刷新负责输入采集两个任务现在变成了查询按键结果写显存数据一个任务。任务少了出bug的概率自然降下来。1.3 I2C接口相比SPI和并口的实际意义从接口选型的角度I2C的意义在于能用最少的两根线SCL和SDA完成所有通信。实际项目中主控的GPIO资源往往比Flash容量还紧张尤其在做小家电、智能插座、温控面板这类产品时一颗8脚或者20脚的MCU要把每个引脚都精打细算几个ADC通道采集传感器、几个IO口控制继电器、几个口接蜂鸣器和LED。能省出5到8个IO口整个BOM和布线难度完全不一样。当然有人会提SPISPI速度更快但标准的SPI最少也要三根线SCK、MOSI、MISO而且很多MCU的SPI外设要复用引脚冲突经常需要跳线解决。I2C还有一个好处就是同一根总线上可以挂多颗从设备——显示屏挂一颗0x70地址的芯片温湿度传感器挂一颗0x44地址的芯片只要地址不冲突就都能访问。这在做多模块集成的产品时对PCB布局非常友好。FZH1643选择I2C作为通信口还有一个很实际的原因I2C是半双工、带应答机制的协议适合这种主控问一句、芯片答一句的交互模型。写显存数据时主控连续发送字节芯片收到后回ACK读按键状态时主控先写寄存器地址再切换为读模式芯片把数据送上总线。整个过程逻辑清晰即使通信出错主控也能通过ACK状态和超时机制判断出来。2. 双模式切换的实际形态显示扫描与低功耗之间的取舍2.1 说的双模式是哪两个模式双模式这个说法容易让人迷糊有人理解为两种显示模式有人理解成两种通信模式。以这类芯片的常见设计来看实际指的是两种工作状态正常扫描模式和低功耗待机模式。正常扫描模式下芯片内部振荡器持续运行LCD扫描波形持续输出键盘扫描也同步工作所有的显示数据和按键状态都能实时反映而低功耗待机模式下芯片会关掉LCD扫描输出、暂停键盘扫描只保留I2C接口的寄存器读写能力让整个芯片的耗电降到微安级别。为什么需要这种切换因为段码LCD的扫描驱动本质上是一个持续消耗能量的过程——COM和SEG之间反复充放电加上偏压产生电路的静态功耗让显示驱动成为整机功耗的大头。很多产品要求待机功耗极低比如电池供电的遥控器、温控面板、智能电表几毫安的显示扫描电流都是不能接受的。所以FZH1643这类芯片会把关显示、停扫描做成一个明确的模式而不是让用户简单地写一个空显存来假装关闭。2.2 模式切换的内部状态变化振荡器、扫描时钟、显存保持模式切换并不是简单地把某个GPIO输出关掉芯片内部是一整套联动动作。正常扫描模式下内部RC振荡器或者外部晶振持续运行分频电路产生LCD扫描用的帧时钟和键盘扫描用的行扫描时钟这些时钟分别送到COM扫描发生器和键盘扫描状态机。切换到低功耗待机模式时振荡器会停掉或者降频分频链路的输出被切断COM口和SEG口进入高阻或者固定电平状态键盘扫描状态机复位到空闲状态。值得注意的是显存里的数据并不会因为模式切换而清零——它只是不再被映射到输出引脚上而已。这意味着主控在待机前写入的显示数据唤醒后会原样恢复不需要重新初始化一遍。这个特性在实战中很有用。比如温控面板从待机唤醒用户希望看到的是进入待机前的温度数值而不是一个空屏幕再重新刷一遍。我实际测试过好几次发现只要在写显存后、进入待机前做一个写命令同步操作唤醒后显示内容就能无缝恢复不会出现花屏或闪烁。2.3 一次完整模式切换的操作流程以FZH1643的典型指令结构为例切换到待机模式需要使用系统命令常见的做法是向命令寄存器写入特定的模式控制字。以下是一段参考流程代码具体命令字以实际数据手册为准void FZH1643_EnterStandby(void) { uint8_t cmd 0x02; // 示例系统命令进入待机模式 I2C_Start(); I2C_WriteByte(0xE0); // 器件写地址示例值 I2C_WriteByte(cmd); // 发出模式切换命令 I2C_Stop(); } void FZH1643_ExitStandby(void) { uint8_t cmd 0x01; // 示例系统命令进入正常模式 I2C_Start(); I2C_WriteByte(0xE0); I2C_WriteByte(cmd); I2C_Stop(); }这段代码看着简单但里面有两点容易忽略。第一模式切换命令发出后芯片内部有一定响应时间即使数据手册没有明说也建议在切换后延时1到2毫秒再继续后续操作尤其是从待机唤醒后立刻写显存的情况太急了可能丢数据。第二如果总线上还挂了其他设备发完模式切换命令之后最好等一下让芯片内部状态稳定不要立刻发起下一笔通信否则可能因为从机正在切换内部时钟而收不到ACK。实际产品中我通常会设计一个电源状态机上电初始化为正常模式 → 工作若干秒无操作 → 进入待机模式 → 检测外部按键或RTC中断后唤醒。FZH1643的待机模式功耗本身已经很低但要注意主控侧的唤醒来源如果按键扫描也被关掉了那唤醒就不能依赖读键值通常要预留一个独立的按键中断脚或者用定时器周期性唤醒主控后再由主控唤醒显示芯片。3. 键盘扫描的硬件与软件配合矩阵结构、防抖、读键流程3.1 矩阵键盘的检测原理以及芯片内部做了什么矩阵键盘的基本原理是用较少的IO口检测较多的按键。以4×4矩阵为例4根行线和4根列线交叉每个交叉点放一个按键共16个按键。检测时扫描方让行线逐行输出低电平或高电平然后读列线状态某一行被驱动为低时如果该行与某列交叉的按键被按下对应的列线就会被拉低或拉高视电路极性而定由此确定按键位置。FZH1643做扫描时内部状态机自动完成这个行驱动列读取的过程扫描频率通常在几十到几百赫兹之间取决于芯片配置的分频系数。主控完全不需要介入每一步的行线切换和列线采样只需要最后读扫描结果即可。这对主控的一个隐藏好处是按键检测的时间抖动大大降低因为硬件扫描是固定节奏运行的不像软件扫描那样容易受中断优先级、任务调度影响。3.2 扫描结果寄存器的组织方式键盘扫描的结果会以位图bitmap形式存放在芯片的按键寄存器区。每一位对应一个按键位置值为1表示按下为0表示未按下。比如8个按键的键盘用1个字节就能表示全部状态如果是16个按键就占2个字节。FZH1643的寄存器地址空间里一般会给按键状态分配连续的地址区域主控通过I2C读命令一次性读回。这里有一个实际经验值得分享很多新手读按键时喜欢把整个寄存器区都读回来然后逐位判断。但如果按键数量不多建议只读取涉及到的寄存器字节减少I2C通信时间。I2C速度在100kHz或400kHz时多读一个字节看起来不慢但在低功耗场景下总线活动时间直接关系到平均功耗。只读必要的字节不仅通信更快还能让MCU更快进入sleep状态。3.3 防抖的边界问题芯片自带的防抖够不够机械按键按下和释放的瞬间触点会经历一个几毫秒到十几毫秒的抖动过程。直接读取电平会因为抖动产生多次跳变软件上通常采用延时采样或者连续数次采样一致才判定有效。FZH1643这类带硬件扫描的芯片在扫描结果里已经内置了防抖设计——扫描周期本身构成了时间窗口只有持续被扫描到一定次数的状态变化才会更新到按键寄存器。但千万不要以为芯片自带防抖就可以完全丢掉软件防抖。以我实际调试的经验芯片的硬件防抖解决的是同一时刻电平跳变的物理抖动而软件层面的功能防抖比如按键松开后必须间隔多少秒才能再次触发是需要主控自己实现的。例如一个加温按键用户长按时希望持续加温就需要主控自己判断按下持续时间用户短按时希望单次触发动作就需要主控监测从按下到松开的完整事件。这些业务逻辑芯片不可能替你完成必须在主控的按键任务中处理。按键读回的推荐流程可以这样组织uint8_t FZH1643_GetKey(void) { uint8_t key_sts[2]; FZH1643_ReadKeys(key_sts, 2); // 通过I2C读取2字节按键寄存器 if(key_sts[0] 0 key_sts[1] 0) return 0; // 没有按键 for(uint8_t i 0; i 16; i) { uint8_t byte_idx i / 8; uint8_t bit_pos i % 8; if(key_sts[byte_idx] (1 bit_pos)) { return (i 1); // 返回按键编号1~16 } } return 0; }在主控代码里这个函数周期性调用比如每隔5到10ms再配合上一次的状态做边沿检测就能区分按下事件松开事件和持续按住。我一般会再套一层简单的状态机用于区分短按和长按这是产品交互层面的常用需求和芯片硬件扫描没有冲突。4. I2C接口实操从从机地址到显存和按键寄存器的读写4.1 从机地址与命令字节的结构先看懂芯片怎么组织通信I2C通信的第一步是从机地址。FZH1643作为从设备会有一个固定的7位地址或者支持地址引脚配置。以示例地址0x70为例对应的写地址就是0xE0读地址就是0xE1。这类地址在数据手册的Slave Address章节一定会写明设计PCB的时候如果芯片有地址配置脚提前规划好是接高还是接低避免后面多颗同型号芯片挂同一条总线时地址冲突。FZH1643的命令结构通常会区分两种类型一种是写入命令寄存器控制芯片工作模式、显示配置、偏压设置等另一种是访问显示寄存器和按键寄存器。有的芯片通过第一个字节控制字节来区分接下来是命令还是数据有的则通过寄存器地址的高位来区分。不管哪种方式掌握一个原则就行先发控制/地址信息再发数据读操作先写寄存器地址再切换读方向。4.2 显示数据写入的完整时序段码LCD的每个显示段在芯片内部都对应显存中的一个bit。上位机要显示某个数字实际上是把一个位图数据写入到显存对应的地址里。下面是一个典型的写显示数据流程void FZH1643_WriteDisplayRAM(uint8_t addr, uint8_t *data, uint8_t len) { I2C_Start(); I2C_WriteByte(0xE0); // 写地址 I2C_WriteByte(addr | 0x80); // 寄存器地址带写标志 for(uint8_t i 0; i len; i) { I2C_WriteByte(data[i]); // 连续写入显存数据 } I2C_Stop(); }这里的addr和len取决于屏的段位分布。比如一个8位数字加若干符号的屏如果芯片显存按8bit一个字节组织那么一次写8个字节到显存起始地址就能一次性刷完整个屏幕。实际项目中我习惯在程序里建一个与显存大小对应的缓存数组需要更新显示内容时先改缓存数组再把整个数组通过I2C写进芯片。这样做的原因是改任何一位都需要完整刷新才能保证一致性而且从缓存到芯片的映射关系一目了然方便排查问题。4.3 读取键盘扫描结果以及I2C读操作的方向切换I2C的读操作比写操作多一个方向切换的步骤。主控需要先发出从机地址的写方向把要读取的寄存器地址告诉芯片然后重新发出起始条件Restart或者停止后重新开始再发从机地址的读方向最后读回数据并发出NACK结束。下面是一个参考实现void FZH1643_ReadKeys(uint8_t *buf, uint8_t len) { I2C_Start(); I2C_WriteByte(0xE0); // 写地址 I2C_WriteByte(0x40); // 示例键盘扫描结果寄存器起始地址 I2C_Start(); // 重新发送起始条件 I2C_WriteByte(0xE1); // 读地址 for(uint8_t i 0; i len; i) { buf[i] I2C_ReadByte(i (len - 1)); // 最后一字节返回NACK } I2C_Stop(); }这里最后返回NACK是一个关键细节。I2C协议规定主控读最后一个字节之前和之后要发送NACK来通知从机读完了如果全部都用ACK应答从机会一直往总线上送数据导致多余字节甚至因为总线状态混乱引发错误。很多刚开始调I2C读操作的人卡在这一步总线波形看着乱其实就是应答位没处理好。4.4 初始化驱动的完整代码参考结合前面的内容给一个完整的初始化流程参考包含I2C配置、进入正常模式、设置显示参数、清空显存void FZH1643_Init(void) { uint8_t clear_data[8] {0}; I2C_Init(400000); // 配置I2C时钟为400kHz FZH1643_ExitStandby(); // 从待机或默认状态进入正常模式 delay_ms(2); // 等待内部振荡器稳定 // 发送显示配置命令示例使能显示、设置1/4占空比、1/3偏压 uint8_t cfg 0x28; // 示例配置字实际按手册 FZH1643_WriteCommand(cfg); // 清一次显存避免上电显示乱码 FZH1643_WriteDisplayRAM(0x00, clear_data, 8); }初始化顺序我建议固定为供电稳定 → I2C通信测试读一个固定寄存器校验→ 退出待机模式 → 配置显示参数 → 清显存 → 正常显示。为什么要把I2C通信测试放在前面因为在批量生产中芯片焊接不良或者总线不通是常见故障如果一上电就初始化显示很可能屏幕不亮但主控代码还在跑等发现时已经过了大半天。先读一个芯片版本寄存器或者复位标志寄存器确认通信正常再继续能省很多排查时间。5. 项目实测中遇到的坑偏压、上拉、扫描干扰与上电时序5.1 偏压和占空比没配对显示重影或发暗这是段码LCD项目里最经典的坑。偏压bias和占空比duty必须和屏厂的规格书严格对应。屏厂会说这块屏是1/4 duty、1/3 bias那芯片就必须配置成相同的组合。如果把1/3偏压的屏配成1/2偏压会出现不该亮的段微亮、显示重影反过来如果把1/2偏压的屏配成1/3偏压显示对比度就会变差笔画颜色发浅。我遇到过一块屏刚开始怎么调都是8字显示的时候某些段亮但不完整排查了很久才发现是芯片配置里把占空比设置成了1/3而屏实际是1/4的。这种问题在软件层面看没有任何报错因为寄存器写入都正常但显示效果就是不对。所以做样机阶段第一件事就是跟屏厂确认duty和bias参数然后写进配置注释里防止后面换人维护时改错。5.2 I2C上拉电阻和总线电容的实测取舍I2C总线需要上拉电阻这是常识但具体选多大阻值很多人拿不准。标准模式下总线电容通常是几pF到几十pF阻值太小比如1K会加大灌电流阻值太大比如100K会拉长信号边沿导致波形上升沿太缓在高速模式下产生通信错误。比较常用的区间是2.2K到4.7K具体取决于总线长度和挂载设备数量。当FZH1643和主控距离比较远或者中间加过ESD保护器件时总线电容会明显增加。此时如果系统频率用400kHz建议用逻辑分析仪看下SCL和SDA的上升沿时间如果超过300ns要么换小一点的上拉电阻要么把I2C频率降到100kHz。实测中4.7K在短距离单设备场景非常好用但多设备并连、走线较长时2.2K更稳妥。还要注意SDA上的上拉电阻大小会影响按键读取的稳定性——总线边沿太缓数据采样会发生位移偶尔会出现读到0xFF或0x00这种异常值。5.3 键盘扫描与显示扫描的时间窗冲突以及芯片的仲裁机制FZH1643内部同时运行LCD扫描和键盘扫描很多用户担心两个扫描会不会互相干扰。实际上芯片设计时已经做了时间片仲裁LCD扫描和键盘扫描复用内部时序不会出现同时占用的冲突。但有一个特殊情况I2C访问显存或按键寄存器的瞬间如果正好撞上内部扫描读取显存的周期就可能出现短暂的busy状态表现为I2C通信偶尔延迟一个ACK。这种延迟不是错误但主控代码要有超时容错。我的处理方式是给I2C通信加一个简单的重试机制发送地址后如果没有收到ACK延时1ms再重试连续3次失败才报错误。这个机制能吸收芯片内部的忙碌周期又不至于掩盖真正的硬件故障。另外I2C时钟频率越高单次通信时间越短撞上内部扫描窗口的概率越低所以条件允许时用400kHz而不是100kHz反而更稳。5.4 上电复位时序和看门狗协作FZH1643上电后需要一定时间的内部复位过程典型值是几百微秒到几毫秒。如果主控在芯片还没准备好时就发起I2C通信第一个字节基本拿不到ACK。有些主控的看门狗周期很短如果启动代码里I2C通信失败导致看门狗复位就会形成复位-通信失败-再复位的死循环。解决方法是把初始化做的温柔一点上电后延时50到100毫秒等电源稳定再发起第一次I2C通信。同时把看门狗的喂狗位置放在通信重试循环之外避免重试期间看门狗溢出。这个经验来自一次真实的事故现场有一批设备偶发启动黑屏抓日志发现是电源上电波形有轻微抖动芯片复位不完整而主控复位太勤快两个复位相互打架。加长了初始延时就再没复发过。6. 最后聊点选型和设计的个人看法FZH1643这类合封方案最打动我的地方不是某一项指标多突出而是它把显示驱动和键盘扫描两个脏活累活一起打包了让主控代码可以保持干净。在实际项目选型时我通常会列一个简单的对照表来判断合不合适需要驱动的段码屏有多少段、按键数量有多少、I2C总线上还挂了哪些设备、待机功耗要求是多少、工作温度范围是否覆盖产品应用场景。这些参数和FZH1643的规格一对基本就能判断方向对不对。如果你的产品只需要显示、没有按键那选一颗纯LCD驱动芯片就够了没必要为用不上的键盘扫描功能买单。如果按键数量特别多超过8×8矩阵或者需要支持一些特殊按键行为比如长按连续触发、组合键检测那可能要考虑按键扫描交给主控做、显示单独用驱动芯片的方案。选型不是越集成越好而是看合封的这部分功能是不是正好落在你的需求范围内。按照我个人的调试习惯第一次拿到FZH1643样片不会急着写完整应用而是先用逻辑分析仪把I2C通信抓一遍确认芯片能正常应答、显存读写能回读一致再做显示和按键联调。这一步看起来慢实际上是最快的路径。等这些基础操作都验证过再进到具体的双模式切换、低功耗调优心里就有底了。最后再提醒一句写代码时记得在显存更新函数前后加上合适的延时或者使用Busy标志位判断别太相信瞬间完成I2C从机不是寄存器数组它是带内部时序的物理器件。