10.1 数码管是怎么显示数字的
一个数码管就是 8 个 LED 排成「日」字形:a~g 七段 + dp 小数点。
点亮不同的段,就能拼出不同的数字 —— 本质和你在数电课学的「译码器」是一回事。
10.2 段码怎么来的
把 8 个段看成一个字节的 8 位:
位: dp g f e d c b a
7 6 5 4 3 2 1 0
要显示「0」,需要 a b c d e f 亮、g 灭:
a b c d e f 亮 → 0011 1111 → 0x3F
要显示「1」,只需要 b c 亮:
b c 亮 → 0000 0110 → 0x06
普中代码里的段码表就是这么一个数组(共阴):
unsigned char code gsmg_code[16] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71
}; // 对应 0 1 2 3 4 5 6 7 8 9 A B C D E F
注意 code 关键字:加了它,数组放到 ROM(Flash)里,不占宝贵的 RAM。不加的话 16 个字节就吃掉 512B 里的 16B。
10.3 静态显示:一位数码管
#include "reg52.h"
#define SMG_A_DP_PORT P0 // 段码口接 P0
unsigned char code gsmg_code[16] = {
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71
};
sbit LSA = P2^2; // 位选,74HC138 的三根地址线
sbit LSB = P2^3;
sbit LSC = P2^4;
void main()
{
LSA = 0; LSB = 0; LSC = 0; // 选中第 1 位
SMG_A_DP_PORT = gsmg_code[0];// 显示 "0"
while (1);
}
逐行拆解
| 代码 | 作用 |
#define SMG_A_DP_PORT P0 | 以后写 SMG_A_DP_PORT 就是写 P0,方便移植 |
code | 把数组放 Flash,省 RAM |
LSA/LSB/LSC = 0 | 74HC138 输入 000 → 输出 Y0 有效 → 第 1 位数码管通电 |
P0 = 0x3F | 8 个段输出对应电平 → 显示「0」 |
10.4 为什么用 74HC138 而不是直接 8 根线
8 位数码管如果每位一根使能线,要 8 根 I/O。用 74HC138(3-8 译码器)只需要 3 根:
| LSC LSB LSA | 选中 | LSC LSB LSA | 选中 |
| 0 0 0 | 第 1 位 | 1 0 0 | 第 5 位 |
| 0 0 1 | 第 2 位 | 1 0 1 | 第 6 位 |
| 0 1 0 | 第 3 位 | 1 1 0 | 第 7 位 |
| 0 1 1 | 第 4 位 | 1 1 1 | 第 8 位 |
这就是你数电课学的译码器:3 根输入线 → 8 根输出线里只有一根有效。省了 5 根 I/O,代价是「同一时刻只能亮一位」—— 于是就有了下一章的动态扫描。
10.5 易错点
① 段码表是共阴还是共阳搞错 —— 共阳要取反(0x3F → 0xC0)。现象是「该亮的灭、该灭的亮」。
② 忘了选位 —— LSA/LSB/LSC 不设,可能选中了别的位,你看到的是别的数码管在亮。
③ 数组没加 code —— 编译报 data space memory overlap 或 RAM 不够。
10.6 练习
- 让第 1 位数码管显示 8。
- 让第 8 位数码管显示 F。
- 轮流显示 0~9,每秒变一次。
- 算出显示「7」的段码(只亮 a b c)。
📖 参考答案(点击展开)
- 第 1 位:LSA=LSB=LSC=0;段码
0x7F(全亮)。 - 第 8 位:
LSC=1; LSB=1; LSA=1;;段码 F = 0x71。 for(i=0;i<10;i++){ P0=gsmg_code[i]; delay_ms(1000); }- 只亮 a b c → 位 0,1,2 置 1 →
0b00000111 = 0x07。