尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用188芯片实现数码管50Hz无闪烁:动态扫描刷新率与实战避坑

用188芯片实现数码管50Hz无闪烁:动态扫描刷新率与实战避坑 前段时间调一块六位数码管客户给的验收标准只有一句话肉眼看着不闪。我一开始觉得这事简单动态扫描嘛六位而已51单片机定时器扫就完了。结果第一次上电就翻车——日光灯下看着还行手机一怼上去满屏黑色滚动条再用余光瞟屏幕边缘明显有节奏地一跳一跳。把示波器夹上去一看问题根本不在“亮不亮”而在“刷新率到底由谁决定、是不是稳定”。这篇文章就把我从“能亮”到“敢说它不闪”的完整过程拆开讲。标题里说的188芯片是一类串行接口的数码管驱动IC三根线控制、自带扫描逻辑市面上丝印188或兼容TM1628/TM1638时序的片子都很常见。文章会讲清楚50Hz这个数是怎么来的、188芯片在扫描链路里的位置、怎么把刷新率和亮度算明白以及我实际踩过的五个坑。适合正在做时钟、倒计时、仪表显示的同学参考不管你是用51、STM32、Arduino还是FPGA底层逻辑是一样的。1. 动态扫描为什么会有“可见闪烁”和“半亮”这两个怪问题1.1 动态扫描的本质用1/N的占空比换引脚数数码管这东西一位就是7段加小数点8个引脚。做六位静态驱动每组都要独立锁存或独立驱动板子面积和成本直接翻倍。动态扫描的思路很简单所有位的相同段并联然后靠位选轮流点亮一帧之内每一时刻只有一位在亮。眼睛有视觉暂留看到的效果是“六位同时在亮”。但这里有个关键点LED和荧光粉不一样它没有余辉电流来了立刻亮、电流没了立刻灭。所以动态扫描能不能骗过眼睛完全取决于“每一位亮多久、一秒钟重复多少次”这个重复次数就是刷新率单位Hz。刷新率低了眼睛看到的不是稳定数字而是一格一格跳动的“断片”。同时每一位的导通时间只有静态驱动的1/N6位就是1/6。如果峰值电流还按静态那点值算平均亮度就只有静态的1/6看起来就是“半亮”甚至“暗得离谱”。新手最容易懵的就是这里我明明加电了怎么比静态驱动暗这么多因为你没有把峰值电流按N倍补上去。后面会反复用到这个“平均电流峰值电流×占空比”的关系。1.2 50Hz是怎么被人当成“及格线”的人眼对闪烁的分辨有个大致范围明亮环境下临界融合频率通常在5060Hz之间。电影24帧不觉得闪是因为每帧通过快门遮挡闪了23次等效刷新率上去了。早期电视场频用50HzPAL制式也是这个数字被人记住的重要原因。但50Hz真的只是及格线。中央视觉通常看不出50Hz的闪烁余光对闪烁却极其敏感快速扫视、挥手遮挡、旁边再放一盏高频光源50Hz很容易露馅。经常有人争论“50Hz到底闪不闪”我的观点是个体差异极大有人连60Hz都敏感别拿“我眼睛看不出来”当验收依据。所以我做东西的习惯是设计按50Hz打底实机目标跑到75Hz甚至100Hz。标题问“如何用188芯片实现50Hz无闪烁”我会先泼一盆冷水50Hz是把“不闪”当最低要求不是当最佳目标。1.3 “无闪烁”其实要分三个层次来看我把“不闪”拆成三层方便后面排查问题第一层肉眼直视不闪。刷新率≥50Hz基本能做到。第二层余光、扫视、手机拍照不露馅。需要刷新率更高或者扫描波形非常干净。第三层和环境光源不产生拍频、数据更新不产生撕裂。这跟刷新率数值无关跟同步策略和缓冲策略有关。很多人排错顺序是反的一开始就怀疑芯片、怀疑代码连“自己到底在哪个层次不达标”都没搞清楚。先把目标层次定下来后面每一章都能对上号。2. 188芯片在扫描链路里的真正位置2.1 它不只是“帮MCU省事的三极管”先说清楚我手上这颗188是什么。它三根线STB、CLK、DIO内部有显示RAM、有扫描时序、有段驱动和位驱动上电后MCU只要把显示数据写进去它自己就一帧一帧扫。这跟“三极管MCU逐位扫描”是两码事——后者扫描完全靠MCU定时器中断来做MCU一忙刷新率就掉。国内这个小封装芯片市场很有意思丝印写着188的片子命令格式往往和TM1628/1638这类串行显示驱动IC几乎一样。我焊之前都会花十分钟对一下数据手册的命令表和GRID数量因为同一个丝印下可能是8位版本也可能是10位版本。这一点不确认后面代码全是白写。188芯片的典型引脚功能大致是这样引脚作用STB/CS片选低电平有效一帧命令的开始和结束CLK串行时钟上升沿锁存数据DIO双向数据写显示数据和读按键都走这根线SEG1~SEG8段驱动接数码管a~g和dpGRID1~GRID6/8位驱动接共阴数码管的COM脚VCC/GND5V供电GND要就近接地2.2 和MCU直驱、74HC595、MAX7219方案的本质区别做数码管显示方案其实就那几种选型先看“扫描由谁负责”方案控制引脚扫描由谁做刷新稳定性最典型的坑MCU直驱三极管N8MCU定时器取决于软件主循环阻塞导致刷新率掉到几十Hz74HC595位驱动3~5MCU定时器取决于软件锁存时序不对引发鬼影188类串行驱动芯片3芯片内部硬件保证命令和RAM地址写错显示乱码MAX72193(SPI)芯片内部硬件保证限流电阻精度影响亮度一致性还有同学喜欢用4511这种硬件译码器它能把BCD码变成段码省去软件查表但扫描还是要MCU做本质上还是软件扫描这一列。用188这类芯片最大的好处是扫描频率由芯片内部RC振荡器决定不占CPU也不会因为主循环里跑了个延时函数就闪。但这不代表“上了芯片就不会闪”——后面第四章的坑大部分都是“芯片本身扫得很好却被外部电路和软件更新策略搞砸”。2.3 选型和替换时的兼容性判断如果你手头也有一颗丝印188的片子先别急着写代码按这个顺序做三件事查数据手册的“命令表”看有没有0x40数据命令、0xC0段地址、0x80/0x88显示控制这些命令字。有的话基本上可以按TM16xx系列的逻辑写。数GRID引脚决定你最多接几位。6位显示用8位芯片当然可以但空出来的GRID必须处理否则会出现幽灵残影。看SEG的灌电流/拉电流上限有些兼容片标称每段20mA有些能到40mA。这直接决定你能不能靠芯片直驱还是需要外扩三极管驱动位选。我自己的原则采购时不要只报丝印要让卖家提供PDF并且上电后先写全0、再写全1用示波器确认SEG/GRID波形极性对不对。这一步半小时能省后面三天。3. 从“想要50Hz”到“代码和接线”完整推演3.1 刷新率、导通时间、峰值电流三者互相绑架先说公式。动态扫描的整屏帧率由“每位数导通时间”和“位数”决定帧率 1 / (每位数导通时间 × 位数)要50Hz帧率、6位显示每位数导通时间就是20ms/6≈3.33ms。要跑到75Hz就是13.3ms/6≈2.2ms/位。对于188这类内置扫描芯片这个导通时间由芯片内部振荡器和GRID数决定通常远高于这个值所以我后面会单独说“MCU手动扫描时怎么算”芯片自扫时你要关心的反而是数据更新那一层。数字摆出来更直观位数N50Hz帧率下单管导通时间75Hz帧率下100Hz帧率下等效静态亮度所需的峰值电流(以平均5mA为例)4位5.0ms3.3ms2.5ms20mA6位3.3ms2.2ms1.7ms30mA8位2.5ms1.7ms1.25ms40mA这张表看着简单里面有个工程约束峰值电流不能随便拉。红色数码管一段连续工作电流一般按1020mA设计峰值可以到60mA甚至更高但要查LED规格书。188这类芯片的段驱动上限通常也在3040mA。我一般按“静态5mA等效亮度”来倒推峰值也就是峰值N×5mA6位就是30mA卡在芯片上限附近留一点点余量。真嫌亮度不够就外扩驱动不要硬怼芯片。3.2 188芯片的数据帧格式先搞清楚你的芯片是“几字节一位”这里给一个通用模板。兼容TM1628/1638时序的芯片典型数据帧有三段命令1数据命令比如0x40表示“写显示数据地址自动加一”。命令2地址命令0xC0加上起始地址表示从哪个GRID开始写。数据连续写入若干字节每字节对应一个GRID的段数据。代码可以写成这样我这里是GPIO模拟时序Arduino同理// 假设 STB/CLK/DIO 已经定义好高低电平操作 static void write_byte(uint8_t dat) { for (uint8_t i 0; i 8; i) { CLK_LOW(); DIO (dat 0x01) ? 1 : 0; dat 1; CLK_HIGH(); // 上升沿锁存 } } static void write_cmd(uint8_t cmd) { STB_LOW(); write_byte(cmd); STB_HIGH(); } // 显示缓冲6位 static uint8_t seg_buf[6]; void display_update(void) { STB_LOW(); write_byte(0x40); // 数据命令写显示、地址自增 STB_HIGH(); STB_LOW(); write_byte(0xC0); // 起始地址第0位 for (uint8_t i 0; i 6; i) { write_byte(seg_buf[i]); } STB_HIGH(); } void display_on(uint8_t brightness) // brightness: 0~7 { write_cmd(0x88 | (brightness 0x07)); }有几句话必须交代地址映射到底是“一个GRID一个字节”还是“一个GRID两个字节”不同芯片不一样必须以手上的数据手册为准。段码表也要自己整理数码管a段对应字节的哪一位网上给的段码表不一定和你PCB走线一致。我吃过这个亏接好线后先写一个“扫段程序”一位一位点亮把实际对应关系记下来再做段码表。3.3 初始化顺序先关显示、再清RAM、最后开显示这类芯片的RAM上电状态是随机的。如果上电就开显示你会看到乱码闪一下运气好是一堆不规律的段运气不好整屏全亮。正确顺序是VCC稳定后等10ms以上。发送“显示关闭”命令。给显示RAM全部写0。发送“显示开启亮度”命令。再往RAM里写真正的显示数据。顺序不能反。很多“上电闪一下”的板子就是少了第2和第3步。代码上没多复杂但顺序错了排查起来会怀疑人生。提示清空显示不要用“显示关闭”来代替。显示关闭只是让扫描停掉RAM里的内容还在重新开显示时旧数据会原样回来。要清就写0。3.4 更新策略不要让“刷数据”变成“拖刷新”188芯片自扫的刷新率是硬件保证的但有一个常见误会有人一秒钟只更新一次显示数据更新的时候还先发一条“显示关”写完再“显示开”。这么一做等于人为把整屏刷新率干到了1Hz肉眼直接看到闪——这是自己给自己挖坑。正确的做法是更新显示数据时永远不要关显示。直接用“写数据命令”覆盖RAM即可芯片内部扫描不会停下来等。6个字节用100kHz时钟写也就一两毫秒在20ms一帧的尺度上完全无感人眼根本看不出“撕裂”。如果是手动扫描MCU定时器中断方式那就要注意撕裂问题中断里切换位选时先把所有段灭掉再断位选再送新段码最后开下一位。只要在“灭段→断位→送码→开位”的间隙里更新缓冲区就不会出现某一个位显示新旧混合数据的瞬间。更稳妥的办法是双缓冲主循环往后台缓冲写中断里在间隙时刻交换指针。4. 我实际踩过的五个坑以及每一个的根因4.1 鬼影先“灭段”还是先“断位”鬼影是所有动态扫描都会遇到的问题表现形式是你明明只显示了“123456”却能在某个数字旁边看到前一位的淡淡残影或者移动视线时字符边缘有拖影。根因在切换瞬间。手动扫描时如果先打开下一位的位选再更新段码那么开位选的瞬间SEG线上还是上一位的数据这段时间里上一位的段会在下一位上亮一下。这个时间虽然短但亮度高、重复频率高视觉上就是拖影。正确顺序永远是灭段 → 断位 → 送新段码 → 开下一位。对188这类芯片内部扫描本来就带消隐间隙正常不需要你操心。但如果你跟我一样因为电流不够在外部加了位驱动三极管那就要注意三极管的关断存储时间了。NPN位驱动三极管在关断时有几百纳秒到几微秒的拖尾处理办法是基极对地接一颗10k电阻加速放电或者干脆换成MOS管。4.2 亮度不均你以为在写代码其实在搞电源第二个坑是亮度不均。同事第一反应是“段码表错了”其实大多数时候是电源被动态扫描电流脉冲打崩了。动态扫描下每一瞬间只有一个位在工作这一位的全部段电流同时从电源抽取波形是一串脉冲。如果PCB上逻辑地和显示地共用一条细走线这段走线的寄生电阻就会在脉冲来时产生压降导致点亮的段越多电源跌得越狠其他位看起来越暗。我当时处理办法是显示电源和逻辑电源分开走线星形接地在显示电源入口放100470μF电解电容每个数码管COM脚附近再放0.1μF陶瓷电容段限流电阻每段一颗绝不共用。段电阻共用是另一个高频错误——如果一位的8段共用一颗电阻亮的段数一变每段电流都变亮度直接跟着数字内容走。限流电阻计算给个例子5V供电、红色数码管压降约2.0V、188芯片输出级压降按0.5V估目标峰值电流20mA电阻就是(5−2.0−0.5)/0.02≈125Ω取120Ω。这个计算口径要记牢很多人把驱动芯片的压降忘了电阻选大了一圈整体亮度就上不去。4.3 市电50Hz环境的“拍频闪”这个坑最隐蔽。有块板子拿回家一测就闪在办公室却好好的。后来发现问题出在“数码管刷新率”和“环境光源100Hz纹波”之间的拍频。室内LED灯和荧光灯驱动电源绝大多数是整流后的脉动供电光强本身有100Hz的纹波50Hz市电的两倍频。如果你的数码管帧率恰好接近50Hz但又不是严格电网同步比如晶振漂移导致49.7Hz两个频率会叠加出一个慢变的亮暗周期视觉上就是“呼吸感”。这其实不是数码管扫得不够快而是两个光源在打架。对策有三个可以叠加使用把刷新率提高到75Hz或更高避开和100Hz纹波的低频拍频。显示供电加一级LDO或LC滤波把电源纹波压下去数码管亮度就不随市电波动。如果项目必须锁在50Hz比如要做电网同步显示那就从交流零点检测来同步刷新让显示帧率和市电同源。另外提醒一句用手机拍照验证是否闪烁时室内LED灯的100Hz纹波会叠加进画面让你误判数码管在闪。拍照只能做参考下结论还得看示波器。4.4 共阴共阳接反的“怪症状”188这类芯片大多按共阴数码管设计GRID做低电平扫描SEG做高电平点亮。你要是不小心用了共阳数码管症状会很怪可能完全不亮可能乱码更常见的是“段和位之间微弱漏光”——因为电流会顺着错误的路径从SEG灌进去经过LED再到COM最后从另外的COM脚偷偷回来。拿到新数码管先别急着焊用万用表二极管档量黑表笔接COM红表笔轮流点各段亮了就是共阴反过来是共阳。像3611BS这类一体式模块不同厂家丝印规则还不一样COM脚定义差异很大一定要实测确认。解决方案就三条路换成共阴数码管如果手上只有共阳加PNP三极管做位驱动反相段驱动再加限流或者选一颗专门支持共阳的驱动芯片。不要在共阳上硬套共阴代码那不是改个取反就能救回来的电流路径都不对。4.5 初始化顺序和未用GRID的处理最后一个坑是用8位GRID的芯片驱动6位数码管时踩的。没用的两位不接数码管但RAM地址还占着如果里面的数据不是0扫描每到这两个GRID时外部引脚上会出现不该有的电平变化在某些走线条件下会感应到旁边数字位上形成“幽灵位”。处理办法初始化时把所有GRID的RAM都写0包括不用的那几位同时如果芯片支持“扫描位数设置”把它设成实际位数让芯片跳过空GRID。这样既省电也杜绝幽灵残影。命令里如果看到类似“扫描配置寄存器”的东西别跳过看一眼。5. 怎么证明它真的不闪人眼、手机、示波器三板斧5.1 人眼检验法直视、余光、扫视人眼是最快的初筛工具但要用对方法。三步走正对着屏幕盯住一个字符看10秒看有没有抖动、跳动。眼睛不看屏幕用余光注意屏幕边缘同时用手掌在屏幕前快速划过看字符有没有“断开”或者“梳子状”分层。左右快速摇头或滚动眼球观察字符边缘有没有拖尾。这三步比单纯“盯着看”敏感得多。余光对高频闪烁的感知比中央视觉强扫视则会把时间上的亮度波动展开成空间上的条纹。如果三步都干净基本可以确认视觉上无闪烁。5.2 手机相机法别把快门当成示波器用手机拍数码管很容易拍出黑色滚动条。原因不是你的板子在闪而是手机CMOS是滚动快门逐行曝光和显示刷新率存在采样差频。显示刷新率越高暗带越细越密刷新率越低暗带越宽、滚动越明显。所以拍照的正确用法是“横向对比”同一个手机、同一个快门分别拍已知的60Hz、100Hz方案和你的板子对比暗带形态。如果你板子的暗带明显比100Hz方案宽很多那大概率低于目标如果暗带细密几乎看不见就不用纠结。还要补一句手机显示“不闪”不等于人眼“不闪”——由于采样频率关系手机可能漏拍某些频率的闪烁。所以手机法只用来过滤“明显不合格”最终结论以人眼三步法和示波器为准。5.3 示波器是唯一能下结论的工具示波器才是判官。我常测四个点测试点正常波形异常现象对应问题GRID1对GND周期20ms50Hz低电平宽度约3.3ms6位周期抖动、宽度漂移扫描时序不稳/晶振问题SEG-X段对GND周期20ms的脉冲串占空比约1/6脉冲边缘拖尾、振铃鬼影、驱动器件关断慢VCC对GND直流纹波50mV周期性跌落超过200mV电源去耦不足/走线太细DIO信号仅写入时有一串脉冲长期活跃/波形畸形命令模式错误、STB时序不对测的时候注意探头地线要短别用长鳄鱼夹否则看到的全是探头自己捡的噪声。举个例子如果你量到GRID周期不是20ms而是30ms说明实际帧率只有33Hz肉眼不闪才怪如果周期是20ms但低电平宽度忽大忽小说明芯片配置或外部驱动在抖动要回到第三章检查。还可以在COM回路里串一颗1Ω采样电阻测位电流的峰值和平均值反推实际占空比和亮度一致性。这套做完“不闪”就不再是感觉而是有波形图可查的结论了。6. 从“不闪”到“更好看”顺手能做的几个优化6.1 用芯片的PWM亮度寄存器代替硬件减流188这类芯片一般自带亮度控制本质是改变扫描期间段的导通占空比常见有8级或16级。调亮度优先用这个而不是去改限流电阻。原因是改限流电阻会让峰值电流偏离设计点一方面亮度一致性变差另一方面可能让驱动级进入线性区波形变难看不说芯片还可能发热。PWM亮度只是改占空比扫描频率不变、峰值电流不变闪烁特性不会变差。6.2 给扫描留一点“呼吸时间”如果你走的是MCU手动扫描路线我会在每位数导通时间后面特意留50100μs的全灭时间。以6位、50Hz为例每周期20ms每位分到3.3ms扣掉0.1ms消隐实际亮度只损失3%换来的是切换瞬间鬼影彻底消失。这3%的亮度损失靠把峰值电流提高一点点就补回来了但鬼影问题不是提高电流能补的。我对这个“呼吸时间”的评价就一句值得。6.3 时钟/倒计时产品的局部更新建议最后给做时钟、秒表、倒计时的同学一个建议显示数据按秒变化时不要为了省事每秒重发整个缓冲除非你有刷新整屏的强迫症。更好的做法是写一个带“起始地址和长度”的局部更新函数哪位变了就更新哪几位。这不光是为了省那几毫秒更是为了减少每一次更新引入的EMI脉冲以及降低多个任务抢总线时的冲突概率。代码层面只是把display_update改成带参数的版本成本很低效果很好。我个人的体会是数码管显示这块九成问题不是芯片不够好而是“以为扫描由芯片负责就不用管时序”和“以为代码对了就不用管电源”这两种心态。把帧率当设计指标、把消隐当基本礼仪、把电源当第一优先级基本就不会翻车。188芯片用顺手之后我只改一个寄存器就能从50Hz推到75Hz肉眼几乎察觉不到差异但手机拍出来、余光瞟过去差距立刻就能看出来——这也是我为什么建议你把50Hz当底线而不是目标的原因。
返回列表