第六章 动态数码管显示
1. 导入
在第五章中,我们学习了静态数码管显示,即每个数码管的段选线独立控制,优点是显示稳定、程序简单,但缺点是占用I/O资源多(每位需8个引脚)。当需要显示多位数字时,I/O口资源迅速耗尽。
本章将介绍动态数码管显示 技术,通过"扫描方式 "实现多位数码管的显示,显著减少I/O占用。其核心思想是:利用人眼视觉惰性,快速轮流点亮各个数码管,形成"同时显示"的错觉。
本章目标:
- 理解动态扫描原理;
- 掌握位选与段选控制方法;
- 实现多位数码管的数字显示;
- 解决重影、亮度不均等问题;
- 为后续实现电子钟、计数器等应用打下基础。
2. 硬件设计
2.1 动态数码管结构
多位数码管(如4位)内部由多个7段数码管组成,其特点是:
- 段选线并联:所有数码管的a、b、c、d、e、f、g、dp分别连在一起;
- 位选线独立:每个数码管的公共端(COM)单独引出,用于选择哪一位显示。
常见封装为12脚或16脚,如4位数码管通常为12脚:8根段选线 + 4根位选线。
2.2 数码管类型
仍分为:
- 共阴极:位选线接GND使该位导通,段选线输出高电平点亮段;
- 共阳极:位选线接VCC使该位导通,段选线输出低电平点亮段。
本章以共阴极4位数码管为例。
2.3 电路连接
| 功能 | 连接方式 |
|---|---|
| 段选线(a~g, dp) | 接P0口(P0.0 ~ P0.7) |
| 位选线(D1~D4) | 接P2.0 ~ P2.3 |
| P0口上拉电阻 | 外接10kΩ排阻(因P0为开漏) |
说明:
- 段选线控制显示内容;
- 位选线控制哪一位被点亮;
- 某一位被选中时,其COM端接地(通过三极管或直接I/O控制)。
位选驱动增强(可选)
若I/O驱动能力不足,可在位选线使用NPN三极管(如S8550)进行电流放大:
- P2.0 → 三极管基极(通过1kΩ电阻)
- 发射极 → VCC
- 集电极 → 数码管COM端
此时为高电平有效位选。
3. 软件设计
3.1 段码表定义
复用第五章的共阴极段码表:
c
#include <reg52.h>
unsigned char code seg_code[] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
3.2 位选控制
定义位选控制宏(共阴极,低电平有效):
c
#define DIGIT1 P2 = P2 & 0xFE | 0x0E // P2.0=0, P2.1~3=1 → 选中第1位
#define DIGIT2 P2 = P2 & 0xFD | 0x0D // P2.1=0
#define DIGIT3 P2 = P2 & 0xFB | 0x0B // P2.2=0
#define DIGIT4 P2 = P2 & 0xF7 | 0x07 // P2.3=0
更简洁方式:直接赋值
c
void select_digit(unsigned char n) {
switch(n) {
case 1: P2 = (P2 & 0xF0) | 0x0E; break;
case 2: P2 = (P2 & 0xF0) | 0x0D; break;
case 3: P2 = (P2 & 0xF0) | 0x0B; break;
case 4: P2 = (P2 & 0xF0) | 0x07; break;
default: break;
}
}
实际常用:P2.03 控制位选,P2.47 保留,故先清低4位再设置。
3.3 基本显示流程
动态扫描步骤:
- 关闭所有位选(可选);
- 输出当前位的段码;
- 选中该位;
- 延时1~5ms;
- 关闭该位;
- 切换到下一位,循环。
c
void display(unsigned char *num, unsigned char len) {
unsigned char i;
for (i = 0; i < len; i++) {
P0 = 0x00; // 消隐,防止重影
P2 = (P2 & 0xF0) | 0x0F; // 关闭所有位选(P2.0~3=1)
P0 = seg_code[num[i]]; // 输出第i位的段码
P2 = (P2 & 0xF0) | (0x0F & ~(1 << i)); // 选中第i位(低电平)
delay_ms(1); // 显示1ms
}
}
说明:
~(1 << i)生成第i位为0,其余为1。
3.4 显示多位数字(示例:显示1234)
c
unsigned char show[] = {1, 2, 3, 4}; // 显示内容
void main() {
while(1) {
display(show, 4);
}
}
注意:
display函数需在主循环中持续调用,扫描频率建议 ≥ 50Hz,避免闪烁。
3.5 消除重影(Ghosting)
重影原因:前一位未完全关闭时,后一位已开启,导致两位列同时亮。
解决方法:
- 消隐法:在切换位前,先将段选线清零;
- 快速扫描:提高扫描频率;
- 关闭位选:切换时短暂关闭所有位选。
改进版扫描函数:
c
void display(unsigned char *num, unsigned char len) {
unsigned char i;
for (i = 0; i < len; i++) {
P0 = 0x00; // 消隐
P2 = (P2 & 0xF0) | 0x0F; // 关闭所有位选
P0 = seg_code[num[i]]; // 设置段码
P2 = (P2 & 0xF0) | (0x0F & ~(1 << i)); // 选中第i位
delay_ms(1);
}
}
3.6 显示小数点
若需在第2位显示小数点(如12.34),则:
c
P0 = seg_code[num[i]] | 0x80; // 仅在第2位加小数点
可在display函数中判断位数:
c
if (i == 1) { // 第2位(索引1)
P0 = seg_code[num[i]] | 0x80;
} else {
P0 = seg_code[num[i]];
}
3.7 使用定时器中断扫描(预告)
当前方法在主循环中扫描,若主程序有复杂逻辑,可能导致显示不稳定。后续可使用定时器中断:
- 定时器每1ms中断一次;
- 在中断中切换数码管位;
- 实现非阻塞、高稳定性显示。
留待"定时器"章节深入。
3.8 编译与下载
- Keil中创建工程,写入代码;
- 确保段选与位选连接正确;
- 编译生成HEX;
- 下载至单片机;
- 观察是否显示1234,无闪烁、无重影。
若显示错乱:
- 检查位选逻辑是否正确;
- 确认段码表适用于共阴极;
- 增加消隐步骤。
4. 小结
本章通过实现动态数码管显示,掌握了多位数字显示的核心技术,主要内容包括:
- 硬件连接:理解段选与位选结构,正确连接共阴极数码管;
- 扫描原理:利用视觉惰性实现"多路复用";
- 软件实现:编写扫描函数,支持多位显示、小数点控制;
- 问题解决:掌握消隐、防重影、亮度均衡等技巧;
- 扩展能力:为实现计时器、频率计等应用奠定基础。
4.1 常见问题与解决
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 显示闪烁 | 扫描频率太低 | 增加扫描次数或减少延时 |
| 重影 | 未消隐或切换太快 | 加入消隐步骤,短暂关闭所有位 |
| 某位不亮 | 位选线未导通 | 检查P2口输出与三极管驱动 |
| 亮度不均 | 各位显示时间不同 | 保证每位延时一致 |
| 显示乱码 | 段码表错误 | 确认数码管类型并重新编码 |
4.2 下一步学习建议
- 使用定时器中断实现动态扫描,解放主程序;
- 实现电子钟功能,显示时分秒;
- 引入按键调节时间;
- 学习矩阵键盘,扩展输入方式。
本章标志着你已掌握高效的多位显示技术,下一章将进入定时器/计数器模块的学习,实现精准时间控制与非阻塞操作。