数码管动态扫描:用时分复用让多位显示常亮
更新时间:2026-09-24。本文是「集成电路大类 · 芯片设计」入门层(beginner)数字逻辑基础第三十八篇。前面解复用与译码器埋了「位选轮流选通」的伏笔,这一篇把它做成 FPGA 上板最常见的外设——多位七段数码管的动态扫描,并推广到 LED 点阵和键盘矩阵。这是「用时间换引脚」最经典的数字设计案例,学完它,你会理解生活里几乎所有数字显示屏和矩阵键盘的工作方式。
本文要回答的问题
- 为什么不让每位数码管独立接线?动态扫描到底省了哪些 IO?
- 段选、位选怎么配合?共阴和共阳的段码为什么恰好相反?
- 明明每次只有一位被点亮,为什么看上去所有位都常亮?刷新要多快?
- 亮度由什么决定?什么是鬼影/拖影,为什么切换位之前要先消隐?
- LED 点阵和键盘矩阵为什么是同一套行列扫描?
一、先算接线账:静态驱动为什么不可行
一个七段数码管有 a~g 七个笔画段,外加一个小数点 dp,共 8 个发光二极管。最朴素的想法是静态驱动:每位的每一段单独接一根 IO,想让哪段亮就把哪根线置有效。一位要 8 根线,看起来不多;可数码管设备从来不是一位的——实验板常见 4 位或 8 位,电梯楼层屏、电子钟经常是 6~8 位。
| 显示位数 D | 静态驱动 IO = 8×D | 动态扫描 IO = 8+D | 加 3-8 译码后 |
|---|---|---|---|
| 2 | 16 | 10 | 8+2(无需译码) |
| 4 | 32 | 12 | 8+3 = 11 |
| 8 | 64 | 16 | 8+3 = 11 |
| 16 | 128 | 24 | 8+4 = 12 |
8 位就要 64 根 IO,这意味着一个中等规模 FPGA 大半的引脚全拿去接显示,布线、封装、PCB 面积都受不了;而每位要显示的数字在绝大多数时间里根本不变,让 64 根线一直「占着」是巨大的浪费。引脚是芯片按颗数计价的资源:封装从 100 脚换成 144 脚,芯片单价和板子面积都要跳一档,而多出来的这些线只是在重复驱动同一种器件。解决思路来自通信工程里的常识:一根线传多路数据,靠把时间切片——这就是时分复用。电话干线用一根线路轮流传上千路话音,数码管做的事一模一样,只是每一路的「通话内容」是一个数字的段码。
数码管动态扫描的做法是:所有位的同名段(所有 a 段、所有 b 段……)并联成 8 根共享段线,每位的公共端各接一根位选线;任一时刻只选通一位,在这一小段时间里,8 根段线上放「这一位」的段码。下一拍关掉这一位、选通下一位,段线改放下一位的段码。如此高速循环,D 位显示只需要 8 + D 根线,位选再加一级译码器则只需 8 + log2(D) 根。注意省下的不只是芯片引脚:板子上少走几十根线,接插件、过孔和布线层数都跟着省,量产时每一处节省都会被出货量放大,这才是动态扫描成为行业标配的真正理由。
二、段选与位选的结构
下图画出动态扫描的完整结构。核心被拆成两侧:段线一侧是「数据总线」,位选一侧是「译码选通」。
读图时抓住两条路径:段码从扫描逻辑出发,沿 8 根共享段线同时到达所有位;但位选译码在同一拍只让一个驱动管导通,于是只有公共端被选通的那一位真正形成电流回路。换个说法,段线是在不停地对所有位「广播」,位选则决定这一拍「谁接收」。这正是「一路段数据按时分方式送到不同目的地」的解复用:段线是数据输入,位号是地址,D 个数码管是 D 个输出通道。
驱动管一般用三极管或 MOSFET,位线还可经过专用驱动芯片扩流,原因见第五节的电流账。
当然,动态扫描不是任何场合都最优。只有一两位显示、或要求极低功耗的设备(比如电池供电的温控器),静态驱动反而更合适:每位配一片锁存器,数字不变时完全没有开关动作,也不存在占空比带来的亮度损失。选择标准很实际——IO 紧张且位数多就扫描,IO 富裕且追求省电或极致亮度就静态。工程设计里几乎没有绝对先进的方案,只有与约束匹配的方案。
三、段码:字形表与共阴/共阳
扫描逻辑内部有一张字形表(font ROM):输入是该位要显示的数字(0~9、A~F 等),输出是 8 bit 段码。七段按位排列,例如约定 {dp, g, f, e, d, c, b, a},数字 0 亮 a~f、灭 g,对应码就是 0011_1111(0x3F);数字 8 全亮,是 0111_1111(0x7F);数字 1 只亮 b、c,是 0000_0110(0x06)。这张表通常用 Verilog case 或一小块 ROM 实现,本质是用表实现组合逻辑,比硬写逻辑表达式直观得多。段码的标准译码关系在码制转换器与七段译码里已经推导过。
同一张字形表不能直接通吃所有管子,因为数码管内部 8 个 LED 的公共端接法分两种:
| 共阴(common cathode) | 共阳(common anode) | |
|---|---|---|
| 公共端 COM | 接地 | 接电源 |
| 段端给什么电平点亮 | 高电平 | 低电平 |
| 字形码中点亮段 | 为 1 | 为 0 |
| 段码关系 | 基准 | 共阴码按位取反 |
再看几个常考字形加深理解:数字 2 亮 a、b、g、e、d,码是 0101_1011(0x5B);数字 6 比它多亮 f,是 0111_1101(0x7D);9 只灭 e、d,是 0110_1111(0x6F);字母 A 亮全部段唯独 e、d 灭,是 0111_0111(0x77)。小数点 dp 就是最高位那根线,时钟显示里「秒」的闪烁效果其实就是在对应位段码的最高位上定时加 1、减 1。
混用两种管子是上板第一坑:给共阳管送共阴码,该亮的灭、该灭的亮,轻则数字错乱,重则看起来「全亮」或「全灭」。写驱动前先看开发板原理图确认管型,共阳板就在段码输出处整体取反。第二坑是不限流:每个 LED 必须在段线上串限流电阻(或使用恒流驱动),否则导通电流远超芯片引脚与 IO PAD 的驱动能力,烧管脚或烧灯。电阻取值可以按欧姆律估算:红光 LED 正向压降约 1.8~2.0V,若电源 3.3V、想让单段电流约 8mA,则电阻约为 (3.3−2.0)/0.008 ≈ 160Ω,实际常用 200~220Ω 留余量;电源是 5V 时电阻相应加大到 330~470Ω。多位数码管模块常在内部已把电阻做成排阻,上板前用万用表量一下即可。
四、扫描时序:分频、计数与段码轮放
硬件结构定了,还需要一个「节拍器」让它转起来。用一个较快时钟经分频与时钟使能产生扫描使能脉冲 scan_ce,一个小计数器在每个使能脉冲上加 1,循环给出当前位号;段码用该位号从显示缓存里取出对应数字、查字形表得到;位选则把二进制位号译成 one-hot,只选通当前位。
always_ff @(posedge clk) // 扫描位号 0 -> D-1 循环
if (scan_ce) an_idx <= an_idx + 1'b1;
// 该位的数字 -> 段码(共阴字形表)
seg = font_rom(disp_data[an_idx]);
// 位选:one-hot 译码,仅选通当前位(共阳板整体取反)
always_comb an = ~(8'b1 << an_idx);下图把连续三拍的时序摊开。重点看段线和位选的先后关系:消隐区间里段码先换好、稳定下来,下一位才开通。

图中每一位只在自己的时隙(1~3、4~6)导通,段线在两个时隙之间的 3~4、6~7 小段进入消隐。肉眼看到的不是这个脉冲序列,而是每位每秒被点亮几十上百次后的平均效果——人眼的视觉暂留约 1/10 秒量级,LED 熄灭后的余晖也会补上间隙,只要每位每秒被刷新约 50~60 次以上,闪烁就融合成稳定常亮。工程上留足余量:每位刷新约 100~200Hz,8 位显示的整体扫描频率就是约 800Hz~1.6kHz。
分频系数可以照着目标频率直接算。假设 FPGA 输入时钟 50MHz、8 位显示、希望每位刷新 125Hz,则扫描使能频率应为 8×125Hz = 1kHz,对 50MHz 取 50000 分频即可;把计数器计到 24999 翻转一次使能脉冲,低 3 位计数器正好循环给出 0~7 的位号。刷新率太低,余光会先在周边视野里被察觉(人眼对余光区的闪烁比对直视区更敏感),所以数码管晃一下头看到拖尾,往往就是刷新没做够。刷新率太高也不划算:驱动管开关损耗增加,段码刚跳变还没稳定就开通,反而容易出鬼影。
一个有意思的验证现象:把手机摄像头对准刷新中的数码管屏,取景器里经常出现一道道滚动的明暗横纹。人眼看不出闪烁,是因为视觉暂留把脉冲融合了;摄像头是逐行曝光的,每行曝光时间远短于 1ms,恰好能「抓」到此刻哪一位导通。横纹不影响使用,却直观证明了屏幕确实在高速轮点而非常亮。
五、亮度、消隐与电流账
亮度由占空比决定。 D 位循环时每位只在 1/D 的时间里导通,平均光通量近似正比于占空比 1/D:4 位扫描每位有 25% 的时间,8 位只剩 12.5%,所以位数越多显示越暗。补偿办法是提高单位时间的段电流(LED 允许短时间过驱动,只要平均电流不超标)或改用恒流驱动芯片;灰度屏则是在时隙内再做 PWM,改变导通时长得到多级亮度。
消隐(blanking)是防鬼影的关键。 切换位的正确顺序是「先关显示 → 换段码 → 等段码稳定 → 再开通下一位」。如果边选通边换段,旧段码会在新位上短暂出现——哪怕只有几微秒,由于每位被高频重复,这个错误也会被积分成肉眼可见的相邻位串光,即鬼影(ghost)。段码和位选都由组合逻辑产生时,两条路径延迟不同,尤其要插入这一小段死区。
电流账决定了必须外接驱动。 一位 8 段全亮时,假设单段电流 8mA,合起来约 64mA 全从这一位的公共端驱动管流过,所以位选器件要按「8 倍单段电流」选型;而段线虽被 D 位共用,任一时刻只有一位导通,段引脚按单段峰值电流设计即可。FPGA 管脚通常只能承受十几 mA,无论段侧还是位侧都不该直接硬扛,这就是多位扫描普遍使用三极管/MOSFET 或 TM1637、MAX7219 一类专用驱动 IC 的原因(参见 IO 与 PAD)。这些专用芯片把字形表寄存、段限流、位选扩流和消隐时序全部集成进去,主控只需通过类 I2C 或 SPI 的串行接口把数字发进去,刷新由芯片自己完成,因此 MCU 方案里几乎见不到「裸扫」;而学习阶段手写扫描逻辑的价值,正在于看明白这些芯片内部替你做了什么。
六、推广:LED 点阵与键盘矩阵
把数码管的「段 + 位」结构旋转九十度、再扩成网格,就得到 LED 点阵:行线相当于位选,由译码器和驱动管逐行选通;列线相当于段线,在该行选通期间送入这一行的图案数据。逐行循环刷新,一整屏文字或图像就稳定呈现,灰度靠列数据的 PWM 控制。LED 点阵本质就是「二维化、网格化的数码管动态扫描」,消隐、占空比、行驱动管要扛整行电流的规则完全照搬。
工程上的 LED 点阵模块几乎都按 8×8 一块组织,列侧用 74HC595 一类串入并出移位寄存器级联:主控只需 3 根线(数据、时钟、锁存)就能把任意宽度的一行数据串行移入,解决列数增长后 IO 不够的问题。门店招牌、公交路牌常见的 16×16 汉字单元,就是四块 8×8 拼成,再按 1/16 占空比逐行扫描;户外大屏则是把这种单元继续拼接,加上逐点亮度校正。无论屏多大,驱动逻辑都不超出本节这张行列表。
同一套行列结构反过来用于输入,就是键盘矩阵:
| 数码管动态扫描 | LED 点阵 | 键盘矩阵 | |
|---|---|---|---|
| 行/位侧 | 位选译码,逐位选通 | 逐行选通 | 逐行输出有效电平 |
| 列/段侧 | 放该位段码 | 放该行图案 | 做输入,读列电平 |
| 省 IO 的结果 | 8+D 驱动 D 位 | K+L 驱动 K×L 灯 | K+L 扫描 K×L 键 |
| 特有处理 | 消隐防鬼影 | PWM 调灰度 | 按键防抖 |
扫描时某行被拉有效,若读回某一列也有效,就定位到「该行该列」的键被按下;配合防抖逻辑消除机械簧片的抖动,4×4 键盘只需 8 根 IO 就能读出 16 个键。家用电器、工业面板的键盘几乎都是这样做的。键盘矩阵有一个专属陷阱:同时按下三个角点的键时,电流会经由另外两个键绕出一条旁路,让本来没按的键也被读成按下,叫「重键鬼影」。计算器键盘在每个键上串一只二极管截断反向旁路,就能支持多键同时识别;游戏键盘宣传的「全键无冲」本质上也是在矩阵里加二极管并改用更复杂的扫描编码。至此可以看到一条贯穿的主线:凡是「控制点少于被控对象」的场合,就把对象排成矩阵,用时间分片轮流访问。
这一篇在知识体系里的位置
本篇属于入门层数字逻辑的外设应用收束篇,回答「译码器、计数器、分频器凑在一起能做成什么」。向原理读,底层机制在解复用与译码器应用和分频与时钟使能;向配套逻辑读,段码查表依赖数据通路与组合逻辑和码制转换器/七段译码,键盘部分依赖脉冲整形与防抖;向硬件约束读,限流与扩流的依据在芯片引脚与 IO。理解动态扫描后,再看真实世界的显示屏、点阵屏和矩阵键盘,都是这一张图的变体;下一次拆开任何带数字显示的小电器,都可以试着找出它的段线、位选管和驱动芯片,把实物与本文的时序一一对照。
总结
数码管动态扫描把每位同名段并联成 8 根共享段线、公共端各接位选驱动管,用「段码广播 + 译码轮流选通一位」的时分复用把 IO 从 8×D 降到 8+D;段码由字形表查出,共阴点亮段为 1、共阳按位取反,混用必乱且每段须限流;位号由分频节拍驱动计数器循环,每位亮度正比于占空比 1/D,每位刷新百 Hz 以上、整屏约 1kHz 即靠视觉暂留常亮;切换必须「先消隐、再换段码、后开下一位」否则旧码串到新位形成鬼影,位选管要扛 8 段同时电流故常外接驱动管或专用驱动 IC;LED 点阵是二维行列扫描、键盘矩阵是「行驱动、列读回」的同源结构,配合防抖即可用少量 IO 驱动大量灯或按键。