4 位数码管显示控制器设计
数码管动态扫描显示
- 数码管是最直观的数字显示设备, 几乎每个嵌入式开发板上都有.
- 本实验我们设计一个 4 位数码管控制器, CPU 只需要写入要显示的数值, 硬件自动完成扫描显示.
七段数码管原理
数码管长什么样?
──a──
│ │
f b
│ │
──g──
│ │
e c
│ │
──d── ● dp
7 个笔段 (a-g) 加 1 个小数点 (dp), 通过组合可以显示 0-9 和 A-F.
共阴极 vs 共阳极
| 类型 | 公共端 | 点亮方式 |
|---|---|---|
| 共阴极 | 接地 GND | 笔段输出高电平点亮 |
| 共阳极 | 接电源 VCC | 笔段输出低电平点亮 |
本实验假设使用共阴极数码管.
字形编码表
共阴极数码管的字形编码表如下所示。举个简单的例子,如果我们要显示数字 "5", 就需要点亮 a, c, d, f, g 五个笔段:
| 数字 | g | f | e | d | c | b | a | 十六进制 |
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0x3F |
| 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0x06 |
| 2 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0x5B |
| 3 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0x4F |
| 4 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0x66 |
| 5 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 0x6D |
| 6 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0x7D |
| 7 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0x07 |
| 8 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0x7F |
| 9 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0x6F |
| A | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0x77 |
| b | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0x7C |
| C | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0x39 |
| d | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0x5E |
| E | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0x79 |
| F | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0x71 |
动态扫描原理
为什么不能静态显示?
如果 4 位数码管都用静态显示:
- 每位需要 8 根线 (7 段 + 1 小数点)
- 4 位就需要 32 根线!
- IO 资源浪费太严重
动态扫描的思路
利用人眼的"视觉暂留"效应:
- 第 1ms: 只点亮第 1 位, 显示它的内容
- 第 2ms: 只点亮第 2 位, 显示它的内容
- 第 3ms: 只点亮第 3 位, 显示它的内容
- 第 4ms: 只点亮第 4 位, 显示它的内容
- 循环往复...
只要扫描够快 (>50Hz), 人眼就感觉 4 位同时亮着!
时间轴 ──────────────────────────────────────►
位选: DIG0 DIG1 DIG2 DIG3 DIG0 ...
┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐
│ │ │ │ │ │ │ │ │ │
──┘ └────┘ └────┘ └────┘ └────┘ └──
段选: 显示0 显示1 显示2 显示3 显示0 ...
├─2ms─┤
├──────────── 8ms 一个周期 ────────────┤
硬件模块设计
数码管硬件代码组成
AHBlite_Segdisp.v
- 功能: 数码管控制器的 AHB 接口包装器,负责接收总线上写入的数据并传给显示核心。
Segdisp.v
- 功能: 数码管动态扫描控制器。
- 子模块:
clk_division.v: 分频产生扫描时钟 (约 1ms)。counter.v: 位选循环计数器。Mux.v: 多路复用器,根据当前位选择显示数据。seg_sel_decoder.v: 位选译码器。seg_led_decoder.v: 段选译码器(7 段编码和小数点)。
必读通知
在数码管显示设计部分,我们同样延续前面实验的练习方式,请动手补全以下 Verilog 文件:
clk_division.v- 任务: 实现扫描时钟分频逻辑。
- 说明: 定义计数器与输出寄存器,按参数
DIVCLK_CNTMAX(24_999)分频,产生约 1 ms 的扫描时钟。
Segdisp.v- 任务: 实例化关键子模块。
- 说明: 在提示位置实例化
clk_division与counter,并确保扫描位选、数据移位等路径连通。
- 说明: 在提示位置实例化
- 任务: 实例化关键子模块。
seg_led_decoder.v- 任务: 完成 4-bit 到 7 段 + dp 的译码表。
- 说明: 根据本章节中的字形编码表,补齐
case语句的所有分支,default 建议输出8'h00防止乱码。
仿真与调试
由于本节没有配套的自动化 Testbench 激励文件,建议同学亲自编写简单的激励波形,尝试在仿真环境里对 Segdisp 模块进行功能验证,有助于理解动态扫描的节奏与时序细节。
仿真要点
- 观察 dig_sel 是否在 0-1-2-3 之间循环
- 观察 dig_out 是否正确输出位选信号
- 观察 seg_out 是否正确输出对应的字形码
- 测量扫描周期是否约为 8ms (4 位 × 2ms)
在实际下板测试时:
- 如果数码管闪烁, 说明扫描频率太低
- 如果某一位特别亮, 说明那一位的点亮时间比其他位长
- 如果显示乱码, 检查字形编码是否正确
思考题
- 如何实现数码管亮度调节? (提示: PWM)
- 如果要显示 "HELP" 这样的字母, 译码表需要怎么扩展?
- 扫描频率设为多少比较合适? 太高会有什么问题?